v4.6.0
authorHailoRT-Automation <concat@hailo.ai>
Tue, 29 Mar 2022 16:08:05 +0000 (19:08 +0300)
committerHailoRT-Automation <concat@hailo.ai>
Tue, 29 Mar 2022 16:08:05 +0000 (19:08 +0300)
370 files changed:
.logo.svg [new file with mode: 0644]
CMakeLists.txt [new file with mode: 0644]
README.md [new file with mode: 0644]
common/config_definitions.json [new file with mode: 0644]
common/config_schema.json [new file with mode: 0644]
common/include/byte_order.h [new file with mode: 0644]
common/include/context_switch_defs.h [new file with mode: 0644]
common/include/control_protocol.h [new file with mode: 0644]
common/include/d2h_events.h [new file with mode: 0644]
common/include/firmware_header.h [new file with mode: 0644]
common/include/firmware_header_utils.h [new file with mode: 0644]
common/include/firmware_status.h [new file with mode: 0644]
common/include/firmware_version.h [new file with mode: 0644]
common/include/logger_level.h [new file with mode: 0644]
common/include/md5.h [new file with mode: 0644]
common/include/sensor_config_exports.h [new file with mode: 0644]
common/include/status.h [new file with mode: 0644]
common/include/stdfloat.h [new file with mode: 0644]
common/include/user_config_common.h [new file with mode: 0644]
common/include/utils.h [new file with mode: 0644]
common/src/firmware_header_utils.c [new file with mode: 0644]
common/src/firmware_status.c [new file with mode: 0644]
common/src/md5.c [new file with mode: 0644]
hailort/CMakeLists.txt [new file with mode: 0644]
hailort/LICENSE [new file with mode: 0644]
hailort/LICENSE-3RD-PARTY.md [new file with mode: 0644]
hailort/common/CMakeLists.txt [new file with mode: 0644]
hailort/common/async_thread.hpp [new file with mode: 0644]
hailort/common/barrier.cpp [new file with mode: 0644]
hailort/common/barrier.hpp [new file with mode: 0644]
hailort/common/circular_buffer.hpp [new file with mode: 0644]
hailort/common/compiler_extensions_compat.hpp [new file with mode: 0644]
hailort/common/ethernet_utils.hpp [new file with mode: 0644]
hailort/common/file_utils.cpp [new file with mode: 0644]
hailort/common/file_utils.hpp [new file with mode: 0644]
hailort/common/filesystem.hpp [new file with mode: 0644]
hailort/common/latency_meter.hpp [new file with mode: 0644]
hailort/common/logger_macros.hpp [new file with mode: 0644]
hailort/common/os/posix/ethernet_utils.cpp [new file with mode: 0644]
hailort/common/os/posix/filesystem.cpp [new file with mode: 0644]
hailort/common/os/posix/process.cpp [new file with mode: 0644]
hailort/common/os/posix/socket.cpp [new file with mode: 0644]
hailort/common/os/posix/traffic_control.cpp [new file with mode: 0644]
hailort/common/os/posix/traffic_control.hpp [new file with mode: 0644]
hailort/common/os/windows/ethernet_utils.cpp [new file with mode: 0644]
hailort/common/os/windows/filesystem.cpp [new file with mode: 0644]
hailort/common/os/windows/process.cpp [new file with mode: 0644]
hailort/common/os/windows/socket.cpp [new file with mode: 0644]
hailort/common/os/windows/string_conversion.cpp [new file with mode: 0644]
hailort/common/os/windows/string_conversion.hpp [new file with mode: 0644]
hailort/common/process.hpp [new file with mode: 0644]
hailort/common/runtime_statistics_internal.hpp [new file with mode: 0644]
hailort/common/socket.hpp [new file with mode: 0644]
hailort/common/string_utils.cpp [new file with mode: 0644]
hailort/common/string_utils.hpp [new file with mode: 0644]
hailort/common/utils.hpp [new file with mode: 0644]
hailort/drivers/common/hailo_ioctl_common.h [new file with mode: 0644]
hailort/hailortcli/CMakeLists.txt [new file with mode: 0644]
hailort/hailortcli/benchmark_command.cpp [new file with mode: 0644]
hailort/hailortcli/benchmark_command.hpp [new file with mode: 0644]
hailort/hailortcli/board_config_command.cpp [new file with mode: 0644]
hailort/hailortcli/board_config_command.hpp [new file with mode: 0644]
hailort/hailortcli/command.cpp [new file with mode: 0644]
hailort/hailortcli/command.hpp [new file with mode: 0644]
hailort/hailortcli/common.cpp [new file with mode: 0644]
hailort/hailortcli/common.hpp [new file with mode: 0644]
hailort/hailortcli/download_action_list_command.cpp [new file with mode: 0644]
hailort/hailortcli/download_action_list_command.hpp [new file with mode: 0644]
hailort/hailortcli/fw_config_command.cpp [new file with mode: 0644]
hailort/hailortcli/fw_config_command.hpp [new file with mode: 0644]
hailort/hailortcli/fw_config_serializer.cpp [new file with mode: 0644]
hailort/hailortcli/fw_config_serializer.hpp [new file with mode: 0644]
hailort/hailortcli/fw_control_command.cpp [new file with mode: 0644]
hailort/hailortcli/fw_control_command.hpp [new file with mode: 0644]
hailort/hailortcli/fw_logger_command.cpp [new file with mode: 0644]
hailort/hailortcli/fw_logger_command.hpp [new file with mode: 0644]
hailort/hailortcli/fw_update_command.cpp [new file with mode: 0644]
hailort/hailortcli/fw_update_command.hpp [new file with mode: 0644]
hailort/hailortcli/generate_definitions_json_str.in [new file with mode: 0644]
hailort/hailortcli/graph_printer.cpp [new file with mode: 0644]
hailort/hailortcli/graph_printer.hpp [new file with mode: 0644]
hailort/hailortcli/hailortcli.cpp [new file with mode: 0644]
hailort/hailortcli/hailortcli.hpp [new file with mode: 0644]
hailort/hailortcli/infer_stats_printer.cpp [new file with mode: 0644]
hailort/hailortcli/infer_stats_printer.hpp [new file with mode: 0644]
hailort/hailortcli/inference_progress.cpp [new file with mode: 0644]
hailort/hailortcli/inference_progress.hpp [new file with mode: 0644]
hailort/hailortcli/inference_result.hpp [new file with mode: 0644]
hailort/hailortcli/parse_hef_command.cpp [new file with mode: 0644]
hailort/hailortcli/parse_hef_command.hpp [new file with mode: 0644]
hailort/hailortcli/power_measurement_command.cpp [new file with mode: 0644]
hailort/hailortcli/power_measurement_command.hpp [new file with mode: 0644]
hailort/hailortcli/run_command.cpp [new file with mode: 0644]
hailort/hailortcli/run_command.hpp [new file with mode: 0644]
hailort/hailortcli/scan_command.cpp [new file with mode: 0644]
hailort/hailortcli/scan_command.hpp [new file with mode: 0644]
hailort/hailortcli/sensor_config_command.cpp [new file with mode: 0644]
hailort/hailortcli/sensor_config_command.hpp [new file with mode: 0644]
hailort/hailortcli/ssb_update_command.cpp [new file with mode: 0644]
hailort/hailortcli/ssb_update_command.hpp [new file with mode: 0644]
hailort/hailortcli/temp_measurement.cpp [new file with mode: 0644]
hailort/hailortcli/temp_measurement.hpp [new file with mode: 0644]
hailort/hailortcli/udp_rate_limiter_command.cpp [new file with mode: 0644]
hailort/hailortcli/udp_rate_limiter_command.hpp [new file with mode: 0644]
hailort/libhailort/CMakeLists.txt [new file with mode: 0644]
hailort/libhailort/bindings/CMakeLists.txt [new file with mode: 0644]
hailort/libhailort/bindings/gstreamer/AUTHORS [new file with mode: 0644]
hailort/libhailort/bindings/gstreamer/CMakeLists.txt [new file with mode: 0644]
hailort/libhailort/bindings/gstreamer/COPYING [new file with mode: 0644]
hailort/libhailort/bindings/gstreamer/LICENSE [new file with mode: 0644]
hailort/libhailort/bindings/gstreamer/README [new file with mode: 0644]
hailort/libhailort/bindings/gstreamer/cmake/FindHailoRT.cmake [new file with mode: 0644]
hailort/libhailort/bindings/gstreamer/gst-hailo/common.cpp [new file with mode: 0644]
hailort/libhailort/bindings/gstreamer/gst-hailo/common.hpp [new file with mode: 0644]
hailort/libhailort/bindings/gstreamer/gst-hailo/gsthailodevicestats.cpp [new file with mode: 0644]
hailort/libhailort/bindings/gstreamer/gst-hailo/gsthailodevicestats.hpp [new file with mode: 0644]
hailort/libhailort/bindings/gstreamer/gst-hailo/gsthailonet.cpp [new file with mode: 0644]
hailort/libhailort/bindings/gstreamer/gst-hailo/gsthailonet.hpp [new file with mode: 0644]
hailort/libhailort/bindings/gstreamer/gst-hailo/gsthailoplugin.cpp [new file with mode: 0644]
hailort/libhailort/bindings/gstreamer/gst-hailo/gsthailorecv.cpp [new file with mode: 0644]
hailort/libhailort/bindings/gstreamer/gst-hailo/gsthailorecv.hpp [new file with mode: 0644]
hailort/libhailort/bindings/gstreamer/gst-hailo/gsthailosend.cpp [new file with mode: 0644]
hailort/libhailort/bindings/gstreamer/gst-hailo/gsthailosend.hpp [new file with mode: 0644]
hailort/libhailort/bindings/gstreamer/gst-hailo/hailo_events/hailo_events.cpp [new file with mode: 0644]
hailort/libhailort/bindings/gstreamer/gst-hailo/hailo_events/hailo_events.hpp [new file with mode: 0644]
hailort/libhailort/bindings/gstreamer/gst-hailo/hailo_output_info.hpp [new file with mode: 0644]
hailort/libhailort/bindings/gstreamer/gst-hailo/metadata/hailo_buffer_flag_meta.cpp [new file with mode: 0644]
hailort/libhailort/bindings/gstreamer/gst-hailo/metadata/hailo_buffer_flag_meta.hpp [new file with mode: 0644]
hailort/libhailort/bindings/gstreamer/gst-hailo/metadata/tensor_meta.cpp [new file with mode: 0644]
hailort/libhailort/bindings/gstreamer/gst-hailo/metadata/tensor_meta.hpp [new file with mode: 0644]
hailort/libhailort/bindings/gstreamer/gst-hailo/network_group_handle.cpp [new file with mode: 0644]
hailort/libhailort/bindings/gstreamer/gst-hailo/network_group_handle.hpp [new file with mode: 0644]
hailort/libhailort/bindings/python/CMakeLists.txt [new file with mode: 0644]
hailort/libhailort/bindings/python/__init__.py [new file with mode: 0644]
hailort/libhailort/bindings/python/examples/hef_infer_pipeline_vstream.py [new file with mode: 0644]
hailort/libhailort/bindings/python/examples/hef_infer_virtual_stream.py [new file with mode: 0644]
hailort/libhailort/bindings/python/platform/hailo_platform/__init__.py [new file with mode: 0644]
hailort/libhailort/bindings/python/platform/hailo_platform/common/__init__.py [new file with mode: 0644]
hailort/libhailort/bindings/python/platform/hailo_platform/common/compatibility/__init__.py [new file with mode: 0644]
hailort/libhailort/bindings/python/platform/hailo_platform/common/logger/__init__.py [new file with mode: 0644]
hailort/libhailort/bindings/python/platform/hailo_platform/common/logger/logger.py [new file with mode: 0644]
hailort/libhailort/bindings/python/platform/hailo_platform/common/paths_manager/__init__.py [new file with mode: 0644]
hailort/libhailort/bindings/python/platform/hailo_platform/common/paths_manager/config.py [new file with mode: 0644]
hailort/libhailort/bindings/python/platform/hailo_platform/common/paths_manager/paths.py [new file with mode: 0644]
hailort/libhailort/bindings/python/platform/hailo_platform/common/paths_manager/version.py [new file with mode: 0644]
hailort/libhailort/bindings/python/platform/hailo_platform/common/targets/__init__.py [new file with mode: 0644]
hailort/libhailort/bindings/python/platform/hailo_platform/common/targets/inference_targets.py [new file with mode: 0644]
hailort/libhailort/bindings/python/platform/hailo_platform/common/tools/__init__.py [new file with mode: 0644]
hailort/libhailort/bindings/python/platform/hailo_platform/common/tools/cmd_utils/__init__.py [new file with mode: 0644]
hailort/libhailort/bindings/python/platform/hailo_platform/common/tools/cmd_utils/base_utils.py [new file with mode: 0644]
hailort/libhailort/bindings/python/platform/hailo_platform/common/tools/cmd_utils/hailo_device_utils.py [new file with mode: 0644]
hailort/libhailort/bindings/python/platform/hailo_platform/config_definitions.json [new file with mode: 0644]
hailort/libhailort/bindings/python/platform/hailo_platform/drivers/__init__.py [new file with mode: 0644]
hailort/libhailort/bindings/python/platform/hailo_platform/drivers/control_object.py [new file with mode: 0644]
hailort/libhailort/bindings/python/platform/hailo_platform/drivers/ethernet_utils.py [new file with mode: 0644]
hailort/libhailort/bindings/python/platform/hailo_platform/drivers/hailo_controller/__init__.py [new file with mode: 0644]
hailort/libhailort/bindings/python/platform/hailo_platform/drivers/hailo_controller/hailo_control_protocol.py [new file with mode: 0644]
hailort/libhailort/bindings/python/platform/hailo_platform/drivers/hailo_controller/i2c_slaves.py [new file with mode: 0644]
hailort/libhailort/bindings/python/platform/hailo_platform/drivers/hailo_controller/power_measurement.py [new file with mode: 0644]
hailort/libhailort/bindings/python/platform/hailo_platform/drivers/hailort/__init__.py [new file with mode: 0644]
hailort/libhailort/bindings/python/platform/hailo_platform/drivers/hailort/pyhailort.py [new file with mode: 0644]
hailort/libhailort/bindings/python/platform/hailo_platform/drivers/hw_object.py [new file with mode: 0644]
hailort/libhailort/bindings/python/platform/hailo_platform/paths_manager/__init__.py [new file with mode: 0644]
hailort/libhailort/bindings/python/platform/hailo_platform/paths_manager/paths.py [new file with mode: 0644]
hailort/libhailort/bindings/python/platform/hailo_platform/tools/__init__.py [new file with mode: 0644]
hailort/libhailort/bindings/python/platform/hailo_platform/tools/benchmark_command.py [new file with mode: 0644]
hailort/libhailort/bindings/python/platform/hailo_platform/tools/cmd_utils/__init__.py [new file with mode: 0644]
hailort/libhailort/bindings/python/platform/hailo_platform/tools/cmd_utils/main.py [new file with mode: 0644]
hailort/libhailort/bindings/python/platform/hailo_platform/tools/firmware/__init__.py [new file with mode: 0644]
hailort/libhailort/bindings/python/platform/hailo_platform/tools/firmware/configure_firmware.py [new file with mode: 0644]
hailort/libhailort/bindings/python/platform/hailo_platform/tools/firmware/example_config.json [new file with mode: 0644]
hailort/libhailort/bindings/python/platform/hailo_platform/tools/firmware/firmware_config.py [new file with mode: 0644]
hailort/libhailort/bindings/python/platform/hailo_platform/tools/firmware/sensor_config.py [new file with mode: 0644]
hailort/libhailort/bindings/python/platform/hailo_platform/tools/firmware/update_firmware.py [new file with mode: 0644]
hailort/libhailort/bindings/python/platform/hailo_platform/tools/firmware/update_second_stage.py [new file with mode: 0644]
hailort/libhailort/bindings/python/platform/hailo_platform/tools/fw_control.py [new file with mode: 0644]
hailort/libhailort/bindings/python/platform/hailo_platform/tools/infer_cli.py [new file with mode: 0644]
hailort/libhailort/bindings/python/platform/hailo_platform/tools/run_command.py [new file with mode: 0644]
hailort/libhailort/bindings/python/platform/hailo_platform/tools/udp_rate_limiter.py [new file with mode: 0644]
hailort/libhailort/bindings/python/platform/hailo_platform/tools/watchdog.py [new file with mode: 0644]
hailort/libhailort/bindings/python/platform/requirements.txt [new file with mode: 0644]
hailort/libhailort/bindings/python/platform/setup.py [new file with mode: 0644]
hailort/libhailort/bindings/python/platform/tutorials/notebooks/Inference Tutorial.ipynb [new file with mode: 0644]
hailort/libhailort/bindings/python/platform/tutorials/notebooks/Power Measurement Tutorial.ipynb [new file with mode: 0644]
hailort/libhailort/bindings/python/src/CMakeLists.txt [new file with mode: 0644]
hailort/libhailort/bindings/python/src/__init__.py [new file with mode: 0644]
hailort/libhailort/bindings/python/src/bindings_common.hpp [new file with mode: 0644]
hailort/libhailort/bindings/python/src/hef_api.hpp [new file with mode: 0644]
hailort/libhailort/bindings/python/src/internal/CMakeLists.txt [new file with mode: 0644]
hailort/libhailort/bindings/python/src/internal/control_api.cpp [new file with mode: 0644]
hailort/libhailort/bindings/python/src/internal/control_api.hpp [new file with mode: 0644]
hailort/libhailort/bindings/python/src/internal/pyhailort_internal.cpp [new file with mode: 0644]
hailort/libhailort/bindings/python/src/internal/pyhailort_internal.hpp [new file with mode: 0644]
hailort/libhailort/bindings/python/src/pyhailort.cpp [new file with mode: 0644]
hailort/libhailort/bindings/python/src/utils.hpp [new file with mode: 0644]
hailort/libhailort/bindings/python/src/vdevice_api.hpp [new file with mode: 0644]
hailort/libhailort/bindings/python/src/vstream_api.hpp [new file with mode: 0644]
hailort/libhailort/cmake/common_compiler_options.cmake [new file with mode: 0644]
hailort/libhailort/cmake/execute_cmake.cmake [new file with mode: 0644]
hailort/libhailort/cmake/toolchains/linux.aarch64.cmake [new file with mode: 0644]
hailort/libhailort/cmake/toolchains/linux.android28-arm64-v8a.cmake [new file with mode: 0644]
hailort/libhailort/cmake/toolchains/linux.armv7l.cmake [new file with mode: 0644]
hailort/libhailort/cmake/toolchains/linux.armv7lhf.cmake [new file with mode: 0644]
hailort/libhailort/cmake/toolchains/linux.x86_64.cmake [new file with mode: 0644]
hailort/libhailort/cmake/toolchains/qnx.aarch64.cmake [new file with mode: 0644]
hailort/libhailort/cmake/toolchains/qnx.x86_64.cmake [new file with mode: 0644]
hailort/libhailort/cmake/toolchains/toolchains.yaml [new file with mode: 0644]
hailort/libhailort/cmake/toolchains/windows.x86_64.cmake [new file with mode: 0644]
hailort/libhailort/doc/CMakeLists.txt [new file with mode: 0644]
hailort/libhailort/doc/Doxyfile.in [new file with mode: 0644]
hailort/libhailort/examples/CMakeLists.txt [new file with mode: 0644]
hailort/libhailort/examples/README.md [new file with mode: 0644]
hailort/libhailort/examples/c/CMakeLists.txt [new file with mode: 0644]
hailort/libhailort/examples/c/common.h [new file with mode: 0644]
hailort/libhailort/examples/c/data_quantization_example.c [new file with mode: 0644]
hailort/libhailort/examples/c/hailo_thread.h [new file with mode: 0644]
hailort/libhailort/examples/c/infer_pipeline_example.c [new file with mode: 0644]
hailort/libhailort/examples/c/multi_device_example.c [new file with mode: 0644]
hailort/libhailort/examples/c/multi_network_vstream_example.c [new file with mode: 0644]
hailort/libhailort/examples/c/raw_streams_example.c [new file with mode: 0644]
hailort/libhailort/examples/c/switch_hefs_example.c [new file with mode: 0644]
hailort/libhailort/examples/c/switch_single_io_hefs_example.c [new file with mode: 0644]
hailort/libhailort/examples/c/vstreams_example.c [new file with mode: 0644]
hailort/libhailort/examples/cmake/FindHailoRT.cmake [new file with mode: 0644]
hailort/libhailort/examples/cpp/CMakeLists.txt [new file with mode: 0644]
hailort/libhailort/examples/cpp/infer_pipeline_example.cpp [new file with mode: 0644]
hailort/libhailort/examples/cpp/multi_device_example.cpp [new file with mode: 0644]
hailort/libhailort/examples/cpp/multi_network_vstream_example.cpp [new file with mode: 0644]
hailort/libhailort/examples/cpp/raw_streams_example.cpp [new file with mode: 0644]
hailort/libhailort/examples/cpp/switch_hefs_example.cpp [new file with mode: 0644]
hailort/libhailort/examples/cpp/switch_hefs_example_threads_reuse.cpp [new file with mode: 0644]
hailort/libhailort/examples/cpp/vstreams_example.cpp [new file with mode: 0644]
hailort/libhailort/hef.proto [new file with mode: 0644]
hailort/libhailort/include/hailo/buffer.hpp [new file with mode: 0644]
hailort/libhailort/include/hailo/device.hpp [new file with mode: 0644]
hailort/libhailort/include/hailo/event.hpp [new file with mode: 0644]
hailort/libhailort/include/hailo/expected.hpp [new file with mode: 0644]
hailort/libhailort/include/hailo/hailort.h [new file with mode: 0644]
hailort/libhailort/include/hailo/hailort.hpp [new file with mode: 0644]
hailort/libhailort/include/hailo/hailort_common.hpp [new file with mode: 0644]
hailort/libhailort/include/hailo/hef.hpp [new file with mode: 0644]
hailort/libhailort/include/hailo/inference_pipeline.hpp [new file with mode: 0644]
hailort/libhailort/include/hailo/network_group.hpp [new file with mode: 0644]
hailort/libhailort/include/hailo/network_rate_calculator.hpp [new file with mode: 0644]
hailort/libhailort/include/hailo/platform.h [new file with mode: 0644]
hailort/libhailort/include/hailo/quantization.hpp [new file with mode: 0644]
hailort/libhailort/include/hailo/runtime_statistics.hpp [new file with mode: 0644]
hailort/libhailort/include/hailo/stream.hpp [new file with mode: 0644]
hailort/libhailort/include/hailo/transform.hpp [new file with mode: 0644]
hailort/libhailort/include/hailo/vdevice.hpp [new file with mode: 0644]
hailort/libhailort/include/hailo/vstream.hpp [new file with mode: 0644]
hailort/libhailort/src/CMakeLists.txt [new file with mode: 0644]
hailort/libhailort/src/buffer.cpp [new file with mode: 0644]
hailort/libhailort/src/context_switch/active_network_group_holder.hpp [new file with mode: 0644]
hailort/libhailort/src/context_switch/config_manager.hpp [new file with mode: 0644]
hailort/libhailort/src/context_switch/hcp_config_activated_network_group.cpp [new file with mode: 0644]
hailort/libhailort/src/context_switch/hcp_config_manager.cpp [new file with mode: 0644]
hailort/libhailort/src/context_switch/hcp_config_network_group.cpp [new file with mode: 0644]
hailort/libhailort/src/context_switch/hef_metadata.cpp [new file with mode: 0644]
hailort/libhailort/src/context_switch/hef_metadata.hpp [new file with mode: 0644]
hailort/libhailort/src/context_switch/multi_context/resource_manager.hpp [new file with mode: 0644]
hailort/libhailort/src/context_switch/multi_context/vdma_config_activated_network_group.hpp [new file with mode: 0644]
hailort/libhailort/src/context_switch/multi_context/vdma_config_manager.hpp [new file with mode: 0644]
hailort/libhailort/src/context_switch/multi_context/vdma_config_network_group.hpp [new file with mode: 0644]
hailort/libhailort/src/context_switch/network_group.cpp [new file with mode: 0644]
hailort/libhailort/src/context_switch/network_group_internal.hpp [new file with mode: 0644]
hailort/libhailort/src/context_switch/resource_manager.cpp [new file with mode: 0644]
hailort/libhailort/src/context_switch/single_context/hcp_config_activated_network_group.hpp [new file with mode: 0644]
hailort/libhailort/src/context_switch/single_context/hcp_config_manager.hpp [new file with mode: 0644]
hailort/libhailort/src/context_switch/single_context/hcp_config_network_group.hpp [new file with mode: 0644]
hailort/libhailort/src/context_switch/vdma_config_activated_network_group.cpp [new file with mode: 0644]
hailort/libhailort/src/context_switch/vdma_config_manager.cpp [new file with mode: 0644]
hailort/libhailort/src/context_switch/vdma_config_network_group.cpp [new file with mode: 0644]
hailort/libhailort/src/control.cpp [new file with mode: 0644]
hailort/libhailort/src/control.hpp [new file with mode: 0644]
hailort/libhailort/src/control_protocol.cpp [new file with mode: 0644]
hailort/libhailort/src/control_protocol.hpp [new file with mode: 0644]
hailort/libhailort/src/core_device.cpp [new file with mode: 0644]
hailort/libhailort/src/core_device.hpp [new file with mode: 0644]
hailort/libhailort/src/core_stream.cpp [new file with mode: 0644]
hailort/libhailort/src/core_stream.hpp [new file with mode: 0644]
hailort/libhailort/src/d2h_event_queue.hpp [new file with mode: 0644]
hailort/libhailort/src/d2h_events_parser.cpp [new file with mode: 0644]
hailort/libhailort/src/device.cpp [new file with mode: 0644]
hailort/libhailort/src/device_internal.cpp [new file with mode: 0644]
hailort/libhailort/src/device_internal.hpp [new file with mode: 0644]
hailort/libhailort/src/eth_device.cpp [new file with mode: 0644]
hailort/libhailort/src/eth_device.hpp [new file with mode: 0644]
hailort/libhailort/src/eth_stream.cpp [new file with mode: 0644]
hailort/libhailort/src/eth_stream.hpp [new file with mode: 0644]
hailort/libhailort/src/event_internal.hpp [new file with mode: 0644]
hailort/libhailort/src/hailort.cpp [new file with mode: 0644]
hailort/libhailort/src/hailort_common.cpp [new file with mode: 0644]
hailort/libhailort/src/hailort_defaults.hpp [new file with mode: 0644]
hailort/libhailort/src/hailort_logger.cpp [new file with mode: 0644]
hailort/libhailort/src/hailort_logger.hpp [new file with mode: 0644]
hailort/libhailort/src/hef.cpp [new file with mode: 0644]
hailort/libhailort/src/hef_internal.hpp [new file with mode: 0644]
hailort/libhailort/src/hlpcie.cpp [new file with mode: 0644]
hailort/libhailort/src/hlpcie.hpp [new file with mode: 0644]
hailort/libhailort/src/hw_consts.hpp [new file with mode: 0644]
hailort/libhailort/src/inference_pipeline.cpp [new file with mode: 0644]
hailort/libhailort/src/intermediate_buffer.cpp [new file with mode: 0644]
hailort/libhailort/src/intermediate_buffer.hpp [new file with mode: 0644]
hailort/libhailort/src/layer_info.hpp [new file with mode: 0644]
hailort/libhailort/src/mipi_stream.cpp [new file with mode: 0644]
hailort/libhailort/src/mipi_stream.hpp [new file with mode: 0644]
hailort/libhailort/src/network_rate_calculator.cpp [new file with mode: 0644]
hailort/libhailort/src/os/CMakeLists.txt [new file with mode: 0644]
hailort/libhailort/src/os/file_descriptor.hpp [new file with mode: 0644]
hailort/libhailort/src/os/hailort_driver.hpp [new file with mode: 0755]
hailort/libhailort/src/os/microsec_timer.hpp [new file with mode: 0644]
hailort/libhailort/src/os/mmap_buffer.hpp [new file with mode: 0644]
hailort/libhailort/src/os/posix/file_descriptor.cpp [new file with mode: 0644]
hailort/libhailort/src/os/posix/hailort_driver.cpp [new file with mode: 0755]
hailort/libhailort/src/os/posix/microsec_timer.cpp [new file with mode: 0644]
hailort/libhailort/src/os/posix/mmap_buffer.cpp [new file with mode: 0644]
hailort/libhailort/src/os/posix/pcie_driver_sysfs.cpp [new file with mode: 0644]
hailort/libhailort/src/os/posix/pcie_driver_sysfs.hpp [new file with mode: 0644]
hailort/libhailort/src/os/posix/qnx/event.cpp [new file with mode: 0644]
hailort/libhailort/src/os/posix/unix/event.cpp [new file with mode: 0644]
hailort/libhailort/src/os/windows/event.cpp [new file with mode: 0644]
hailort/libhailort/src/os/windows/file_descriptor.cpp [new file with mode: 0644]
hailort/libhailort/src/os/windows/hailort_driver.cpp [new file with mode: 0644]
hailort/libhailort/src/os/windows/microsec_timer.cpp [new file with mode: 0644]
hailort/libhailort/src/os/windows/mmap_buffer.cpp [new file with mode: 0644]
hailort/libhailort/src/os/windows/osdep.hpp [new file with mode: 0644]
hailort/libhailort/src/pcie_device.cpp [new file with mode: 0644]
hailort/libhailort/src/pcie_device.hpp [new file with mode: 0644]
hailort/libhailort/src/pcie_stream.cpp [new file with mode: 0644]
hailort/libhailort/src/pcie_stream.hpp [new file with mode: 0644]
hailort/libhailort/src/pipeline.cpp [new file with mode: 0644]
hailort/libhailort/src/pipeline.hpp [new file with mode: 0644]
hailort/libhailort/src/sensor_config_utils.cpp [new file with mode: 0644]
hailort/libhailort/src/sensor_config_utils.hpp [new file with mode: 0644]
hailort/libhailort/src/stream.cpp [new file with mode: 0644]
hailort/libhailort/src/stream_internal.cpp [new file with mode: 0644]
hailort/libhailort/src/stream_internal.hpp [new file with mode: 0644]
hailort/libhailort/src/thread_safe_map.hpp [new file with mode: 0644]
hailort/libhailort/src/thread_safe_queue.hpp [new file with mode: 0644]
hailort/libhailort/src/token_bucket.hpp [new file with mode: 0644]
hailort/libhailort/src/transform.cpp [new file with mode: 0644]
hailort/libhailort/src/transform_internal.hpp [new file with mode: 0644]
hailort/libhailort/src/udp.cpp [new file with mode: 0644]
hailort/libhailort/src/udp.hpp [new file with mode: 0644]
hailort/libhailort/src/vdevice.cpp [new file with mode: 0644]
hailort/libhailort/src/vdevice_internal.hpp [new file with mode: 0644]
hailort/libhailort/src/vdevice_stream.cpp [new file with mode: 0644]
hailort/libhailort/src/vdevice_stream.hpp [new file with mode: 0644]
hailort/libhailort/src/vdma_buffer.cpp [new file with mode: 0644]
hailort/libhailort/src/vdma_buffer.hpp [new file with mode: 0644]
hailort/libhailort/src/vdma_channel.cpp [new file with mode: 0644]
hailort/libhailort/src/vdma_channel.hpp [new file with mode: 0644]
hailort/libhailort/src/vdma_channel_regs.hpp [new file with mode: 0644]
hailort/libhailort/src/vdma_descriptor_list.cpp [new file with mode: 0644]
hailort/libhailort/src/vdma_descriptor_list.hpp [new file with mode: 0644]
hailort/libhailort/src/vdma_device.cpp [new file with mode: 0644]
hailort/libhailort/src/vdma_device.hpp [new file with mode: 0644]
hailort/libhailort/src/vdma_stream.cpp [new file with mode: 0644]
hailort/libhailort/src/vdma_stream.hpp [new file with mode: 0644]
hailort/libhailort/src/vstream.cpp [new file with mode: 0644]
hailort/libhailort/src/vstream_internal.hpp [new file with mode: 0644]
hailort/pre_build/CMakeLists.txt [new file with mode: 0644]
hailort/pre_build/external/CMakeLists.txt [new file with mode: 0644]
hailort/pre_build/tools/CMakeLists.txt [new file with mode: 0644]
hailort/scripts/configure_ethernet_buffers.sh [new file with mode: 0755]
hailort/scripts/configure_interface.sh [new file with mode: 0755]
hailort/scripts/download_hefs.cmd [new file with mode: 0644]
hailort/scripts/download_hefs.sh [new file with mode: 0755]
hailort/scripts/ubuntu_ethernet_statistics.sh [new file with mode: 0755]

diff --git a/.logo.svg b/.logo.svg
new file mode 100644 (file)
index 0000000..62315a5
--- /dev/null
+++ b/.logo.svg
@@ -0,0 +1,212 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   id="svg4986"
+   version="1.1"
+   inkscape:version="0.91 r13725"
+   xml:space="preserve"
+   width="454.94739"
+   height="132.55138"
+   viewBox="0 0 454.94739 132.55138"
+   sodipodi:docname="Hailo_Logo_RGB.svg"><metadata
+     id="metadata4992"><rdf:RDF><cc:Work
+         rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
+     id="defs4990"><clipPath
+       clipPathUnits="userSpaceOnUse"
+       id="clipPath5014"><path
+         d="M 0,110 370,110 370,0 0,0 0,110 Z"
+         id="path5016"
+         inkscape:connector-curvature="0" /></clipPath></defs><sodipodi:namedview
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1"
+     objecttolerance="10"
+     gridtolerance="10"
+     guidetolerance="10"
+     inkscape:pageopacity="0"
+     inkscape:pageshadow="2"
+     inkscape:window-width="2048"
+     inkscape:window-height="1100"
+     id="namedview4988"
+     showgrid="false"
+     inkscape:zoom="2.9109866"
+     inkscape:cx="216.90465"
+     inkscape:cy="59.262279"
+     inkscape:window-x="0"
+     inkscape:window-y="0"
+     inkscape:window-maximized="1"
+     inkscape:current-layer="g4994"
+     fit-margin-top="0"
+     fit-margin-left="0"
+     fit-margin-right="0"
+     fit-margin-bottom="0" /><g
+     id="g4994"
+     inkscape:groupmode="layer"
+     inkscape:label="Hailo_Logo_RGB"
+     transform="matrix(1.25,0,0,-1.25,-5.127625,136.24475)"><g
+       id="g4996"
+       transform="translate(172.3149,50.2915)"><path
+         d="M 0,0 -14.161,0 -40.557,47.98 -40.674,48.181 -40.793,47.98 -67.189,0 -81.35,0 l 34.496,58.684 -0.011,0.02 0.023,0 12.334,0 0.023,0 -0.011,-0.02 L 0,0 Z"
+         style="fill:#0098c4;fill-opacity:1;fill-rule:nonzero;stroke:none"
+         id="path4998"
+         inkscape:connector-curvature="0" /></g><path
+       d="m 186.287,50.291 14.16,0 0,58.704 -14.16,0 0,-58.704 z"
+       style="fill:#0098c4;fill-opacity:1;fill-rule:nonzero;stroke:none"
+       id="path5000"
+       inkscape:connector-curvature="0" /><g
+       id="g5002"
+       transform="translate(4.2656,50.2915)"><path
+         d="m 0,0 14.298,0 0,24.747 44.268,0 0,-24.747 14.161,0 0,58.704 -14.161,0 0,-22.272 -44.268,0 0,22.272 L 0,58.704 0,0 Z"
+         style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
+         id="path5004"
+         inkscape:connector-curvature="0" /></g><g
+       id="g5006"
+       transform="translate(221.0957,50.2915)"><path
+         d="m 0,0 59.941,0 0,10.999 -45.643,0 0,47.705 L 0,58.704 0,0 Z"
+         style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
+         id="path5008"
+         inkscape:connector-curvature="0" /></g><g
+       id="g5010"><g
+         id="g5012"
+         clip-path="url(#clipPath5014)"><g
+           id="g5018"
+           transform="translate(368.06,64.1768)"><path
+             d="m 0,0 0,30.933 c 0,11.274 -3.712,13.886 -15.123,13.886 l -44.956,0 c -11.411,0 -15.123,-2.612 -15.123,-13.886 l 0,-30.933 c 0,-11.136 3.575,-13.885 15.123,-13.885 l 44.956,0 C -3.712,-13.885 0,-11.136 0,0 m -14.16,-2.612 -46.881,0 0,36.569 46.881,0 0,-36.569 z"
+             style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
+             id="path5020"
+             inkscape:connector-curvature="0" /></g><g
+           id="g5022"
+           transform="translate(15.4511,10.2974)"><path
+             d="m 0,0 0.106,-1.816 c -1.637,-0.106 -4.272,-0.16 -7.903,-0.16 -1.086,0 -1.945,0.289 -2.577,0.868 -0.632,0.578 -0.957,1.357 -0.975,2.337 l 0,12.068 c 0.018,0.979 0.343,1.758 0.975,2.337 0.632,0.578 1.491,0.867 2.577,0.867 3.631,0 6.266,-0.053 7.903,-0.16 L 0,14.499 l -7.396,0 c -1.069,0 -1.603,-0.587 -1.603,-1.762 l 0,-4.006 7.957,0 0,-1.896 -7.957,0 0,-5.046 C -8.999,0.596 -8.465,0 -7.396,0 L 0,0 Z"
+             style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
+             id="path5024"
+             inkscape:connector-curvature="0" /></g><g
+           id="g5026"
+           transform="translate(23.7817,21.3521)"><path
+             d="m 0,0 0.134,-2.056 c 1.833,1.584 3.604,2.376 5.313,2.376 1.816,0 2.911,-0.765 3.284,-2.296 1.763,1.531 3.507,2.296 5.234,2.296 1.121,0 1.98,-0.303 2.577,-0.907 0.596,-0.606 0.895,-1.496 0.895,-2.671 l 0,-9.746 -2.244,0 0,9.106 c -0.018,0.8 -0.182,1.383 -0.494,1.748 -0.311,0.365 -0.823,0.547 -1.535,0.547 -0.623,0 -1.224,-0.142 -1.802,-0.426 -0.579,-0.285 -1.402,-0.82 -2.47,-1.603 l 0,-9.372 -2.19,0 0,9.106 c 0,0.818 -0.164,1.405 -0.494,1.762 C 5.879,-1.78 5.367,-1.603 4.673,-1.603 4.05,-1.603 3.449,-1.745 2.871,-2.029 2.292,-2.314 1.469,-2.84 0.401,-3.605 l 0,-9.399 -2.27,0 L -1.869,0 0,0 Z"
+             style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
+             id="path5028"
+             inkscape:connector-curvature="0" /></g><g
+           id="g5030"
+           transform="translate(50.1899,17.6938)"><path
+             d="m 0,0 0,-6.889 c 1.815,-0.534 3.204,-0.801 4.166,-0.801 1.174,0 1.993,0.329 2.456,0.988 0.462,0.658 0.694,1.922 0.694,3.791 0,1.816 -0.218,3.089 -0.654,3.819 C 6.226,1.637 5.482,2.002 4.432,2.002 3.755,2.002 3.084,1.847 2.417,1.535 1.749,1.224 0.943,0.711 0,0 m -0.401,3.658 0.187,-2.083 c 0.694,0.748 1.531,1.335 2.51,1.762 0.979,0.428 1.94,0.642 2.884,0.642 1.549,0 2.679,-0.57 3.391,-1.709 0.712,-1.14 1.068,-2.867 1.068,-5.181 0,-2.456 -0.391,-4.196 -1.175,-5.22 -0.783,-1.024 -2.029,-1.535 -3.738,-1.535 -1.709,0 -3.311,0.445 -4.806,1.335 C -0.027,-8.937 0,-9.809 0,-10.948 l 0,-3.765 -2.27,0 0,18.371 1.869,0 z"
+             style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
+             id="path5032"
+             inkscape:connector-curvature="0" /></g><g
+           id="g5034"
+           transform="translate(74.581,18.7085)"><path
+             d="m 0,0 c -0.543,0.694 -1.526,1.041 -2.95,1.041 -1.425,0 -2.404,-0.347 -2.938,-1.041 -0.534,-0.694 -0.8,-1.985 -0.8,-3.872 0,-1.887 0.266,-3.177 0.8,-3.872 0.534,-0.694 1.513,-1.041 2.938,-1.041 1.424,0 2.407,0.347 2.95,1.041 0.543,0.695 0.814,1.985 0.814,3.872 C 0.814,-1.985 0.543,-0.694 0,0 m -2.95,2.964 c 2.242,0 3.822,-0.517 4.739,-1.549 0.917,-1.033 1.375,-2.795 1.375,-5.287 0,-2.492 -0.458,-4.254 -1.375,-5.287 -0.917,-1.032 -2.497,-1.549 -4.739,-1.549 -2.226,0 -3.801,0.517 -4.727,1.549 -0.926,1.033 -1.388,2.795 -1.388,5.287 0,2.492 0.462,4.254 1.388,5.287 0.926,1.032 2.501,1.549 4.727,1.549"
+             style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
+             id="path5036"
+             inkscape:connector-curvature="0" /></g><g
+           id="g5038"
+           transform="translate(103.165,21.3521)"><path
+             d="m 0,0 -3.792,-12.363 c -0.106,-0.428 -0.4,-0.641 -0.881,-0.641 l -2.056,0 c -0.213,0 -0.405,0.067 -0.574,0.2 -0.169,0.134 -0.271,0.307 -0.307,0.521 l -2.136,8.838 c -0.053,0.249 -0.125,0.601 -0.213,1.055 -0.09,0.454 -0.161,0.787 -0.214,1.001 l -0.321,0 -0.454,-2.056 -2.109,-8.838 c -0.125,-0.481 -0.427,-0.721 -0.908,-0.721 l -2.029,0 c -0.481,0 -0.775,0.213 -0.881,0.641 L -20.667,0 l 2.35,0 2.697,-9.586 c 0.16,-0.552 0.302,-1.202 0.427,-1.949 l 0.347,0 0.481,1.949 2.162,8.865 c 0.089,0.48 0.374,0.721 0.855,0.721 l 2.056,0 c 0.445,0 0.729,-0.25 0.854,-0.748 l 2.137,-8.838 c 0.035,-0.196 0.106,-0.512 0.213,-0.948 0.107,-0.436 0.187,-0.77 0.24,-1.001 l 0.348,0 c 0.017,0.089 0.075,0.369 0.173,0.841 0.098,0.471 0.191,0.841 0.281,1.108 L -2.35,0 0,0 Z"
+             style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
+             id="path5040"
+             inkscape:connector-curvature="0" /></g><g
+           id="g5042"
+           transform="translate(110.2675,15.5044)"><path
+             d="M 0,0 5.5,0 C 6.444,0 6.916,0.641 6.916,1.922 6.898,2.759 6.649,3.355 6.168,3.711 5.687,4.067 4.869,4.245 3.711,4.245 2.34,4.245 1.389,3.947 0.854,3.351 0.32,2.754 0.036,1.637 0,0 m 6.088,-1.763 -6.061,0 c 0.124,-1.495 0.489,-2.501 1.095,-3.017 0.605,-0.516 1.61,-0.774 3.017,-0.774 1.477,0 2.981,0.107 4.512,0.32 l 0.241,-1.522 c -1.158,-0.48 -2.84,-0.721 -5.047,-0.721 -2.243,0 -3.841,0.521 -4.793,1.562 -0.952,1.042 -1.428,2.808 -1.428,5.301 0,2.51 0.462,4.267 1.388,5.273 0.926,1.006 2.457,1.509 4.592,1.509 C 5.545,6.168 6.96,5.816 7.85,5.113 8.74,4.41 9.186,3.346 9.186,1.922 9.221,-0.534 8.188,-1.763 6.088,-1.763"
+             style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
+             id="path5044"
+             inkscape:connector-curvature="0" /></g><g
+           id="g5046"
+           transform="translate(133.9511,21.6724)"><path
+             d="m 0,0 -0.267,-2.136 -0.748,0 c -0.658,0 -1.339,-0.13 -2.042,-0.387 -0.704,-0.259 -1.642,-0.681 -2.817,-1.269 l 0,-9.532 -2.27,0 0,13.004 1.763,0 0.24,-2.056 C -4.13,-0.792 -2.279,0 -0.587,0 L 0,0 Z"
+             style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
+             id="path5048"
+             inkscape:connector-curvature="0" /></g><path
+           d="m 141.801,8.348 -2.243,0 0,13.004 2.243,0 0,-13.004 z m -1.656,18.531 1.069,0 c 0.462,0 0.694,-0.232 0.694,-0.694 l 0,-1.522 c 0,-0.463 -0.232,-0.694 -0.694,-0.694 l -1.069,0 c -0.462,0 -0.694,0.231 -0.694,0.694 l 0,1.522 c 0,0.462 0.232,0.694 0.694,0.694"
+           style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
+           id="path5050"
+           inkscape:connector-curvature="0" /><g
+           id="g5052"
+           transform="translate(150.6127,21.3521)"><path
+             d="m 0,0 0.16,-2.056 c 2.1,1.584 4.041,2.376 5.821,2.376 2.35,0 3.525,-1.193 3.525,-3.578 l 0,-9.746 -2.27,0 0,9.106 c 0,0.854 -0.143,1.45 -0.427,1.788 -0.285,0.339 -0.766,0.507 -1.442,0.507 -0.712,0 -1.429,-0.16 -2.15,-0.48 -0.72,-0.32 -1.677,-0.846 -2.87,-1.575 l 0,-9.346 -2.27,0 L -1.923,0 0,0 Z"
+             style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
+             id="path5054"
+             inkscape:connector-curvature="0" /></g><g
+           id="g5056"
+           transform="translate(169.25,15.6777)"><path
+             d="m 0,0 c 0.516,-0.365 1.415,-0.547 2.697,-0.547 1.282,0 2.18,0.182 2.697,0.547 0.516,0.365 0.774,0.983 0.774,1.856 0,0.872 -0.254,1.491 -0.761,1.855 C 4.899,4.076 3.996,4.259 2.697,4.259 1.415,4.259 0.516,4.076 0,3.711 -0.517,3.347 -0.774,2.728 -0.774,1.856 -0.774,0.983 -0.517,0.365 0,0 M 4.593,-6.715 0.16,-6.101 c -0.908,-0.694 -1.362,-1.523 -1.362,-2.483 0,-0.908 0.267,-1.504 0.801,-1.79 0.534,-0.284 1.611,-0.427 3.231,-0.427 1.585,0 2.648,0.147 3.191,0.441 0.543,0.294 0.814,0.885 0.814,1.776 0,0.64 -0.138,1.077 -0.413,1.308 -0.276,0.231 -0.886,0.418 -1.829,0.561 M 9.666,4.419 7.743,4.259 C 8.099,3.671 8.277,2.871 8.277,1.856 8.277,0.396 7.859,-0.659 7.022,-1.309 6.186,-1.958 4.744,-2.283 2.697,-2.283 1.7,-2.283 0.801,-2.194 0,-2.016 c -0.338,-0.41 -0.437,-0.859 -0.294,-1.348 0.143,-0.49 0.543,-0.788 1.202,-0.895 l 4.86,-0.774 c 1.21,-0.178 2.06,-0.557 2.549,-1.135 0.49,-0.579 0.735,-1.419 0.735,-2.523 0,-1.513 -0.45,-2.564 -1.349,-3.151 -0.899,-0.588 -2.514,-0.881 -4.846,-0.881 -2.35,0 -3.983,0.289 -4.9,0.868 -0.917,0.578 -1.375,1.606 -1.375,3.084 0,0.765 0.16,1.379 0.48,1.842 0.321,0.463 0.864,0.899 1.629,1.308 -0.658,0.517 -0.961,1.202 -0.907,2.057 0.053,0.854 0.391,1.522 1.014,2.002 -1.104,0.587 -1.655,1.718 -1.655,3.391 0,1.495 0.414,2.564 1.241,3.204 0.828,0.641 2.274,0.962 4.34,0.962 1.228,0 2.233,-0.107 3.017,-0.321 l 4.058,0 -0.133,-1.255 z"
+             style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
+             id="path5058"
+             inkscape:connector-curvature="0" /></g><path
+           d="m 201.456,8.348 -2.35,0 0,18.424 2.35,0 0,-18.424 z"
+           style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
+           id="path5060"
+           inkscape:connector-curvature="0" /><g
+           id="g5062"
+           transform="translate(210.4013,21.3521)"><path
+             d="m 0,0 0.16,-2.056 c 2.1,1.584 4.041,2.376 5.821,2.376 2.35,0 3.525,-1.193 3.525,-3.578 l 0,-9.746 -2.27,0 0,9.106 c 0,0.854 -0.143,1.45 -0.427,1.788 -0.285,0.339 -0.766,0.507 -1.442,0.507 -0.712,0 -1.429,-0.16 -2.15,-0.48 -0.72,-0.32 -1.677,-0.846 -2.87,-1.575 l 0,-9.346 -2.27,0 L -1.923,0 0,0 Z"
+             style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
+             id="path5064"
+             inkscape:connector-curvature="0" /></g><g
+           id="g5066"
+           transform="translate(230.08,19.563)"><path
+             d="M 0,0 0,-7.637 C 0,-8.26 0.133,-8.705 0.4,-8.972 0.667,-9.239 1.122,-9.373 1.762,-9.373 l 1.976,0 0.267,-1.735 c -0.819,-0.231 -1.798,-0.347 -2.937,-0.347 -1.086,0 -1.914,0.302 -2.483,0.908 -0.57,0.605 -0.855,1.477 -0.855,2.616 l 0,7.931 -2.35,0 0,1.655 2.35,0.107 0,3.872 2.27,0 0,-3.845 4.139,0 L 4.139,0 0,0 Z"
+             style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
+             id="path5068"
+             inkscape:connector-curvature="0" /></g><g
+           id="g5070"
+           transform="translate(241.455,15.5044)"><path
+             d="M 0,0 5.5,0 C 6.444,0 6.916,0.641 6.916,1.922 6.898,2.759 6.649,3.355 6.168,3.711 5.687,4.067 4.869,4.245 3.711,4.245 2.34,4.245 1.389,3.947 0.854,3.351 0.32,2.754 0.036,1.637 0,0 m 6.088,-1.763 -6.061,0 c 0.124,-1.495 0.489,-2.501 1.095,-3.017 0.605,-0.516 1.61,-0.774 3.017,-0.774 1.477,0 2.981,0.107 4.512,0.32 l 0.241,-1.522 c -1.158,-0.48 -2.84,-0.721 -5.047,-0.721 -2.243,0 -3.841,0.521 -4.793,1.562 -0.952,1.042 -1.428,2.808 -1.428,5.301 0,2.51 0.462,4.267 1.388,5.273 0.926,1.006 2.457,1.509 4.592,1.509 C 5.545,6.168 6.96,5.816 7.85,5.113 8.74,4.41 9.186,3.346 9.186,1.922 9.221,-0.534 8.188,-1.763 6.088,-1.763"
+             style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
+             id="path5072"
+             inkscape:connector-curvature="0" /></g><g
+           id="g5074"
+           transform="translate(259.0776,27.0391)"><path
+             d="m 0,0 0,-15.113 c -0.018,-0.57 0.125,-1.001 0.427,-1.295 0.303,-0.294 0.748,-0.441 1.336,-0.441 l 1.255,0 0.267,-1.735 c -0.481,-0.231 -1.273,-0.347 -2.377,-0.347 -0.979,0 -1.754,0.289 -2.323,0.868 -0.57,0.578 -0.855,1.392 -0.855,2.443 L -2.27,0 0,0 Z"
+             style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
+             id="path5076"
+             inkscape:connector-curvature="0" /></g><g
+           id="g5078"
+           transform="translate(269.7314,27.0391)"><path
+             d="m 0,0 0,-15.113 c -0.018,-0.57 0.125,-1.001 0.427,-1.295 0.303,-0.294 0.748,-0.441 1.336,-0.441 l 1.255,0 0.267,-1.735 c -0.481,-0.231 -1.273,-0.347 -2.377,-0.347 -0.979,0 -1.754,0.289 -2.323,0.868 -0.57,0.578 -0.855,1.392 -0.855,2.443 L -2.27,0 0,0 Z"
+             style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
+             id="path5080"
+             inkscape:connector-curvature="0" /></g><path
+           d="m 280.546,8.348 -2.243,0 0,13.004 2.243,0 0,-13.004 z m -1.656,18.531 1.068,0 c 0.463,0 0.694,-0.232 0.694,-0.694 l 0,-1.522 c 0,-0.463 -0.231,-0.694 -0.694,-0.694 l -1.068,0 c -0.463,0 -0.694,0.231 -0.694,0.694 l 0,1.522 c 0,0.462 0.231,0.694 0.694,0.694"
+           style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
+           id="path5082"
+           inkscape:connector-curvature="0" /><g
+           id="g5084"
+           transform="translate(289.8642,15.6777)"><path
+             d="m 0,0 c 0.516,-0.365 1.415,-0.547 2.697,-0.547 1.282,0 2.18,0.182 2.697,0.547 0.516,0.365 0.774,0.983 0.774,1.856 0,0.872 -0.254,1.491 -0.761,1.855 C 4.899,4.076 3.996,4.259 2.697,4.259 1.415,4.259 0.516,4.076 0,3.711 -0.517,3.347 -0.774,2.728 -0.774,1.856 -0.774,0.983 -0.517,0.365 0,0 M 4.593,-6.715 0.16,-6.101 c -0.908,-0.694 -1.362,-1.523 -1.362,-2.483 0,-0.908 0.267,-1.504 0.801,-1.79 0.534,-0.284 1.611,-0.427 3.231,-0.427 1.585,0 2.648,0.147 3.191,0.441 0.543,0.294 0.814,0.885 0.814,1.776 0,0.64 -0.138,1.077 -0.413,1.308 -0.276,0.231 -0.886,0.418 -1.829,0.561 M 9.666,4.419 7.743,4.259 C 8.099,3.671 8.277,2.871 8.277,1.856 8.277,0.396 7.859,-0.659 7.022,-1.309 6.186,-1.958 4.744,-2.283 2.697,-2.283 1.7,-2.283 0.801,-2.194 0,-2.016 c -0.338,-0.41 -0.437,-0.859 -0.294,-1.348 0.143,-0.49 0.543,-0.788 1.202,-0.895 l 4.86,-0.774 c 1.21,-0.178 2.06,-0.557 2.549,-1.135 0.49,-0.579 0.735,-1.419 0.735,-2.523 0,-1.513 -0.45,-2.564 -1.349,-3.151 -0.899,-0.588 -2.514,-0.881 -4.846,-0.881 -2.35,0 -3.983,0.289 -4.9,0.868 -0.917,0.578 -1.375,1.606 -1.375,3.084 0,0.765 0.16,1.379 0.48,1.842 0.321,0.463 0.864,0.899 1.629,1.308 -0.658,0.517 -0.961,1.202 -0.907,2.057 0.053,0.854 0.391,1.522 1.014,2.002 -1.104,0.587 -1.655,1.718 -1.655,3.391 0,1.495 0.414,2.564 1.241,3.204 0.828,0.641 2.274,0.962 4.34,0.962 1.228,0 2.233,-0.107 3.017,-0.321 l 4.058,0 -0.133,-1.255 z"
+             style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
+             id="path5086"
+             inkscape:connector-curvature="0" /></g><g
+           id="g5088"
+           transform="translate(306.7929,15.5044)"><path
+             d="M 0,0 5.5,0 C 6.444,0 6.916,0.641 6.916,1.922 6.898,2.759 6.649,3.355 6.168,3.711 5.687,4.067 4.869,4.245 3.711,4.245 2.34,4.245 1.389,3.947 0.854,3.351 0.32,2.754 0.036,1.637 0,0 m 6.088,-1.763 -6.061,0 c 0.124,-1.495 0.489,-2.501 1.095,-3.017 0.605,-0.516 1.61,-0.774 3.017,-0.774 1.477,0 2.981,0.107 4.512,0.32 l 0.241,-1.522 c -1.158,-0.48 -2.84,-0.721 -5.047,-0.721 -2.243,0 -3.841,0.521 -4.793,1.562 -0.952,1.042 -1.428,2.808 -1.428,5.301 0,2.51 0.462,4.267 1.388,5.273 0.926,1.006 2.457,1.509 4.592,1.509 C 5.545,6.168 6.96,5.816 7.85,5.113 8.74,4.41 9.186,3.346 9.186,1.922 9.221,-0.534 8.188,-1.763 6.088,-1.763"
+             style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
+             id="path5090"
+             inkscape:connector-curvature="0" /></g><g
+           id="g5092"
+           transform="translate(323.935,21.3521)"><path
+             d="m 0,0 0.16,-2.056 c 2.1,1.584 4.041,2.376 5.821,2.376 2.35,0 3.525,-1.193 3.525,-3.578 l 0,-9.746 -2.27,0 0,9.106 c 0,0.854 -0.143,1.45 -0.427,1.788 -0.285,0.339 -0.766,0.507 -1.442,0.507 -0.712,0 -1.429,-0.16 -2.15,-0.48 -0.72,-0.32 -1.677,-0.846 -2.87,-1.575 l 0,-9.346 -2.27,0 L -1.923,0 0,0 Z"
+             style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
+             id="path5094"
+             inkscape:connector-curvature="0" /></g><g
+           id="g5096"
+           transform="translate(349.7817,10.2173)"><path
+             d="m 0,0 0.241,-1.522 c -1.282,-0.445 -2.769,-0.667 -4.46,-0.667 -2.242,0 -3.831,0.511 -4.766,1.535 -0.934,1.023 -1.402,2.79 -1.402,5.3 0,2.51 0.468,4.272 1.402,5.287 0.935,1.015 2.532,1.522 4.793,1.522 1.709,0 3.106,-0.205 4.192,-0.614 L -0.293,9.372 c -1.175,0.107 -2.35,0.16 -3.525,0.16 -1.567,0 -2.653,-0.346 -3.258,-1.041 -0.605,-0.694 -0.907,-1.976 -0.907,-3.845 0,-1.887 0.302,-3.177 0.907,-3.872 0.605,-0.694 1.691,-1.041 3.258,-1.041 1.424,0 2.697,0.089 3.818,0.267"
+             style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
+             id="path5098"
+             inkscape:connector-curvature="0" /></g><g
+           id="g5100"
+           transform="translate(357.125,15.5044)"><path
+             d="M 0,0 5.5,0 C 6.444,0 6.916,0.641 6.916,1.922 6.898,2.759 6.649,3.355 6.168,3.711 5.687,4.067 4.869,4.245 3.711,4.245 2.34,4.245 1.389,3.947 0.854,3.351 0.32,2.754 0.036,1.637 0,0 m 6.088,-1.763 -6.061,0 c 0.124,-1.495 0.489,-2.501 1.095,-3.017 0.605,-0.516 1.61,-0.774 3.017,-0.774 1.477,0 2.981,0.107 4.512,0.32 l 0.241,-1.522 c -1.158,-0.48 -2.84,-0.721 -5.047,-0.721 -2.243,0 -3.841,0.521 -4.793,1.562 -0.952,1.042 -1.428,2.808 -1.428,5.301 0,2.51 0.462,4.267 1.388,5.273 0.926,1.006 2.457,1.509 4.592,1.509 C 5.545,6.168 6.96,5.816 7.85,5.113 8.74,4.41 9.186,3.346 9.186,1.922 9.221,-0.534 8.188,-1.763 6.088,-1.763"
+             style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
+             id="path5102"
+             inkscape:connector-curvature="0" /></g></g></g></g></svg>
\ No newline at end of file
diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644 (file)
index 0000000..68b135b
--- /dev/null
@@ -0,0 +1,102 @@
+cmake_minimum_required(VERSION 3.0.0)
+
+option(HAILO_BUILD_PYBIND "Build Python binding" OFF)
+option(HAILO_BUILD_PYHAILORT_VENV "Build pyhailort in venv" ON)
+option(HAILO_BUILD_EMULATOR "Build hailort for emulator" OFF)
+option(HAILO_BUILD_UT "Build Unit Tests" OFF)
+option(HAILO_BUILD_GSTREAMER "Compile gstreamer plugins" OFF)
+option(HAILO_BUILD_EXAMPLES "Build examples" OFF)
+
+find_program(CCACHE_PROGRAM ccache)
+if(CCACHE_PROGRAM)
+    set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE "${CCACHE_PROGRAM}")
+endif()
+
+project(HailoRT)
+
+# Check build type
+if (NOT CMAKE_BUILD_TYPE)
+    message(STATUS "No build type selected, default to Debug")
+    set(CMAKE_BUILD_TYPE "Debug")
+endif()
+message(STATUS "Building ${PROJECT_NAME} in ${CMAKE_BUILD_TYPE}")
+
+# Set compiler flags in HAILORT_COMPILE_OPTIONS
+# TODO: Change HAILORT_COMPILE_OPTIONS to add_compile_options
+if(WIN32)
+    # TODO: set this eventually? set(HAILORT_COMPILE_OPTIONS /Wall)
+    set(HAILORT_COMPILE_OPTIONS ${HAILORT_COMPILE_OPTIONS}
+        /W4
+        /WX
+        /DWIN32_LEAN_AND_MEAN
+        /DNOMINMAX                  # NOMINMAX is required in order to play nice with std::min/std::max (otherwise Windows.h defines it's own)
+        /D_HAILO_EXPORTING
+        /wd4201                     # Anonymous union/struct
+        /wd4251                     # C++ ABI with STL
+    )
+    add_definitions(-D_CRT_SECURE_NO_WARNINGS)  # Disable "unsafe function" warnings
+
+    if (CMAKE_BUILD_TYPE STREQUAL "Release") 
+        set(HAILORT_COMPILE_OPTIONS ${HAILORT_COMPILE_OPTIONS} /O2 /DNDEBUG /Zi)
+    elseif(CMAKE_BUILD_TYPE STREQUAL "Debug")
+        set(HAILORT_COMPILE_OPTIONS ${HAILORT_COMPILE_OPTIONS} /Od /Zi /DDEBUG)
+    else()
+        message(FATAL_ERROR "Invalid value for CMAKE_BUILD_TYPE: ${CMAKE_BUILD_TYPE}")
+    endif()
+    add_link_options("$<$<NOT:$<CONFIG:Debug>>:/DEBUG>")
+    add_link_options("$<$<NOT:$<CONFIG:Debug>>:/OPT:REF>")
+    add_link_options("$<$<NOT:$<CONFIG:Debug>>:/OPT:ICF>")
+
+elseif(UNIX)
+    if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
+        set(HAILORT_COMPILE_OPTIONS ${HAILORT_COMPILE_OPTIONS} -Werror -Wall -Wextra -Wconversion)
+    elseif(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
+        set(HAILORT_COMPILE_OPTIONS ${HAILORT_COMPILE_OPTIONS} -Werror -Wall -Wextra 
+            # TODO: remove me warnings
+            -Wno-conversion
+            -Wno-deprecated-declarations
+            -Wno-inconsistent-missing-override
+            )
+    else()
+        message(FATAL_ERROR "Invalid value for CMAKE_CXX_COMPILER_ID: ${CMAKE_CXX_COMPILER_ID}")
+    endif()
+
+    if (CMAKE_BUILD_TYPE STREQUAL "Release") 
+        set(HAILORT_COMPILE_OPTIONS ${HAILORT_COMPILE_OPTIONS} -O3 -DNDEBUG)
+    elseif(CMAKE_BUILD_TYPE STREQUAL "Debug")
+        set(HAILORT_COMPILE_OPTIONS ${HAILORT_COMPILE_OPTIONS} -O0 -g -DDEBUG)
+    else()
+        message(FATAL_ERROR "Invalid value for CMAKE_BUILD_TYPE: ${CMAKE_BUILD_TYPE}")
+    endif()
+else()
+    message(FATAL_ERROR "Unexpeced host, stopping build")
+endif()
+
+enable_testing()
+
+# Flag for emulator (FPGA/Veloce)
+if(HAILO_BUILD_EMULATOR)
+    message(WARNING "HailoRT is building with Emulator flag on")
+    set(HAILORT_COMPILE_OPTIONS ${HAILORT_COMPILE_OPTIONS} -DHAILO_EMULATOR)
+endif()
+
+# Prevent in-tree building 
+if("${CMAKE_SOURCE_DIR}" STREQUAL "${CMAKE_BINARY_DIR}") 
+    message(FATAL_ERROR "In-source builds are not allowed.
+    Please remove the `CMakeCache.txt` file and `CMakeFiles` directory from `${CMAKE_SOURCE_DIR}`
+    In order to build, please create a new `build` directory and run `cmake ..` from there.")
+endif()
+
+# Enable output of compile commands during generation
+set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
+
+# Set validation dir
+set(PLATFORM_VALIDATION_DIRECTORY "${CMAKE_CURRENT_LIST_DIR}/platform_internals/hailo_platform_internals/validation")
+
+if (NOT DEFINED HEFS_DIR)
+    set(HEFS_DIR "${PLATFORM_VALIDATION_DIRECTORY}/hefs/latest")
+    message(STATUS "No HEFS_DIR provided, using default ('${HEFS_DIR}')")
+endif()
+
+# Add subdirectories
+add_subdirectory(hailort)
diff --git a/README.md b/README.md
new file mode 100644 (file)
index 0000000..edc09bf
--- /dev/null
+++ b/README.md
@@ -0,0 +1,43 @@
+<p align="left">
+  <img src=".logo.svg" />
+</p>
+
+
+# HailoRT #
+
+HailoRT is a light-weight and production-grade run-time library, which runs on the host processor, and
+implements a robust user-space run-time library (HailoRT Library) responsible for operating a Hailo device, with intuitive APIs in C/C++ for optimized performance.
+
+HailoRT is comprised of the following main components:
+- HailoRT Library.
+- HailoRT CLI - command line application used to control the Hailo device, run inference using the device,
+ collect inference statistics and device events, etc.
+- [**HailoRT PCIe Driver**](https://github.com/hailo-ai/hailort-drivers) - the device driver used to manage the Hailo device, communicate with the device and transfer
+    data to/from the device. The PCIe driver includes the Hailo-8 firmware that runs on the Hailo device, manages the boot and control of the Hailo device.
+- pyHailoRT - HailoRT Python API (wraps the run-time library)
+- HailoRT GStreamer element (HailoNet).
+
+HailoRT supports Linux and Windows, and can be compiled from sources to be integrated with various x86 and ARM processors.
+
+## Usage
+
+See [**hailo.ai developer zone documentation**](https://hailo.ai/developer-zone/documentation/hailort/latest/) (registration is required for  full documentation access).
+
+## Changelog
+
+See [**hailo.ai developer zone - HailoRT changelog**](https://hailo.ai/developer-zone/documentation/hailort/latest/?sp_referrer=changelog/changelog.html) (registration required).
+
+## Contact
+
+Contact information and support is available at [**hailo.ai**](https://hailo.ai/contact-us/).
+
+## About Hailo-8™
+
+Hailo-8 is a deep learning processor for edge devices. The Hailo-8 provides groundbraking efficiency for neural network deployment.
+The Hailo-8 edge AI processor, featuring up to 26 tera-operations per second (TOPS), significantly outperforms all other edge processors.
+Hailo-8 is available in various form-factors, including the Hailo-8 M.2 Module.
+
+The Hailo-8 AI processor is designed to fit into a multitude of smart machines and devices, for a wide variety of sectors including Automotive, Smart Cities, Industry 4.0,
+Retail and Smart Homes.
+
+For more information, please visit [**hailo.ai**](https://hailo.ai/).
diff --git a/common/config_definitions.json b/common/config_definitions.json
new file mode 100644 (file)
index 0000000..f1efdca
--- /dev/null
@@ -0,0 +1,79 @@
+{
+    "_comment": 
+        [
+        "This file defines the available fields of the firmwares config. It is not used to serialize any data.",
+        "WARNING! DO NOT CHANGE THE ORDER OF THE DEFINITIONS AS IT WILL CHANGE THEIR GENERATED VALUES!"
+        ],
+    "version": 0,
+    "categories": 
+    {
+        "network": 
+        {
+            "entries":
+            {
+                "should_use_dhcp": {"size": 1, "deserialize_as": "bool"},
+                "mac_address": {"size": 1, "length": 6, "deserialize_as": "mac_address"},
+                "static_ip_address": {"size": 4, "deserialize_as": "ipv4"},
+                "static_gw_address": {"size": 4, "deserialize_as": "ipv4"},
+                "static_netmask": {"size": 4, "deserialize_as": "ipv4"},
+                "rx_pause_frames_enable": {"size": 1, "deserialize_as": "bool"}
+            }
+        },
+        "system":
+        {
+            "entries":
+            {
+                "name": {"size": 1, "length": 32, "deserialize_as": "str"},
+                "app_watchdog_enable": {"size": 1, "deserialize_as": "bool"},
+                "app_watchdog_cycles": {"size": 4, "deserialize_as": "int"},
+                "core_watchdog_enable": {"size": 1, "deserialize_as": "bool"},
+                "core_watchdog_cycles": {"size": 4, "deserialize_as": "int"},
+                "watchdog_mode" : {"size": 1, "deserialize_as": "watchdog_mode"},
+                "max_neural_network_core_clock_rate": {"size": 4, "deserialize_as": "clock_frequency"},
+                "supported_aspm_states": {"size": 1, "deserialize_as": "supported_aspm_states"},
+                "bus_0_i2c_speed": {"size": 1, "deserialize_as": "i2c_speed"},
+                "bus_1_i2c_speed": {"size": 1, "deserialize_as": "i2c_speed"},
+                "bus_2_i2c_speed": {"size": 1, "deserialize_as": "i2c_speed"},
+                "bus_3_i2c_speed": {"size": 1, "deserialize_as": "i2c_speed"},
+                "supported_aspm_l1_substates": {"size": 1, "deserialize_as": "supported_aspm_l1_substates"},
+                "overcurrent_parameters_source": {"size": 1, "deserialize_as": "overcurrent_parameters_source"},
+                "overcurrent_monitoring_red_threshold": {"size": 4, "deserialize_as": "int"},
+                "overcurrent_conversion_time_microseconds": {"size": 4, "deserialize_as": "conversion_time"},
+                "temperature_parameters_source": {"size": 1, "deserialize_as": "temperature_parameters_source"},
+                "temperature_red_threshold": {"size": 1, "deserialize_as": "int"},
+                "temperature_red_hysteresis_threshold": {"size": 1, "deserialize_as": "int"},
+                "temperature_orange_threshold": {"size": 1, "deserialize_as": "int"},
+                "temperature_orange_hysteresis_threshold": {"size": 1, "deserialize_as": "int"},
+                "temperature_throttling_enable": {"size": 1, "deserialize_as": "bool"},
+                "overcurrent_monitoring_orange_threshold_enable": {"size": 1, "deserialize_as": "bool"}
+            }
+        },
+        "control":
+        {
+            "entries":
+            {
+                "udp_port": {"size": 2, "deserialize_as": "int"}
+            }
+        },
+        "d2h_event":
+        {
+            "entries":
+            {
+                "host_udp_port": {"size": 2, "deserialize_as": "int"},
+                "src_udp_port": {"size": 2, "deserialize_as": "int"},
+                "host_ip_address": {"size": 4, "deserialize_as": "ipv4"},
+                "connection_type": {"size": 1, "deserialize_as": "bool"}
+            }
+        },
+        "logger":
+        {
+            "entries":
+            {
+                "send_via_pci": {"size": 1, "deserialize_as": "bool"},
+                "send_via_uart": {"size": 1, "deserialize_as": "bool"},
+                "logger_level": {"size": 4, "deserialize_as": "logger_level"}
+            }
+        }
+    }
+}
+
diff --git a/common/config_schema.json b/common/config_schema.json
new file mode 100644 (file)
index 0000000..6639124
--- /dev/null
@@ -0,0 +1,144 @@
+{
+    "$schema": "https://json-schema.org/draft/2020-12/schema",
+    "title": "config_schema",
+    "description": "schema for user config",
+    "type": "object",
+    "properties":
+    {
+        "network":
+        {
+            "type": "object",
+            "properties":
+            {
+                "should_use_dhcp": {"type": "boolean"},
+                "mac_address": {"$ref": "#/definitions/mac_address"},
+                "static_ip_address": {"type": "string", "format": "ipv4"},
+                "static_gw_address": {"type": "string", "format": "ipv4"},
+                "static_netmask": {"type": "string", "format": "ipv4"},
+                "rx_pause_frames_enable": {"type": "boolean"}
+            }
+        },
+        "system":
+        {
+            "type": "object",
+            "properties":
+            {
+                "name": {"type": "string", "maxLength": 32},
+                "app_watchdog_enable": {"type": "boolean"},
+                "app_watchdog_cycles": {"$ref": "#/definitions/uint16_t"},
+                "core_watchdog_enable": {"type": "boolean"},
+                "core_watchdog_cycles": {"$ref": "#/definitions/uint16_t"},
+                "watchdog_mode" : {"$ref": "#/definitions/watchdog_mode"},
+                "max_neural_network_core_clock_rate": {"$ref": "#/definitions/clock_frequency"},
+                "supported_aspm_states": {"$ref": "#/definitions/supported_aspm_states"},
+                "bus_0_i2c_speed": {"$ref": "#/definitions/i2c_speed"},
+                "bus_1_i2c_speed": {"$ref": "#/definitions/i2c_speed"},
+                "bus_2_i2c_speed": {"$ref": "#/definitions/i2c_speed"},
+                "bus_3_i2c_speed": {"$ref": "#/definitions/i2c_speed"},
+                "supported_aspm_l1_substates": {"$ref": "#/definitions/supported_aspm_l1_substates"},
+                "overcurrent_parameters_source": {"$ref": "#/definitions/overcurrent_parameters_source"},
+                "overcurrent_monitoring_red_threshold": {"$ref": "#/definitions/uint32_t"},
+                "overcurrent_conversion_time_microseconds": {"$ref": "#/definitions/conversion_time"},
+                "temperature_parameters_source": {"$ref": "#/definitions/temperature_parameters_source"},
+                "temperature_red_threshold": {"$ref": "#/definitions/int8_t"},
+                "temperature_red_hysteresis_threshold": {"$ref": "#/definitions/int8_t"},
+                "temperature_orange_threshold": {"$ref": "#/definitions/int8_t"},
+                "temperature_orange_hysteresis_threshold": {"$ref": "#/definitions/int8_t"},
+                "overcurrent_monitoring_orange_threshold_enable": {"type": "boolean"}
+            }
+        },
+        "control":
+        {
+            "type": "object",
+            "properties":
+            {
+                "udp_port": {"$ref": "#/definitions/uint16_t"}
+            }
+        },
+        "d2h_event":
+        {
+            "type": "object",
+            "properties":
+            {
+                "host_udp_port": {"$ref": "#/definitions/uint16_t"},
+                "src_udp_port": {"$ref": "#/definitions/uint16_t"},
+                "host_ip_address": {"type": "string", "format": "ipv4"},
+                "connection_type": {"type": "boolean"}
+            }
+        },
+        "logger":
+        {
+            "type": "object",
+            "properties":
+            {
+                "send_via_pci": {"type": "boolean"},
+                "send_via_uart": {"type": "boolean"},
+                "logger_level": {"$ref": "#/definitions/logger_level"}
+            }
+        }
+    },
+    "definitions":
+    {
+        "mac_address":
+        {
+            "type": "string",
+            "pattern": "^(([0-9a-fA-F]{2}[:]){5}|([0-9a-fA-F]{2}){5})([0-9a-fA-F]{2})$"
+        },
+        "clock_frequency":
+        {
+            "enum":
+                ["100MHZ", "200MHZ", "400MHZ"]
+        },
+        "supported_aspm_states":
+        {
+            "enum":
+                ["ASPM_DISABLED", "ASPM_L1_ONLY", "ASPM_L0S_L1",
+                 "ASPM DISABLED", "ASPM L1 ONLY", "ASPM L0S L1"]
+        },
+        "i2c_speed":
+        {
+            "enum":
+                ["I2C_SPEED_STANDARD", "I2C_SPEED_FAST",
+                 "I2C SPEED STANDARD", "I2C SPEED FAST"]
+        },
+        "supported_aspm_l1_substates":
+        {
+            "enum":
+                ["ASPM_L1_SUBSTATES_DISABLED", "ASPM_L1_SUBSTATES_L11_ONLY", "ASPM_L1_SUBSTATES_L11_L12",
+                 "ASPM L1 SUBSTATES DISABLED", "ASPM L1 SUBSTATES L1.1 ONLY", "ASPM L1 SUBSTATES L1.1 L1.2"]
+        },
+        "watchdog_mode":
+        {
+            "enum":
+                ["WD_MODE_HW_SW", "WD_MODE_HW_ONLY",
+                 "WD MODE HW SW", "WD MODE HW ONLY"]
+        },
+        "logger_level":
+        {
+            "enum":
+                ["TRACE", "DEBUG", "INFO", "WARNING", "ERROR", "FATAL"]
+        },
+        "overcurrent_parameters_source":
+        {
+            "enum":
+                ["FW_VALUES", "USER_CONFIG_VALUES", 
+                 "BOARD_CONFIG_VALUES", "OVERCURRENT_DISABLED",
+                 "FW VALUES", "USER CONFIG VALUES", 
+                 "BOARD CONFIG VALUES", "OVERCURRENT DISABLED"]
+        },
+        "temperature_parameters_source":
+        {
+            "enum":
+                ["FW_VALUES", "USER_CONFIG_VALUES", 
+                 "FW VALUES", "USER CONFIG VALUES"]
+        },
+        "conversion_time":
+        {
+            "enum":
+                [140, 204, 332, 588,
+                 1100, 2116, 4156, 8244]
+        },
+        "uint16_t": {"type": "integer", "minimum": 0, "maximum": 65535},
+        "uint32_t": {"type": "integer", "minimum": 0, "maximum": 4294967295}
+    }
+}
\ No newline at end of file
diff --git a/common/include/byte_order.h b/common/include/byte_order.h
new file mode 100644 (file)
index 0000000..79ffd0a
--- /dev/null
@@ -0,0 +1,70 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+**/
+/**
+ * @file byte_order.h
+ * @brief Defines byte order operations.
+**/
+
+#ifndef __BYTE_ORDER_H__
+#define __BYTE_ORDER_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdint.h>
+
+#if !defined(__BYTE_ORDER__)
+// TODO: Check this better?
+#if defined(_MSC_VER)
+#define __ORDER_LITTLE_ENDIAN__ (1)
+#define __BYTE_ORDER__ __ORDER_LITTLE_ENDIAN__
+#else
+#error "Unexpected byte order"
+#endif
+#endif
+
+#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
+
+#define BYTE_ORDER__htonl(x) (x)
+#define BYTE_ORDER__ntohs(x) (x)
+#define BYTE_ORDER__ntohl(x) (x)
+#define BYTE_ORDER__htons(x) (x)
+#define BYTE_ORDER__ntohll(x) (x)
+#define BYTE_ORDER__htonll(x) (x)
+
+
+#elif __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+#define BYTE_ORDER__htons(n) ((uint16_t)((uint16_t)((((uint16_t)(n) & 0xFF)) << 8) | (((uint16_t)(n) & 0xFF00) >> 8)))
+#define BYTE_ORDER__ntohs(n) ((uint16_t)((uint16_t)((((uint16_t)(n) & 0xFF)) << 8) | (((uint16_t)(n) & 0xFF00) >> 8)))
+
+#define BYTE_ORDER__htonl(n) (((((uint32_t)(n) & 0xFF)) << 24) | \
+                          ((((uint32_t)(n) & 0xFF00)) << 8) | \
+                          ((((uint32_t)(n) & 0xFF0000)) >> 8) | \
+                          ((((uint32_t)(n) & 0xFF000000)) >> 24))
+
+#define BYTE_ORDER__ntohl(n) (((((uint32_t)(n) & 0xFF)) << 24) | \
+                          ((((uint32_t)(n) & 0xFF00)) << 8) | \
+                          ((((uint32_t)(n) & 0xFF0000)) >> 8) | \
+                          ((((uint32_t)(n) & 0xFF000000)) >> 24))
+
+#define BYTE_ORDER__htonll(n) (((uint64_t) BYTE_ORDER__htonl((n) & 0xFFFFFFFF) << 32) | \
+                          (uint64_t) BYTE_ORDER__htonl((n) >> 32))
+
+#define BYTE_ORDER__ntohll(n) (((uint64_t) BYTE_ORDER__htonl((n) & 0xFFFFFFFF) << 32) | \
+                          (uint64_t) BYTE_ORDER__htonl((n) >> 32))
+
+#endif 
+
+#define BYTE_ORDER__switch_endiannessl(n) (((((uint32_t)(n) & 0xFF)) << 24) | \
+                          ((((uint32_t)(n) & 0xFF00)) << 8) | \
+                          ((((uint32_t)(n) & 0xFF0000)) >> 8) | \
+                          ((((uint32_t)(n) & 0xFF000000)) >> 24))
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __BYTE_ORDER_H__ */
diff --git a/common/include/context_switch_defs.h b/common/include/context_switch_defs.h
new file mode 100644 (file)
index 0000000..b993a3b
--- /dev/null
@@ -0,0 +1,300 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+**/
+/**
+ * @file context_switch_defs.h
+ * @brief Declerations of all context switch related structs.
+**/
+
+#ifndef __CONTEXT_SWITCH_DEFS__
+#define __CONTEXT_SWITCH_DEFS__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "control_protocol.h"
+
+/**********************************************************************
+ * Defines
+ **********************************************************************/
+
+#define CONTEXT_SWITCH_DEFS__TIMESTAMP_INIT_VALUE (0xFFFFFFFF)
+#define CONTEXT_SWITCH_DEFS__ENABLE_LCU_DEFAULT_KERNEL_ADDRESS (1)
+#define CONTEXT_SWITCH_DEFS__ENABLE_LCU_DEFAULT_KERNEL_COUNT (2)
+#define CONTEXT_SWITCH_DEFS__ENABLE_LCU_DEFAULT_BATCH_SIZE (1)
+
+#define CONTEXT_SWITCH_DEFS__PACKED_LCU_ID_LCU_INDEX_SHIFT (0)
+#define CONTEXT_SWITCH_DEFS__PACKED_LCU_ID_LCU_INDEX_WIDTH (4)
+#define CONTEXT_SWITCH_DEFS__PACKED_LCU_ID_LCU_INDEX_MASK (0x0f)
+#define CONTEXT_SWITCH_DEFS__PACKED_LCU_ID_LCU_INDEX_READ(src) \
+    (((uint8_t)(src) & CONTEXT_SWITCH_DEFS__PACKED_LCU_ID_LCU_INDEX_MASK) >> CONTEXT_SWITCH_DEFS__PACKED_LCU_ID_LCU_INDEX_SHIFT)
+
+#define CONTEXT_SWITCH_DEFS__PACKED_LCU_ID_CLUSTER_INDEX_SHIFT (CONTEXT_SWITCH_DEFS__PACKED_LCU_ID_LCU_INDEX_WIDTH)
+#define CONTEXT_SWITCH_DEFS__PACKED_LCU_ID_CLUSTER_INDEX_MASK (0x70)
+#define CONTEXT_SWITCH_DEFS__PACKED_LCU_ID_CLUSTER_INDEX_READ(src) \
+    (((uint8_t)(src) & CONTEXT_SWITCH_DEFS__PACKED_LCU_ID_CLUSTER_INDEX_MASK) >> CONTEXT_SWITCH_DEFS__PACKED_LCU_ID_CLUSTER_INDEX_SHIFT)
+
+#define CONTEXT_SWITCH_DEFS__PACKED_LCU_ID_SET(dst, cluster_index, lcu_index)                                                       \
+    (dst) = (((lcu_index) << CONTEXT_SWITCH_DEFS__PACKED_LCU_ID_LCU_INDEX_SHIFT) & CONTEXT_SWITCH_DEFS__PACKED_LCU_ID_LCU_INDEX_MASK) |  \
+    (((cluster_index) << CONTEXT_SWITCH_DEFS__PACKED_LCU_ID_CLUSTER_INDEX_SHIFT) & CONTEXT_SWITCH_DEFS__PACKED_LCU_ID_CLUSTER_INDEX_MASK)
+
+
+#pragma pack(push, 1)
+typedef struct {
+    uint16_t core_bytes_per_buffer;
+    uint16_t core_buffers_per_frame;
+    uint16_t periph_bytes_per_buffer;
+    uint16_t periph_buffers_per_frame;
+    uint16_t feature_padding_payload;
+    uint16_t buffer_padding_payload;
+    uint16_t buffer_padding;
+} CONTEXT_SWITCH_DEFS__stream_reg_info_t;
+
+#if defined(_MSC_VER)
+typedef enum : uint8_t {
+#else
+typedef enum __attribute__((packed)) {
+#endif
+    CONTEXT_SWITCH_DEFS__ACTION_TYPE_FETCH_VDMA_DESCRIPTORS = 0,
+    CONTEXT_SWITCH_DEFS__ACTION_TYPE_TRIGGER_SEQUENCER,
+    CONTEXT_SWITCH_DEFS__ACTION_TYPE_FETCH_DATA_FROM_VDMA_CHANNEL,
+    CONTEXT_SWITCH_DEFS__ACTION_TYPE_ENABLE_LCU_DEFAULT,
+    CONTEXT_SWITCH_DEFS__ACTION_TYPE_ENABLE_LCU_NON_DEFAULT,
+    CONTEXT_SWITCH_DEFS__ACTION_TYPE_DISABLE_LCU,
+    CONTEXT_SWITCH_DEFS__ACTION_TYPE_ACTIVATE_BOUNDARY_INPUT,
+    CONTEXT_SWITCH_DEFS__ACTION_TYPE_ACTIVATE_BOUNDARY_OUTPUT,
+    CONTEXT_SWITCH_DEFS__ACTION_TYPE_ACTIVATE_INTER_CONTEXT_INPUT,
+    CONTEXT_SWITCH_DEFS__ACTION_TYPE_ACTIVATE_INTER_CONTEXT_OUTPUT,
+    CONTEXT_SWITCH_DEFS__ACTION_TYPE_ACTIVATE_DDR_BUFFER_INPUT,
+    CONTEXT_SWITCH_DEFS__ACTION_TYPE_ACTIVATE_DDR_BUFFER_OUTPUT,
+    CONTEXT_SWITCH_DEFS__ACTION_TYPE_DEACTIVATE_VDMA_CHANNEL,
+    CONTEXT_SWITCH_DEFS__ACTION_TYPE_CHANGE_VDMA_TO_STREAM_MAPPING,
+    CONTEXT_SWITCH_DEFS__ACTION_TYPE_ADD_DDR_PAIR_INFO,
+    CONTEXT_SWITCH_DEFS__ACTION_TYPE_DDR_BUFFERING_START,
+    CONTEXT_SWITCH_DEFS__ACTION_TYPE_LCU_INTERRUPT,
+    CONTEXT_SWITCH_DEFS__ACTION_TYPE_SEQUENCER_DONE_INTERRUPT,
+    CONTEXT_SWITCH_DEFS__ACTION_TYPE_INPUT_CHANNEL_TRANSFER_DONE_INTERRUPT,
+    CONTEXT_SWITCH_DEFS__ACTION_TYPE_OUTPUT_CHANNEL_TRANSFER_DONE_INTERRUPT,
+    CONTEXT_SWITCH_DEFS__ACTION_TYPE_MODULE_CONFIG_DONE_INTERRUPT,
+    CONTEXT_SWITCH_DEFS__ACTION_TYPE_APPLICATION_CHANGE_INTERRUPT,
+    CONTEXT_SWITCH_DEFS__ACTION_TYPE_ACTIVATE_CFG_CHANNEL,
+    CONTEXT_SWITCH_DEFS__ACTION_TYPE_DEACTIVATE_CFG_CHANNEL,
+    CONTEXT_SWITCH_DEFS__ACTION_TYPE_REPEATED_ACTION,
+    CONTEXT_SWITCH_DEFS__ACTION_TYPE_WAIT_FOR_DMA_IDLE_ACTION,
+    CONTEXT_SWITCH_DEFS__ACTION_TYPE_WAIT_FOR_NMS_IDLE,
+    CONTEXT_SWITCH_DEFS__ACTION_TYPE_FETCH_CCW_BURSTS,
+    
+    /* Must be last */
+    CONTEXT_SWITCH_DEFS__ACTION_TYPE_COUNT
+} CONTEXT_SWITCH_DEFS__ACTION_TYPE_t;
+
+typedef struct {
+    CONTEXT_SWITCH_DEFS__ACTION_TYPE_t action_type;
+    uint32_t time_stamp;
+} CONTEXT_SWITCH_DEFS__common_action_header_t;
+
+/**
+ * The layout of a repeated action in the action_list will be as follows:
+ * 1) CONTEXT_SWITCH_DEFS__common_action_header_t with 'action_type' CONTEXT_SWITCH_DEFS__ACTION_TYPE_REPEATED_ACTION
+ * 2) CONTEXT_SWITCH_DEFS__repeated_action_header_t
+ * 3) 'count' sub-actions whose type matches the 'sub_action_type' defined by (1).
+ *    The sub-actions will be consecutive, and won't have 'CONTEXT_SWITCH_DEFS__common_action_header_t's
+ * 
+ * E.g - 3 repeated 'CONTEXT_SWITCH_DEFS__enable_lcu_action_default_data_t's:
+ * |-------------------------------------------------------------------------------------------------------|
+ * | action_list |                                        data                                             |
+ * |-------------------------------------------------------------------------------------------------------|
+ * |    ...      |                                                                                         |
+ * |     |       | CONTEXT_SWITCH_DEFS__common_action_header_t {                                           |
+ * |     |       |     .action_type = CONTEXT_SWITCH_DEFS__ACTION_TYPE_REPEATED_ACTION;                    |
+ * |     |       |     .time_stamp = <time_of_last_executed_action_in_repeated>;                           |
+ * |     |       | }                                                                                       |
+ * |     |       | CONTEXT_SWITCH_DEFS__repeated_action_header_t {                                         |
+ * |     |       |     .count = 3;                                                                         |
+ * |     |       |     .last_executed = <last_action_executed_in_repeated>;                                |
+ * |     |       |     .sub_action_type = CONTEXT_SWITCH_DEFS__ACTION_TYPE_ENABLE_LCU_DEFAULT;             |
+ * |     |       | }                                                                                       |
+ * |     |       | CONTEXT_SWITCH_DEFS__enable_lcu_action_default_data_t { .packed_lcu_id=<some_lcu_id>; } |
+ * |     |       | CONTEXT_SWITCH_DEFS__enable_lcu_action_default_data_t { .packed_lcu_id=<some_lcu_id>; } |
+ * |     V       | CONTEXT_SWITCH_DEFS__enable_lcu_action_default_data_t { .packed_lcu_id=<some_lcu_id>; } |
+ * |    ...      | (Next action starting with CONTEXT_SWITCH_DEFS__common_action_header_t)                 |
+ * |-------------------------------------------------------------------------------------------------------|
+ * See also: "CONTROL_PROTOCOL__REPEATED_ACTION_t" in "control_protocol.h"
+ */
+typedef struct {
+    uint8_t count;
+    uint8_t last_executed;
+    CONTEXT_SWITCH_DEFS__ACTION_TYPE_t sub_action_type;
+} CONTEXT_SWITCH_DEFS__repeated_action_header_t;
+
+typedef struct {
+    uint16_t descriptors_count;
+    uint8_t cfg_channel_number;
+} CONTEXT_SWITCH_DEFS__read_vdma_action_data_t;
+
+typedef struct {
+    uint16_t ccw_bursts;
+    uint8_t cfg_channel_number;
+} CONTEXT_SWITCH_DEFS__fetch_ccw_bursts_action_data_t;
+
+typedef struct {
+    uint8_t cluster_index;
+    CONTORL_PROTOCOL__sequencer_config_t sequencer_config;
+} CONTEXT_SWITCH_DEFS__trigger_sequencer_action_data_t;
+
+typedef struct {
+    uint8_t packed_lcu_id;
+    uint16_t kernel_done_address;
+    uint32_t kernel_done_count;
+} CONTEXT_SWITCH_DEFS__enable_lcu_action_non_default_data_t;
+
+/* Default action - kernel_done_address and kernel_done_count has default values */
+typedef struct {
+    uint8_t packed_lcu_id;
+} CONTEXT_SWITCH_DEFS__enable_lcu_action_default_data_t;
+
+typedef struct {
+    uint8_t packed_lcu_id;
+} CONTEXT_SWITCH_DEFS__disable_lcu_action_data_t;
+
+typedef struct {
+    uint8_t vdma_channel_index;
+    uint8_t edge_layer_direction;
+    bool is_inter_context;
+} CONTEXT_SWITCH_DEFS__deactivate_vdma_channel_action_data_t;
+
+typedef struct {
+    uint8_t vdma_channel_index;
+    uint8_t stream_index;
+    uint32_t channel_credits;
+    uint8_t credit_type;
+    uint16_t periph_bytes_per_buffer;
+} CONTEXT_SWITCH_DEFS__fetch_data_action_data_t;
+
+typedef struct {
+    uint8_t vdma_channel_index;
+    uint8_t stream_index;
+    bool is_dummy_stream;
+} CONTEXT_SWITCH_DEFS__change_vdma_to_stream_mapping_data_t;
+
+typedef struct {
+    uint8_t h2d_vdma_channel_index;
+    uint8_t d2h_vdma_channel_index;
+    uint32_t descriptors_per_batch;
+    uint16_t programmed_descriptors_count;
+} CONTEXT_SWITCH_DEFS__add_ddr_pair_info_action_data_t;
+
+/* wait for interrupt structs */
+typedef struct {
+    uint8_t packed_lcu_id;
+} CONTEXT_SWITCH_DEFS__lcu_interrupt_data_t;
+
+typedef struct {
+    uint8_t vdma_channel_index;
+} CONTEXT_SWITCH_DEFS__vdma_dataflow_interrupt_data_t;
+
+typedef struct {
+    uint8_t sequencer_index;
+} CONTEXT_SWITCH_DEFS__sequencer_interrupt_data_t;
+
+typedef struct {
+    uint8_t aggregator_index;
+    uint8_t pred_cluster_ob_index;
+    uint8_t pred_cluster_ob_cluster_index;
+    uint8_t pred_cluster_ob_interface;
+    uint8_t succ_prepost_ob_index;
+    uint8_t succ_prepost_ob_interface;
+} CONTEXT_SWITCH_DEFS__wait_nms_idle_data_t;
+
+typedef struct {
+    uint8_t vdma_channel_index;
+    uint8_t stream_index;
+    bool is_inter_context;
+} CONTEXT_SWITCH_DEFS__wait_dma_idle_data_t;
+
+typedef struct {
+    uint8_t module_index;
+} CONTEXT_SWITCH_DEFS__module_config_done_interrupt_data_t;
+
+typedef struct {
+    uint8_t application_index;
+} CONTEXT_SWITCH_DEFS__application_change_interrupt_data_t;
+
+/* edge layers structs */
+typedef struct {
+    uint8_t stream_index;
+    uint8_t vdma_channel_index;
+    CONTEXT_SWITCH_DEFS__stream_reg_info_t stream_reg_info;
+    bool is_single_context_app;
+} CONTEXT_SWITCH_DEFS__activate_boundary_input_data_t;
+
+typedef struct {
+    uint8_t stream_index;
+    uint8_t vdma_channel_index;
+    CONTEXT_SWITCH_DEFS__stream_reg_info_t stream_reg_info;
+    uint64_t host_descriptors_base_address;
+    uint16_t initial_host_available_descriptors;
+    uint8_t desc_list_depth;
+} CONTEXT_SWITCH_DEFS__activate_inter_context_input_data_t;
+
+typedef struct {
+    uint8_t stream_index;
+    uint8_t vdma_channel_index;
+    CONTEXT_SWITCH_DEFS__stream_reg_info_t stream_reg_info;
+    uint64_t host_descriptors_base_address;
+    uint16_t initial_host_available_descriptors;
+    uint8_t desc_list_depth;
+    bool fw_managed_channel;
+} CONTEXT_SWITCH_DEFS__activate_ddr_buffer_input_data_t;
+
+typedef struct {
+    uint8_t stream_index;
+    uint8_t vdma_channel_index;
+    CONTEXT_SWITCH_DEFS__stream_reg_info_t stream_reg_info;
+    uint32_t frame_credits_in_bytes;
+    uint16_t desc_page_size;
+} CONTEXT_SWITCH_DEFS__activate_boundary_output_data_t;
+
+typedef struct {
+    uint8_t stream_index;
+    uint8_t vdma_channel_index;
+    CONTEXT_SWITCH_DEFS__stream_reg_info_t stream_reg_info;
+    // TODO: add this to CONTEXT_SWITCH_DEFS__stream_reg_info_t
+    uint32_t frame_credits_in_bytes;
+    uint64_t host_descriptors_base_address;
+    uint16_t initial_host_available_descriptors;
+    uint16_t desc_page_size;
+    uint8_t desc_list_depth;
+} CONTEXT_SWITCH_DEFS__activate_inter_context_output_data_t;
+
+typedef struct {
+    uint8_t stream_index;
+    uint8_t vdma_channel_index;
+    CONTEXT_SWITCH_DEFS__stream_reg_info_t stream_reg_info;
+    uint32_t frame_credits_in_bytes;
+    uint64_t host_descriptors_base_address;
+    uint16_t initial_host_available_descriptors;
+    uint16_t desc_page_size;
+    uint8_t desc_list_depth;
+    bool fw_managed_channel;
+} CONTEXT_SWITCH_DEFS__activate_ddr_buffer_output_data_t;
+
+typedef struct {
+    uint8_t channel_index;
+    uint64_t host_descriptors_base_address;
+    uint16_t initial_host_available_descriptors;
+} CONTEXT_SWITCH_DEFS__activate_cfg_channel_t;
+
+typedef struct {
+    uint8_t channel_index;
+} CONTEXT_SWITCH_DEFS__deactivate_cfg_channel_t;
+
+#pragma pack(pop)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __CONTEXT_SWITCH_DEFS__ */
diff --git a/common/include/control_protocol.h b/common/include/control_protocol.h
new file mode 100644 (file)
index 0000000..627ee04
--- /dev/null
@@ -0,0 +1,1735 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+**/
+/**
+ * @file control_protocol.h
+ * @brief Defines control protocol.
+**/
+
+#ifndef __CONTROL_PROTOCOL_H__
+#define __CONTROL_PROTOCOL_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stddef.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <limits.h>
+
+#include "stdfloat.h"
+#include "firmware_version.h"
+#include "sensor_config_exports.h"
+#include "md5.h"
+#include "status.h"
+#include "utils.h"
+#include "user_config_common.h"
+
+
+#define CONTROL_PROTOCOL__MAX_REQUEST_PAYLOAD_SIZE (1024)
+#define CONTROL_PROTOCOL__MAX_READ_MEMORY_DATA_SIZE (1024)
+#define CONTROL_PROTOCOL__MAX_I2C_REGISTER_SIZE (4)
+#define CONTROL_PROTOCOL__MAX_BOARD_NAME_LENGTH (32)
+#define CONTROL_PROTOCOL__MAX_SERIAL_NUMBER_LENGTH (16)
+#define CONTROL_PROTOCOL__MAX_PART_NUMBER_LENGTH (16)
+#define CONTROL_PROTOCOL__MAX_PRODUCT_NAME_LENGTH (42)
+#define CONTROL_PROTOCOL__MAX_CONTEXT_SWITCH_APPLICATIONS (8)
+#define CONTROL_PROTOCOL__MAX_NUMBER_OF_CLUSTERS (8)
+#define CONTROL_PROTOCOL__MAX_CONTROL_LENGTH (1500)
+#define CONTROL_PROTOCOL__MAX_TOTAL_CONTEXTS (32)
+#define CONTROL_PROTOCOL__SOC_ID_LENGTH (32)
+#define CONTROL_PROTOCOL__MAX_CFG_CHANNELS (4)
+#define CONTROL_PROTOCOL__MAX_NETWORKS_PER_NETWORK_GROUP (8)
+/* Tightly coupled with the sizeof PROCESS_MONITOR__detection_results_t 
+    and HAILO_SOC_PM_VALUES_BYTES_LENGTH */
+#define PM_RESULTS_LENGTH (24)
+/* Tightly coupled to ETHERNET_SERVICE_MAC_ADDRESS_LENGTH */
+#define MAC_ADDR_BYTES_LEN (6)
+#define LOT_ID_BYTES_LEN (8)
+
+/* Tightly coupled to HAILO_MAX_TEMPERATURE_THROTTLING_LEVELS_NUMBER */
+#define MAX_TEMPERATURE_THROTTLING_LEVELS_NUMBER (4)
+
+#define CONTROL_PROTOCOL__MAX_NUMBER_OF_POWER_MEASUREMETS (4)
+#define CONTROL_PROTOCOL__DEFAULT_INIT_SAMPLING_PERIOD_US (CONTROL_PROTOCOL__PERIOD_1100US)
+#define CONTROL_PROTOCOL__DEFAULT_INIT_AVERAGING_FACTOR (CONTROL_PROTOCOL__AVERAGE_FACTOR_1)
+
+#define CONTROL_PROTOCOL__REQUEST_BASE_SIZE (sizeof(CONTROL_PROTOCOL__request_header_t) + sizeof(uint32_t))
+#define CONTROL_PROTOCOL__OPCODE_INVALID  0xFFFFFFFF
+
+#define CONTROL_PROTOCOL__TRIGGER_SUB_INDEX_SHIFT (0)
+#define CONTROL_PROTOCOL__TRIGGER_SUB_INDEX_BIT_MASK (0x000000FF)
+#define CONTROL_PROTOCOL__TRIGGER_INDEX_SHIFT (16)
+#define CONTROL_PROTOCOL__TRIGGER_INDEX_BIT_MASK (0x00FF0000)
+#define CONTROL_PROTOCOL__TRIGGER_TYPE_SHIFT (28)
+#define CONTROL_PROTOCOL__TRIGGER_TYPE_BIT_MASK (0xF0000000)
+
+// Tightly coupled with BOARD_CONFIG_supported_features_t struct
+#define CONTROL_PROTOCOL__SUPPORTED_FEATURES_ETHERNET_BIT_OFFSET (0)
+#define CONTROL_PROTOCOL__SUPPORTED_FEATURES_MIPI_BIT_OFFSET (1)
+#define CONTROL_PROTOCOL__SUPPORTED_FEATURES_PCIE_BIT_OFFSET (2)
+#define CONTROL_PROTOCOL__SUPPORTED_FEATURES_CURRENT_MONITORING_BIT_OFFSET (3)
+#define CONTROL_PROTOCOL__SUPPORTED_FEATURES_MDIO_BIT_OFFSET (4)
+
+#define CONTROL_PROTOCOL_NUM_BIST_CLUSTER_STEPS (8)
+
+/* Value to represent an operation should be performed on all streams. */
+#define CONTROL_PROTOCOL__ALL_DATAFLOW_MANAGERS (0xFF)
+
+#define CONTROL_PROTOCOL__WRITE_TRIGGER_SUB_INDEX(val)\
+    (((uint32_t)val) << CONTROL_PROTOCOL__TRIGGER_SUB_INDEX_SHIFT)
+#define CONTROL_PROTOCOL__READ_TRIGGER_SUB_INDEX(val)\
+    (((uint32_t)(val) & CONTROL_PROTOCOL__TRIGGER_SUB_INDEX_BIT_MASK) >> (CONTROL_PROTOCOL__TRIGGER_SUB_INDEX_SHIFT))
+#define CONTROL_PROTOCOL__WRITE_TRIGGER_INDEX(val)\
+    (((uint32_t)val) << CONTROL_PROTOCOL__TRIGGER_INDEX_SHIFT)
+#define CONTROL_PROTOCOL__READ_TRIGGER_INDEX(val)\
+    (((uint32_t)(val) & CONTROL_PROTOCOL__TRIGGER_INDEX_BIT_MASK) >> (CONTROL_PROTOCOL__TRIGGER_INDEX_SHIFT))
+#define CONTROL_PROTOCOL__WRITE_TRIGGER_TYPE(val)\
+    (((uint32_t)val) << CONTROL_PROTOCOL__TRIGGER_TYPE_SHIFT)
+#define CONTROL_PROTOCOL__READ_TRIGGER_TYPE(val)\
+    (((uint32_t)(val) & CONTROL_PROTOCOL__TRIGGER_TYPE_BIT_MASK) >> (CONTROL_PROTOCOL__TRIGGER_TYPE_SHIFT))
+
+
+#define CONTROL_PROTOCOL__OPCODES_VARIABLES \
+    CONTROL_PROTOCOL__OPCODE_X(HAILO_CONTROL_OPCODE_IDENTIFY,                                  true,  CPU_ID_APP_CPU)\
+    CONTROL_PROTOCOL__OPCODE_X(HAILO_CONTROL_OPCODE_WRITE_MEMORY,                              false, CPU_ID_APP_CPU)\
+    CONTROL_PROTOCOL__OPCODE_X(HAILO_CONTROL_OPCODE_READ_MEMORY,                               false, CPU_ID_APP_CPU)\
+    CONTROL_PROTOCOL__OPCODE_X(HAILO_CONTROL_OPCODE_CONFIG_STREAM,                             false, CPU_ID_APP_CPU)\
+    CONTROL_PROTOCOL__OPCODE_X(HAILO_CONTROL_OPCODE_OPEN_STREAM,                               false, CPU_ID_APP_CPU)\
+    CONTROL_PROTOCOL__OPCODE_X(HAILO_CONTROL_OPCODE_CLOSE_STREAM,                              false, CPU_ID_APP_CPU)\
+    CONTROL_PROTOCOL__OPCODE_X(HAILO_CONTROL_OPCODE_PHY_OPERATION,                             false, CPU_ID_APP_CPU)\
+    CONTROL_PROTOCOL__OPCODE_X(HAILO_CONTROL_OPCODE_RESET,                                     true, CPU_ID_APP_CPU)\
+    CONTROL_PROTOCOL__OPCODE_X(HAILO_CONTROL_OPCODE_CONFIG_CORE_TOP,                           false, CPU_ID_APP_CPU)\
+    CONTROL_PROTOCOL__OPCODE_X(HAILO_CONTROL_OPCODE_POWER_MEASUEMENT,                          false, CPU_ID_APP_CPU)\
+    CONTROL_PROTOCOL__OPCODE_X(HAILO_CONTROL_OPCODE_SET_POWER_MEASUEMENT,                      false, CPU_ID_APP_CPU)\
+    CONTROL_PROTOCOL__OPCODE_X(HAILO_CONTROL_OPCODE_GET_POWER_MEASUEMENT,                      false, CPU_ID_APP_CPU)\
+    CONTROL_PROTOCOL__OPCODE_X(HAILO_CONTROL_OPCODE_START_POWER_MEASUEMENT,                    false, CPU_ID_APP_CPU)\
+    CONTROL_PROTOCOL__OPCODE_X(HAILO_CONTROL_OPCODE_STOP_POWER_MEASUEMENT,                     false, CPU_ID_APP_CPU)\
+    CONTROL_PROTOCOL__OPCODE_X(HAILO_CONTROL_OPCODE_START_FIRMWARE_UPDATE,                     true, CPU_ID_APP_CPU)\
+    CONTROL_PROTOCOL__OPCODE_X(HAILO_CONTROL_OPCODE_WRITE_FIRMWARE_UPDATE,                     true, CPU_ID_APP_CPU)\
+    CONTROL_PROTOCOL__OPCODE_X(HAILO_CONTROL_OPCODE_VALIDATE_FIRMWARE_UPDATE,                  true, CPU_ID_APP_CPU)\
+    CONTROL_PROTOCOL__OPCODE_X(HAILO_CONTROL_OPCODE_FINISH_FIRMWARE_UPDATE,                    true, CPU_ID_APP_CPU)\
+    CONTROL_PROTOCOL__OPCODE_X(HAILO_CONTROL_OPCODE_EXAMINE_USER_CONFIG,                       true, CPU_ID_APP_CPU)\
+    CONTROL_PROTOCOL__OPCODE_X(HAILO_CONTROL_OPCODE_READ_USER_CONFIG,                          true, CPU_ID_APP_CPU)\
+    CONTROL_PROTOCOL__OPCODE_X(HAILO_CONTROL_OPCODE_ERASE_USER_CONFIG,                         true, CPU_ID_APP_CPU)\
+    CONTROL_PROTOCOL__OPCODE_X(HAILO_CONTROL_OPCODE_WRITE_USER_CONFIG,                         true, CPU_ID_APP_CPU)\
+    CONTROL_PROTOCOL__OPCODE_X(HAILO_CONTROL_OPCODE_I2C_WRITE,                                 false, CPU_ID_APP_CPU)\
+    CONTROL_PROTOCOL__OPCODE_X(HAILO_CONTROL_OPCODE_I2C_READ,                                  false, CPU_ID_APP_CPU)\
+    CONTROL_PROTOCOL__OPCODE_X(HAILO_CONTROL_OPCODE_NN_CORE_LATENCY_MEASUREMENT_CONFIG,        false, CPU_ID_APP_CPU)\
+    CONTROL_PROTOCOL__OPCODE_X(HAILO_CONTROL_OPCODE_NN_CORE_LATENCY_MEASUREMENT_READ,          false, CPU_ID_APP_CPU)\
+    CONTROL_PROTOCOL__OPCODE_X(HAILO_CONTROL_OPCODE_SENSOR_STORE_CONFIG,                       false, CPU_ID_APP_CPU)\
+    CONTROL_PROTOCOL__OPCODE_X(HAILO_CONTROL_OPCODE_SENSOR_GET_CONFIG,                         false, CPU_ID_APP_CPU)\
+    CONTROL_PROTOCOL__OPCODE_X(HAILO_CONTROL_OPCODE_SENSOR_SET_GENERIC_I2C_SLAVE,              false, CPU_ID_APP_CPU)\
+    CONTROL_PROTOCOL__OPCODE_X(HAILO_CONTROL_OPCODE_SENSOR_LOAD_AND_START,                     false, CPU_ID_APP_CPU)\
+    CONTROL_PROTOCOL__OPCODE_X(HAILO_CONTROL_OPCODE_SENSOR_RESET,                              false, CPU_ID_APP_CPU)\
+    CONTROL_PROTOCOL__OPCODE_X(HAILO_CONTROL_OPCODE_SENSOR_GET_SECTIONS_INFO,                  false, CPU_ID_APP_CPU)\
+    CONTROL_PROTOCOL__OPCODE_X(HAILO_CONTROL_OPCODE_CONTEXT_SWITCH_SET_MAIN_HEADER,            false, CPU_ID_CORE_CPU)\
+    CONTROL_PROTOCOL__OPCODE_X(HAILO_CONTROL_OPCODE_CONTEXT_SWITCH_SET_CONTEXT_INFO,           false, CPU_ID_CORE_CPU)\
+    CONTROL_PROTOCOL__OPCODE_X(HAILO_CONTROL_OPCODE_IDLE_TIME_SET_MEASUREMENT,                 false, CPU_ID_APP_CPU)\
+    CONTROL_PROTOCOL__OPCODE_X(HAILO_CONTROL_OPCODE_IDLE_TIME_GET_MEASUREMENT,                 false, CPU_ID_APP_CPU)\
+    CONTROL_PROTOCOL__OPCODE_X(HAILO_CONTROL_OPCODE_DOWNLOAD_CONTEXT_ACTION_LIST,              false, CPU_ID_CORE_CPU)\
+    CONTROL_PROTOCOL__OPCODE_X(HAILO_CONTROL_OPCODE_CHANGE_CONTEXT_SWITCH_STATUS,              false, CPU_ID_CORE_CPU)\
+    CONTROL_PROTOCOL__OPCODE_X(HAILO_CONTROL_OPCODE_APP_WD_ENABLE,                             false, CPU_ID_APP_CPU)\
+    CONTROL_PROTOCOL__OPCODE_X(HAILO_CONTROL_OPCODE_APP_WD_CONFIG,                             false, CPU_ID_APP_CPU)\
+    CONTROL_PROTOCOL__OPCODE_X(HAILO_CONTROL_OPCODE_APP_PREVIOUS_SYSTEM_STATE,                 false, CPU_ID_APP_CPU)\
+    CONTROL_PROTOCOL__OPCODE_X(HAILO_CONTROL_OPCODE_SET_DATAFLOW_INTERRUPT,                    false, CPU_ID_CORE_CPU)\
+    CONTROL_PROTOCOL__OPCODE_X(HAILO_CONTROL_OPCODE_CORE_IDENTIFY,                             true, CPU_ID_CORE_CPU)\
+    CONTROL_PROTOCOL__OPCODE_X(HAILO_CONTROL_OPCODE_D2H_EVENT_MANAGER_SET_HOST_INFO,           false, CPU_ID_APP_CPU)\
+    CONTROL_PROTOCOL__OPCODE_X(HAILO_CONTROL_OPCODE_D2H_EVENT_MANAGER_SEND_EVENT_HOST_INFO,    false, CPU_ID_APP_CPU)\
+    CONTROL_PROTOCOL__OPCODE_X(HAILO_CONTROL_OPCODE_SWITCH_APPLICATION,                        false, CPU_ID_CORE_CPU)\
+    CONTROL_PROTOCOL__OPCODE_X(HAILO_CONTROL_OPCODE_GET_CHIP_TEMPERATURE,                      false, CPU_ID_APP_CPU)\
+    CONTROL_PROTOCOL__OPCODE_X(HAILO_CONTROL_OPCODE_READ_BOARD_CONFIG,                         true, CPU_ID_APP_CPU)\
+    CONTROL_PROTOCOL__OPCODE_X(HAILO_CONTROL_OPCODE_WRITE_BOARD_CONFIG,                        true, CPU_ID_APP_CPU)\
+    CONTROL_PROTOCOL__OPCODE_X(HAILO_CONTROL_OPCODE_GET_SOC_ID /* obsolete */,                 false, CPU_ID_APP_CPU)\
+    CONTROL_PROTOCOL__OPCODE_X(HAILO_CONTROL_OPCODE_ENABLE_DEBUGGING,                          false, CPU_ID_APP_CPU)\
+    CONTROL_PROTOCOL__OPCODE_X(HAILO_CONTROL_OPCODE_GET_DEVICE_INFORMATION,                    false, CPU_ID_APP_CPU)\
+    CONTROL_PROTOCOL__OPCODE_X(HAILO_CONTROL_OPCODE_CONFIG_CONTEXT_SWITCH_BREAKPOINT,          false, CPU_ID_CORE_CPU)\
+    CONTROL_PROTOCOL__OPCODE_X(HAILO_CONTROL_OPCODE_GET_CONTEXT_SWITCH_BREAKPOINT_STATUS,      false, CPU_ID_CORE_CPU)\
+    CONTROL_PROTOCOL__OPCODE_X(HAILO_CONTROL_OPCODE_GET_CONTEXT_SWITCH_MAIN_HEADER,            false, CPU_ID_CORE_CPU)\
+    CONTROL_PROTOCOL__OPCODE_X(HAILO_CONTROL_OPCODE_SET_FW_LOGGER,                             false, CPU_ID_APP_CPU)\
+    CONTROL_PROTOCOL__OPCODE_X(HAILO_CONTROL_OPCODE_WRITE_SECOND_STAGE_TO_INTERNAL_MEMORY,     true, CPU_ID_APP_CPU)\
+    CONTROL_PROTOCOL__OPCODE_X(HAILO_CONTROL_OPCODE_COPY_SECOND_STAGE_TO_FLASH,                true, CPU_ID_APP_CPU)\
+    CONTROL_PROTOCOL__OPCODE_X(HAILO_CONTROL_OPCODE_SET_PAUSE_FRAMES,                          false, CPU_ID_APP_CPU)\
+    CONTROL_PROTOCOL__OPCODE_X(HAILO_CONTROL_OPCODE_CONFIG_CONTEXT_SWITCH_TIMESTAMP,           false, CPU_ID_CORE_CPU)\
+    CONTROL_PROTOCOL__OPCODE_X(HAILO_CONTROL_OPCODE_RUN_BIST_TEST,                             false, CPU_ID_APP_CPU)\
+    CONTROL_PROTOCOL__OPCODE_X(HAILO_CONTROL_OPCODE_SET_CLOCK_FREQ,                            false, CPU_ID_APP_CPU)\
+    CONTROL_PROTOCOL__OPCODE_X(HAILO_CONTROL_OPCODE_GET_HEALTH_INFORMATION,                    false, CPU_ID_APP_CPU)\
+    CONTROL_PROTOCOL__OPCODE_X(HAILO_CONTROL_OPCODE_SET_THROTTLING_STATE,                      false, CPU_ID_APP_CPU)\
+    CONTROL_PROTOCOL__OPCODE_X(HAILO_CONTROL_OPCODE_GET_THROTTLING_STATE,                      false, CPU_ID_APP_CPU)\
+    CONTROL_PROTOCOL__OPCODE_X(HAILO_CONTROL_OPCODE_SENSOR_SET_I2C_BUS_INDEX,                  false, CPU_ID_APP_CPU)\
+    CONTROL_PROTOCOL__OPCODE_X(HAILO_CONTROL_OPCODE_SET_OVERCURRENT_STATE,                     false, CPU_ID_APP_CPU)\
+    CONTROL_PROTOCOL__OPCODE_X(HAILO_CONTROL_OPCODE_GET_OVERCURRENT_STATE,                     false, CPU_ID_APP_CPU)\
+    CONTROL_PROTOCOL__OPCODE_X(HAILO_CONTROL_OPCODE_CORE_PREVIOUS_SYSTEM_STATE,                false, CPU_ID_CORE_CPU)\
+    CONTROL_PROTOCOL__OPCODE_X(HAILO_CONTROL_OPCODE_CORE_WD_ENABLE,                            false, CPU_ID_CORE_CPU)\
+    CONTROL_PROTOCOL__OPCODE_X(HAILO_CONTROL_OPCODE_CORE_WD_CONFIG,                            false, CPU_ID_CORE_CPU)\
+
+typedef enum {
+#define CONTROL_PROTOCOL__OPCODE_X(name, is_critical, cpu_id) name,
+    CONTROL_PROTOCOL__OPCODES_VARIABLES
+#undef CONTROL_PROTOCOL__OPCODE_X
+
+    /* Must be last!! */
+    HAILO_CONTROL_OPCODE_COUNT
+} CONTROL_PROTOCOL__OPCODE_t;
+
+extern bool g_CONTROL_PROTOCOL__is_critical[HAILO_CONTROL_OPCODE_COUNT];
+
+typedef enum {
+    CONTROL_PROTOCOL__PROTOCOL_VERSION_INITIAL = 0,
+    CONTROL_PROTOCOL__PROTOCOL_VERSION_1 = 1,
+    CONTROL_PROTOCOL__PROTOCOL_VERSION_2 = 2
+} CONTROL_PROTOCOL__protocol_version_t;
+
+#define CONTROL_PROTOCOL__PROTOCOL_VERSION (CONTROL_PROTOCOL__PROTOCOL_VERSION_2)
+/*Note: Must be the same as hailo_cpu_id_t in hailort.h */
+typedef enum {
+    CPU_ID_APP_CPU,
+    CPU_ID_CORE_CPU,
+    CPU_ID_UNKNOWN
+} CPU_ID_t;
+
+extern CPU_ID_t g_CONTROL_PROTOCOL__cpu_id[HAILO_CONTROL_OPCODE_COUNT];
+
+typedef enum {
+    CONTROL_PROTOCOL__ACK_UNSET = 0,
+    CONTROL_PROTOCOL__ACK_SET = 1
+} CONTROL_PROTOCOL__ACK_VALUES_t;
+
+/* Note: Must be the same as hailo_dvm_options_t in hailort.h */
+typedef enum DVM_options_e {
+    CONTROL_PROTOCOL__DVM_OPTIONS_VDD_CORE = 0,
+    CONTROL_PROTOCOL__DVM_OPTIONS_VDD_IO,
+    CONTROL_PROTOCOL__DVM_OPTIONS_MIPI_AVDD,
+    CONTROL_PROTOCOL__DVM_OPTIONS_MIPI_AVDD_H,
+    CONTROL_PROTOCOL__DVM_OPTIONS_USB_AVDD_IO,
+    CONTROL_PROTOCOL__DVM_OPTIONS_VDD_TOP,
+    CONTROL_PROTOCOL__DVM_OPTIONS_USB_AVDD_IO_HV,
+    CONTROL_PROTOCOL__DVM_OPTIONS_AVDD_H,
+    CONTROL_PROTOCOL__DVM_OPTIONS_SDIO_VDD_IO,
+    CONTROL_PROTOCOL__DVM_OPTIONS_OVERCURRENT_PROTECTION,
+
+    /* Must be right after the physical DVMS list */
+    CONTROL_PROTOCOL__DVM_OPTIONS_COUNT,
+    CONTROL_PROTOCOL__DVM_OPTIONS_EVB_TOTAL_POWER = INT_MAX - 1,
+    CONTROL_PROTOCOL__DVM_OPTIONS_AUTO = INT_MAX,
+} CONTROL_PROTOCOL__dvm_options_t;
+
+/* Note: Must be the same as hailo_power_measurement_types_t in hailort.h */
+typedef enum POWER__measurement_types_e {
+    CONTROL_PROTOCOL__POWER_MEASUREMENT_TYPES__SHUNT_VOLTAGE = 0,
+    CONTROL_PROTOCOL__POWER_MEASUREMENT_TYPES__BUS_VOLTAGE,
+    CONTROL_PROTOCOL__POWER_MEASUREMENT_TYPES__POWER,
+    CONTROL_PROTOCOL__POWER_MEASUREMENT_TYPES__CURRENT,
+
+    /* Must be Last! */
+    CONTROL_PROTOCOL__POWER_MEASUREMENT_TYPES__COUNT,
+    CONTROL_PROTOCOL__POWER_MEASUREMENT_TYPES__AUTO = INT_MAX,
+} CONTROL_PROTOCOL__power_measurement_types_t;
+
+/* Note: Must be the same as hailo_sampling_period_t in hailort.h */
+typedef enum POWER__sampling_period_e {
+    CONTROL_PROTOCOL__PERIOD_140US = 0,
+    CONTROL_PROTOCOL__PERIOD_204US,
+    CONTROL_PROTOCOL__PERIOD_332US,
+    CONTROL_PROTOCOL__PERIOD_588US,
+    CONTROL_PROTOCOL__PERIOD_1100US,
+    CONTROL_PROTOCOL__PERIOD_2116US,
+    CONTROL_PROTOCOL__PERIOD_4156US,
+    CONTROL_PROTOCOL__PERIOD_8244US,
+} CONTROL_PROTOCOL__sampling_period_t;
+
+/* Note: Must be the same as hailo_averaging_factor_t in hailort.h */
+typedef enum POWER__averaging_factor_e {
+    CONTROL_PROTOCOL__AVERAGE_FACTOR_1 = 0,
+    CONTROL_PROTOCOL__AVERAGE_FACTOR_4,
+    CONTROL_PROTOCOL__AVERAGE_FACTOR_16,
+    CONTROL_PROTOCOL__AVERAGE_FACTOR_64,
+    CONTROL_PROTOCOL__AVERAGE_FACTOR_128,
+    CONTROL_PROTOCOL__AVERAGE_FACTOR_256,
+    CONTROL_PROTOCOL__AVERAGE_FACTOR_512,
+    CONTROL_PROTOCOL__AVERAGE_FACTOR_1024,
+} CONTROL_PROTOCOL__averaging_factor_t;
+
+typedef enum {
+    CONTROL_PROTOCOL__PHY_OPERATION_RESET = 0,
+
+    /* Must be last! */
+    CONTROL_PROTOCOL__PHY_OPERATION_COUNT
+} CONTROL_PROTOCOL__phy_operation_t;
+
+/* TODO: add compile time assertion that the protocol is 32-bit aligned */
+/* START OF NETWORK STRUCTURES */
+#pragma pack(push, 1)
+typedef struct {
+    uint32_t ack : 1;
+    uint32_t reserved : 31;
+} CONTROL_PROTOCOL__flags_struct_t;
+
+/* Union for easy arithmetic manipulations */
+typedef union {
+    CONTROL_PROTOCOL__flags_struct_t bitstruct;
+    uint32_t integer;
+} CONTROL_PROTOCOL_flags_t;
+
+typedef struct {
+    uint32_t version;
+    CONTROL_PROTOCOL_flags_t flags;
+    uint32_t sequence;
+    uint32_t opcode;
+} CONTROL_PROTOCOL__common_header_t;
+
+typedef struct {
+    /* Must be first in order to support parsing */
+    CONTROL_PROTOCOL__common_header_t common_header;
+} CONTROL_PROTOCOL__request_header_t;
+
+typedef struct {
+    uint32_t major_status;
+    uint32_t minor_status;
+} CONTROL_PROTOCOL__status_t;
+
+typedef struct {
+    /* Must be first in order to support parsing */
+    CONTROL_PROTOCOL__common_header_t common_header;
+    CONTROL_PROTOCOL__status_t status;
+} CONTROL_PROTOCOL__response_header_t;
+
+#if defined(_MSC_VER)
+// TODO: warning C4200
+#pragma warning(push)
+#pragma warning(disable: 4200)
+#endif
+typedef struct {
+    uint32_t length;
+    uint8_t data[0];
+} CONTROL_PROTOCOL__parameter_t;
+#if defined(_MSC_VER)
+#pragma warning(pop)
+#endif
+
+#if defined(_MSC_VER)
+// TODO: warning C4200
+#pragma warning(push)
+#pragma warning(disable: 4200)
+#endif
+typedef struct {
+    uint32_t parameter_count;
+    uint8_t parameters[0];
+} CONTROL_PROTOCOL__payload_t;
+#if defined(_MSC_VER)
+#pragma warning(pop)
+#endif
+
+typedef struct {
+    uint32_t protocol_version_length;
+    uint32_t protocol_version;
+    uint32_t fw_version_length;
+    firmware_version_t fw_version;
+    uint32_t logger_version_length;
+    uint32_t logger_version;
+    uint32_t board_name_length;
+    uint8_t board_name[CONTROL_PROTOCOL__MAX_BOARD_NAME_LENGTH];
+    uint32_t device_architecture_length;
+    uint32_t device_architecture;
+    uint32_t serial_number_length;
+    uint8_t serial_number[CONTROL_PROTOCOL__MAX_SERIAL_NUMBER_LENGTH];
+    uint32_t part_number_length;
+    uint8_t part_number[CONTROL_PROTOCOL__MAX_PART_NUMBER_LENGTH];
+    uint32_t product_name_length;
+    uint8_t product_name[CONTROL_PROTOCOL__MAX_PRODUCT_NAME_LENGTH];
+} CONTROL_PROTOCOL_identify_response_t;
+
+typedef struct {
+    uint32_t fw_version_length;
+    firmware_version_t fw_version;
+} CONTROL_PROTOCOL__core_identify_response_t;
+
+typedef enum {
+    CONTROL_PROTOCOL__HAILO8_A0 = 0,
+    CONTROL_PROTOCOL__HAILO8_B0,
+    CONTROL_PROTOCOL__MERCURY_CA,
+    CONTROL_PROTOCOL__MERCURY_VPU,
+    /* Must be last!! */
+    CONTROL_PROTOCOL__DEVICE_ARCHITECTURE_COUNT
+} CONTROL_PROTOCOL__device_architecture_t;
+
+typedef enum {
+    CONTROL_PROTOCOL__MIPI_DESKEW__FORCE_DISABLE = 0,
+    CONTROL_PROTOCOL__MIPI_DESKEW__FORCE_ENABLE,
+    CONTROL_PROTOCOL__MIPI_DESKEW__DEFAULT
+} CONTROL_PROTOCOL__mipi_deskew_enable_t;
+
+typedef struct {
+    uint32_t address_length;
+    uint32_t address;
+    uint32_t data_count_length;
+    uint32_t data_count;
+} CONTROL_PROTOCOL__read_memory_request_t;
+
+typedef struct {
+    uint32_t data_length;
+    uint8_t data[CONTROL_PROTOCOL__MAX_READ_MEMORY_DATA_SIZE];
+} CONTROL_PROTOCOL__read_memory_response_t;
+
+#if defined(_MSC_VER)
+// TODO: warning C4200
+#pragma warning(push)
+#pragma warning(disable: 4200)
+#endif
+typedef struct {
+    uint32_t address_length;
+    uint32_t address;
+    uint32_t data_length;
+    uint8_t  data[0];
+} CONTROL_PROTOCOL__write_memory_request_t;
+
+
+#if defined(_MSC_VER)
+#pragma warning(pop)
+#endif
+
+// Tightly coupled with hailo_fw_logger_interface_t
+typedef enum {
+    CONTROL_PROTOCOL__INTERFACE_PCIE = 1 << 0,
+    CONTROL_PROTOCOL__INTERFACE_UART = 1 << 1
+} CONTROL_PROTOCOL_interface_t;
+
+#define CONTROL_PROTOCOL__FW_MAX_LOGGER_LEVEL (FW_LOGGER_LEVEL_FATAL)
+
+#define CONTROL_PROTOCOL__FW_MAX_LOGGER_INTERFACE (CONTROL_PROTOCOL__INTERFACE_PCIE | CONTROL_PROTOCOL__INTERFACE_UART)
+
+typedef struct {
+    uint32_t level_length;
+    uint8_t level;  // CONTROL_PROTOCOL_interface_t
+    uint32_t logger_interface_bit_mask_length;
+    uint8_t logger_interface_bit_mask;
+} CONTROL_PROTOCOL__set_fw_logger_request_t;
+
+typedef struct {
+    uint32_t should_activate_length;
+    bool should_activate;
+} CONTROL_PROTOCOL__set_throttling_state_request_t;
+
+typedef struct {
+    uint32_t is_active_length;
+    bool is_active;
+} CONTROL_PROTOCOL__get_throttling_state_response_t;
+
+typedef struct {
+    uint32_t should_activate_length;
+    bool should_activate;
+} CONTROL_PROTOCOL__set_overcurrent_state_request_t;
+
+typedef struct {
+    uint32_t is_required_length;
+    bool is_required;
+} CONTROL_PROTOCOL__get_overcurrent_state_response_t;
+
+typedef struct {
+    uint32_t clock_freq_length;
+    uint32_t clock_freq;
+} CONTROL_PROTOCOL__set_clock_freq_request_t;
+
+typedef struct {
+    uint16_t core_bytes_per_buffer;
+    uint16_t core_buffers_per_frame;
+    uint16_t periph_bytes_per_buffer;
+    uint16_t feature_padding_payload;
+    uint16_t buffer_padding_payload;
+    uint16_t buffer_padding;
+} CONTROL_PROTOCOL__nn_stream_config_t;
+
+typedef struct {
+    uint16_t host_udp_port;
+    uint16_t chip_udp_port;
+    uint16_t max_udp_payload_size;
+    bool should_send_sync_packets;
+    uint32_t buffers_threshold;
+    bool use_rtp;
+} CONTROL_PROTOCOL__udp_output_config_params_t;
+
+typedef struct {
+    uint8_t should_sync;
+    uint32_t frames_per_sync;
+    uint32_t packets_per_frame;
+    uint16_t sync_size;
+} CONTROL_PROTOCOL__udp_input_config_sync_t;
+
+typedef struct {
+    uint16_t listening_port;
+    CONTROL_PROTOCOL__udp_input_config_sync_t sync;
+    uint32_t buffers_threshold;
+    bool use_rtp;
+} CONTROL_PROTOCOL__udp_input_config_params_t;
+
+typedef struct {
+    uint8_t data_type;
+    uint16_t img_width_pixels; // sensor_out == mipi_in == ISP_in
+    uint16_t img_height_pixels; // sensor_out == mipi_in == ISP_in
+    uint8_t pixels_per_clock;
+    uint8_t number_of_lanes;
+    uint8_t clock_selection;
+    uint8_t virtual_channel_index;
+    uint32_t data_rate;
+} CONTROL_PROTOCOL__mipi_common_config_params_t;
+
+typedef struct {
+    bool isp_enable;
+    uint8_t isp_img_in_order;
+    uint8_t isp_img_out_data_type;
+    bool isp_crop_enable;
+    uint16_t isp_crop_output_width_pixels; // mipi_out == ISP_out == shmifo_in
+    uint16_t isp_crop_output_height_pixels; // mipi_out == ISP_out == shmifo_in
+    uint16_t isp_crop_output_width_start_offset_pixels;
+    uint16_t isp_crop_output_height_start_offset_pixels;
+    bool isp_test_pattern_enable;
+    bool isp_configuration_bypass;
+    bool isp_run_time_ae_enable;
+    bool isp_run_time_awb_enable;
+    bool isp_run_time_adt_enable;
+    bool isp_run_time_af_enable;
+    uint16_t isp_run_time_calculations_interval_ms;
+    uint8_t isp_light_frequency;
+} CONTROL_PROTOCOL__isp_config_params_t;
+
+typedef struct {
+    CONTROL_PROTOCOL__mipi_common_config_params_t common_params;
+    uint8_t mipi_rx_id;
+    CONTROL_PROTOCOL__isp_config_params_t isp_params;
+} CONTROL_PROTOCOL__mipi_input_config_params_t;
+
+typedef struct {
+    CONTROL_PROTOCOL__mipi_common_config_params_t common_params;
+    uint8_t mipi_tx_id;
+    uint8_t fifo_threshold_percent;
+    uint8_t deskew_enable;
+} CONTROL_PROTOCOL__mipi_output_config_params_t;
+
+typedef enum {
+    CONTROL_PROTOCOL__PCIE_DATAFLOW_TYPE_CONTINUOUS = 0,
+    /* Type 1 (which is CFG flow channel) is not a valid option to be set by the user */
+    CONTROL_PROTOCOL__PCIE_DATAFLOW_TYPE_BURST = 2,
+
+    /* Must be last */
+    CONTROL_PROTOCOL__PCIE_DATAFLOW_TYPE_COUNT,
+} CONTROL_PROTOCOL__pcie_dataflow_type_t;
+
+typedef struct {
+    uint8_t pcie_channel_index;
+    uint16_t desc_page_size;
+} CONTROL_PROTOCOL__pcie_output_config_params_t;
+
+typedef struct {
+    uint8_t pcie_channel_index;
+    uint8_t pcie_dataflow_type;
+} CONTROL_PROTOCOL__pcie_input_config_params_t;
+
+typedef union {
+    CONTROL_PROTOCOL__udp_output_config_params_t udp_output;
+    CONTROL_PROTOCOL__udp_input_config_params_t udp_input;
+    CONTROL_PROTOCOL__mipi_input_config_params_t mipi_input;
+    CONTROL_PROTOCOL__mipi_output_config_params_t mipi_output;
+    CONTROL_PROTOCOL__pcie_input_config_params_t pcie_input;
+    CONTROL_PROTOCOL__pcie_output_config_params_t pcie_output;
+} CONTROL_PROTOCOL__communication_config_prams_t;
+
+// Tightly coupled with hailo_power_mode_t
+typedef enum {
+    CONTROL_PROTOCOL__MODE_PERFORMANCE       = 0,
+    CONTROL_PROTOCOL__MODE_ULTRA_PERFORMANCE = 1,
+    
+    /* Must be last */
+    CONTROL_PROTOCOL__POWER_MODE_COUNT
+} CONTROL_PROTOCOL__power_mode_t;
+
+typedef struct {
+    uint32_t stream_index_length;
+    uint8_t stream_index;
+    uint32_t is_input_length;
+    uint8_t is_input;
+    uint32_t communication_type_length;
+    uint32_t communication_type;
+    uint32_t skip_nn_stream_config_length;
+    uint8_t skip_nn_stream_config;
+    uint32_t power_mode_length;
+    uint8_t power_mode; // CONTROL_PROTOCOL__power_mode_t
+    uint32_t nn_stream_config_length;
+    CONTROL_PROTOCOL__nn_stream_config_t nn_stream_config;
+    // Should be last for size calculations
+    uint32_t communication_params_length;
+    CONTROL_PROTOCOL__communication_config_prams_t communication_params;
+} CONTROL_PROTOCOL__config_stream_request_t;
+
+typedef struct {
+    uint32_t dataflow_manager_id_length;
+    uint8_t dataflow_manager_id;
+    uint32_t is_input_length;
+    uint8_t is_input;
+} CONTROL_PROTOCOL__open_stream_request_t;
+
+typedef struct {
+    uint32_t dataflow_manager_id_length;
+    uint8_t dataflow_manager_id;
+    uint32_t is_input_length;
+    uint8_t is_input;
+} CONTROL_PROTOCOL__close_stream_request_t;
+
+typedef struct {
+    uint32_t operation_type_length;
+    uint32_t operation_type;
+} CONTROL_PROTOCOL__phy_operation_request_t;
+
+typedef struct {
+    uint32_t rx_pause_frames_enable_length;
+    uint8_t rx_pause_frames_enable;
+} CONTROL_PROTOCOL__set_pause_frames_t;
+
+typedef struct {
+    uint32_t reset_type_length;
+    uint32_t reset_type;
+} CONTROL_PROTOCOL__reset_request_t;
+
+typedef enum {
+    CONTROL_PROTOCOL__CONFIG_CORE_TOP_TYPE_AHB_TO_AXI = 0,
+
+    /* Must be last! */
+    CONTROL_PROTOCOL__CONFIG_CORE_TOP_OPCODE_COUNT
+} CONTROL_PROTOCOL__config_core_top_type_t;
+
+typedef struct {
+    uint8_t enable_use_64bit_data_only;
+} CONTROL_PROTOCOL__config_ahb_to_axi_params_t;
+
+typedef union {
+    CONTROL_PROTOCOL__config_ahb_to_axi_params_t ahb_to_axi;
+} CONTROL_PROTOCOL__config_core_top_params_t;
+
+typedef struct {
+    uint32_t config_type_length;
+    uint32_t config_type;
+    uint32_t config_params_length;
+    CONTROL_PROTOCOL__config_core_top_params_t config_params;
+} CONTROL_PROTOCOL__config_core_top_request_t;
+
+typedef struct {
+    uint32_t dvm_length;
+    uint32_t dvm;
+    uint32_t measurement_type_length;
+    uint32_t measurement_type;
+} CONTROL_PROTOCOL__power_measurement_request_t;
+
+typedef struct {
+    uint32_t power_measurement_length;
+    float32_t power_measurement;
+    uint32_t dvm_length;
+    uint32_t dvm;
+    uint32_t measurement_type_length;
+    uint32_t measurement_type;
+} CONTROL_PROTOCOL__power_measurement_response_t;
+
+typedef struct {
+    uint32_t index_length;
+    uint32_t index;
+    uint32_t dvm_length;
+    uint32_t dvm;
+    uint32_t measurement_type_length;
+    uint32_t measurement_type;
+} CONTROL_PROTOCOL__set_power_measurement_request_t;
+
+typedef struct {
+    uint32_t dvm_length;
+    uint32_t dvm;
+    uint32_t measurement_type_length;
+    uint32_t measurement_type;
+} CONTROL_PROTOCOL__set_power_measurement_response_t;
+
+typedef struct {
+    uint32_t index_length;
+    uint32_t index;
+    uint32_t should_clear_length;
+    uint8_t should_clear;
+} CONTROL_PROTOCOL__get_power_measurement_request_t;
+
+typedef struct {
+    uint32_t total_number_of_samples_length;
+    uint32_t total_number_of_samples;
+    uint32_t min_value_length;
+    float32_t min_value;
+    uint32_t max_value_length;
+    float32_t max_value;
+    uint32_t average_value_length;
+    float32_t average_value;
+    uint32_t average_time_value_milliseconds_length;
+    float32_t average_time_value_milliseconds;
+} CONTROL_PROTOCOL__get_power_measurement_response_t;
+
+typedef struct {
+    uint32_t delay_milliseconds_length;
+    uint32_t delay_milliseconds;
+    uint32_t averaging_factor_length;
+    uint16_t averaging_factor;
+    uint32_t sampling_period_length;
+    uint16_t sampling_period;
+} CONTROL_PROTOCOL__start_power_measurement_request_t;
+
+
+typedef struct {
+    uint32_t endianness_length;
+    uint8_t endianness;
+    uint32_t slave_address_length;
+    uint16_t slave_address;
+    uint32_t register_address_size_length;
+    uint8_t register_address_size;
+    uint32_t bus_index_length;
+    uint8_t bus_index;
+    uint32_t should_hold_bus_length;
+    uint8_t should_hold_bus;
+} CONTROL_PROTOCOL__i2c_slave_config_t;
+
+
+#if defined(_MSC_VER)
+// TODO: warning C4200
+#pragma warning(push)
+#pragma warning(disable: 4200)
+#endif
+typedef struct {
+    CONTROL_PROTOCOL__i2c_slave_config_t slave_config;
+    uint32_t register_address_size;
+    uint32_t register_address;
+    uint32_t data_length;
+    uint8_t data[0];
+} CONTROL_PROTOCOL__i2c_write_request_t;
+#if defined(_MSC_VER)
+#pragma warning(pop)
+#endif
+
+typedef struct {
+    CONTROL_PROTOCOL__i2c_slave_config_t slave_config;
+    uint32_t register_address_size;
+    uint32_t register_address;
+    uint32_t data_length_length;
+    uint32_t data_length;
+} CONTROL_PROTOCOL__i2c_read_request_t;
+
+typedef struct {
+    uint32_t data_length;
+    uint8_t data[CONTROL_PROTOCOL__MAX_I2C_REGISTER_SIZE];
+} CONTROL_PROTOCOL__i2c_read_response_t;
+
+#if defined(_MSC_VER)
+// TODO: warning C4200
+#pragma warning(push)
+#pragma warning(disable: 4200)
+#endif
+typedef struct {
+    uint32_t offset_length;
+    uint32_t offset;
+    uint32_t data_length;
+    uint8_t data[0];
+} CONTROL_PROTOCOL__write_firmware_update_request_t;
+#if defined(_MSC_VER)
+#pragma warning(pop)
+#endif
+
+typedef struct {
+    uint32_t expected_md5_length;
+    MD5_SUM_t expected_md5;
+    uint32_t firmware_size_length;
+    uint32_t firmware_size;
+} CONTROL_PROTOCOL__validate_firmware_update_request_t;
+
+typedef CONTROL_PROTOCOL__write_firmware_update_request_t CONTROL_PROTOCOL__write_second_stage_to_internal_memory_request_t;
+typedef struct {
+    uint32_t expected_md5_length;
+    MD5_SUM_t expected_md5;
+    uint32_t second_stage_size_length;
+    uint32_t second_stage_size;
+} CONTROL_PROTOCOL__copy_second_stage_to_flash_request_t; 
+
+typedef struct {
+    uint32_t version_length;
+    uint32_t version;
+    uint32_t entry_count_length;
+    uint32_t entry_count;
+    uint32_t total_size_length;
+    uint32_t total_size;
+} CONTROL_PROTOCOL__examine_user_config_response_t;
+
+typedef struct {
+    uint32_t latency_measurement_en_length;
+    uint8_t latency_measurement_en;
+    uint32_t inbound_start_buffer_number_length;
+    uint32_t inbound_start_buffer_number;
+    uint32_t outbound_stop_buffer_number_length;
+    uint32_t outbound_stop_buffer_number;
+    uint32_t inbound_stream_index_length;
+    uint32_t inbound_stream_index;
+    uint32_t outbound_stream_index_length;
+    uint32_t outbound_stream_index;
+} CONTROL_PROTOCOL__latency_config_request_t;
+
+
+#if defined(_MSC_VER)
+// TODO: warning C4200
+#pragma warning(push)
+#pragma warning(disable: 4200)
+#endif
+typedef struct {
+    uint32_t section_index_length;
+    uint32_t section_index;
+    uint32_t is_first_length;
+    uint32_t is_first;
+    uint32_t start_offset_length;
+    uint32_t start_offset;
+    uint32_t reset_data_size_length;
+    uint32_t reset_data_size;
+    uint32_t sensor_type_length;
+    uint32_t sensor_type;
+    uint32_t total_data_size_length;
+    uint32_t total_data_size;
+    uint32_t config_height_length;
+    uint16_t config_height;
+    uint32_t config_width_length;
+    uint16_t config_width;
+    uint32_t config_fps_length;
+    uint16_t config_fps;
+    uint32_t config_name_length;
+    uint8_t  config_name[MAX_CONFIG_NAME_LEN];
+    uint32_t data_length;
+    uint8_t data[0];
+} CONTROL_PROTOCOL__sensor_store_config_request_t;
+#if defined(_MSC_VER)
+#pragma warning(pop)
+#endif
+
+typedef struct {
+    uint32_t section_index_length;
+    uint32_t section_index;
+    uint32_t offset_length;
+    uint32_t offset;
+    uint32_t data_size_length;
+    uint32_t data_size;
+} CONTROL_PROTOCOL__sensor_get_config_request_t;
+
+typedef struct {
+    uint32_t sensor_type_length;
+    uint32_t sensor_type;
+    uint32_t i2c_bus_index_length;
+    uint32_t i2c_bus_index;
+} CONTROL_PROTOCOL__sensor_set_i2c_bus_index_t;
+
+typedef struct {
+    uint32_t section_index_length;
+    uint32_t section_index;
+} CONTROL_PROTOCOL__sensor_load_config_request_t;
+
+typedef struct {
+    uint32_t slave_address_length;
+    uint16_t slave_address;
+    uint32_t register_address_size_length;
+    uint8_t register_address_size;
+    uint32_t bus_index_length;
+    uint8_t  bus_index;
+    uint32_t should_hold_bus_length;
+    uint8_t should_hold_bus;
+    uint32_t endianness_length;
+    uint8_t endianness;
+}CONTROL_PROTOCOL__sensor_set_generic_i2c_slave_request_t;
+
+typedef struct {
+    uint32_t section_index_length;
+    uint32_t section_index;
+} CONTROL_PROTOCOL__sensor_reset_request_t;
+
+typedef struct {
+    uint32_t data_length;
+    uint8_t data[CONTROL_PROTOCOL__MAX_READ_MEMORY_DATA_SIZE];
+} CONTROL_PROTOCOL__sensor_get_config_response_t;
+
+typedef struct {
+    uint32_t data_length;
+    uint8_t data[CONTROL_PROTOCOL__MAX_READ_MEMORY_DATA_SIZE];
+} CONTROL_PROTOCOL__sensor_get_sections_info_response_t;
+
+typedef struct {
+    uint32_t inbound_to_outbound_latency_nsec_length;
+    uint32_t inbound_to_outbound_latency_nsec;
+} CONTROL_PROTOCOL__latency_read_response_t;
+
+typedef enum {
+    CONTROL_PROTOCOL__CONTEXT_SWITCH_VER_V1_0_0 = 0x010000,
+} CONTROL_PROTOCOL__CONTEXT_SWITCH_VERSION_t;
+
+typedef struct {
+    uint8_t dynamic_contexts_count;
+    uint32_t host_boundary_channels_bitmap;
+    uint32_t host_ddr_channels_bitmap;
+    uint8_t cfg_channels_count;
+    uint8_t cfg_channel_numbers[CONTROL_PROTOCOL__MAX_CFG_CHANNELS];
+    uint8_t power_mode; // CONTROL_PROTOCOL__power_mode_t
+    uint8_t networks_count;
+    uint16_t batch_size[CONTROL_PROTOCOL__MAX_NETWORKS_PER_NETWORK_GROUP];
+} CONTROL_PROTOCOL__application_header_t;
+
+typedef struct {
+    bool is_abbale_supported;
+} CONTROL_PROTOCOL__VALIDATION_FEATURE_LIST_t;
+
+typedef struct {
+    uint32_t context_switch_version_length;
+    uint32_t context_switch_version;
+    uint32_t validation_features_length;
+    CONTROL_PROTOCOL__VALIDATION_FEATURE_LIST_t validation_features;
+    uint32_t application_count_length;
+    uint8_t application_count;
+    uint32_t application_header_length;
+    CONTROL_PROTOCOL__application_header_t application_header[CONTROL_PROTOCOL__MAX_CONTEXT_SWITCH_APPLICATIONS];
+} CONTROL_PROTOCOL__context_switch_set_main_header_request_t;
+
+typedef enum {
+    CONTROL_PROTOCOL__WATCHDOG_MODE_HW_SW = 0,
+    CONTROL_PROTOCOL__WATCHDOG_MODE_HW_ONLY,
+
+    /* must be last*/
+    CONTROL_PROTOCOL__WATCHDOG_NUM_MODES,
+} CONTROL_PROTOCOL__WATCHDOG_MODE_t;
+
+typedef struct {
+    CONTROL_PROTOCOL__CONTEXT_SWITCH_VERSION_t context_switch_version;
+    CONTROL_PROTOCOL__VALIDATION_FEATURE_LIST_t validation_features;
+    uint8_t application_count;
+    CONTROL_PROTOCOL__application_header_t application_header[CONTROL_PROTOCOL__MAX_CONTEXT_SWITCH_APPLICATIONS];
+} CONTROL_PROTOCOL__context_switch_main_header_t;
+
+typedef struct {
+    uint32_t should_enable_length;
+    uint8_t should_enable;
+} CONTROL_PROTOCOL__wd_enable_request_t;
+
+typedef struct {
+    uint32_t wd_cycles_length;
+    uint32_t wd_cycles;
+    uint32_t wd_mode_length;
+    uint8_t wd_mode;
+} CONTROL_PROTOCOL__wd_config_request_t;
+
+/* TODO: Define bit struct (SDK-14509). */
+typedef uint32_t CONTROL_PROTOCOL__system_state_t;
+typedef struct {
+    uint32_t system_state_length;
+    CONTROL_PROTOCOL__system_state_t system_state;
+} CONTROL_PROTOCOL__previous_system_state_response_t;
+
+typedef struct {
+    float32_t ts0_temperature;
+    float32_t ts1_temperature;
+    uint16_t sample_count;
+} CONTROL_PROTOCOL__temperature_info_t;
+
+typedef struct {
+    uint32_t info_length;
+    CONTROL_PROTOCOL__temperature_info_t info;
+} CONTROL_PROTOCOL__get_chip_temperature_response_t;
+
+typedef enum {
+    CONTROL_PROTOCOL__CONTEXT_SWITCH_TRIGGER_TYPE_NONE = 0,
+    CONTROL_PROTOCOL__CONTEXT_SWITCH_TRIGGER_TYPE_LCU,
+    CONTROL_PROTOCOL__CONTEXT_SWITCH_TRIGGER_TYPE_INPUT_STREAM,
+    CONTROL_PROTOCOL__CONTEXT_SWITCH_TRIGGER_TYPE_OUTPUT_STREAM,
+    CONTROL_PROTOCOL__CONTEXT_SWITCH_TRIGGER_TYPE_NMS_IDLE,
+    CONTROL_PROTOCOL__CONTEXT_SWITCH_TRIGGER_TYPE_DMA_IDLE,
+
+    /* must be last*/
+    CONTROL_PROTOCOL__CONTEXT_SWITCH_TRIGGER_TYPE_COUNT,
+} CONTROL_PROTOCOL__CONTEXT_SWITCH_TRIGGER_TYPE_t;
+
+typedef enum {
+    /* this enum starts from 128 for each debug while reading memory buffer */
+    CONTROL_PROTOCOL__CONTEXT_SWITCH_ACTION_START_INDEX = 128,
+    CONTROL_PROTOCOL__CONTEXT_SWITCH_ACTION_READ_VDMA = CONTROL_PROTOCOL__CONTEXT_SWITCH_ACTION_START_INDEX,
+    CONTROL_PROTOCOL__CONTEXT_SWITCH_ACTION_TRIGGER_SEQUENCER,
+    CONTROL_PROTOCOL__CONTEXT_SWITCH_ACTION_WAIT_FOR_SEQUENCER_DONE,
+    CONTROL_PROTOCOL__CONTEXT_SWITCH_ACTION_TRIGGER_NEW_DATA_FROM_DATA_INPUT,
+    CONTROL_PROTOCOL__CONTEXT_SWITCH_ACTION_ENABLE_LCU_NON_DEFAULT,
+    CONTROL_PROTOCOL__CONTEXT_SWITCH_ACTION_DISABLE_LCU,
+    CONTROL_PROTOCOL__CONTEXT_SWITCH_ACTION_WAIT_FOR_MODULE_CONFIG_DONE,
+    CONTROL_PROTOCOL__CONTEXT_SWITCH_ACTION_ADD_DDR_PAIR_INFO,
+    CONTROL_PROTOCOL__CONTEXT_SWITCH_ACTION_ADD_DDR_BUFFERING_START,
+    CONTROL_PROTOCOL__CONTEXT_SWITCH_ACTION_ENABLE_LCU_DEFAULT,
+    CONTROL_PROTOCOL__CONTEXT_SWITCH_ACTION_ADD_REPEATED,
+    CONTROL_PROTOCOL__CONTEXT_SWITCH_ACTION_FETCH_CCW_BURSTS,
+
+    /* must be last*/
+    CONTROL_PROTOCOL__CONTEXT_SWITCH_ACTION_COUNT,
+} CONTROL_PROTOCOL__CONTEXT_SWITCH_ACTION_TYPE_t;
+
+typedef uint8_t CONTROL_PROTOCOL__TRIGGER_TYPE_t;
+
+typedef struct {
+    /* Empty struct - place holder */
+    uint8_t reserved;
+} CONTROL_PROTOCOL__TRIGGER_NONE_t;
+
+typedef struct {
+    uint8_t cluster_index;
+    uint8_t lcu_index;
+} CONTROL_PROTOCOL__TRIGGER_LCU_t;
+
+typedef struct {
+    uint8_t stream_index;
+} CONTROL_PROTOCOL__TRIGGER_INPUT_STREAM_t;
+
+typedef struct {
+    uint8_t stream_index;
+} CONTROL_PROTOCOL__TRIGGER_OUTPUT_STREAM_t;
+
+typedef struct {
+    uint8_t aggregator_index;
+    uint8_t pred_cluster_ob_index;
+    uint8_t pred_cluster_ob_cluster_index;
+    uint8_t pred_cluster_ob_interface;
+    uint8_t succ_prepost_ob_index;
+    uint8_t succ_prepost_ob_interface;
+} CONTROL_PROTOCOL__TRIGGER_NMS_IDLE_t;
+
+typedef struct {
+    uint8_t stream_index;
+} CONTROL_PROTOCOL__TRIGGER_DMA_IDLE_t;
+
+typedef union {
+    CONTROL_PROTOCOL__TRIGGER_NONE_t none_trigger;
+    CONTROL_PROTOCOL__TRIGGER_LCU_t lcu_trigger;
+    CONTROL_PROTOCOL__TRIGGER_INPUT_STREAM_t input_stream_trigger;
+    CONTROL_PROTOCOL__TRIGGER_OUTPUT_STREAM_t output_stream_trigger;
+    CONTROL_PROTOCOL__TRIGGER_NMS_IDLE_t nms_idle_trigger;
+    CONTROL_PROTOCOL__TRIGGER_DMA_IDLE_t dma_idle_trigger;
+} CONTROL_PROTOCOL__trigger_parameters_t;
+
+typedef struct {
+    CONTROL_PROTOCOL__TRIGGER_TYPE_t type;
+    CONTROL_PROTOCOL__trigger_parameters_t params;
+} CONTROL_PROTOCOL__TRIGGER_t;
+
+typedef uint8_t CONTROL_PROTOCOL__shmifo_to_pcie_channel_mapping_t;
+
+typedef enum {
+    CONTROL_PROTOCOL__EDGE_CONNECTION_TYPE_NETWORK_BOUNDARY_INPUT,
+    CONTROL_PROTOCOL__EDGE_CONNECTION_TYPE_NETWORK_BOUNDARY_OUTPUT,
+    CONTROL_PROTOCOL__EDGE_CONNECTION_TYPE_INTERMEDIATE_BUFFER_INPUT,
+    CONTROL_PROTOCOL__EDGE_CONNECTION_TYPE_INTERMEDIATE_BUFFER_OUTPUT,
+    CONTROL_PROTOCOL__EDGE_CONNECTION_TYPE_DDR_BUFFER_INPUT,
+    CONTROL_PROTOCOL__EDGE_CONNECTION_TYPE_DDR_BUFFER_OUTPUT,
+
+    /* must be last */
+    CONTROL_PROTOCOL__EDGE_CONNECTION_TYPE_COUNT
+} CONTROL_PROTOCOL__EDGE_CONNECTION_TYPE_t;
+
+typedef struct {
+    uint8_t communication_type;
+    uint8_t edge_connection_type;
+} CONTROL_PROTOCOL__edge_layer_header_t;
+
+typedef struct {
+    uint8_t stream_index;
+    uint8_t vdma_channel_index;
+    uint8_t network_index;
+    CONTROL_PROTOCOL__nn_stream_config_t nn_stream_config;
+} CONTROL_PROTOCOL__edge_layer_common_info_t;
+
+typedef struct {
+    uint64_t host_descriptors_base_address;
+    uint16_t initial_host_available_descriptors;
+    uint8_t desc_list_depth;
+} CONTROL_PROTOCOL__host_desc_address_info_t;
+
+typedef struct {
+    CONTROL_PROTOCOL__edge_layer_common_info_t common_info;
+    uint32_t frame_credits_in_bytes;
+    uint16_t desc_page_size;
+} CONTROL_PROTOCOL__network_boundary_output_t;
+
+typedef struct {
+    CONTROL_PROTOCOL__edge_layer_common_info_t common_info;
+    uint32_t frame_credits_in_bytes;
+    CONTROL_PROTOCOL__host_desc_address_info_t host_desc_address_info;
+    uint16_t desc_page_size;
+} CONTROL_PROTOCOL__inter_context_output_t;
+
+typedef struct {
+    CONTROL_PROTOCOL__edge_layer_common_info_t common_info;
+    uint32_t frame_credits_in_bytes;
+    CONTROL_PROTOCOL__host_desc_address_info_t host_desc_address_info;
+    uint16_t desc_page_size;
+    bool fw_managed_channel;
+} CONTROL_PROTOCOL__ddr_buffer_output_t;
+
+
+typedef struct {
+    CONTROL_PROTOCOL__edge_layer_common_info_t common_info;
+} CONTROL_PROTOCOL__eth_network_boundary_output_t;
+
+typedef struct {
+    CONTROL_PROTOCOL__edge_layer_common_info_t common_info;
+    uint16_t desc_page_size;
+} CONTROL_PROTOCOL__network_boundary_input_t;
+
+typedef struct {
+    CONTROL_PROTOCOL__edge_layer_common_info_t common_info;
+    CONTROL_PROTOCOL__host_desc_address_info_t host_desc_address_info;
+    uint16_t desc_page_size;
+    uint16_t context_credits_in_descriptors;
+} CONTROL_PROTOCOL__inter_context_input_t;
+
+typedef struct {
+    CONTROL_PROTOCOL__edge_layer_common_info_t common_info;
+    CONTROL_PROTOCOL__host_desc_address_info_t host_desc_address_info;
+    bool fw_managed_channel;
+} CONTROL_PROTOCOL__ddr_buffer_input_t;
+
+typedef struct {
+    CONTROL_PROTOCOL__edge_layer_common_info_t common_info;
+} CONTROL_PROTOCOL__eth_network_boundary_input_t;
+
+typedef struct {
+    uint8_t should_use_stream_remap;
+} CONTROL_PROTOCOL__stream_remap_data_t;
+
+#if defined(_MSC_VER)
+// TODO: warning C4200
+#pragma warning(push)
+#pragma warning(disable: 4200)
+#endif
+typedef struct {
+    uint32_t is_first_control_per_context_length;
+    uint8_t is_first_control_per_context;
+    uint32_t is_last_control_per_context_length;
+    uint8_t is_last_control_per_context;
+    uint32_t context_cfg_base_address_length;
+    uint64_t context_cfg_base_address[CONTROL_PROTOCOL__MAX_CFG_CHANNELS];
+    uint32_t context_cfg_total_descriptors_length;
+    uint16_t context_cfg_total_descriptors[CONTROL_PROTOCOL__MAX_CFG_CHANNELS];
+    uint32_t context_stream_remap_data_length;
+    CONTROL_PROTOCOL__stream_remap_data_t context_stream_remap_data;
+    uint32_t number_of_edge_layers_length;
+    uint8_t number_of_edge_layers;
+    uint32_t number_of_trigger_groups_length;
+    uint8_t number_of_trigger_groups;
+    uint32_t context_network_data_length;
+    uint8_t context_network_data[0];
+} CONTROL_PROTOCOL__context_switch_set_context_info_request_t;
+#if defined(_MSC_VER)
+#pragma warning(pop)
+#endif
+
+typedef uint8_t CONTROL_PROTOCOL__ACTION_TYPE_t;
+
+/* Each CONTROL_PROTOCOL__*_ACTION_t must start with a CONTROL_PROTOCOL__ACTION_HEADER_t */
+typedef struct {
+    /* Must be first */
+    CONTROL_PROTOCOL__ACTION_TYPE_t action_type;
+    bool is_repeated;
+} CONTROL_PROTOCOL__ACTION_HEADER_t;
+
+/**
+ * Repeated actions are sent in the following manner via the control protocol:
+ * 1) CONTROL_PROTOCOL__REPEATED_ACTION_t with:
+ *    a) 'action_type' = CONTROL_PROTOCOL__CONTEXT_SWITCH_ACTION_ADD_REPEATED
+ *    b) 'is_repeated' = false
+ * 2) 'count' sub-actions whose type matches the 'sub_action_type' defined by (1).
+ *    The sub-actions will be consecutive, and will all be marked as 'is_repeated' = true in thier headers.
+ *    The sub-actions may be in different slices, if there is a 'CONTROL_PROTOCOL__CONTEXT_SWITCH_TRIGGER_TYPE_NONE' between them.
+ * 
+ * E.g. - 3 repeated 'CONTROL_PROTOCOL__CONTEXT_SWITCH_ACTION_ENABLE_LCU_DEFAULT's:
+ * |--------------------------------------------------------------------------------------------------|
+ * |   time      |                                        data                                        |
+ * |--------------------------------------------------------------------------------------------------|
+ * |    ...      |                                                                                    |
+ * |     |       | CONTROL_PROTOCOL__REPEATED_ACTION_t {                                              |
+ * |     |       |   .header = { CONTROL_PROTOCOL__CONTEXT_SWITCH_ACTION_ADD_REPEATED, false};        |
+ * |     |       |   .sub_action_type = CONTROL_PROTOCOL__CONTEXT_SWITCH_ACTION_ENABLE_LCU_DEFAULT;   |
+ * |     |       |   .num_actions = 3;                                                                |
+ * |     |       | }                                                                                  |
+ * |     |       | CONTROL_PROTOCOL__ENABLE_LCU_DEFAULT_ACTION_t {                                    |
+ * |     |       |   .header = { CONTROL_PROTOCOL__CONTEXT_SWITCH_ACTION_ENABLE_LCU_DEFAULT, true };  |
+ * |     |       |   .cluster_index = <some_cluster_index>;                                           |
+ * |     |       |   .lcu_index = <some_lcu_index>;                                                   |
+ * |     |       | }                                                                                  |
+ * |     |       | CONTROL_PROTOCOL__ENABLE_LCU_DEFAULT_ACTION_t {                                    |
+ * |     |       |   .header = { CONTROL_PROTOCOL__CONTEXT_SWITCH_ACTION_ENABLE_LCU_DEFAULT, true };  |
+ * |     |       |   .cluster_index = <some_cluster_index>;                                           |
+ * |     |       |   .lcu_index = <some_lcu_index>;                                                   |
+ * |     |       | }                                                                                  |
+ * |     |       | CONTROL_PROTOCOL__ENABLE_LCU_DEFAULT_ACTION_t {                                    |
+ * |     |       |   .header = { CONTROL_PROTOCOL__CONTEXT_SWITCH_ACTION_ENABLE_LCU_DEFAULT, true };  |
+ * |     |       |   .cluster_index = <some_cluster_index>;                                           |
+ * |     |       |   .lcu_index = <some_lcu_index>;                                                   |
+ * |     V       | }                                                                                  |
+ * |    ...      | (Next action control)                                                              |
+ * |--------------------------------------------------------------------------------------------------|
+ * See also: "CONTEXT_SWITCH_DEFS__repeated_action_header_t" in "context_switch_defs.h"
+ */
+typedef struct {
+    /* Must be first */
+    CONTROL_PROTOCOL__ACTION_HEADER_t header;
+    CONTROL_PROTOCOL__ACTION_TYPE_t sub_action_type;
+    uint8_t num_actions;
+} CONTROL_PROTOCOL__REPEATED_ACTION_t;
+
+typedef struct {
+    /* Must be first */
+    CONTROL_PROTOCOL__ACTION_HEADER_t header;
+    uint16_t descriptors_count;
+    uint8_t cfg_channel_handle;
+} CONTROL_PROTOCOL__READ_VDMA_ACTION_t;
+
+typedef struct {
+    /* Must be first */
+    CONTROL_PROTOCOL__ACTION_HEADER_t header;
+    uint16_t ccw_bursts;
+    uint8_t cfg_channel_handle;
+} CONTROL_PROTOCOL__FETCH_CCW_BURSTS_ACTION_t;
+
+typedef struct {
+    uint8_t initial_l3_cut;
+    uint16_t initial_l3_offset;
+    uint32_t active_apu;
+    uint32_t active_ia;
+    uint64_t active_sc;
+    uint64_t active_l2;
+    uint64_t l2_offset_0;
+    uint64_t l2_offset_1;
+} CONTORL_PROTOCOL__sequencer_config_t;
+
+typedef struct {
+    /* Must be first */
+    CONTROL_PROTOCOL__ACTION_HEADER_t header;
+    uint8_t cluster_index;
+    CONTORL_PROTOCOL__sequencer_config_t sequencer_config;
+} CONTROL_PROTOCOL__TRIGGER_SEQUENCER_ACTION_t;
+
+typedef struct {
+    /* Must be first */
+    CONTROL_PROTOCOL__ACTION_HEADER_t header;
+    uint8_t sequencer_index;
+} CONTROL_PROTOCOL__WAIT_FOR_SEQUENCER_ACTION_t;
+
+typedef struct {
+    /* Must be first */
+    CONTROL_PROTOCOL__ACTION_HEADER_t header;
+    uint8_t stream_index;
+} CONTROL_PROTOCOL__FETCH_NEW_DATA_ACTION_t;
+
+typedef struct {
+    /* Must be first */
+    CONTROL_PROTOCOL__ACTION_HEADER_t header;
+    uint8_t cluster_index;
+    uint8_t lcu_index;
+    uint16_t kernel_done_address;
+    uint32_t kernel_done_count;
+    uint8_t network_index;
+} CONTROL_PROTOCOL__ENABLE_LCU_NON_DEAFULT_ACTION_t;
+
+typedef struct {
+    /* Must be first */
+    CONTROL_PROTOCOL__ACTION_HEADER_t header;
+    uint8_t cluster_index;
+    uint8_t lcu_index;
+} CONTROL_PROTOCOL__ENABLE_LCU_DEFAULT_ACTION_t;
+
+typedef struct {
+    /* Must be first */
+    CONTROL_PROTOCOL__ACTION_HEADER_t header;
+    uint8_t cluster_index;
+    uint8_t lcu_index;
+} CONTROL_PROTOCOL__DISABLE_LCU_ACTION_t;
+
+typedef struct {
+    /* Must be first */
+    CONTROL_PROTOCOL__ACTION_HEADER_t header;
+    uint8_t module_index;
+} CONTORL_PROTOCOL__WAIT_FOR_MODULE_CONFIG_DONE_ACTION_t;
+
+typedef struct {
+    /* Must be first */
+    CONTROL_PROTOCOL__ACTION_HEADER_t header;
+    uint8_t h2d_vdma_channel_index;
+    uint8_t d2h_vdma_channel_index;
+    uint32_t descriptors_per_frame;
+    uint16_t programmed_descriptors_count;
+} CONTROL_PROTOCOL__ADD_DDR_PAIR_ACTION_t;
+
+typedef struct {
+    /* Must be first */
+    CONTROL_PROTOCOL__ACTION_HEADER_t header;
+} CONTROL_PROTOCOL__ADD_DDR_BUFFERING_START_ACTION_t;
+
+typedef struct {
+    CONTROL_PROTOCOL__TRIGGER_t trigger;
+    uint16_t triggers_action_count;
+} CONTROL_PROTOCOL__trigger_group_t;
+
+typedef CONTROL_PROTOCOL__read_memory_request_t CONTROL_PROTOCOL__read_user_config_request_t;
+typedef CONTROL_PROTOCOL__read_memory_response_t CONTROL_PROTOCOL__read_user_config_response_t;
+typedef CONTROL_PROTOCOL__write_memory_request_t CONTROL_PROTOCOL__write_user_config_request_t;
+
+typedef struct {
+    uint32_t measurement_enable_length;
+    uint8_t measurement_enable;
+} CONTROL_PROTOCOL__idle_time_set_measurement_request_t;
+
+typedef struct {
+    uint32_t idle_time_ns_length;
+    uint64_t idle_time_ns;
+} CONTROL_PROTOCOL__idle_time_get_measurement_response_t;
+
+typedef struct {
+    uint32_t context_index_length;
+    uint8_t context_index;
+    uint32_t action_list_offset_length;
+    uint16_t action_list_offset;
+} CONTROL_PROTOCOL__download_context_action_list_request_t;
+
+#if defined(_MSC_VER)
+// TODO: warning C4200
+#pragma warning(push)
+#pragma warning(disable: 4200)
+#endif
+typedef struct {
+    uint32_t base_address_length;
+    uint32_t base_address;
+    uint32_t is_action_list_end_length;
+    uint8_t is_action_list_end;
+    uint32_t batch_counter_length;
+    uint32_t batch_counter;
+    uint32_t action_list_length;
+    uint8_t action_list[0];
+} CONTROL_PROTOCOL__download_context_action_list_response_t;
+#if defined(_MSC_VER)
+#pragma warning(pop)
+#endif
+
+typedef enum {
+    CONTROL_PROTOCOL__CONTEXT_SWITCH_STATUS_RESET = 0,
+    CONTROL_PROTOCOL__CONTEXT_SWITCH_STATUS_ENABLED,
+    CONTROL_PROTOCOL__CONTEXT_SWITCH_STATUS_PAUSED,
+
+    /* must be last*/
+    CONTROL_PROTOCOL__CONTEXT_SWITCH_STATUS_COUNT,
+} CONTROL_PROTOCOL__CONTEXT_SWITCH_STATUS_t;
+
+typedef struct {
+    uint32_t state_machine_status_length;
+    uint8_t state_machine_status;
+    uint32_t application_index_length;
+    uint8_t application_index;
+} CONTROL_PROTOCOL__change_context_switch_status_request_t;
+
+typedef struct {
+    uint32_t interrupt_type_length;
+    uint8_t interrupt_type;
+    uint32_t interrupt_index_length;
+    uint8_t interrupt_index;
+    uint32_t interrupt_sub_index_length;
+    uint8_t interrupt_sub_index;
+} CONTROL_PROTOCOL__set_dataflow_interrupt_request_t;
+
+typedef struct {
+    uint32_t application_index_length;
+    uint8_t application_index;
+} CONTROL_PROTOCOL__switch_application_request_t;
+
+typedef struct {
+    uint32_t connection_type_length;
+    uint8_t  connection_type;
+    uint32_t host_ip_address_length;
+    uint32_t host_ip_address;
+    uint32_t host_port_length;
+    uint16_t host_port;
+}CONTROL_PROTOCOL__d2h_event_manager_set_new_host_info_request_t;
+
+typedef struct {
+    uint32_t priority_length;
+    uint8_t  priority;
+}CONTROL_PROTOCOL__d2h_event_manager_send_host_info_event_request_t;
+
+typedef CONTROL_PROTOCOL__read_memory_request_t CONTROL_PROTOCOL__read_board_config_request_t;
+typedef CONTROL_PROTOCOL__read_memory_response_t CONTROL_PROTOCOL__read_board_config_response_t;
+typedef CONTROL_PROTOCOL__write_memory_request_t CONTROL_PROTOCOL__write_board_config_request_t;
+/* Tightly coupled hailo_device_supported_features_t */
+typedef uint64_t CONTROL_PROTOCOL__supported_features_t;
+
+/* Tightly coupled hailo_device_boot_source_t */
+typedef enum {
+    CONTROL_PROTOCOL__BOOT_SOURCE_INVALID = 0,
+    CONTROL_PROTOCOL__BOOT_SOURCE_PCIE,
+    CONTROL_PROTOCOL__BOOT_SOURCE_FLASH
+} CONTROL_PROTOCOL__boot_source_t;
+
+/* CONTROL_PROTOCOL_fuse_info_t sturct will be packed to unit_level_tracking_id field in hailo_extended_device_information_t */
+/* CONTROL_PROTOCOL_fuse_info_t size is tightly coupled HAILO_UNIT_LEVEL_TRACKING_BYTES_LEN */
+typedef struct {
+    uint8_t lot_id[LOT_ID_BYTES_LEN];
+    uint32_t die_wafer_info;
+} CONTROL_PROTOCOL_fuse_info_t;
+
+typedef struct {
+    uint32_t neural_network_core_clock_rate_length;
+    uint32_t neural_network_core_clock_rate;
+    uint32_t supported_features_length;
+    CONTROL_PROTOCOL__supported_features_t supported_features;
+    uint32_t boot_source_length;
+    uint32_t boot_source; /*CONTROL_PROTOCOL__boot_source_t*/
+    uint32_t lcs_length;
+    uint8_t lcs;
+    uint32_t soc_id_length;
+    uint8_t soc_id[CONTROL_PROTOCOL__SOC_ID_LENGTH];
+    uint32_t eth_mac_length;
+    uint8_t eth_mac_address[MAC_ADDR_BYTES_LEN];
+    uint32_t fuse_info_length;
+    CONTROL_PROTOCOL_fuse_info_t fuse_info;
+    uint32_t pd_info_length;
+    uint8_t pd_info[PM_RESULTS_LENGTH];
+} CONTROL_PROTOCOL__get_extended_device_information_response_t;
+
+/* Tightly coupled to hailo_throttling_level_t */
+typedef struct {
+    float32_t temperature_threshold;
+    float32_t hysteresis_temperature_threshold;
+    uint32_t throttling_nn_clock_freq;
+} CONTROL_PROTOCOL__throttling_level_t;
+
+/* Tightly coupled to hailo_health_info_t */
+typedef struct {
+    uint32_t overcurrent_protection_active_length;
+    bool overcurrent_protection_active;
+    uint32_t current_overcurrent_zone_length;
+    uint8_t current_overcurrent_zone;
+    uint32_t red_overcurrent_threshold_length;
+    float32_t red_overcurrent_threshold;
+    uint32_t orange_overcurrent_threshold_length;
+    float32_t orange_overcurrent_threshold;
+    uint32_t temperature_throttling_active_length;
+    bool temperature_throttling_active;
+    uint32_t current_temperature_zone_length;
+    uint8_t current_temperature_zone;
+    uint32_t current_temperature_throttling_level_length;
+    int8_t current_temperature_throttling_level;
+    uint32_t temperature_throttling_levels_length;
+    CONTROL_PROTOCOL__throttling_level_t temperature_throttling_levels[MAX_TEMPERATURE_THROTTLING_LEVELS_NUMBER];
+    uint32_t orange_temperature_threshold_length;
+    int32_t orange_temperature_threshold;
+    uint32_t orange_hysteresis_temperature_threshold_length;
+    int32_t orange_hysteresis_temperature_threshold;
+    uint32_t red_temperature_threshold_length;
+    int32_t red_temperature_threshold;
+    uint32_t red_hysteresis_temperature_threshold_length;
+    int32_t red_hysteresis_temperature_threshold;
+} CONTROL_PROTOCOL__get_health_information_response_t;
+
+typedef enum {
+    CONTROL_PROTOCOL__CONTEXT_SWITCH_BREAKPOINT_CONTROL_SET = 0,
+    CONTROL_PROTOCOL__CONTEXT_SWITCH_BREAKPOINT_CONTROL_CONTINUE,
+    CONTROL_PROTOCOL__CONTEXT_SWITCH_BREAKPOINT_CONTROL_CLEAR,
+
+    /* Must be last */
+    CONTROL_PROTOCOL__CONTEXT_SWITCH_BREAKPOINT_CONTROL_COUNT
+} CONTROL_PROTOCOL__context_switch_breakpoint_control_t;
+
+typedef enum {
+    CONTROL_PROTOCOL__CONTEXT_SWITCH_DEBUG_SYS_STATUS_CLEARED = 0,
+    CONTROL_PROTOCOL__CONTEXT_SWITCH_DEBUG_SYS_STATUS_WAITING_FOR_BREAKPOINT,
+    CONTROL_PROTOCOL__CONTEXT_SWITCH_DEBUG_SYS_STATUS_REACHED_BREAKPOINT,
+
+    /* Must be last */
+    CONTROL_PROTOCOL__CONTEXT_SWITCH_DEBUG_SYS_STATUS_COUNT,
+} CONTROL_PROTOCOL__context_switch_debug_sys_status_t;
+
+typedef struct {
+    bool break_at_any_application_index;
+    uint8_t application_index;
+    bool break_at_any_batch_index;
+    uint16_t batch_index;
+    bool break_at_any_context_index;
+    uint8_t context_index;
+    bool break_at_any_action_index;
+    uint16_t action_index;
+} CONTROL_PROTOCOL__context_switch_breakpoint_data_t;
+
+typedef struct {
+    uint32_t breakpoint_id_length;
+    uint32_t breakpoint_id;
+    uint32_t breakpoint_control_length;
+    uint8_t breakpoint_control;
+    uint32_t breakpoint_data_length;
+    CONTROL_PROTOCOL__context_switch_breakpoint_data_t breakpoint_data;
+} CONTROL_PROTOCOL__config_context_switch_breakpoint_request_t;
+
+typedef struct {
+    uint32_t breakpoint_id_length;
+    uint32_t breakpoint_id;
+} CONTROL_PROTOCOL__get_context_switch_breakpoint_status_request_t;
+
+typedef struct {
+    uint32_t breakpoint_status_length;
+    uint8_t breakpoint_status;
+} CONTROL_PROTOCOL__get_context_switch_breakpoint_status_response_t;
+
+typedef struct {
+    uint32_t is_rma_length;
+    uint8_t is_rma;
+} CONTROL_PROTOCOL__enable_debugging_request_t;
+
+typedef struct {
+    uint32_t main_header_length;
+    CONTROL_PROTOCOL__context_switch_main_header_t main_header;
+} CONTROL_PROTOCOL__get_context_switch_main_header_response_t;
+
+typedef struct {
+    uint32_t batch_index_length;
+    uint16_t batch_index;
+    uint32_t enable_user_configuration_length;
+    uint8_t enable_user_configuration;
+} CONTROL_PROTOCOL__config_context_switch_timestamp_request_t;
+
+typedef struct {
+    uint32_t dataflow_manager_id_length;
+    uint8_t dataflow_manager_id;
+} CONTROL_PROTOCOL__config_stream_response_t;
+
+typedef enum {
+    CONTROL_PROTOCOL__TOP_MEM_BLOCK_CRYPTO_1 = 0,
+    CONTROL_PROTOCOL__TOP_MEM_BLOCK_L4_0_2,
+    CONTROL_PROTOCOL__TOP_MEM_BLOCK_L4_1_3,
+    CONTROL_PROTOCOL__TOP_MEM_BLOCK_L4_2_4,
+    CONTROL_PROTOCOL__TOP_MEM_BLOCK_L4_3_5,
+    CONTROL_PROTOCOL__TOP_MEM_BLOCK_SAGE1_CPU_6,
+    CONTROL_PROTOCOL__TOP_MEM_BLOCK_SAGE1_CPU_FAST_BUS_7,
+    CONTROL_PROTOCOL__TOP_MEM_BLOCK_SAGE1_DEBUG_8,
+    CONTROL_PROTOCOL__TOP_MEM_BLOCK_SAGE1_ETH_9,
+    CONTROL_PROTOCOL__TOP_MEM_BLOCK_SAGE1_FLASH_10,
+    CONTROL_PROTOCOL__TOP_MEM_BLOCK_SAGE1_H264_11,
+    CONTROL_PROTOCOL__TOP_MEM_BLOCK_SAGE1_ISP_12,
+    CONTROL_PROTOCOL__TOP_MEM_BLOCK_SAGE1_MIPI_RX_13,
+    CONTROL_PROTOCOL__TOP_MEM_BLOCK_SAGE1_MIPI_TX_14,
+    CONTROL_PROTOCOL__TOP_MEM_BLOCK_SAGE1_PCIE_15,
+    CONTROL_PROTOCOL__TOP_MEM_BLOCK_SAGE1_SDIO_16,
+    CONTROL_PROTOCOL__TOP_MEM_BLOCK_SAGE1_SOFTMAX_17,
+    CONTROL_PROTOCOL__TOP_MEM_BLOCK_SAGE1_USB_18,
+    CONTROL_PROTOCOL__TOP_MEM_BLOCK_SAGE1_19,
+    /*the cluster ring_s*/
+    CONTROL_PROTOCOL__TOP_MEM_BLOCK_SUB_SERVER0_20,
+    CONTROL_PROTOCOL__TOP_MEM_BLOCK_SUB_SERVER1_21,
+    CONTROL_PROTOCOL__TOP_MEM_BLOCK_SUB_SERVER2_22,
+    CONTROL_PROTOCOL__TOP_MEM_BLOCK_SUB_SERVER3_23,
+    CONTROL_PROTOCOL__TOP_MEM_BLOCK_SUB_SERVER4_24,
+    CONTROL_PROTOCOL__TOP_MEM_BLOCK_SUB_SERVER5_25,
+    CONTROL_PROTOCOL__TOP_MEM_BLOCK_SUB_SERVER6_26,
+    CONTROL_PROTOCOL__TOP_MEM_BLOCK_SUB_SERVER7_27,
+    CONTROL_PROTOCOL__TOP_NUM_MEM_BLOCKS
+} CONTROL_PROTOCOL__biTOP_st_top_mem_block_t;
+
+/*only allowing bist on the following memories*/
+ #define CONTROL_PROTOCOL__BIST_TOP_WHITELIST ((1 << CONTROL_PROTOCOL__TOP_MEM_BLOCK_L4_0_2) | \
+                                            (1 << CONTROL_PROTOCOL__TOP_MEM_BLOCK_L4_1_3) | \
+                                            (1 << CONTROL_PROTOCOL__TOP_MEM_BLOCK_L4_2_4) | \
+                                            (1 <<  CONTROL_PROTOCOL__TOP_MEM_BLOCK_L4_3_5))
+
+                                            /*only allowing bist on the following memories*/
+ #define CONTROL_PROTOCOL__BIST_TOP_BYPASS_ALL_MASK (0x7FFFF)
+
+typedef struct {
+    uint32_t is_top_test_length;
+    bool is_top_test;
+    uint32_t top_bypass_bitmap_length;
+    uint32_t top_bypass_bitmap;
+    uint32_t cluster_index_length;
+    uint8_t cluster_index;
+    uint32_t cluster_bypass_bitmap_0_length;
+    uint32_t cluster_bypass_bitmap_0;
+    uint32_t cluster_bypass_bitmap_1_length;
+    uint32_t cluster_bypass_bitmap_1;
+} CONTROL_PROTOCOL__run_bist_test_request_t;
+
+typedef union {
+    CONTROL_PROTOCOL_identify_response_t identity_response;
+    CONTROL_PROTOCOL__core_identify_response_t core_identity_response;
+    CONTROL_PROTOCOL__read_memory_response_t read_memory_response;
+    CONTROL_PROTOCOL__power_measurement_response_t measure_power_response;
+    CONTROL_PROTOCOL__set_power_measurement_response_t set_measure_power_response;
+    CONTROL_PROTOCOL__get_power_measurement_response_t get_measure_power_response;
+    CONTROL_PROTOCOL__examine_user_config_response_t examine_user_config_response;
+    CONTROL_PROTOCOL__read_user_config_response_t read_user_config_response;
+    CONTROL_PROTOCOL__i2c_read_response_t i2c_read_response;
+    CONTROL_PROTOCOL__latency_read_response_t latency_read_response;
+    CONTROL_PROTOCOL__sensor_get_config_response_t sensor_get_config_response;
+    CONTROL_PROTOCOL__sensor_get_sections_info_response_t sensor_get_sections_info_response;
+    CONTROL_PROTOCOL__idle_time_get_measurement_response_t idle_time_get_measurement_response;
+    CONTROL_PROTOCOL__download_context_action_list_response_t download_context_action_list_response;
+    CONTROL_PROTOCOL__previous_system_state_response_t previous_system_state_response;
+    CONTROL_PROTOCOL__get_chip_temperature_response_t get_chip_temperature_response;
+    CONTROL_PROTOCOL__read_board_config_response_t read_board_config_response;
+    CONTROL_PROTOCOL__get_extended_device_information_response_t get_extended_device_information_response;
+    CONTROL_PROTOCOL__get_context_switch_breakpoint_status_response_t get_context_switch_breakpoint_status_response;
+    CONTROL_PROTOCOL__get_context_switch_main_header_response_t get_context_switch_main_header_response;
+    CONTROL_PROTOCOL__config_stream_response_t config_stream_response;
+    CONTROL_PROTOCOL__get_health_information_response_t get_health_information_response;
+    CONTROL_PROTOCOL__get_throttling_state_response_t get_throttling_state_response;
+    CONTROL_PROTOCOL__get_overcurrent_state_response_t get_overcurrent_state_response;
+   // Note: This array is larger than any legal request:
+   // * Functions in this module won't write more than CONTROL_PROTOCOL__MAX_CONTROL_LENGTH bytes
+   //   when recieving a pointer to CONTROL_PROTOCOL__request_parameters_t.
+   // * Hence, CONTROL_PROTOCOL__response_parameters_t can be stored on the stack of the calling function.
+   uint8_t max_response_size[CONTROL_PROTOCOL__MAX_CONTROL_LENGTH];
+} CONTROL_PROTOCOL__response_parameters_t;
+
+typedef union {
+   CONTROL_PROTOCOL__read_memory_request_t read_memory_request;
+   CONTROL_PROTOCOL__write_memory_request_t write_memory_request;
+   CONTROL_PROTOCOL__config_stream_request_t config_stream_request;
+   CONTROL_PROTOCOL__open_stream_request_t open_stream_request;
+   CONTROL_PROTOCOL__close_stream_request_t close_stream_request;
+   CONTROL_PROTOCOL__phy_operation_request_t phy_operation_request;
+   CONTROL_PROTOCOL__reset_request_t reset_resquest;
+   CONTROL_PROTOCOL__config_core_top_request_t config_core_top_request;
+   CONTROL_PROTOCOL__power_measurement_request_t measure_power_request;
+   CONTROL_PROTOCOL__set_power_measurement_request_t set_measure_power_request;
+   CONTROL_PROTOCOL__get_power_measurement_request_t get_measure_power_request;
+   CONTROL_PROTOCOL__start_power_measurement_request_t start_measure_power_request;
+   CONTROL_PROTOCOL__i2c_write_request_t i2c_write_request;
+   CONTROL_PROTOCOL__i2c_read_request_t i2c_read_request;
+   CONTROL_PROTOCOL__write_firmware_update_request_t write_firmware_update_request;
+   CONTROL_PROTOCOL__validate_firmware_update_request_t validate_firmware_update_request;
+   CONTROL_PROTOCOL__read_user_config_request_t read_user_config_request;
+   CONTROL_PROTOCOL__write_user_config_request_t write_user_config_request;
+   CONTROL_PROTOCOL__latency_config_request_t latency_config_request;
+   CONTROL_PROTOCOL__sensor_store_config_request_t sensor_store_config_request;
+   CONTROL_PROTOCOL__sensor_load_config_request_t sensor_load_config_request;
+   CONTROL_PROTOCOL__sensor_reset_request_t sensor_reset_request;
+   CONTROL_PROTOCOL__sensor_get_config_request_t sensor_get_config_request;
+   CONTROL_PROTOCOL__sensor_set_generic_i2c_slave_request_t sensor_set_generic_i2c_slave_request;
+   CONTROL_PROTOCOL__context_switch_set_main_header_request_t context_switch_set_main_header_request;
+   CONTROL_PROTOCOL__context_switch_set_context_info_request_t context_switch_set_context_info_request;
+   CONTROL_PROTOCOL__idle_time_set_measurement_request_t idle_time_set_measurement_request;
+   CONTROL_PROTOCOL__download_context_action_list_request_t download_context_action_list_request;
+   CONTROL_PROTOCOL__change_context_switch_status_request_t change_context_switch_status_request;
+   CONTROL_PROTOCOL__wd_enable_request_t wd_enable_request;
+   CONTROL_PROTOCOL__wd_config_request_t wd_config_request;
+   CONTROL_PROTOCOL__set_dataflow_interrupt_request_t set_dataflow_interrupt_request;
+   CONTROL_PROTOCOL__d2h_event_manager_set_new_host_info_request_t d2h_event_manager_set_new_host_info_request;
+   CONTROL_PROTOCOL__d2h_event_manager_send_host_info_event_request_t d2h_event_manager_send_host_info_event_request;
+   CONTROL_PROTOCOL__read_board_config_request_t read_board_config_request;
+   CONTROL_PROTOCOL__write_board_config_request_t write_board_config_request;
+   CONTROL_PROTOCOL__switch_application_request_t switch_application_request;
+   CONTROL_PROTOCOL__config_context_switch_breakpoint_request_t config_context_switch_breakpoint_request;
+   CONTROL_PROTOCOL__get_context_switch_breakpoint_status_request_t get_context_switch_breakpoint_status_request;
+   CONTROL_PROTOCOL__enable_debugging_request_t enable_debugging_request;
+   CONTROL_PROTOCOL__set_fw_logger_request_t set_fw_logger_request;
+   CONTROL_PROTOCOL__write_second_stage_to_internal_memory_request_t write_second_stage_to_internal_memory_request; 
+   CONTROL_PROTOCOL__copy_second_stage_to_flash_request_t copy_second_stage_to_flash_request;
+   CONTROL_PROTOCOL__set_pause_frames_t set_pause_frames_request;
+   CONTROL_PROTOCOL__config_context_switch_timestamp_request_t config_context_switch_timestamp_request;
+   CONTROL_PROTOCOL__run_bist_test_request_t run_bist_test_request;
+   CONTROL_PROTOCOL__set_clock_freq_request_t set_clock_freq_request; 
+   CONTROL_PROTOCOL__set_throttling_state_request_t set_throttling_state_request;
+   CONTROL_PROTOCOL__sensor_set_i2c_bus_index_t sensor_set_i2c_bus_index;
+   CONTROL_PROTOCOL__set_overcurrent_state_request_t set_overcurrent_state_request;
+   // Note: This array is larger than any legal request:
+   // * Functions in this module won't write more than CONTROL_PROTOCOL__MAX_CONTROL_LENGTH bytes
+   //   when recieving a pointer to CONTROL_PROTOCOL__request_parameters_t.
+   // * Hence, CONTROL_PROTOCOL__request_parameters_t can be stored on the stack of the calling function.
+   uint8_t max_request_size[CONTROL_PROTOCOL__MAX_CONTROL_LENGTH];
+} CONTROL_PROTOCOL__request_parameters_t;
+
+typedef struct {
+    CONTROL_PROTOCOL__request_header_t header;
+    uint32_t parameter_count;
+    /* Must be last */
+    CONTROL_PROTOCOL__request_parameters_t parameters;
+} CONTROL_PROTOCOL__request_t;
+
+typedef struct {
+    CONTROL_PROTOCOL__response_header_t header;
+    uint32_t parameter_count;
+    /* Must be last */
+    CONTROL_PROTOCOL__response_parameters_t parameters;
+} CONTROL_PROTOCOL__response_t;
+
+#pragma pack(pop)
+/* END OF NETWORK STRUCTURES */
+
+#define CONTROL_PROTOCOL__MAX_REQUEST_PARAMETERS_LENGTH \
+    (CONTROL_PROTOCOL__MAX_CONTROL_LENGTH - offsetof(CONTROL_PROTOCOL__request_t, parameters))
+#define CONTROL_PROTOCOL__MAX_RESPONSE_PARAMETERS_LENGTH \
+    (CONTROL_PROTOCOL__MAX_CONTROL_LENGTH - offsetof(CONTROL_PROTOCOL__response_t, parameters))
+
+#define CONTROL_PROTOCOL__ACTION_LIST_RESPONSE_MAX_SIZE \
+    (CONTROL_PROTOCOL__MAX_RESPONSE_PARAMETERS_LENGTH - sizeof(CONTROL_PROTOCOL__download_context_action_list_response_t))
+
+/* Context switch structs - as it's used by the control.c file and inter cpu control */
+#define CONTROL_PROTOCOL__CONTEXT_NETWORK_DATA_SINGLE_CONTROL_MAX_SIZE \
+    (CONTROL_PROTOCOL__MAX_REQUEST_PARAMETERS_LENGTH - sizeof(CONTROL_PROTOCOL__context_switch_set_context_info_request_t))
+
+typedef struct {
+    bool is_first_control_per_context;
+    bool is_last_control_per_context;
+    uint64_t context_cfg_base_address[CONTROL_PROTOCOL__MAX_CFG_CHANNELS];
+    uint16_t context_cfg_total_descriptors[CONTROL_PROTOCOL__MAX_CFG_CHANNELS];
+    CONTROL_PROTOCOL__stream_remap_data_t context_stream_remap_data;
+    uint8_t number_of_edge_layers;
+    uint8_t number_of_trigger_groups;
+    uint32_t context_network_data_length;
+    uint8_t context_network_data[CONTROL_PROTOCOL__CONTEXT_NETWORK_DATA_SINGLE_CONTROL_MAX_SIZE];
+} CONTROL_PROTOCOL__context_switch_context_info_single_control_t;
+
+/* Context switch user structs */
+
+#define CONTROL_PROTOCOL__CONTEXT_NETWORK_DATA_MAX_SIZE (8 * 1024)
+
+typedef struct {
+    uint8_t interrupt_type;
+    uint8_t interrupt_index;
+    uint8_t interrupt_sub_index;
+} CONTROL_PROTOCOL__dataflow_interrupt_t;
+/* End of context switch structs */
+
+typedef enum {
+    CONTROL_PROTOCOL__MESSAGE_TYPE__REQUEST = 0,
+    CONTROL_PROTOCOL__MESSAGE_TYPE__RESPONSE,
+} CONTROL_PROTOCOL__message_type_t;
+
+typedef enum {
+    CONTROL_PROTOCOL__COMMUNICATION_TYPE_UDP = 0,
+    CONTROL_PROTOCOL__COMMUNICATION_TYPE_MIPI,
+    CONTROL_PROTOCOL__COMMUNICATION_TYPE_PCIE,
+    CONTROL_PROTOCOL__COMMUNICATION_TYPE_INTER_CPU,
+
+    /* Must be last! */
+    CONTROL_PROTOCOL__COMMUNICATION_TYPE_COUNT
+} CONTROL_PROTOCOL__communication_type_t;
+
+typedef enum {
+    CONTROL_PROTOCOL__RESET_TYPE__CHIP = 0,
+    CONTROL_PROTOCOL__RESET_TYPE__NN_CORE,
+    CONTROL_PROTOCOL__RESET_TYPE__SOFT,
+    CONTROL_PROTOCOL__RESET_TYPE__FORCED_SOFT,
+
+    /* Must be last! */
+    CONTROL_PROTOCOL__RESET_TYPE__COUNT
+} CONTROL_PROTOCOL__reset_type_t;
+
+
+typedef union {
+    /* Needed in order to parse unknown header */
+    CONTROL_PROTOCOL__common_header_t common;
+
+    CONTROL_PROTOCOL__request_header_t request;
+    CONTROL_PROTOCOL__response_header_t response;
+} CONTROL_PROTOCOL__message_header_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __CONTROL_PROTOCOL_H__ */
diff --git a/common/include/d2h_events.h b/common/include/d2h_events.h
new file mode 100644 (file)
index 0000000..b293a97
--- /dev/null
@@ -0,0 +1,167 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+**/
+/**
+ * @file d2h_events.h
+ * @brief Declares all d2h event manager structures relevant in the host.
+**/
+
+#ifndef __D2H_EVENTS_H__
+#define __D2H_EVENTS_H__
+#ifdef __cplusplus
+extern "C" {
+#endif
+#include "status.h"
+#include "stdfloat.h"
+
+/**
+ *  @brief The d2h event manager structures relevant in the host
+ */
+
+typedef enum {
+    D2H_EVENT_PRIORITY_INFO = 0,
+    D2H_EVENT_PRIORITY_CRITICAL,
+
+    /* Must be last */
+    D2H_EVENT_PRIORITY_COUNT
+} D2H_EVENT_PRIORITY_t;
+
+typedef enum {
+    D2H_EVENT_COMMUNICATION_TYPE_UDP = 0,
+    D2H_EVENT_COMMUNICATION_TYPE_PCIE,
+    D2H_EVENT_COMMUNICATION_TYPE__COUNT
+} D2H_EVENT_COMMUNICATION_TYPE_t;
+
+typedef struct {
+    uint32_t version;
+    uint32_t sequence;
+    uint32_t priority;
+    uint32_t module_id;
+    uint32_t event_id;
+    uint32_t parameter_count;
+    uint32_t payload_length;
+} D2H_EVENT_HEADER_t;
+
+/* D2H_EVENT_ID_t Should be in the same order as the structs in D2H_EVENT__message_parameters_t union, since the host will parse according to this enum */
+/* For example ETHERNET_SERVICE_RX_ERROR_EVENT_ID is 0, so D2H_EVENT_rx_error_event_message_t is the first struct in D2H_EVENT__message_parameters_t */
+typedef enum {
+    ETHERNET_SERVICE_RX_ERROR_EVENT_ID = 0,
+    D2H_HOST_INFO_EVENT_ID,
+    HEALTH_MONITOR_TEMPERATURE_ALARM_D2H_EVENT_ID,
+    HEALTH_MONITOR_CLOSED_STREAMS_D2H_EVENT_ID,
+    HEALTH_MONITOR_OVERCURRENT_PROTECTION_ALERT_EVENT_ID,
+    HEALTH_MONITOR_LCU_ECC_CORRECTABLE_EVENT_ID,
+    HEALTH_MONITOR_LCU_ECC_UNCORRECTABLE_EVENT_ID,
+    HEALTH_MONITOR_CPU_ECC_ERROR_EVENT_ID,
+    HEALTH_MONITOR_CPU_ECC_FATAL_EVENT_ID,
+    CONTEXT_SWITCH_BREAKPOINT_REACHED,
+    HEALTH_MONITOR_CLOCK_CHANGED_EVENT_ID,
+    D2H_EVENT_ID_COUNT /* Must be last*/
+} D2H_EVENT_ID_t;
+
+/* D2H_EVENT_rx_error_event_message_t should be the same as hailo_rx_error_notification_message_t */
+typedef struct {
+    uint32_t error; 
+    uint32_t queue_number;
+    uint32_t rx_errors_count;
+} D2H_EVENT_rx_error_event_message_t;
+#define D2H_EVENT_RX_ERROR_EVENT_PARAMETER_COUNT  (3)
+
+/* D2H_EVENT_host_info_event_message_t should be the same as hailo_debug_notification_message_t */
+typedef struct {
+    uint32_t connection_status;
+    uint32_t connection_type;
+    uint32_t pcie_is_active;
+    uint32_t host_port;
+    uint32_t host_ip_addr;
+} D2H_EVENT_host_info_event_message_t;
+#define D2H_EVENT_HOST_INFO_EVENT_PARAMETER_COUNT  (5)
+
+/* D2H_EVENT_health_monitor_closed_streams_event_message_t should be the same as hailo_health_monitor_dataflow_shutdown_notification_message_t */
+typedef struct {
+    uint32_t closed_input_streams;
+    uint32_t closed_output_streams;
+    float32_t ts0_temperature;
+    float32_t ts1_temperature;
+} D2H_EVENT_health_monitor_closed_streams_event_message_t;
+
+#define D2H_EVENT_HEALTH_MONITOR_CLOSED_STREAMS_EVENT_PARAMETER_COUNT  (4)
+
+/* D2H_EVENT_health_monitor_temperature_alarm_event_message_t should be the same as hailo_health_monitor_temperature_alarm_notification_message_t */
+typedef struct {
+    uint32_t temperature_zone;
+    uint32_t alarm_ts_id;
+    float32_t ts0_temperature;
+    float32_t ts1_temperature;
+} D2H_EVENT_health_monitor_temperature_alarm_event_message_t;
+
+#define D2H_EVENT_HEALTH_MONITOR_TEMPERATURE_ALARM_EVENT_PARAMETER_COUNT  (4)
+
+/* D2H_EVENT_health_monitor_overcurrent_alert_event_message_t should be the same as hailo_health_monitor_overcurrent_alert_notification_message_t */
+typedef struct {
+    uint32_t overcurrent_zone;
+    float32_t exceeded_alert_threshold;
+    float32_t sampled_current_during_alert;
+} D2H_EVENT_health_monitor_overcurrent_alert_event_message_t;
+
+#define D2H_EVENT_HEALTH_MONITOR_OVERCURRENT_ALERT_EVENT_PARAMETER_COUNT  (2)
+
+/* D2H_EVENT_health_monitor_lcu_ecc_error_event_message_t should be the same as hailo_health_monitor_lcu_ecc_error_notification_message_t */
+typedef struct {
+    uint16_t cluster_bitmap;
+} D2H_EVENT_health_monitor_lcu_ecc_error_event_message_t;
+
+/* D2H_EVENT_health_monitor_cpu_ecc_event_message_t should be the same as hailo_health_monitor_cpu_ecc_error_notification_message_t */
+#define D2H_EVENT_HEALTH_MONITOR_LCU_ECC_ERROR_EVENT_PARAMETER_COUNT  (1)
+typedef struct {
+    uint32_t memory_bitmap;
+} D2H_EVENT_health_monitor_cpu_ecc_event_message_t;
+
+#define D2H_EVENT_HEALTH_MONITOR_CPU_ECC_EVENT_PARAMETER_COUNT  (1)
+
+/* D2H_EVENT_context_switch_breakpoint_reached_event_massage_t should be the same as 
+ * CONTROL_PROTOCOL__context_switch_breakpoint_data_t and hailo_context_switch_breakpoint_reached_notification_message_t */
+typedef struct {
+    uint8_t application_index;
+    uint16_t batch_index;
+    uint8_t context_index;
+    uint16_t action_index;
+} D2H_EVENT_context_switch_breakpoint_reached_event_massage_t;
+
+#define D2H_EVENT_CONTEXT_SWITCH_BREAKPOINT_REACHED_EVENT_PARAMETER_COUNT  (4)
+
+typedef struct {
+    uint32_t previous_clock;
+    uint32_t current_clock;
+} D2H_EVENT_health_monitor_clock_changed_event_message_t;
+
+#define D2H_EVENT_HEALTH_MONITOR_CLOCK_CHANGED_EVENT_PARAMETER_COUNT  (2)
+
+/* D2H_EVENT__message_parameters_t should be in the same order as hailo_notification_message_parameters_t */
+typedef union {
+   D2H_EVENT_rx_error_event_message_t rx_error_event;
+   D2H_EVENT_host_info_event_message_t host_info_event;
+   D2H_EVENT_health_monitor_closed_streams_event_message_t health_monitor_closed_streams_event;
+   D2H_EVENT_health_monitor_temperature_alarm_event_message_t health_monitor_temperature_alarm_event;
+   D2H_EVENT_health_monitor_overcurrent_alert_event_message_t health_monitor_overcurrent_alert_event;
+   D2H_EVENT_health_monitor_lcu_ecc_error_event_message_t health_monitor_lcu_ecc_error_event;
+   D2H_EVENT_health_monitor_cpu_ecc_event_message_t health_monitor_cpu_ecc_event;
+   D2H_EVENT_context_switch_breakpoint_reached_event_massage_t context_switch_breakpoint_reached_event;
+   D2H_EVENT_health_monitor_clock_changed_event_message_t health_monitor_clock_changed_event;
+} D2H_EVENT__message_parameters_t;
+
+typedef struct {
+    D2H_EVENT_HEADER_t header;
+    D2H_EVENT__message_parameters_t message_parameters;
+} D2H_EVENT_MESSAGE_t;
+
+#define PCIE_D2H_EVENT_MAX_SIZE (0x370)
+/**********************************************************************
+ * Public Functions
+ **********************************************************************/
+HAILO_COMMON_STATUS_t D2H_EVENTS__parse_event(D2H_EVENT_MESSAGE_t *d2h_event_message);
+#ifdef __cplusplus
+}
+#endif
+#endif /* __D2H_EVENTS_H__ */
diff --git a/common/include/firmware_header.h b/common/include/firmware_header.h
new file mode 100644 (file)
index 0000000..c7d5254
--- /dev/null
@@ -0,0 +1,85 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+**/
+/**
+ * @file firmware_header.h
+ * @brief Contains definitions needed for generating and handling a firmware header.
+**/
+
+#ifndef __FIRMWARE_HEADER__
+#define __FIRMWARE_HEADER__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdint.h>
+#include "utils.h"
+
+#define FIRMWARE_HEADER_MAGIC_HAILO8 (0x1DD89DE0)
+#define FIRMWARE_HEADER_MAGIC_MERCURY (0xE905DAAB)
+
+typedef enum {
+    FIRMWARE_HEADER_VERSION_INITIAL = 0,
+
+    /* MUST BE LAST */
+    FIRMWARE_HEADER_VERSION_COUNT
+} firmware_header_version_t;
+
+typedef enum {
+    FIRMWARE_TYPE_HAILO8 = 0,
+    FIRMWARE_TYPE_MERCURY
+} firmware_type_t;
+
+
+#ifdef MERCURY
+#define COMPILED_FIRMWARE_TYPE (FIRMWARE_TYPE_MERCURY)
+#elif defined(HAILO8_B0)
+#define COMPILED_FIRMWARE_TYPE (FIRMWARE_TYPE_HAILO8)
+#endif /* MERCURY */
+
+typedef struct {
+    uint32_t magic;
+    uint32_t header_version;
+    uint32_t firmware_major;
+    uint32_t firmware_minor;
+    uint32_t firmware_revision;
+    uint32_t code_size;
+} firmware_header_t;
+
+#if defined(_MSC_VER)
+// TODO: warning C4200
+#pragma warning(push)
+#pragma warning(disable: 4200)
+#endif
+typedef struct {
+    uint32_t key_size;
+    uint32_t content_size;
+    uint8_t certificates_data[0];
+} secure_boot_certificate_t;
+#if defined(_MSC_VER)
+#pragma warning(pop)
+#endif
+
+
+#define MINIMUM_FIRMWARE_CODE_SIZE (20*4)
+// Tightly coupled with ld script
+#define MAXIMUM_APP_FIRMWARE_CODE_SIZE (0x40000)
+#define MAXIMUM_CORE_FIRMWARE_CODE_SIZE (0x18000)
+#define MAXIMUM_SECOND_STAGE_CODE_SIZE (0x80000)
+#define MAXIMUM_FIRMWARE_CERT_KEY_SIZE (0x1000)
+#define MAXIMUM_FIRMWARE_CERT_CONTENT_SIZE (0x1000)
+
+typedef enum {
+    FW_BINARY_TYPE_INVALID = 0,
+    FW_BINARY_TYPE_APP_FIRMWARE,
+    FW_BINARY_TYPE_CORE_FIRMWARE,
+    FW_BINARY_TYPE_SECOND_STAGE_BOOT
+} FW_BINARY_TYPE_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __FIRMWARE_HEADER__ */
diff --git a/common/include/firmware_header_utils.h b/common/include/firmware_header_utils.h
new file mode 100644 (file)
index 0000000..1a001f0
--- /dev/null
@@ -0,0 +1,109 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+**/
+/**
+ * @file firmware_header_utils.h
+ * @brief Utilities for working with the firmware header.
+**/
+
+#ifndef __FIRMWARE_HEADER_UTILS__
+#define __FIRMWARE_HEADER_UTILS__
+#include <stdint.h>
+#include <stdbool.h>
+#include "status.h"
+#include "firmware_header.h"
+#include "firmware_version.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define REVISION_NUMBER_SHIFT (0)
+#define REVISION_APP_CORE_FLAG_BIT_SHIFT (27)
+#define REVISION_RESERVED_0_FLAG_BIT_SHIFT (28)
+#define REVISION_RESERVED_1_FLAG_BIT_SHIFT (29)
+#define REVISION_DEV_FLAG_BIT_SHIFT (30)
+#define REVISION_SECOND_STAGE_FLAG_BIT_SHIFT (31)
+
+#define REVISION_NUMBER_WIDTH (27U)
+#define REVISION_APP_CORE_FLAG_BIT_WIDTH (1U)
+#define REVISION_RESERVED_0_FLAG_BIT_WIDTH (1U)
+#define REVISION_RESERVED_1_FLAG_BIT_WIDTH (1U)
+#define REVISION_DEV_FLAG_BIT_WIDTH (1U)
+#define REVISION_SECOND_STAGE_FLAG_BIT_WIDTH (1U)
+
+#define REVISION_NUMBER_MASK (GET_MASK(REVISION_NUMBER_WIDTH, REVISION_NUMBER_SHIFT))
+#define REVISION_APP_CORE_FLAG_BIT_MASK (GET_MASK(REVISION_APP_CORE_FLAG_BIT_WIDTH, REVISION_APP_CORE_FLAG_BIT_SHIFT))
+#define REVISION_RESERVED_0_FLAG_BIT_MASK (GET_MASK(REVISION_RESERVED_0_FLAG_BIT_WIDTH, REVISION_RESERVED_0_FLAG_BIT_SHIFT))
+#define REVISION_RESERVED_1_FLAG_BIT_MASK (GET_MASK(REVISION_RESERVED_1_FLAG_BIT_WIDTH, REVISION_RESERVED_1_FLAG_BIT_SHIFT))
+#define REVISION_DEV_FLAG_BIT_MASK (GET_MASK(REVISION_DEV_FLAG_BIT_WIDTH, REVISION_DEV_FLAG_BIT_SHIFT))
+#define REVISION_SECOND_STAGE_FLAG_BIT_MASK (GET_MASK(REVISION_SECOND_STAGE_FLAG_BIT_WIDTH, REVISION_SECOND_STAGE_FLAG_BIT_SHIFT))
+
+#define GET_REVISION_NUMBER_VALUE(binary_revision) (REVISION_NUMBER_MASK & binary_revision)
+#define IS_REVISION_DEV(binary_revision) (REVISION_DEV_FLAG_BIT_MASK == (REVISION_DEV_FLAG_BIT_MASK & binary_revision))
+#define DEV_STRING_NOTE(__is_release) ((__is_release)? "" : " (dev)")
+
+/**
+* Validates the FW headers.
+* this function is used from the firmware, from HailoRT and from the second_stage bootloader to validate the headers.
+* @param[in] address                    Address of the firmware.
+* @param[in] firmware_size              Size of the firmware.
+*                                       If the actual size is unknown, set this to the maximum possible size (for example the size of the Flash
+*                                       section holding the FW).
+* @param[in] is_firmware_size_unknown   Set to true if the firmware size is unknown (for example when loading from Flash).
+* @param[out] out_app_firmware_header   (optional) App firmware header
+* @param[out] out_core_firmware_header  (optional) Core firmware header
+* @param[out] out_firmware_cert         (optional) Firmware certificate header
+*/
+HAILO_COMMON_STATUS_t FIRMWARE_HEADER_UTILS__validate_fw_headers(uintptr_t firmware_base_address,
+                                                                 uint32_t firmware_size,
+                                                                 bool is_firmware_size_unknown,
+                                                                 firmware_header_t **out_app_firmware_header,
+                                                                 firmware_header_t **out_core_firmware_header,
+                                                                 secure_boot_certificate_t **out_firmware_cert,
+                                                                 firmware_type_t firmware_type);
+/**
+* Validates the Second stage headers.
+* this function is used from the firmware and from HailoRT to validate the headers.
+* @param[in] address                        Address of the second stage.
+* @param[in] second_stage_size              Size of the second_stage.
+*                                           If the actual size is unknown, set this to the maximum possible size (for example the size of the Flash
+*                                           section holding the Second stage).
+* @param[out] out_second_stage_header       (optional) second_stage header
+*/
+HAILO_COMMON_STATUS_t FIRMWARE_HEADER_UTILS__validate_second_stage_headers(uintptr_t second_stage_base_size,
+                                                                           uint32_t second_stage_size,
+                                                                           firmware_header_t **out_second_stage_header,
+                                                                           firmware_type_t firmware_type);
+
+/**
+* Returns the binary type (App firmware, Core firmware or Second Stage boot)
+* @param[in] binary_revision     The binary revision (Third part in binary version)
+*/
+FW_BINARY_TYPE_t FIRMWARE_HEADER_UTILS__get_fw_binary_type(uint32_t binary_revision);
+
+/**
+* Returns true if the new binary version is older then the minumum allowed.
+* @param[in] new_binary_version               A pointer to the new binary version to update to
+* @param[in] minimum_allowed_binary_version   A pointer to the minimum binary version that is allowed to be upgraded to
+*/
+HAILO_COMMON_STATUS_t FIRMWARE_HEADER_UTILS__is_binary_being_downgraded(const firmware_version_t *new_binary_version, 
+                                                                        const firmware_version_t *minimum_allowed_binary_version);
+
+/**
+* Validates the binary version to prevent downgrade.
+* this function is used from the firmware and from HailoRT to prevent downgrade.
+* @param[in] new_binary_version               A pointer to the new binary version to update to
+* @param[in] minimum_allowed_binary_version   A pointer to the minimum binary version that is allowed to be upgraded to
+* @param[in] fw_binary_type                   A bit that indicates which binary type is passed (app firmware, core firmware, second stage boot)
+*/
+HAILO_COMMON_STATUS_t FIRMWARE_HEADER_UTILS__validate_binary_version(const firmware_version_t *new_binary_version, 
+                                                                     const firmware_version_t *minimum_allowed_binary_version,
+                                                                     FW_BINARY_TYPE_t fw_binary_type);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __FIRMWARE_HEADER_UTILS__ */
diff --git a/common/include/firmware_status.h b/common/include/firmware_status.h
new file mode 100644 (file)
index 0000000..391213e
--- /dev/null
@@ -0,0 +1,1025 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+**/
+/**
+ * @file firmware_status.h
+ * @brief Defines firmware status codes.
+**/
+
+#ifndef __FIRMWARE_STATUS_H__
+#define __FIRMWARE_STATUS_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "status.h"
+#include "utils.h"
+
+
+#define FIRMWARE_STATUS__MASK_FROM_OFFSET_AND_WIDTH(__offset, __width) (((1UL << (__width)) -1UL) << (__offset))
+#define FIRMWARE_STATUS__FIELD_EXTRACT(__input, __offset, __width) \
+    (((FIRMWARE_STATUS__MASK_FROM_OFFSET_AND_WIDTH((__offset), (__width)) & (__input)) >> (__offset)))
+
+/*
+ * +--------------+------------+------------+------------+
+ * |  Bits 0-15   | Bits 16-23 | Bits 24-27 | Bits 28-31 |
+ * +--------------+------------+------------+------------+
+ * | Status value | Module     | Reserved   | Component  |
+ * +--------------+------------+------------+------------+
+ *
+ * */
+
+#define FIRMWARE_STATUS__COMPONENT_OFFSET_BITS (28UL)
+#define FIRMWARE_STATUS__COMPONENT_WIDTH_BITS (4UL)
+#define FIRMWARE_STATUS__COMPONENT_MASK (FIRMWARE_STATUS__MASK_FROM_OFFSET_AND_WIDTH( \
+            FIRMWARE_STATUS__COMPONENT_OFFSET_BITS, FIRMWARE_STATUS__COMPONENT_WIDTH_BITS))
+#define FIRMWARE_STATUS__COMPONENT_MAX ((1UL << FIRMWARE_STATUS__COMPONENT_WIDTH_BITS) - 1UL)
+#define FIRMWARE_STATUS__COMPONENT_SHIFT(__component) ((__component) << FIRMWARE_STATUS__COMPONENT_OFFSET_BITS)
+#define FIRMWARE_STATUS__COMPONENT_GET(__status) (FIRMWARE_STATUS__FIELD_EXTRACT((__status), \
+            FIRMWARE_STATUS__COMPONENT_OFFSET_BITS, FIRMWARE_STATUS__COMPONENT_WIDTH_BITS))
+
+#define FIRMWARE_STATUS__MODULE_INDEX_OFFSET_BITS (16UL)
+#define FIRMWARE_STATUS__MODULE_INDEX_WIDTH_BITS (8UL)
+#define FIRMWARE_STATUS__MODULE_INDEX_MASK (FIRMWARE_STATUS__MASK_FROM_OFFSET_AND_WIDTH( \
+            FIRMWARE_STATUS__MODULE_INDEX_OFFSET_BITS, FIRMWARE_STATUS__MODULE_INDEX_WIDTH_BITS))
+#define FIRMWARE_STATUS__MODULE_INDEX_MAX ((1UL << FIRMWARE_STATUS__MODULE_INDEX_WIDTH_BITS) - 1UL)
+#define FIRMWARE_STATUS__MODULE_INDEX_SHIFT(__module) ((__module) << FIRMWARE_STATUS__MODULE_INDEX_OFFSET_BITS)
+#define FIRMWARE_STATUS__MODULE_INDEX_GET(__status) (FIRMWARE_STATUS__FIELD_EXTRACT((__status), \
+            FIRMWARE_STATUS__MODULE_INDEX_OFFSET_BITS, FIRMWARE_STATUS__MODULE_INDEX_WIDTH_BITS))
+
+#define FIRMWARE_STATUS__VALUE_OFFSET_BITS (0UL)
+#define FIRMWARE_STATUS__VALUE_WIDTH_BITS (16UL)
+#define FIRMWARE_STATUS__VALUE_MASK (FIRMWARE_STATUS__MASK_FROM_OFFSET_AND_WIDTH( \
+            FIRMWARE_STATUS__VALUE_OFFSET_BITS, FIRMWARE_STATUS__VALUE_WIDTH_BITS))
+#define FIRMWARE_STATUS__VALUE_MAX ((1UL << FIRMWARE_STATUS__VALUE_WIDTH_BITS) - 1UL)
+#define FIRMWARE_STATUS__VALUE_SHIFT(__value) ((__value) << FIRMWARE_STATUS__VALUE_OFFSET_BITS)
+#define FIRMWARE_STATUS__VALUE_GET(__status) (FIRMWARE_STATUS__FIELD_EXTRACT((__status), \
+            FIRMWARE_STATUS__VALUE_OFFSET_BITS, FIRMWARE_STATUS__VALUE_WIDTH_BITS))
+
+#define FIRMWARE_STATUS__COMPONENT_ID (4)
+
+
+/* 
+Updating rules:
+1. Always add new statues at the end of the module's statuses
+2. Always add new modules at the end
+3. DO NOT remove existing statuses
+4. DO NOT remove existing modules
+5. DO NOT change the order of existing statuses or modules
+*/
+
+#define FIRMWARE_STATUS__VARIABLES \
+   \
+   FIRMWARE_MODULE__X(FIRMWARE_MODULE__GENERAL)\
+   FIRMWARE_STATUS__X(HAILO_STATUS_UNINITIALIZED)\
+   FIRMWARE_STATUS__X(HAILO_STATUS_INVALID_INTERRUPT_HANDLER)\
+   FIRMWARE_STATUS__X(HAILO_STATUS_CONVERT_TO_TEXTUAL_FAILED)\
+   FIRMWARE_STATUS__X(SYSTEM_STATUS_STATUS_INVALID_PARAMS)\
+   FIRMWARE_STATUS__X(HAILO_STATUS_UNREACHABLE)\
+   FIRMWARE_STATUS__X(HAILO_STATUS_ERROR_HANDLING_UNDEFINED_EXCEPTION)\
+   FIRMWARE_STATUS__X(HAILO_STATUS_ERROR_HANDLING_NMI)\
+   FIRMWARE_STATUS__X(HAILO_STATUS_ERROR_HANDLING_HARD_FAULT)\
+   FIRMWARE_STATUS__X(HAILO_STATUS_ERROR_HANDLING_MPU_FAULT)\
+   FIRMWARE_STATUS__X(HAILO_STATUS_ERROR_HANDLING_BUS_FAULT)\
+   FIRMWARE_STATUS__X(HAILO_STATUS_ERROR_HANDLING_USAGE_FAULT)\
+   FIRMWARE_STATUS__X(HAILO_STATUS_ERROR_SMART_FAIL)\
+   FIRMWARE_STATUS__X(HAILO_STATUS_ERROR_SMART_TIMEOUT)\
+   FIRMWARE_STATUS__X(HAILO_STATUS_ERROR_SMART_NOT_STARTED)\
+   FIRMWARE_STATUS__X(HAILO_STATUS_ERROR_SMART_UNSUPPORTED_OPERATION)\
+   FIRMWARE_STATUS__X(HAILO_STATUS_ERROR_SMART_BIST_NULL_PARAMS)\
+   FIRMWARE_STATUS__X(HAILO_STATUS_ERROR_SMART_BIST_INVALID_BYPASS)\
+   FIRMWARE_STATUS__X(HAILO_STATUS_SOC_INIT_FAILED)\
+   FIRMWARE_STATUS__X(HAILO_STATUS_QSPI_INIT_FAILED)\
+   FIRMWARE_STATUS__X(HAILO_STATUS_INITIAL_I2C_INIT_FAILED)\
+   FIRMWARE_STATUS__X(HAILO_STATUS_SOC_LOAD_CLOCK_FROM_CONFIG_FAILED)\
+   FIRMWARE_STATUS__X(HAILO_STATUS_SECOND_I2C_INIT_FAILED)\
+   FIRMWARE_STATUS__X(HAILO_STATUS_SETTING_PCIE_POWER_MODES_FAILED)\
+   FIRMWARE_STATUS__X(HAILO_STATUS_ETHERNET_SERVICE_INIT_FAILED)\
+   FIRMWARE_STATUS__X(HAILO_STATUS_LWIP_INIT_FAILED)\
+   FIRMWARE_STATUS__X(HAILO_STATUS_CONTROL_TASK_INIT_FAILED)\
+   FIRMWARE_STATUS__X(HAILO_STATUS_PCIE_INIT_FAILED)\
+   FIRMWARE_STATUS__X(HAILO_STATUS_SECURE_SERVICE_INIT_FAILED)\
+   FIRMWARE_STATUS__X(HAILO_STATUS_MAILBOX_INIT_FAILED)\
+   FIRMWARE_STATUS__X(HAILO_STATUS_SENDING_CORE_CPU_LOADED_EVENT_FAILED)\
+   FIRMWARE_STATUS__X(HAILO_STATUS_INTER_CPU_EVENT_MANAGER_INIT_FAILED)\
+   FIRMWARE_STATUS__X(HAILO_STATUS_SOC_CORE_CPU_INIT_FAILED)\
+   FIRMWARE_STATUS__X(HAILO_STATUS_UPDATE_CLOCK_CORE_CPU_FAILED)\
+   FIRMWARE_STATUS__X(HAILO_STATUS_CONTROL_ETH_INIT_FAILED)\
+   FIRMWARE_STATUS__X(HAILO_STATUS_SEMAPHORE_INIT_FAILED)\
+   \
+   FIRMWARE_MODULE__X(FIRMWARE_MODULE__DATAFLOW)\
+   FIRMWARE_STATUS__X(HAILO_DATAFLOW_STATUS_INVALID_PARAMETER)\
+   FIRMWARE_STATUS__X(HAILO_DATAFLOW_STATUS_INVALID_STREAM_INDEX)\
+   FIRMWARE_STATUS__X(HAILO_DATAFLOW_STATUS_STREAM_ALREADY_USED)\
+   FIRMWARE_STATUS__X(HAILO_DATAFLOW_STATUS_INBOUND_INTERRUPT_SETUP_FAILED)\
+   FIRMWARE_STATUS__X(HAILO_DATAFLOW_STATUS_OUTBOUND_INTERRUPT_SETUP_FAILED)\
+   FIRMWARE_STATUS__X(HAILO_DATAFLOW_STATUS_SET_QUEUE_UDP_FILTER_FAILED)\
+   FIRMWARE_STATUS__X(HAILO_DATAFLOW_STATUS_INVALID_RECEIVE_COMMUNICATION)\
+   FIRMWARE_STATUS__X(HAILO_DATAFLOW_STATUS_QUEUE_UDP_DATAFLOW_FAILED)\
+   FIRMWARE_STATUS__X(HAILO_DATAFLOW_STATUS_FREE_TX_DESCIPTORS_FAILED)\
+   FIRMWARE_STATUS__X(HAILO_DATAFLOW_STATUS_ETHERNET_DATAFLOW_INIT_FAILED)\
+   FIRMWARE_STATUS__X(HAILO_DATAFLOW_STATUS_ETHERNET_INTERRUPT_ENABLE_FAILED)\
+   FIRMWARE_STATUS__X(HAILO_DATAFLOW_STATUS_STREAM_ALREADY_CLOSED)\
+   FIRMWARE_STATUS__X(HAILO_DATAFLOW_STATUS_INVALID_BYTES_PER_BUFFER_VALUE)\
+   FIRMWARE_STATUS__X(HAILO_DATAFLOW_STATUS_INVALID_COMMUNICATION_TYPE)\
+   FIRMWARE_STATUS__X(HAILO_DATAFLOW_STATUS_STREAM_NOT_CONFIGURED)\
+   FIRMWARE_STATUS__X(HAILO_DATAFLOW_STATUS_FIFO_ADDRESS_NOT_FOUND)\
+   FIRMWARE_STATUS__X(HAILO_DATAFLOW_STATUS_GET_QUEUE_TX_MEMORY_FAILED)\
+   FIRMWARE_STATUS__X(HAILO_DATAFLOW_STATUS_INVALID_BUFFERS_THRESHOLD)\
+   FIRMWARE_STATUS__X(HAILO_DATAFLOW_STATUS_RELEASE_DATAFLOW_RX_DESCRIPTORS_FAILED)\
+   FIRMWARE_STATUS__X(HAILO_DATAFLOW_STATUS_UDP_TX_MEMORY_FULL)\
+   FIRMWARE_STATUS__X(HAILO_DATAFLOW_STATUS_MIPI_START_FAILED)\
+   FIRMWARE_STATUS__X(HAILO_DATAFLOW_STATUS_MIPI_TX_IS_NOT_SUPPORTED)\
+   FIRMWARE_STATUS__X(HAILO_DATAFLOW_STATUS_PACKETS_PER_SYNC_OVERFLOWS)\
+   FIRMWARE_STATUS__X(HAILO_DATAFLOW_STATUS_PCIE_CONFIGURE_CHANNEL_FAILED)\
+   FIRMWARE_STATUS__X(HAILO_DATAFLOW_STATUS_PCIE_RELEASE_CHANNEL_FAILED)\
+   FIRMWARE_STATUS__X(HAILO_DATAFLOW_STATUS_UNSUPPORTED)\
+   FIRMWARE_STATUS__X(HAILO_DATAFLOW_STATUS_ALLOCATE_RX_QUEUE_FAILED)\
+   FIRMWARE_STATUS__X(HAILO_DATAFLOW_STATUS_UNUSED_DATAFLOW_MANAGER)\
+   FIRMWARE_STATUS__X(HAILO_DATAFLOW_STATUS_LATENCY_MEASUREMENT_IN_PROGRESS)\
+   FIRMWARE_STATUS__X(HAILO_DATAFLOW_STATUS_LATENCY_MEASUREMENT_OVERFLOW)\
+   FIRMWARE_STATUS__X(HAILO_DATAFLOW_STATUS_LATENCY_MEASUREMENT_UNDERFLOW)\
+   FIRMWARE_STATUS__X(HAILO_DATAFLOW_STATUS_LATENCY_MEASUREMENT_READ_WHILE_DISABLED)\
+   FIRMWARE_STATUS__X(HAILO_DATAFLOW_STATUS_INPUT_SYNC_NOT_SUPPORTED)\
+   FIRMWARE_STATUS__X(HAILO_DATAFLOW_STATUS_NO_AVAILABLE_DATAFLOW_MANAGER)\
+   FIRMWARE_STATUS__X(HAILO_DATAFLOW_STATUS_INVALID_DATAFLOW_MANAGER_ID)\
+   FIRMWARE_STATUS__X(HAILO_DATAFLOW_STATUS_DATAFLOW_IS_ACTIVE_ON_ANOTHER_MANAGER)\
+   \
+   FIRMWARE_MODULE__X(FIRMWARE_MODULE__ETHERNET_SERVICE)\
+   FIRMWARE_STATUS__X(ETHERNET_SERVICE_STATUS_INVALID_PARAMS)\
+   FIRMWARE_STATUS__X(ETHERNET_SERVICE_STATUS_CEDI_PROBE_FAILED)\
+   FIRMWARE_STATUS__X(ETHERNET_SERVICE_STATUS_MISMATCHING_PDATA_SIZE)\
+   FIRMWARE_STATUS__X(ETHERNET_SERVICE_STATUS_MISMATCHING_RX_DESC_LIST_SIZE)\
+   FIRMWARE_STATUS__X(ETHERNET_SERVICE_STATUS_MISMATCHING_TX_DESC_LIST_SIZE)\
+   FIRMWARE_STATUS__X(ETHERNET_SERVICE_STATUS_MISMATCHING_STATISTICS_SIZE)\
+   FIRMWARE_STATUS__X(ETHERNET_SERVICE_STATUS_ADD_RX_BUFFER_FAILED)\
+   FIRMWARE_STATUS__X(ETHERNET_SERVICE_STATUS_ADD_DATAFLOW_RX_PAYLOAD_BUFFER_FAILED)\
+   FIRMWARE_STATUS__X(ETHERNET_SERVICE_STATUS_ADD_DATAFLOW_RX_HEADER_BUFFER_FAILED)\
+   FIRMWARE_STATUS__X(ETHERNET_SERVICE_STATUS_CEDI_INIT_FAILED)\
+   FIRMWARE_STATUS__X(ETHERNET_SERVICE_STATUS_READ_RX_BUFFER_FAILED)\
+   FIRMWARE_STATUS__X(ETHERNET_SERVICE_STATUS_READ_RX_HEADER_BUFFER_FAILED)\
+   FIRMWARE_STATUS__X(ETHERNET_SERVICE_STATUS_READ_RX_PAYLOAD_BUFFER_FAILED)\
+   FIRMWARE_STATUS__X(ETHERNET_SERVICE_STATUS_RX_HEADER_NO_DATA)\
+   FIRMWARE_STATUS__X(ETHERNET_SERVICE_STATUS_RX_PAYLOAD_NO_DATA)\
+   FIRMWARE_STATUS__X(ETHERNET_SERVICE_STATUS_RX_HEADER_UNEXPECTED_FRAME_DESCRIPTOR)\
+   FIRMWARE_STATUS__X(ETHERNET_SERVICE_STATUS_RX_PAYLOAD_UNEXPECTED_FRAME_DESCRIPTOR)\
+   FIRMWARE_STATUS__X(ETHERNET_SERVICE_STATUS_TX_QUEUE_FAILED)\
+   FIRMWARE_STATUS__X(ETHERNET_SERVICE_STATUS_FREE_TX_DESCRIPTOR_ERROR)\
+   FIRMWARE_STATUS__X(ETHERNET_SERVICE_STATUS_SET_T1_SCREEN_REG_FAILED)\
+   FIRMWARE_STATUS__X(ETHERNET_SERVICE_STATUS_INTERRUPT_SETUP_FAILED)\
+   FIRMWARE_STATUS__X(ETHERNET_SERVICE_STATUS_INTERRUPT_ENABLE_FAILED)\
+   FIRMWARE_STATUS__X(ETHERNET_SERVICE_STATUS_DATAFLOW_INTERRUPT_SETUP_FAILED)\
+   FIRMWARE_STATUS__X(ETHERNET_SERVICE_STATUS_DATAFLOW_INTERRUPT_ENABLE_FAILED)\
+   FIRMWARE_STATUS__X(ETHERNET_SERVICE_STATUS_MDIO_WRITE_TIMEOUT)\
+   FIRMWARE_STATUS__X(ETHERNET_SERVICE_STATUS_MDIO_WRITE_FAILED)\
+   FIRMWARE_STATUS__X(ETHERNET_SERVICE_STATUS_MDIO_READ_TIMEOUT)\
+   FIRMWARE_STATUS__X(ETHERNET_SERVICE_STATUS_MDIO_READ_FAILED)\
+   FIRMWARE_STATUS__X(ETHERNET_SERVICE_STATUS_SET_QN_EVENT_FAILED)\
+   FIRMWARE_STATUS__X(ETHERNET_SERVICE_STATUS_SET_DATAFLOW_EVENT_FAILED)\
+   FIRMWARE_STATUS__X(ETHERNET_SERVICE_STATUS_TX_QUEUE_SEG_ALLOC_FAILED)\
+   FIRMWARE_STATUS__X(ETHERNET_SERVICE_STATUS_SET_TX_CHECKSUM_OFFLOAD_FAILED)\
+   FIRMWARE_STATUS__X(ETHERNET_SERVICE_STATUS_SET_HDR_DATA_SPLIT_FAILED)\
+   FIRMWARE_STATUS__X(ETHERNET_SERVICE_STATUS_INVALID_MAC_SIZE)\
+   FIRMWARE_STATUS__X(ETHERNET_SERVICE_STATUS_GET_SPECIFIC_ADDR_FAILED)\
+   FIRMWARE_STATUS__X(ETHERNET_SERVICE_STATUS_FREE_PAYLOAD_TX_DESCRIPTOR_FAILED)\
+   FIRMWARE_STATUS__X(ETHERNET_SERVICE_STATUS_FREE_HEADER_TX_DESCRIPTOR_ERROR)\
+   FIRMWARE_STATUS__X(ETHERNET_SERVICE_STATUS_MISMATCHING_FIFO_ADDRESS)\
+   FIRMWARE_STATUS__X(ETHERNET_SERVICE_STATUS_OPTIMIZED_EDD_DATAFLOW_TX_FAILED)\
+   FIRMWARE_STATUS__X(ETHERNET_SERVICE_STATUS_SET_Q0_EVENT_FAILED)\
+   FIRMWARE_STATUS__X(ETHERNET_SERVICE_STATUS_SWAP_DATAFLOW_DESCRIPTOR_FAILED)\
+   FIRMWARE_STATUS__X(ETHERNET_SERVICE_STATUS_DATAFLOW_RX_NO_DATA)\
+   FIRMWARE_STATUS__X(ETHERNET_SERVICE_STATUS_UNALIGNED_RX_INJECTION)\
+   FIRMWARE_STATUS__X(ETHERNET_SERVICE_STATUS_DATAFLOW_HANDLE_INJECTION_FAILED)\
+   FIRMWARE_STATUS__X(ETHERNET_SERVICE_STATUS_RX_UNEXPECTED_FRAME_SIZE)\
+   FIRMWARE_STATUS__X(ETHERNET_SERVICE_STATUS_RX_NO_DATA)\
+   FIRMWARE_STATUS__X(ETHERNET_SERVICE_STATUS_RX_UNEXPECTED_FRAME_DESCRIPTOR)\
+   FIRMWARE_STATUS__X(ETHERNET_SERVICE_STATUS_SET_TIMER_INC_FAILED)\
+   FIRMWARE_STATUS__X(ETHERNET_SERVICE_DATAFLOW_RX_QUEUE_UNAVAILABLE)\
+   FIRMWARE_STATUS__X(ETHERNET_SERVICE_DATAFLOW_RX_QUEUE_ALREADY_FREE)\
+   FIRMWARE_STATUS__X(ETHERNET_SERVICE_STATUS_RESET_RX_QUEUE_FAILED)\
+   FIRMWARE_STATUS__X(ETHERNET_SERVICE_STATUS_RX_STREAM_TO_ETHERNET_QUEUE_FAILED)\
+   FIRMWARE_STATUS__X(ETHERNET_SERVICE_STATUS_ETHERNET_UNSUPPORTED)\
+   FIRMWARE_STATUS__X(ETHERNET_SERVICE_STATUS_MDIO_UNSUPPORTED)\
+   FIRMWARE_STATUS__X(ETHERNET_SERVICE_STATUS_PHY_RESET_TIMEOUT)\
+   \
+   FIRMWARE_MODULE__X(FIRMWARE_MODULE__CONTROL)\
+   FIRMWARE_STATUS__X(HAILO_CONTROL_STATUS_INVALID_PCIE_CONTROL_MODE)\
+   FIRMWARE_STATUS__X(HAILO_CONTROL_STATUS_MESSAGE_QUEUE_CREATE_FAILED)\
+   FIRMWARE_STATUS__X(HAILO_CONTROL_STATUS_INVALID_VERSION)\
+   FIRMWARE_STATUS__X(HAILO_CONTROL_STATUS_OVERRUN_BEFORE_PARAMETERS)\
+   FIRMWARE_STATUS__X(HAILO_CONTROL_STATUS_OVERRUN_AT_PARAMETER)\
+   FIRMWARE_STATUS__X(HAILO_CONTROL_STATUS_CONTROL_ACK_RECEIVED)\
+   FIRMWARE_STATUS__X(HAILO_CONTROL_STATUS_INVALID_OPCODE)\
+   FIRMWARE_STATUS__X(HAILO_CONTROL_STATUS_INVALID_COMMUNICATION_TYPE)\
+   FIRMWARE_STATUS__X(HAILO_CONTROL_STATUS_LWIP_TX_QUEUE_FRAME_FAILED)\
+   FIRMWARE_STATUS__X(HAILO_CONTROL_STATUS_PCIE_INIT_FAILED)\
+   FIRMWARE_STATUS__X(HAILO_CONTROL_STATUS_PCIE_REPLY_FAILED)\
+   FIRMWARE_STATUS__X(HAILO_CONTROL_STATUS_INIT_RESPONSE_FAILED)\
+   FIRMWARE_STATUS__X(HAILO_CONTROL_STATUS_INVALID_DEVICE_ARCHITECTURE)\
+   FIRMWARE_STATUS__X(HAILO_CONTROL_STATUS_INVALID_PLATFORM_ID)\
+   FIRMWARE_STATUS__X(HAILO_CONTROL_STATUS_UNSUPPORTED_OPCODE)\
+   FIRMWARE_STATUS__X(HAILO_CONTROL_STATUS_INVALID_PERCENTAGE_RECEIVED)\
+   FIRMWARE_STATUS__X(HAILO_CONTROL_STATUS_COMMUNICATION_PARAMS_UNINITIALIZED)\
+   \
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_INCORRECT_PARAMETER_COUNT)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_INVALID_ADDRESS_SIZE)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_INVALID_DATA_COUNT_SIZE)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_INVALID_STREAM_INDEX_SIZE)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_INVALID_IS_INPUT_SIZE)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_INVALID_COMMUNICATION_TYPE_LENGTH)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_INVALID_COMMUNICATION_TYPE)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_GET_ETHERNET_HEADER_FAILED)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_INVALID_COMMUNICATION_PARAMS_LENGTH)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_DATAFLOW_CONFIG_FAILED)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_DATAFLOW_OPEN_STREAM_FAILED)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_DATAFLOW_CLOSE_STREAM_FAILED)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_DATAFLOW_INVALID_PACKETS_INPUT_BUFFERS_RATIO)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_INVALID_OPERATION_TYPE_SIZE)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_INVALID_OPERATION_TYPE)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_RESET_PHY_FAILED)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_INVALID_RESET_TYPE)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_SYSTEM_RESET_RETURNED)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_DATAFLOW_NOT_IDLE)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_INVALID_BYTES_PER_BUFFER_LENGTH)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_INVALID_BUFFERS_PER_FRAME_LENGTH)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_DATAFLOW_SET_STREAM_REGISTERS_FAILED)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_INVALID_STREAM_INDEX)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_INVALID_CONFIG_TYPE_LENGTH)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_INVALID_CONFIG_CORE_TOP_TYPE)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_INVALID_CONFIG_CORE_TOP_CONFIG_PARAMS_LENGTH)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_INVALID_STREAM_THRESHOLD_LENGTH)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_INVALID_POWER_MEASUREMENT_READ)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_FAILED_SET_POWER_MEASUREMENT)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_FAILED_GET_POWER_MEASUREMENT)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_FAILED_START_POWER_MEASUREMENT)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_FAILED_STOP_POWER_MEASUREMENT)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_FAILED_SINGLE_POWER_MEASUREMENT)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_INVALID_POWER_MEASUREMENT_INDEX)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_UPDATE_START_FAILED)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_INVALID_REGISTER_ADDRESS_SIZE)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_WRITE_FIRMWARE_UPDATE_FAILED)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_VALIDATE_FIRMWARE_UPDATE_FAILED)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_INVALID_MD5_SIZE)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_INVALID_FIRMWARE_SIZE)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_FINISH_FIRMWARE_UPDATE_FAILED)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_USER_CONFIG_EXAMINE_FAILED)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_USER_CONFIG_ERASE_FAILED)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_I2C_WRITE_FAILED)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_I2C_READ_FAILED)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_INVALID_SENSOR_CONFIG_IS_FIRST_LENGTH)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_INVALID_SENSOR_CONFIG_DATA_SIZE_LENGTH)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_INVALID_SENSOR_CONFIG_START_OFFSET_LENGTH)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_INVALID_SENSOR_CONFIG_RESET_DATA_SIZE_LENGTH)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_INVALID_SENSOR_CONFIG_SENSOR_TYPE_LENGTH)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_INVALID_SENSOR_CONFIG_SECTION_INDEX_LENGTH)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_INVALID_SENSOR_CONFIG_TOTAL_DATA_SIZE_LENGTH)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_INVALID_SENSOR_CONFIG_SLAVE_ADDRESS_LENGTH)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_INVALID_SENSOR_CONFIG_SLAVE_REGISTERS_LENGTH)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_INVALID_SENSOR_CONFIG_SLAVE_OFFSET_LENGTH)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_INVALID_SENSOR_CONFIG_SLAVE_BUS_INDEX_LENGTH)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_INVALID_SENSOR_CONFIG_SLAVE_BUS_HOLD_LENGTH)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_INVALID_SENSOR_CONFIG_SLAVE_ENDIANNESS_LENGTH)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_LATENCY_CONFIG_FAILED)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_LATENCY_READ_FAILED)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_INVALID_DATA_LENGTH)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_MIPI_INIT_FAILED)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_PARSE_COMMAND_FAILED)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_EXECUTE_COMMAND_FAILED)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_DATAFLOW_INVALID_FRAMES_PER_SYNC)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_DATAFLOW_INVALID_PACKETS_PER_FRAME)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_DATAFLOW_INVALID_SYNC_SIZE)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_PARSE_MESSAGE_FAILED)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_INVALID_PLATFORM_ID)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_INVALID_DEVICE_ARCHITECTURE)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_UNSUPPORTED_OPCODE_FOR_DEVICE)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_INVALID_USE_RTP_LENGTH)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_INVALID_DATA_RATE)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_INTER_CPU_REQUEST_FAILED)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_INVALID_INTER_CPU_OPCODE)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_CORE_CPU_RETURNED_STATUS_FAILURE)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_INVALID_CONTEXT_SWITCH_VERSION_LENGTH)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_INVALID_CONTEXT_SWITCH_APP_COUNT_LENGTH)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_INVALID_CONTEXT_SWITCH_APP_HEADER_LENGTH)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_INVALID_CONTEXT_SWITCH_CLUSTER_END_GARANTEE_TRIGGER_LENGTH)/* DEPRECATED */\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_INVALID_CONTEXT_SWITCH_NUMBER_OF_EDGE_LAYERS_LENGTH)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_INVALID_CONTEXT_SWITCH_NUMBER_OF_TRIGGER_GROUPS_LENGTH)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_INVALID_CONTEXT_SWITCH_TRIGGER_GROUPS_LENGTH)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_CONTEXT_SWITCH_CONTEXT_NETWORK_DATA_LENGTH_HIGHER_THEN_MAX_CONTROL_SIZE)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_IDLE_TIME_MEASUREMENT_ALREADY_SET)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_FAILED_IDLE_TIME_GET_MEASUREMENT)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_FAILED_IDLE_TIME_SET_MEASUREMENT)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_INVALID_CONTEXT_SWITCH_CONTEXT_INDEX_LENGTH)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_INVALID_PCIE_CONFIG_CHANNEL_NUMBER_LENGTH)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_INVALID_STATE_MACHINE_STATUS_LENGTH)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_NULL_ARGUMENT_PASSED)\
+   FIRMWARE_STATUS__X(CONTROL_TASK_STATUS_ACTION_LIST_LENGTH_EXITED_BUFFER_SIZE)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_INVALID_CPU_ID_LENGTH)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_INVALID_SHOULD_ENABLE_LENGTH)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_WD_ENABLE_FAILED)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_INVALID_WD_CYCLES_LENGTH)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_WD_CONFIG_FAILED)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_PREVIOUS_SYSTEM_STATE_FAILED)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_INVALID_CPU_ID)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_INVALID_SLICING_APPROACH_LENGTH) /* DEPRECATED */\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_INVALID_INTERRUPT_TYPE_LENGTH)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_INVALID_INTERRUPT_INDEX_LENGTH)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_INVALID_INTERRUPT_SUB_INDEX_LENGTH)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_D2H_EVENT_MANAGER_SET_NEW_HOST_INFO_FAIL)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_D2H_EVENT_MANAGER_SET_NEW_HOST_INFO_INVALID_CONNECTION_TYPE_LENGTH)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_D2H_EVENT_MANAGER_SET_NEW_HOST_INFO_INVALID_IP_ADDR_LENGTH)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_D2H_EVENT_MANAGER_SET_NEW_HOST_INFO_INVALID_PORT_LENGTH)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_D2H_EVENT_MANAGER_HOST_INFO_INVALID_PRIORITY_LENGTH)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_D2H_EVENT_MANAGER_HOST_INFO_SEND_EVENT_FAIL)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_CANT_GET_PCIE_CONTROL_PACKET)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_FAILED_SENDING_DATA_TO_CONTROL_QUEUE)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_GOT_INVALID_DVM_FOR_THAT_PLATFORM)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_INVALID_APPLICATION_INDEX_LENGTH)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_CANT_GET_CHIP_TEMPERATURE)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_DIRECT_EEPROM_ADDRESS_ACCESS_NOT_ALLOWED)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_INVALID_ACTION_LIST_OFFSET_LENGTH)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_CANT_GET_SOC_ID)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_CANT_GET_LCS)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_GET_ETH_MAC_FAILED)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_ENABLE_DEBUGGING_FAILED)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_INVALID_SKIP_NN_STREAM_CONFIG_LENGTH)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_INVALID_APPLICATION_BATCH_SIZE_LENGTH)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_MIPI_INCORRECT_IMG_IN_DATA_TYPE)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_MIPI_INCORRECT_PIXELS_PER_CLOCK)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_INVALID_BREAKPOINT_ID_LENGTH)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_INVALID_BREAKPOINT_CONTROL_LENGTH)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_INVALID_BREAKPOINT_DATA_LENGTH)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_INVALID_CONTEXT_SWITCH_IS_FIRST_CONTROL_PER_CONTEXT_LENGTH)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_INVALID_CONTEXT_SWITCH_IS_LAST_CONTROL_PER_CONTEXT_LENGTH)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_INVALID_IS_RMA_SIZE)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_INVALID_PCIE_DATAFLOW_TYPE)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_INVALID_PCIE_CONFIG_CHANNEL_NUMBER)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_INVALID_CONTEXT_STREAM_REMAP_DATA_LENGTH)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_CONTEXT_SWITCH_UNSUPPORTED)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_ACCESS_RESTRICTED_MEMORY_AREA)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_REQUSET_LENGTH_OVERFLOW)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_INVALID_REQUSET_LENGTH)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_UNEXPECTED_ACK_VALUE)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_PART_OF_THE_MESSAGE_NOT_PARSED)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_INVALID_CONTEXT_SWITCH_CONTEXT_CFG_BASE_ADDRESS_LENGTH)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_INVALID_CONTEXT_SWITCH_CONTEXT_CFG_TOTAL_DESCRIPTORS_LENGTH)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_INVALID_ROW_SIZE_LENGTH)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_INVALID_POWER_MODE_LENGTH)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_INVALID_SECOND_STAGE_SIZE)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_COPY_SECOND_STAGE_TO_FLASH_FAILED)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_MIPI_MISMATCH_SIZES_BETWEEN_MIPI_OUTPUT_AND_STREAM)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_MIPI_MISMATCH_SIZES_BETWEEN_OUTPUT_DATA_TYPE_AND_PIXELS_PER_CLOCK)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_INVALID_VALIDATION_FEATURES_LENGTH)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_OBSOLETE_CONTROL)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_INVALID_BATCH_INDEX_LENGTH)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_INVALID_ENABLE_USER_CONFIGURATION_LENGTH)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_SYSTEM_FORCED_RESET_RETURNED)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_INVALID_LOGGER_LEVEL)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_INVALID_LOGGER_INTERFACE)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_INVALID_IS_TOP_TEST_LENGTH)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_INVALID_TOP_BYPASS_BITMAP_LENGTH)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_INVALID_CLUSTER_BITMAP_LENGTH)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_INVALID_CLUSTER_BYPASS_BITMAP_0_LENGTH)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_INVALID_CLUSTER_BYPASS_BITMAP_1_LENGTH)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_BIST_NOT_SUPPORTED)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_INVALID_BYPASS_SELECTION)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_ONLY_POWER_CAN_BE_SAMPLED_WITH_EVB_TOTAL_POWER_OPTION)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_UNSUPPORTED_DEVICE)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_INVALID_DATAFLOW_MANAGER_ID_LENGTH)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_INVALID_NN_STREAM_CONFIG_LENGTH)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_INVALID_IS_SINGLE_CONTEXT_LENGTH)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_INVALID_SLAVE_ENDIANNESS_VALUE)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_PVT_IS_NOT_SUPPORTED)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_FAILED_SETTING_THROTTLING_STATE)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_FAILED_SETTING_OVERCURRENT_STATE)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_CONTROL_UNSUPPORTED)\
+   FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_CONTROL_DEPRECATED)\
+   \
+   FIRMWARE_MODULE__X(FIRMWARE_MODULE__POWER_MEASUREMENT)\
+   FIRMWARE_STATUS__X(HAILO_POWER_MEASUREMENT_STATUS_POWER_INIT_ERROR)\
+   FIRMWARE_STATUS__X(HAILO_POWER_MEASUREMENT_STATUS_DVM_ERROR)\
+   FIRMWARE_STATUS__X(HAILO_POWER_MEASUREMENT_STATUS_INVALID_GLOBAL_ARRAY)\
+   FIRMWARE_STATUS__X(HAILO_POWER_MEASUREMENT_STATUS_NULL_ARGUMENT_PASSED)\
+   FIRMWARE_STATUS__X(HAILO_POWER_MEASUREMENT_STATUS_MAIN_TASK_RETURNED)\
+   FIRMWARE_STATUS__X(HAILO_POWER_MEASUREMENT_STATUS_EVENT_BIT_NOT_SET)\
+   FIRMWARE_STATUS__X(HAILO_POWER_MEASUREMENT_STATUS_EVENT_BIT_NOT_CLEARED)\
+   FIRMWARE_STATUS__X(HAILO_POWER_MEASUREMENT_STATUS_INVALID_MEASUREMENT_ARGUMENTS)\
+   FIRMWARE_STATUS__X(HAILO_POWER_MEASUREMENT_STATUS_INVALID_INTERVAL)\
+   FIRMWARE_STATUS__X(HAILO_POWER_MEASUREMENT_STATUS_ALREADY_STARTED)\
+   FIRMWARE_STATUS__X(HAILO_POWER_MEASUREMENT_STATUS_INIT_DVMS_FAILED)\
+   FIRMWARE_STATUS__X(HAILO_POWER_MEASUREMENT_STATUS_INVALID_DVM)\
+   FIRMWARE_STATUS__X(HAILO_POWER_MEASUREMENT_TASK_NOT_RUNNING)\
+   FIRMWARE_STATUS__X(HAILO_POWER_MEASUREMENT_DVM_ALREADY_IN_USE_FOR_PERIODIC_SAMPLING)\
+   \
+   FIRMWARE_MODULE__X(FIRMWARE_MODULE__MIPI)\
+   FIRMWARE_STATUS__X(HAILO_MIPI_STATUS_INVALID_RX_ID)\
+   FIRMWARE_STATUS__X(HAILO_MIPI_STATUS_INVALID_TX_ID)\
+   FIRMWARE_STATUS__X(HAILO_MIPI_STATUS_UNINITIALIZED_CONFIG)\
+   FIRMWARE_STATUS__X(HAILO_MIPI_STATUS_NULL_CONFIG_PASSED)\
+   FIRMWARE_STATUS__X(HAILO_MIPI_STATUS_TIMEOUT)\
+   FIRMWARE_STATUS__X(HAILO_MIPI_STATUS_INVALID_NUMBER_OF_LANES)\
+   FIRMWARE_STATUS__X(HAILO_MIPI_STATUS_INVALID_DATA_RATE)\
+   FIRMWARE_STATUS__X(HAILO_MIPI_STATUS_INVALID_TX_DATA_TYPE)\
+   FIRMWARE_STATUS__X(HAILO_MIPI_STATUS_INVALID_VIRTUAL_CHANNEL)\
+   FIRMWARE_STATUS__X(HAILO_MIPI_STATUS_CONFIG_CAMERA_FAILED)\
+   FIRMWARE_STATUS__X(HAILO_MIPI_STATUS_INCORRECT_CAMERA_CONFIG_INDEX)\
+   FIRMWARE_STATUS__X(HAILO_MIPI_STATUS_RESET_CAMERA_FAILED)\
+   FIRMWARE_STATUS__X(HAILO_MIPI_STATUS_INVALID_PARAMS)\
+   FIRMWARE_STATUS__X(HAILO_MIPI_STATUS_DATAFLOW_PIPE_UNAVAILABLE)\
+   FIRMWARE_STATUS__X(HAILO_MIPI_STATUS_PIPE_INDEX_DIDNT_INITIALIZED)\
+   FIRMWARE_STATUS__X(HAILO_MIPI_STATUS_DPHY_PIPE_DIDNT_SET)\
+   FIRMWARE_STATUS__X(HAILO_MIPI_STATUS_DPHY_PIPE_DIDNT_ALLOCATED)\
+   FIRMWARE_STATUS__X(HAILO_MIPI_STATUS_MIPI_RX_ID_2_VALID_ONLY_IN_B0) /* DEPRECATED */\
+   FIRMWARE_STATUS__X(HAILO_MIPI_STATUS_MIPI_RX_PHY_WRITE_FAIL)\
+   FIRMWARE_STATUS__X(HAILO_MIPI_STATUS_MIPI_UNSUPPORTED)\
+   FIRMWARE_STATUS__X(HAILO_MIPI_STATUS_INCORRECT_ISP_WINDOW_INPUT)\
+   \
+   FIRMWARE_MODULE__X(FIRMWARE_MODULE__USER_CONFIG)\
+   FIRMWARE_STATUS__X(USER_CONFIG_STATUS_INVALID_PARAMS)\
+   FIRMWARE_STATUS__X(USER_CONFIG_STATUS_INVALID_MAGIC)\
+   FIRMWARE_STATUS__X(USER_CONFIG_INVALID_VERSION)\
+   FIRMWARE_STATUS__X(USER_CONFIG_STATUS_INVALID_CATEGORY)\
+   FIRMWARE_STATUS__X(USER_CONFIG_STATUS_INVALID_ENTRY_ID)\
+   FIRMWARE_STATUS__X(USER_CONFIG_STATUS_INVALID_ENTRY_SIZE)\
+   FIRMWARE_STATUS__X(USER_CONFIG_STATUS_MAXIMUM_SIZE_EXCEEDED)\
+   FIRMWARE_STATUS__X(USER_CONFIG_STATUS_SECTION_ERASE_FAILED)\
+   FIRMWARE_STATUS__X(USER_CONFIG_STATUS_READ_FROM_EEPROM_FAILED)\
+   FIRMWARE_STATUS__X(USER_CONFIG_STATUS_READ_FROM_FLASH_FAILED)\
+   FIRMWARE_STATUS__X(USER_CONFIG_STATUS_READ_FROM_PCIE_PRELOAD_FAILED)\
+   FIRMWARE_STATUS__X(USER_CONFIG_STATUS_NULL_POINTER_PASSED)\
+   \
+   FIRMWARE_MODULE__X(FIRMWARE_MODULE__I2C)\
+   FIRMWARE_STATUS__X(HAILO_I2C_STATUS_UNSUPPORTED_DEVICE)\
+   FIRMWARE_STATUS__X(HAILO_I2C_STATUS_INTERRUPT_SETUP_FAILED)\
+   FIRMWARE_STATUS__X(HAILO_I2C_STATUS_INTERRUPT_ENABLE_FAILED)\
+   FIRMWARE_STATUS__X(HAILO_I2C_STATUS_VENDOR_INIT_FAILED)\
+   FIRMWARE_STATUS__X(HAILO_I2C_STATUS_VENDOR_START_FAILED)\
+   FIRMWARE_STATUS__X(HAILO_I2C_STATUS_VENDOR_READ_FAILED)\
+   FIRMWARE_STATUS__X(HAILO_I2C_STATUS_VENDOR_WRITE_FAILED)\
+   FIRMWARE_STATUS__X(HAILO_I2C_STATUS_VENDOR_TIMEOUT_FAILED)\
+   FIRMWARE_STATUS__X(HAILO_I2C_STATUS_MISMATCHING_PDATA_SIZE)\
+   FIRMWARE_STATUS__X(HAILO_I2C_STATUS_INVALID_WRITE_SIZE)\
+   FIRMWARE_STATUS__X(HAILO_I2C_STATUS_INVALID_SLAVE_INDEX)\
+   FIRMWARE_STATUS__X(HAILO_I2C_STATUS_INVALID_BUS_INDEX)\
+   FIRMWARE_STATUS__X(HAILO_I2C_STATUS_NULL_ARGUMENT_PASSED)\
+   FIRMWARE_STATUS__X(HAILO_I2C_STATUS_SEMAPHORE_CREATION_FAILED)\
+   FIRMWARE_STATUS__X(HAILO_I2C_STATUS_MODULE_UNINITIALIZED)\
+   FIRMWARE_STATUS__X(HAILO_I2C_STATUS_FAILED_TO_ACQUIRE_SEMAPHORE)\
+   FIRMWARE_STATUS__X(HAILO_I2C_STATUS_MISMATCHING_REGS_SIZE)\
+   FIRMWARE_STATUS__X(HAILO_I2C_STATUS_INVALID_ENDIANNESS)\
+   FIRMWARE_STATUS__X(HAILO_I2C_STATUS_INVALID_OPERATION_LENGTH)\
+   FIRMWARE_STATUS__X(HAILO_I2C_STATUS_UNSUPPORTED_BUS_SPEED)\
+   FIRMWARE_STATUS__X(HAILO_I2C_STATUS_FAILED_TO_RELASE_SEMAPHORE)\
+   FIRMWARE_STATUS__X(HAILO_I2C_STATUS_INVALID_SLAVE_ENDIANNESS_VALUE)\
+   \
+   FIRMWARE_MODULE__X(FIRMWARE_MODULE__LWIP)\
+   FIRMWARE_STATUS__X(LWIP_PORT_STATUS_INVALID_PARAMS)\
+   FIRMWARE_STATUS__X(LWIP_MAIN_LOOP_STATUS_FAILED_ADDING_NETWORK_INTERFACE)\
+   FIRMWARE_STATUS__X(LWIP_MAIN_LOOP_STATUS_INTERRUPT_ENABLE_FAILED)\
+   FIRMWARE_STATUS__X(LWIP_MAIN_LOOP_STATUS_DHCP_START_FAILED)\
+   FIRMWARE_STATUS__X(LWIP_CONFIG_STATUS_INVALID_QUEUE_NUMBER)\
+   FIRMWARE_STATUS__X(LWIP_CONFIG_STATUS_QUEUE_ALREADY_USED)\
+   FIRMWARE_STATUS__X(LWIP_CONFIG_STATUS_SET_UDP_FILTER_FAILED)\
+   FIRMWARE_STATUS__X(LWIP_CONFIG_STATUS_INVALID_SOCKET_ID)\
+   FIRMWARE_STATUS__X(LWIP_CONFIG_STATUS_UDP_PCB_NEW_FAILED)\
+   FIRMWARE_STATUS__X(LWIP_CONFIG_STATUS_UDP_BIND_FAILED)\
+   FIRMWARE_STATUS__X(LWIP_CONFIG_STATUS_INTERRUPT_SETUP_FAILED)\
+   FIRMWARE_STATUS__X(LWIP_CONFIG_STATUS_INTERRUPT_ENABLE_FAILED)\
+   FIRMWARE_STATUS__X(LWIP_CONFIG_STATUS_LWIP_SEND_FAILED)\
+   FIRMWARE_STATUS__X(LWIP_TX_STATUS_QUEUE_INIT_FAILED)\
+   FIRMWARE_STATUS__X(LWIP_TX_STATUS_QUEUE_SEND_FAILED)\
+   FIRMWARE_STATUS__X(LWIP_TX_STATUS_QUEUE_RECEIVE_FAILED)\
+   FIRMWARE_STATUS__X(LWIP_TX_STATUS_QUEUE_EMPTY)\
+   FIRMWARE_STATUS__X(LWIP_TX_STATUS_PBUF_ALLOC_FAILED)\
+   FIRMWARE_STATUS__X(LWIP_TX_STATUS_UDP_SENDTO_FAILED)\
+   \
+   FIRMWARE_MODULE__X(FIRMWARE_MODULE__PARALLEL_CAMERA)\
+   FIRMWARE_STATUS__X(HAILO_PARALLEL_STATUS_INTERRUPT_SETUP_FAILED)\
+   FIRMWARE_STATUS__X(HAILO_PARALLEL_STATUS_INTERRUPT_ENABLE_FAILED)\
+   FIRMWARE_STATUS__X(HAILO_PARALLEL_STATUS_ERROR_INDICATION)\
+   FIRMWARE_STATUS__X(HAILO_PARALLEL_STATUS_I2C_CONFIG_FAILED)\
+   \
+   FIRMWARE_MODULE__X(FIRMWARE_MODULE__QSPI)\
+   FIRMWARE_STATUS__X(QSPI_STATUS_PROBE_FAILED)\
+   FIRMWARE_STATUS__X(QSPI_STATUS_PRIVATE_DATA_SIZE_MISMATCH)\
+   FIRMWARE_STATUS__X(QSPI_STATUS_INIT_FAILED)\
+   FIRMWARE_STATUS__X(QSPI_STATUS_DISABLE_WRITE_PROTECT_FAILED)\
+   FIRMWARE_STATUS__X(QSPI_STATUS_MODULE_UNITIALIZED)\
+   FIRMWARE_STATUS__X(QSPI_STATUS_MISALIGNED_ADDRESS)\
+   FIRMWARE_STATUS__X(QSPI_STATUS_BLOCK_ERASE_FAILED)\
+   FIRMWARE_STATUS__X(QSPI_STATUS_CLEAR_AHB_REMAP_FAILED)\
+   \
+   FIRMWARE_MODULE__X(FIRMWARE_MODULE__PCIE_SERVICE)\
+   FIRMWARE_STATUS__X(PCIE_SERVICE_STATUS_INVALID_PARAMETERS)\
+   FIRMWARE_STATUS__X(PCIE_SERVICE_STATUS_INTERRUPT_HANDLER_FAILED)\
+   FIRMWARE_STATUS__X(PCIE_SERVICE_STATUS_INTERRUPT_CORE_HANDLER_FAILED)\
+   FIRMWARE_STATUS__X(PCIE_SERVICE_STATUS_MISMATCHING_MD5)\
+   FIRMWARE_STATUS__X(PCIE_SERVICE_STATUS_UNSUPPORTED_SOC)\
+   FIRMWARE_STATUS__X(PCIE_SERVICE_STATUS_DEBUG_INSUFFICIENT_MEMORY) /* DEPRECATED - See MEMORY_LOGGER */\
+   FIRMWARE_STATUS__X(PCIE_SERVICE_STATUS_NULL_POINTER_PASSED)\
+   FIRMWARE_STATUS__X(PCIE_SERVICE_STATUS_HOST_BASE_ADDRESS_IS_NOT_64KB_ALIGNED)\
+   FIRMWARE_STATUS__X(PCIE_SERVICE_STATUS_NOT_ENOUGH_DESCRIPTORS_IN_HOST)\
+   FIRMWARE_STATUS__X(PCIE_SERVICE_STATUS_PCIE_NOT_CONFIGURED)\
+   FIRMWARE_STATUS__X(PCIE_SERVICE_STATUS_PCIE_UNSUPPORTED)\
+   FIRMWARE_STATUS__X(PCIE_SERVICE_STATUS_GOT_UNALIGNED_ADDRESS_FOR_PCIE_BUFFERS) /* DEPRECATED - See MEMORY_LOGGER */\
+   FIRMWARE_STATUS__X(PCIE_SERVICE_STATUS_INVALID_DEBUG_CHIP_OFFSET) /* DEPRECATED - See MEMORY_LOGGER */\
+   FIRMWARE_STATUS__X(PCIE_SERVICE__CLEARING_CREDITS_TO_OPEN_CHANNEL_IS_NOT_ALLOWED)\
+   FIRMWARE_STATUS__X(PCIE_SERVICE_STATUS_GLUE_LOGIC_MUST_BE_DISABLED_WHEN_CLEARING_CHANNEL_CREDITS)\
+   FIRMWARE_STATUS__X(PCIE_SERVICE_STATUS_VDMA_MUST_BE_DISABLED_WHEN_CLEARING_CHANNEL_CREDITS)\
+   FIRMWARE_STATUS__X(PCIE_SERVICE_STATUS_TRYING_TO_RELEASE_NON_CFG_FLOW_CHANNEL)\
+   FIRMWARE_STATUS__X(PCIE_SERVICE_STATUS_INVALID_H2D_CREDITS)\
+   FIRMWARE_STATUS__X(PCIE_SERVICE_STATUS_INVALID_D2H_CREDITS)\
+   FIRMWARE_STATUS__X(PCIE_SERVICE_STATUS_DESCRIPTOR_COUNT_INDICATE_NOT_ALL_DATA_WAS_TRANSFERRED)\
+   FIRMWARE_STATUS__X(PCIE_SERVICE_STATUS_CHANNEL_CANNOT_FETCH_DESCRITPROTS_WHILE_ABORTED)\
+   FIRMWARE_STATUS__X(PCIE_SERVICE__WAIT_UNTIL_CHANNEL_IS_IDLE_REACHED_TIMEOUT)\
+   FIRMWARE_STATUS__X(PCIE_SERVICE_STATUS_UNSUPPORTED_PERIPH_BYTES_PER_BUFFER)\
+   FIRMWARE_STATUS__X(PCIE_SERVICE_STATUS_GLUE_LOGIC_CHANNEL_OUT_OF_RANGE)\
+   FIRMWARE_STATUS__X(PCIE_SERVICE_STATUS_INVALID_H2D_CHANNEL_INDEX)\
+   FIRMWARE_STATUS__X(PCIE_SERVICE_STATUS_INVALID_D2H_CHANNEL_INDEX)\
+   \
+   FIRMWARE_MODULE__X(FIRMWARE_MODULE__FIRMWARE_UPDATE)\
+   FIRMWARE_STATUS__X(FIRMWARE_UPDATE_STATUS_INVALID_PARAMETERS)\
+   FIRMWARE_STATUS__X(FIRMWARE_UPDATE_STATUS_ERASE_FIRMWARE_SECTION_FAILED)\
+   FIRMWARE_STATUS__X(FIRMWARE_UPDATE_STATUS_FIRMWARE_SIZE_EXCEEDED)\
+   FIRMWARE_STATUS__X(FIRMWARE_UPDATE_STATUS_FIRMWARE_UPDATE_NOT_STARTED)\
+   FIRMWARE_STATUS__X(FIRMWARE_UPDATE_STATUS_FIRMWARE_UPDATE_NOT_VALIDATED)\
+   FIRMWARE_STATUS__X(FIRMWARE_UPDATE_STATUS_UPDATE_SWITCH_FAILED)\
+   FIRMWARE_STATUS__X(FIRMWARE_UPDATE_STATUS_MISMATCHING_MD5)\
+   FIRMWARE_STATUS__X(FIRMWARE_UPDATE_STATUS_UPDATE_SWITCH_MISMATCH)\
+   FIRMWARE_STATUS__X(FIRMWARE_UPDATE_STATUS_INVALID_FIRMWARE)\
+   FIRMWARE_STATUS__X(FIRMWARE_SWITCH_STATUS_ERASE_SWITCH_BLOCK_FAILED)\
+   \
+   FIRMWARE_MODULE__X(FIRMWARE_MODULE__SENSOR_CONFIG)\
+   FIRMWARE_STATUS__X(SENSOR_CONFIG_STATUS_INVALID)\
+   FIRMWARE_STATUS__X(SENSOR_CONFIG_STATUS_STORE_CONFIG_FAIL)\
+   FIRMWARE_STATUS__X(SENSOR_CONFIG_STATUS_LOAD_CONFIG_FAIL)\
+   FIRMWARE_STATUS__X(SENSOR_CONFIG_STATUS_GET_CONFIG_FAIL)\
+   FIRMWARE_STATUS__X(SENSOR_CONFIG_STATUS_INVALID_PARAMS)\
+   FIRMWARE_STATUS__X(SENSOR_CONFIG_STATUS_INVALID_SENSOR_TYPE)\
+   FIRMWARE_STATUS__X(SENSOR_CONFIG_STATUS_ERASE_SECTION_FAILED)\
+   FIRMWARE_STATUS__X(SENSOR_CONFIG_STATUS_I2C_FAILED)\
+   FIRMWARE_STATUS__X(SENSOR_CONFIG_STATUS_SET_I2C_SLAVE_FAILED)\
+   FIRMWARE_STATUS__X(SENSOR_CONFIG_STATUS_I2C_SLAVE_NOT_SET)\
+   FIRMWARE_STATUS__X(SENSOR_CONFIG_STATUS_GET_SECTIONS_INFO_FAILED)\
+   FIRMWARE_STATUS__X(SENSOR_CONFIG_STATUS_SECTION_CONFIG_NOT_SET)\
+   FIRMWARE_STATUS__X(SENSOR_CONFIG_STATUS_RESET_FAILED)\
+   FIRMWARE_STATUS__X(SENSOR_CONFIG_STATUS_SECTION_CONFIG_VERSION_MISMATCH)\
+   FIRMWARE_STATUS__X(SENSOR_CONFIG_STATUS_SENSOR_SET_I2C_BUS_INDEX_FAIL)\
+   \
+   FIRMWARE_MODULE__X(FIRMWARE_MODULE__DEBUG)\
+   FIRMWARE_STATUS__X(LOGGER_STATUS_INVALID)\
+   FIRMWARE_STATUS__X(LOGGER_STATUS_INVALID_PARAM_COUNT)\
+   FIRMWARE_STATUS__X(LOGGER_STATUS_QUEUE_SEND_FAILED) /* DEPRECATED - See UART_LOGGER */\
+   FIRMWARE_STATUS__X(LOGGER_STATUS_FAILED_ACQUIRE_SEMAPHORE) /* DEPRECATED - See UART_LOGGER */\
+   FIRMWARE_STATUS__X(LOGGER_STATUS_FAILED_RELEASE_SEMAPHORE) /* DEPRECATED - See UART_LOGGER */\
+   \
+   FIRMWARE_MODULE__X(FIRMWARE_MODULE__MAILBOX)\
+   FIRMWARE_STATUS__X(MAILBOX_STATUS_GOT_UNSUPPORTED_CPU_ID)\
+   FIRMWARE_STATUS__X(MAILBOX_STATUS_SETUP_INTERRUPT_FAILED)\
+   FIRMWARE_STATUS__X(MAILBOX_STATUS_MAILBOX_INDEX_OUT_OF_BAND)\
+   FIRMWARE_STATUS__X(MAILBOX_STATUS_READ_NULL_VALUE_FROM_THE_MAILBOX)\
+   FIRMWARE_STATUS__X(MAILBOX_STATUS_GOT_NULL_ARGUMENT)\
+   \
+   FIRMWARE_MODULE__X(FIRMWARE_MODULE__SEMAPHORE)\
+   FIRMWARE_STATUS__X(SEMAPHORE_STATUS_GOT_UNSUPPORTED_CPU_ID)\
+   FIRMWARE_STATUS__X(SEMAPHORE_STATUS_TRY_GET_RETURN_INVALID_VALUE)\
+   FIRMWARE_STATUS__X(SEMAPHORE_STATUS_INVALID_STATUS_RESPONSE)\
+   FIRMWARE_STATUS__X(SEMAPHORE_STATUS_INDEX_OUT_OF_BAND)\
+   FIRMWARE_STATUS__X(SEMAPHORE_STATUS_CLEAR_FAILURE)\
+   FIRMWARE_STATUS__X(SEMAPHORE_STATUS_GOT_NULL_ARGUMENT)\
+   \
+   FIRMWARE_MODULE__X(FIRMWARE_MODULE__INTER_CPU_CONTROL)\
+   FIRMWARE_STATUS__X(INTER_CPU_CONTROL_STATUS_UNSUPPORTED_OPCODE)\
+   FIRMWARE_STATUS__X(INTER_CPU_CONTROL_STATUS_RAECHED_TIMEOUT_WHILE_WAITING_FOR_RESPONSE)\
+   FIRMWARE_STATUS__X(INTER_CPU_CONTROL_STATUS_UNEXPECTED_RESPONSE_OPCODE)\
+   FIRMWARE_STATUS__X(INTER_CPU_CONTROL_STATUS_UNSUPPORTED_CONTROL_PROTOCOL_VERSION)\
+   FIRMWARE_STATUS__X(INTER_CPU_CONTROL_STATUS_SEQUENCE_RETRANSMITTED)\
+   FIRMWARE_STATUS__X(INTER_CPU_CONTROL_STATUS_APP_CPU_REACHED_TIMEOUT)\
+   FIRMWARE_STATUS__X(INTER_CPU_CONTROL_STATUS_UNEXPECTED_SEQUENCE_NUMBER)\
+   FIRMWARE_STATUS__X(INTER_CPU_CONTROL_STATUS_NULL_ARGUMENT_PASSED)\
+   FIRMWARE_STATUS__X(INTER_CPU_CONTROL_STATUS_INVALID_PARAMETERS)\
+   FIRMWARE_STATUS__X(INTER_CPU_CONTROL_STATUS_INVALID_REQUEST_SIZE)\
+   FIRMWARE_STATUS__X(INTER_CPU_CONTROL_STATUS_INVALID_RESPONSE_SIZE)\
+   FIRMWARE_STATUS__X(INTER_CPU_CONTROL_STATUS_RECEIVED_NULL_VALUE_FROM_MAILBOX_READ_FUNCTION)\
+   \
+   FIRMWARE_MODULE__X(FIRMWARE_MODULE__INTER_CPU_EVENT_MANAGER)\
+   FIRMWARE_STATUS__X(INTER_CPU_EVENT_MANAGER_STATUS_FAILED_INITIALIZE_SEMAPHORE)\
+   FIRMWARE_STATUS__X(INTER_CPU_EVENT_MANAGER_STATUS_EVENT_INDEX_OUT_OF_RANGE)\
+   FIRMWARE_STATUS__X(INTER_CPU_EVENT_MANAGER_STATUS_NULL_ARGUMENT_PASSED)\
+   FIRMWARE_STATUS__X(INTER_CPU_EVENT_MANAGER_STATUS_TRYING_TO_CLEAR_EVENT_THAT_DIDNT_HAPPEN)\
+   FIRMWARE_STATUS__X(INTER_CPU_EVENT_MANAGER_STATUS_GOT_NULL_POINTER_TO_EVENT)\
+   FIRMWARE_STATUS__X(INTER_CPU_EVENT_MANAGER_STATUS_GOT_UNVALID_EVENT)\
+   FIRMWARE_STATUS__X(INTER_CPU_EVENT_MANAGER_STATUS_WAIT_FOR_EVENT_CLEAR_REACHED_TIMEOUT)\
+   FIRMWARE_STATUS__X(INTER_CPU_EVENT_MANAGER_STATUS_WAIT_FOR_EVENT_REACHED_TIMEOUT)\
+   FIRMWARE_STATUS__X(INTER_CPU_EVENT_MANAGER_STATUS_FAILED_TO_ACQUIRE_SEMAPHORE)\
+   FIRMWARE_STATUS__X(INTER_CPU_EVENT_MANAGER_STATUS_GOT_NULL_ARGUMENT_FROM_MAILBOX_CHECK_DATA)\
+   FIRMWARE_STATUS__X(INTER_CPU_EVENT_MANAGER_STATUS_GOT_UNSUPPORTED_CPU_ID)\
+   FIRMWARE_STATUS__X(INTER_CPU_EVENT_MANAGER_STATUS_PAYLOAD_SIZE_IS_HIGHER_THEN_MAX_SIZE)\
+   FIRMWARE_STATUS__X(INTER_CPU_EVENT_MANAGER_STATUS_GOT_NULL_POINTER_TO_PAYLOAD)\
+   FIRMWARE_STATUS__X(INTER_CPU_EVENT_MANAGER_STATUS_EVENT_WAS_OVERWRITTEN)\
+   FIRMWARE_STATUS__X(INTER_CPU_EVENT_MANAGER_STATUS_INVALID_ARGS)\
+   FIRMWARE_STATUS__X(INTER_CPU_EVENT_MANAGER_STATUS_GOT_EVENT_INDICATION_BUT_CHECK_EVENT_RETURNED_FALSE)\
+   FIRMWARE_STATUS__X(INTER_CPU_CONTROL_STATUS_INVALID_CONNECTION_TYPE)\
+   \
+   FIRMWARE_MODULE__X(FIRMWARE_MODULE__CONTEXT_SWITCH_TASK)\
+   FIRMWARE_STATUS__X(CONTEXT_SWITCH_TASK_STATUS_RECEIVED_INVALID_VERSION)\
+   FIRMWARE_STATUS__X(CONTEXT_SWITCH_TASK_STATUS_MAIN_HEADER_INDICATES_THERE_IS_NO_CONTEXTS)\
+   FIRMWARE_STATUS__X(CONTEXT_SWITCH_TASK_STATUS_NULL_POINTER_PASSED)\
+   FIRMWARE_STATUS__X(CONTEXT_SWITCH_TASK_STATUS_CONTEXT_HEADER_INDICATES_THERE_IS_NO_CONTEXT_NETWORK_INFO)\
+   FIRMWARE_STATUS__X(CONTEXT_SWITCH_TASK_STATUS_MAIN_HEADER_INDICATES_THERE_IS_NO_APPLICATIONS)\
+   FIRMWARE_STATUS__X(CONTEXT_SWITCH_TASK_STATUS_USER_SENT_CONTEXT_CONTROL_BEFORE_MAIN_HEADER_CONTROL)\
+   FIRMWARE_STATUS__X(CONTEXT_SWITCH_TASK_STATUS_CONTEXT_INFO_RECEIVED_TOO_MANY_CONTEXTS)\
+   FIRMWARE_STATUS__X(CONTEXT_SWITCH_TASK_STATUS_RECEIVED_INVALID_TRIGGER_TYPE)\
+   FIRMWARE_STATUS__X(CONTEXT_SWITCH_TASK_STATUS_READ_INVALID_SLICING_APPROACH)/* DEPRECATED */\
+   FIRMWARE_STATUS__X(CONTEXT_SWITCH_TASK_STATUS_RECEIVED_INVALID_ACTION_TYPE)\
+   FIRMWARE_STATUS__X(CONTEXT_SWITCH_TASK_STATUS_UNDEFINED_MAPPING_FOR_SHIMFO)\
+   FIRMWARE_STATUS__X(CONTEXT_SWITCH_TASK_STATUS_MISSMATCH_BETWEEN_TRIGGER_DIRECTION_AND_MAPPING_DIRECTION)\
+   FIRMWARE_STATUS__X(CONTEXT_SWITCH_TASK_STATUS_SLICING_FUNCTION_REACHED_FORBIDDEN_MEMORY_SPACE)\
+   FIRMWARE_STATUS__X(CONTEXT_SWITCH_TASK_STATUS_ADD_TRIGGER_FUNCTION_REACHED_FORBIDDEN_MEMORY_SPACE)\
+   FIRMWARE_STATUS__X(CONTEXT_SWITCH_TASK_STATUS_ADD_ACTION_FUNCTION_REACHED_FORBIDDEN_MEMORY_SPACE)\
+   FIRMWARE_STATUS__X(CONTEXT_SWITCH_TASK_STATUS_ADD_CONTEXT_INFO_FUNCTION_REACHED_FORBIDDEN_MEMORY_SPACE)\
+   FIRMWARE_STATUS__X(CONTEXT_SWITCH_TASK_STATUS_PARSING_ERROR_WHILE_READING_TRIGGER_GROUPS)\
+   FIRMWARE_STATUS__X(CONTEXT_SWITCH_TASK_STATUS_TRIGGER_GROUP_POINTER_REACHED_FORBIDDEN_MEMORY_SPACE)\
+   FIRMWARE_STATUS__X(CONTEXT_SWITCH_TASK_STATUS_DOWNLOAD_ACTION_LIST_INVALID_CONTEXT_INDEX)\
+   FIRMWARE_STATUS__X(CONTEXT_SWITCH_TASK_STATUS_RECEIVED_INVALID_APPLICATION_COUNT)\
+   FIRMWARE_STATUS__X(CONTEXT_SWITCH_TASK_STATUS_RECEIVED_INVALID_TOTAL_CONTEXTS_COUNT)\
+   FIRMWARE_STATUS__X(CONTEXT_SWTICH_STATUS_MISALIGNMENT_ERROR_WHILE_READING_ACTIONS)\
+   FIRMWARE_STATUS__X(CONTEXT_SWITCH_STATUS_READ_INVALID_INTERRUPT_TYPE)\
+   FIRMWARE_STATUS__X(CONTEXT_SWITCH_STATUS_READ_INVALID_ACTION_TYPE)\
+   FIRMWARE_STATUS__X(CONTEXT_SWITCH_STATUS_INVALID_STATE_MACHINE_STATUS)\
+   FIRMWARE_STATUS__X(CONTEXT_SWITCH_STATUS_INVALID_SLICING_APPROACH)/* DEPRECATED */\
+   FIRMWARE_STATUS__X(CONTEXT_SWITCH_STATUS_EMPTY_END_OF_CLUSTER_TRIGGER_ARRAY)\
+   FIRMWARE_STATUS__X(CONTEXT_SWITCH_STATUS_INVALID_CLUSTER_INDEX)\
+   FIRMWARE_STATUS__X(CONTEXT_SWITCH_STATUS_INVALID_LCU_INDEX)\
+   FIRMWARE_STATUS__X(CONTEXT_SWITCH_STATUS_INVALID_CHANNEL_INDEX)\
+   FIRMWARE_STATUS__X(CONTEXT_SWITCH_STATUS_RECEIVED_UNEXPECTED_INTERRUPT)\
+   FIRMWARE_STATUS__X(CONTEXT_SWITCH_STATUS_TIMEOUT_WHILE_WAITING_FOR_INTERRUPT)\
+   FIRMWARE_STATUS__X(CONTEXT_SWITCH_STATUS_INVALID_SEQUENCER_CONFIGURATION)\
+   FIRMWARE_STATUS__X(CONTEXT_SWITCH_STATUS_INVALID_ENABLE_LCU_CONFIGURATION)\
+   FIRMWARE_STATUS__X(CONTEXT_SWITCH_STATUS_INVALID_DISABLE_LCU_CONFIGURATION)\
+   FIRMWARE_STATUS__X(CONTEXT_SWITCH_STATUS_INVALID_EDGE_CONNECTION_TYPE)\
+   FIRMWARE_STATUS__X(CONTEXT_SWITCH_STATUS_INVALID_EDGE_LAYER_INDEX)\
+   FIRMWARE_STATUS__X(CONTEXT_SWITCH_STATUS_INVALID_BYTES_PER_BUFFER_VALUE)\
+   FIRMWARE_STATUS__X(CONTEXT_SWITCH_STATUS_INVALID_CREDITS_SIZE)\
+   FIRMWARE_STATUS__X(CONTEXT_SWITCH_STATUS_EDGE_LAYER_ALREADY_SET)\
+   FIRMWARE_STATUS__X(CONTEXT_SWITCH_STATUS_RECEIVED_INVALID_EXPECTED_INTERRUPT)\
+   FIRMWARE_STATUS__X(CONTEXT_SWITCH_STATUS_PCIE_CONTROL_INTERRUPT_ENABLED_DURING_RUN_TIME_MODE)\
+   FIRMWARE_STATUS__X(CONTEXT_SWITCH_STATUS_INVALID_CONFIG_MODULE_INDEX)\
+   FIRMWARE_STATUS__X(CONTEXT_SWITCH_TASK_STATUS_WAIT_FOR_INTERRUPT_INTERRUPTED_BY_RESET_REQUEST)\
+   FIRMWARE_STATUS__X(CONTEXT_SWITCH_STATUS_EDGE_LAYER_MEMORY_OUT_OF_BOUNDS)\
+   FIRMWARE_STATUS__X(CONTEXT_SWITCH_STATUS_INVALID_EDGE_LAYER_COMMUNICATION_TYPE)\
+   FIRMWARE_STATUS__X(CONTEXT_SWITCH_STATUS_INVALID_KERNEL_DONE_ADDRESS)\
+   FIRMWARE_STATUS__X(CONTEXT_SWITCH_STATUS_INVALID_BUFFERS_PER_FRAME_VALUE)\
+   FIRMWARE_STATUS__X(CONTEXT_SWITCH_STATUS_MULTIPLE_EDGE_LAYERS_FOR_SAME_STREAM)\
+   FIRMWARE_STATUS__X(CONTEXT_SWITCH_STATUS_MULTIPLE_STREAMS_USE_THE_SAME_CHANNEL)\
+   FIRMWARE_STATUS__X(CONTEXT_SWITCH_STATUS_INVALID_CONTEXT_CREDITS_BY_BYTES)\
+   FIRMWARE_STATUS__X(CONTEXT_SWITCH_STATUS_REQUESTED_DESCRIPTORS_EXCEEDED_MAX_ALLOWED)\
+   FIRMWARE_STATUS__X(CONTEXT_SWITCH_STATUS_MISMATCH_BETWEEN_EXPECTED_DESCRIPTOR_COUNT_AND_ACTUAL_REQUESTS)\
+   FIRMWARE_STATUS__X(CONTEXT_SWITCH_STATUS_RECEIVED_APPLICATION_SET_BEFORE_INIT_STATE_DONE)\
+   FIRMWARE_STATUS__X(CONTEXT_SWITCH_STATUS_INVALID_APPLICATION_INDEX)\
+   FIRMWARE_STATUS__X(CONTEXT_SWITCH_STATUS_HOST_DESCRITPOR_BASE_ADDRESS_IS_NOT_64KB_ALIGNED)\
+   FIRMWARE_STATUS__X(CONTEXT_SWITCH_STATUS_INVALID_EDGE_LAYER_DIRECTION)\
+   FIRMWARE_STATUS__X(CONTEXT_SWITCH_STATUS_INVALID_EDGE_LAYER_CREDIT_TYPE)\
+   FIRMWARE_STATUS__X(CONTEXT_SWITCH_STATUS_ADDING_CREDITS_IS_ALLOWED_ONLY_FOR_EDGE_LAYER_DIRECTION_HOST_TO_DEVICE)\
+   FIRMWARE_STATUS__X(CONTEXT_SWITCH_STATUS_PCIE_CHANNEL_INDEX_AND_DIRECTION_MISMATCH)\
+   FIRMWARE_STATUS__X(CONTEXT_SWITCH_STATUS_INVALID_ACTION_LIST_OFFSET)\
+   FIRMWARE_STATUS__X(CONTEXT_SWITCH_STATUS_CHANGING_APP_IS_ALLOWED_IN_RESET_STATE_ONLY)\
+   FIRMWARE_STATUS__X(CONTEXT_SWITCH_STATUS_INVALID_BATCH_SIZE)\
+   FIRMWARE_STATUS__X(CONTEXT_SWITCH_STATUS_RECEIVED_CONFIG_BREAKPOINT_BEFORE_INIT_STATE_DONE)\
+   FIRMWARE_STATUS__X(CONTEXT_SWITCH_STATUS_RECEIVED_INVALID_APPLICATION_INDEX)\
+   FIRMWARE_STATUS__X(CONTEXT_SWITCH_STATUS_RECEIVED_INVALID_BATCH_INDEX_FOR_SINGLE_CONTEXT_APPS)\
+   FIRMWARE_STATUS__X(CONTEXT_SWITCH_STATUS_RECEIVED_INVALID_CONTEXT_INDEX)\
+   FIRMWARE_STATUS__X(CONTEXT_SWITCH_STATUS_RECEIVED_INVALID_BREAKPOINT_ID)\
+   FIRMWARE_STATUS__X(CONTEXT_SWITCH_STATUS_RECEIVED_INVALID_BREAKPOINT_CONTROL)\
+   FIRMWARE_STATUS__X(CONTEXT_SWITCH_STATUS_RECEIVED_INVALID_BATCH_INDEX_AND_CONTEXT_INDEX_COMBO)\
+   FIRMWARE_STATUS__X(CONTEXT_SWITCH_STATUS_USER_MUST_CLEAR_OR_REACH_BREAKPOINT_BEFORE_SETTING_NEW_ONE)\
+   FIRMWARE_STATUS__X(CONTEXT_SWITCH_STATUS_USER_CANT_CONTINUE_WHEN_BREAKPOINT_IS_CLEARED_OR_NOT_REACHED_YET)\
+   FIRMWARE_STATUS__X(CONTEXT_SWITCH_STATUS_CANT_FIND_CONTEXT_DUMMY_STREAM)\
+   FIRMWARE_STATUS__X(CONTEXT_SWITCH_STATUS_TRYING_TO_READ_MAIN_HEADER_BEFORE_INIT_STATUS_DONE)\
+   FIRMWARE_STATUS__X(CONTEXT_SWITCH_STATUS_WAIT_FOR_INPUT_CREDITS_INTERRUPTED_BY_RESET_REQUEST)\
+   FIRMWARE_STATUS__X(CONTEXT_SWITCH_STATUS_COULD_NOT_FIND_MATCH_APP_FOR_CONTEXT_INDEX)\
+   FIRMWARE_STATUS__X(CONTEXT_SWITCH_STATUS_EDGE_LAYERS_ON_MULTIPLE_CONTROLS_IS_NOT_SUPPORTED_YET)\
+   FIRMWARE_STATUS__X(CONTEXT_SWITCH_STATUS_STREAM_CREDIT_NOT_ZERO_AT_END_OF_CONTEXT)\
+   FIRMWARE_STATUS__X(CONTEXT_SWITCH_STATUS_FIFO_COUNTER_NOT_ZERO_AT_END_OF_CONTEXT)\
+   FIRMWARE_STATUS__X(CONTEXT_SWITCH_STATUS_EXECUTE_ACTION_WAS_INTERRUPTED_BY_RESET_REQUEST)\
+   FIRMWARE_STATUS__X(CONTEXT_SWITCH_STATUS_FAILED_TO_RESET_STATE_MACHINE)\
+   FIRMWARE_STATUS__X(CONTEXT_SWITCH_STATUS_SEQUENCER_ABBALE_VALIDATION_FAILED)\
+   FIRMWARE_STATUS__X(CONTEXT_SWITCH_STATUS_USER_TIMESTAMP_CONFIGURATION_IS_DISABLED_ALREADY)\
+   FIRMWARE_STATUS__X(CONTEXT_SWITCH_STATUS_INVALID_DESC_PAGE_SIZE)\
+   FIRMWARE_STATUS__X(CONTEXT_SWITCH_STATUS_INVALID_VDMA_CHANNEL_DEPTH)\
+   FIRMWARE_STATUS__X(CONTEXT_SWITCH_STATUS_CANT_CONFIGURE_HEF_WHILE_ACTIVATED)\
+   FIRMWARE_STATUS__X(CONTEXT_SWITCH_STATUS_DDR_BUFFER_TASK_IS_NOT_IDLE)\
+   FIRMWARE_STATUS__X(CONTEXT_SWITCH_STATUS_DATAFLOW_IS_ACTIVE_ON_ANOTHER_MANAGER)\
+   FIRMWARE_STATUS__X(CONTEXT_SWITCH_STATUS_TEMPERATURE_SAFE_STATE_UNINITALIZED)\
+   FIRMWARE_STATUS__X(CONTEXT_SWITCH_STATUS_HIGH_TEMPERATURE_WHEN_OPENING_STREAM)\
+   FIRMWARE_STATUS__X(CONTEXT_SWITCH_STATUS_INVALID_NETWORK_INDEX)\
+   FIRMWARE_STATUS__X(CONTEXT_SWITCH_STATUS_REPEATED_ACTION_INVALID_HEADER)\
+   FIRMWARE_STATUS__X(CONTEXT_SWITCH_STATUS_REPEATED_ACTION_INVALID_SUBACTION_TYPE)\
+   FIRMWARE_STATUS__X(CONTEXT_SWITCH_STATUS_REPEATED_ACTION_INVALID_ACTION_COUNT)\
+   FIRMWARE_STATUS__X(CONTEXT_SWITCH_STATUS_CURRENT_SAFE_STATE_UNINITALIZED)\
+   FIRMWARE_STATUS__X(CONTEXT_SWITCH_STATUS_HIGH_CURRENT_WHEN_OPENING_STREAM)\
+   FIRMWARE_STATUS__X(CONTEXT_SWITCH_STATUS_WAIT_FOR_PREDICATE_INTERRUPTED_BY_RESET_REQUEST)\
+   FIRMWARE_STATUS__X(CONTEXT_SWITCH_STATUS_WAIT_FOR_DMA_IDLE_INTERRUPTED_BY_RESET_REQUEST)\
+   FIRMWARE_STATUS__X(CONTEXT_SWITCH_STATUS_ACTION_NOT_SUPPORTED)\
+   FIRMWARE_STATUS__X(CONTEXT_SWITCH_STATUS_INVALID_AGGREGATOR_INDEX)\
+   FIRMWARE_STATUS__X(CONTEXT_SWITCH_STATUS_INVALID_OUTPUT_BUFFER_INDEX)\
+   FIRMWARE_STATUS__X(CONTEXT_SWITCH_STATUS_INVALID_OUTPUT_BUFFER_CLUSTER_INDEX)\
+   FIRMWARE_STATUS__X(CONTEXT_SWITCH_STATUS_INVALID_OUTPUT_BUFFER_INTERFACE)\
+   \
+   FIRMWARE_MODULE__X(FIRMWARE_MODULE__D2H_EVENT_MANAGER)\
+   FIRMWARE_STATUS__X(HAILO_D2H_EVENT_MANAGER_STATUS_MESSAGE_HIGH_PRIORITY_QUEUE_CREATE_FAILED)\
+   FIRMWARE_STATUS__X(HAILO_D2H_EVENT_MANAGER_STATUS_MESSAGE_QUEUE_CREATE_FAILED)\
+   FIRMWARE_STATUS__X(HAILO_D2H_EVENT_MANAGER_STATUS_MESSAGE_LOW_PRIORITY_QUEUE_CREATE_FAILED)\
+   FIRMWARE_STATUS__X(HAILO_D2H_EVENT_MANAGER_STATUS_INVALID_MESSAGE_QUEUE_HANDLE)\
+   FIRMWARE_STATUS__X(HAILO_D2H_EVENT_MANAGER_STATUS_SENDING_MESSAGE_FAIL)\
+   FIRMWARE_STATUS__X(HAILO_D2H_EVENT_MANAGER_STATUS_SEND_EVENT_OVER_UDP_FAIL)\
+   FIRMWARE_STATUS__X(HAILO_D2H_EVENT_MANAGER_STATUS_SEND_EVENT_OVER_PCIE_FAIL)\
+   FIRMWARE_STATUS__X(HAILO_D2H_EVENT_MANAGER_STATUS_INVALID_COMMUNICATION_TYPE)\
+   FIRMWARE_STATUS__X(HAILO_D2H_EVENT_MANAGER_STATUS_PCIE_NOT_ACTIVE)\
+   FIRMWARE_STATUS__X(HAILO_D2H_EVENT_MANAGER_STATUS_INVALID_PRIORITY_QUEUE_HANDLE)\
+   FIRMWARE_STATUS__X(HAILO_D2H_EVENT_MANAGER_STATUS_INIT_UDP_FAIL)\
+   \
+   FIRMWARE_MODULE__X(FIRMWARE_MODULE__WD)\
+   FIRMWARE_STATUS__X(WD_STATUS_INVALID_PARAMETERS)\
+   FIRMWARE_STATUS__X(WD_STATUS_CONFIG_WHILE_ENABLED)\
+   FIRMWARE_STATUS__X(WD_STATUS_ENABLE_WHILE_ENABLED) /* DEPRECATED */\
+   FIRMWARE_STATUS__X(WD_STATUS_DISABLE_WHILE_DISABLED) /* DEPRECATED */\
+   FIRMWARE_STATUS__X(WD_STATUS_INVALID_WD_CYCLES)\
+   FIRMWARE_STATUS__X(WD_SERVICE_INIT_FAILED)\
+   FIRMWARE_STATUS__X(WD_SERVICE_WD_UNSUPPORTED)\
+   FIRMWARE_STATUS__X(WD_SERVICE_WD_CONFIG_AND_ENABLE_CPU_ID_INVALID)\
+   \
+   FIRMWARE_MODULE__X(FIRMWARE_MODULE__HEALTH_MONITOR)\
+   FIRMWARE_STATUS__X(HEALTH_MONITOR_CLOSING_STREAMS_FAILED)\
+   FIRMWARE_STATUS__X(HEALTH_MONITOR_SENDING_CLOSED_STREAM_EVENT_TO_HOST_FAILED)\
+   FIRMWARE_STATUS__X(HEALTH_MONITOR_SENDING_ALARM_A_EVENT_TO_HOST_FAILED)\
+   FIRMWARE_STATUS__X(HEALTH_MONITOR_IS_NOT_INITIALIZED_SUCCESSFULLY)\
+   FIRMWARE_STATUS__X(HEALTH_MONITOR_CANT_OPEN_STREAM_TS0_TOO_HOT) /* DEPRECATED - See TEMPERATURE_PROTECTION */\
+   FIRMWARE_STATUS__X(HEALTH_MONITOR_CANT_OPEN_STREAM_TS1_TOO_HOT) /* DEPRECATED - See TEMPERATURE_PROTECTION */\
+   FIRMWARE_STATUS__X(HEALTH_MONITOR_UNKNOWN_ALARM_TYPE)\
+   FIRMWARE_STATUS__X(HEALTH_MONITOR_READING_TEMPERATURE_FAILED)\
+   FIRMWARE_STATUS__X(HEALTH_MONITOR_FAILED_TO_CREATE_QUEUE)\
+   FIRMWARE_STATUS__X(HEALTH_MONITOR_OVERCURRENT_ALERT_ACTION_FAILED)\
+   FIRMWARE_STATUS__X(HEALTH_MONITOR_SENDING_OVERCURRENT_ALARM_EVENT_TO_HOST_FAILED)\
+   FIRMWARE_STATUS__X(HEALTH_MONITOR_QUEUEING_OVERCURRENT_MESSAGE_FAILED)\
+   FIRMWARE_STATUS__X(HEALTH_MONITOR_QUEUEING_CORE_RESET_MESSAGE_FAILED)\
+   FIRMWARE_STATUS__X(HEALTH_MONITOR_QUEUEING_SOFT_RESET_MESSAGE_FAILED)\
+   FIRMWARE_STATUS__X(HEALTH_MONITOR_INVALID_OVERCURRENT_OVERCURRENT_ZONE)\
+   FIRMWARE_STATUS__X(HEALTH_MONITOR_INVALID_MESSAGE_TYPE)\
+   FIRMWARE_STATUS__X(HEALTH_MONITOR_INVALID_TEMPERATURE_ALARM_TYPE) /* DEPRECATED - See TEMPERATURE_PROTECTION */\
+   FIRMWARE_STATUS__X(HEALTH_MONITOR_SETUP_SAFETY_INTERRUPTS_FAILED)\
+   FIRMWARE_STATUS__X(HEALTH_MONITOR_QUEUEING_RESET_CONTEXT_SWITCH_MESSAGE_FAILED)\
+   FIRMWARE_STATUS__X(HEALTH_MONITOR_QUEUEING_TEMPERATURE_MESSAGE_FAILED)\
+   \
+   FIRMWARE_MODULE__X(FIRMWARE_MODULE__PVT_DRIVER)\
+   FIRMWARE_STATUS__X(PVT_DRIVER_WRONG_PVTC_COMPONENT_ID)\
+   FIRMWARE_STATUS__X(PVT_DRIVER_WRONG_PVTC_IP_NUMBER)\
+   FIRMWARE_STATUS__X(PVT_DRIVER_INVALID_SCRATCH_RESET_VALUE)\
+   FIRMWARE_STATUS__X(PVT_DRIVER_INVALID_SCRATCH_TEST_VALUE)\
+   FIRMWARE_STATUS__X(PVT_DRIVER_TS_SDIF_IS_BUSY)\
+   FIRMWARE_STATUS__X(PVT_DRIVER_TS_SDIF_IS_LOCKED)\
+   FIRMWARE_STATUS__X(PVT_DRIVER_ISR_TS_FAULT)\
+   FIRMWARE_STATUS__X(PVT_DRIVER_IS_NOT_INITIALIZED_SUCCESSFULLY)\
+   FIRMWARE_STATUS__X(PVT_INVALID_CLOCK_FREQUENCY)\
+   FIRMWARE_STATUS__X(PVT_SDIF_DATA_FAULT)\
+   FIRMWARE_STATUS__X(PVT_WRITE_PD_SDIF_FAIL_TO_WRITE_IP_CFG)\
+   FIRMWARE_STATUS__X(PVT_WRITE_PD_SDIF_FAIL_TO_WRITE_IP_CTRL)\
+   FIRMWARE_STATUS__X(PVT_WRITE_PD_SMPL_CNT_FAILED_TO_INCREMENT)\
+   FIRMWARE_STATUS__X(PVT_PD_NOT_INITIALIZED_SUCCESSFULLY)\
+   \
+   FIRMWARE_MODULE__X(FIRMWARE_MODULE__ISP)\
+   FIRMWARE_STATUS__X(ISP_UNSUPPORTED_OUTPUT_DATA_TYPE)\
+   FIRMWARE_STATUS__X(ISP_UNSUPPORTED_INPUT_DATA_TYPE)\
+   FIRMWARE_STATUS__X(ISP_UNSUPPORTED_INPUT_BAYER_ORDER)\
+   FIRMWARE_STATUS__X(ISP_WRITE_OUT_OF_PAGE_BOUNDS)\
+   FIRMWARE_STATUS__X(ISP_UNSUPPORTED_OPERATION_TYPE)\
+   FIRMWARE_STATUS__X(ISP_WRITE_VALUE_TOO_BIG)\
+   FIRMWARE_STATUS__X(ISP_AUTO_FOCUS_UNSUPPORTED)\
+   \
+   FIRMWARE_MODULE__X(FIRMWARE_MODULE__BOARD_CONFIG)\
+   FIRMWARE_STATUS__X(BOARD_CONFIG_STATUS_INVALID_MAGIC)\
+   FIRMWARE_STATUS__X(BOARD_CONFIG_STATUS_INVALID_COMMON_HEADER)\
+   FIRMWARE_STATUS__X(BOARD_CONFIG_STATUS_INVALID_INTERNAL_USE_AREA)\
+   FIRMWARE_STATUS__X(BOARD_CONFIG_STATUS_INVALID_BOARD_INFO_AREA)\
+   FIRMWARE_STATUS__X(BOARD_CONFIG_STATUS_INVALID_BOARD_CONFIG)\
+   FIRMWARE_STATUS__X(BOARD_CONFIG_STATUS_SECTION_ERASE_FAILED)\
+   FIRMWARE_STATUS__X(BOARD_CONFIG_STATUS_READ_FROM_EEPROM_FAILED)\
+   FIRMWARE_STATUS__X(BOARD_CONFIG_STATUS_READ_FROM_FLASH_FAILED)\
+   FIRMWARE_STATUS__X(BOARD_CONFIG_STATUS_INVALID_MAX_NEURAL_NETWORK_CORE_CLOCK_RATE) /* DEPRECATED */\
+   FIRMWARE_STATUS__X(BOARD_CONFIG_STATUS_NULL_POINTER_PASSED)\
+   FIRMWARE_STATUS__X(BOARD_CONFIG_STATUS_INVALID_STRUCT_SIZE_PASSED)\
+   FIRMWARE_STATUS__X(BOARD_CONFIG_STATUS_READ_FROM_INVALID_STORAGE)\
+   FIRMWARE_STATUS__X(BOARD_CONFIG_STATUS_READ_FROM_PCIE_PRELOAD_FAILED)\
+   \
+   FIRMWARE_MODULE__X(FIRMWARE_MODULE__FIRMWARE_CONFIGS)\
+   FIRMWARE_STATUS__X(FIRMWARE_CONFIGS_STATUS_BOARD_CONFIG_WRITE_NOT_ALLOWED)\
+   FIRMWARE_STATUS__X(FIRMWARE_CONFIGS_STATUS_USER_CONFIG_WRITE_NOT_ALLOWED)\
+   FIRMWARE_STATUS__X(FIRMWARE_CONFIGS_STATUS_BOARD_CONFIG_NOT_LOADED)\
+   FIRMWARE_STATUS__X(FIRMWARE_CONFIGS_STATUS_USER_CONFIG_NOT_LOADED)\
+   FIRMWARE_STATUS__X(FIRMWARE_CONFIGS_STATUS_BOARD_CONFIG_OVERRUN)\
+   FIRMWARE_STATUS__X(FIRMWARE_CONFIGS_STATUS_USER_CONFIG_OVERRUN)\
+   FIRMWARE_STATUS__X(FIRMWARE_CONFIGS_STATUS_USER_CONFIG_LOAD_NOT_ALLOWED)\
+   FIRMWARE_STATUS__X(FIRMWARE_CONFIGS_STATUS_BOARD_CONFIG_READ_PCIE_PRELOAD_FAILED_TO_GET_BOOT_TYPE)\
+   FIRMWARE_STATUS__X(FIRMWARE_CONFIGS_STATUS_BOARD_CONFIG_READ_PCIE_PRELOAD_FAILED_NOT_BOOT_FROM_PCIE)\
+   FIRMWARE_STATUS__X(FIRMWARE_CONFIGS_STATUS_INVALID_BOARD_CONFIG_MAGIC_WAS_RECEIVED)\
+   FIRMWARE_STATUS__X(FIRMWARE_CONFIGS_STATUS_INVALID_BOARD_CONFIG_COMMON_HEADER_WAS_RECEIVED)\
+   FIRMWARE_STATUS__X(FIRMWARE_CONFIGS_STATUS_USER_CONFIG_NULL_ARGUMENT_PASSED)\
+   FIRMWARE_STATUS__X(FIRMWARE_CONFIGS_STATUS_BOARD_CONFIG_NULL_ARGUMENT_PASSED)\
+   FIRMWARE_STATUS__X(FIRMWARE_CONFIGS_STATUS_BOARD_PCIE_PRELOAD_NOT_ACCESSIBLE_AFTER_BOOT)\
+   FIRMWARE_STATUS__X(FIRMWARE_CONFIGS_STATUS_USER_PCIE_PRELOAD_NOT_ACCESSIBLE_AFTER_BOOT)\
+   \
+   FIRMWARE_MODULE__X(FIRMWARE_MODULE__GPIO)\
+   FIRMWARE_STATUS__X(GPIO_BAD_GPIO_INDEX)\
+   FIRMWARE_STATUS__X(GPIO_BAD_PINMUX_GROUP)\
+   \
+   FIRMWARE_MODULE__X(FIRMWARE_MODULE__OVERCURRENT_PROTECTION)\
+   FIRMWARE_STATUS__X(OVERCURRENT_PROTECTION_INVALID_ALERT_THRESHOLD_VALUE)\
+   FIRMWARE_STATUS__X(OVERCURRENT_PROTECTION_INVALID_SAMPLING_PERIOD_VALUE)\
+   FIRMWARE_STATUS__X(OVERCURRENT_PROTECTION_INVALID_AVERAGING_FACTOR_VALUE)\
+   FIRMWARE_STATUS__X(OVERCURRENT_PROTECTION_UNSUPPORTED_SENSOR_TYPE)\
+   FIRMWARE_STATUS__X(OVERCURRENT_PROTECTION_MODULE_IS_NOT_INITIALIZED)\
+   FIRMWARE_STATUS__X(OVERCURRENT_PROTECTION_ALREADY_ACTIVE)\
+   FIRMWARE_STATUS__X(OVERCURRENT_PROTECTION_IS_NOT_ACTIVE)\
+   FIRMWARE_STATUS__X(OVERCURRENT_PROTECTION_INVALID_BOARD_CONFIG_VALUES)\
+   FIRMWARE_STATUS__X(OVERCURRENT_PROTECTION_NULL_POINTER_PASSED)\
+   \
+   FIRMWARE_MODULE__X(FIRMWARE_MODULE__POWER)\
+   FIRMWARE_STATUS__X(POWER_INVALID_CONVERSION_TYPE)\
+   \
+   FIRMWARE_MODULE__X(FIRMWARE_MODULE__SECURE_DEBUG)\
+   FIRMWARE_STATUS__X(SOC_ID_COMPUTATION_ERROR)\
+   FIRMWARE_STATUS__X(LCS_GET_FAILED)\
+   FIRMWARE_STATUS__X(SECURE_SERVICE_MODULE_IS_NOT_INITIALIZED)\
+   FIRMWARE_STATUS__X(GOT_INVALID_LCS_FROM_CC)\
+   FIRMWARE_STATUS__X(WRONG_LCS_FOR_RMA_ENTRANCE)\
+   FIRMWARE_STATUS__X(DEVELOPER_CERTIFICATE_VERIFICATION_FAILED)\
+   FIRMWARE_STATUS__X(DEVELOPER_CERTIFICATE_INCOMPATIBLE_FOR_RMA_ENTRANCE)\
+   FIRMWARE_STATUS__X(RMA_ENTRANCE_FAILED)\
+   FIRMWARE_STATUS__X(DEBUG_CERTIFICATE_TYPE_MISMATCH)\
+   \
+   FIRMWARE_MODULE__X(FIRMWARE_MODULE__POWER_STATE)\
+   FIRMWARE_STATUS__X(POWER_STATE_STATUS_INVALID_STATE)\
+   \
+   FIRMWARE_MODULE__X(FIRMWARE_MODULE__SOC)\
+   FIRMWARE_STATUS__X(SOC_STATUS_NULL_POINTER_PASSED)\
+   FIRMWARE_STATUS__X(SOC_STATUS_INVALID_BOOT_SOURCE)\
+   FIRMWARE_STATUS__X(SOC_STATUS_SOFT_RESET_FAILED_INITIALIZING_CRYPTOCELL)\
+   FIRMWARE_STATUS__X(SOC_INVALID_CLOCK_RATE)\
+   \
+   FIRMWARE_MODULE__X(FIRMWARE_MODULE__SECOND_STAGE_UPDATE)\
+   FIRMWARE_STATUS__X(SECOND_STAGE_UPDATE_STATUS_INVALID_PARAMETERS)\
+   FIRMWARE_STATUS__X(SECOND_STAGE_UPDATE_STATUS_BINARY_SIZE_EXCEEDED)\
+   FIRMWARE_STATUS__X(SECOND_STAGE_UPDATE_STATUS_MISMATCHING_MD5)\
+   FIRMWARE_STATUS__X(SECOND_STAGE_UPDATE_STATUS_CORE_IS_NOT_IDLE)\
+   FIRMWARE_STATUS__X(SECOND_STAGE_UPDATE_STATUS_INVALID_SECOND_STAGE)\
+   \
+   FIRMWARE_MODULE__X(FIRMWARE_MODULE__DATAFLOW_COMMON)\
+   FIRMWARE_STATUS__X(DATAFLOW_COMMON_STATUS_INVALID_BYTES_PER_BUFFER_VALUE)\
+   FIRMWARE_STATUS__X(DATAFLOW_COMMON_STATUS_INVALID_BUFFERS_PER_FRAME_VALUE)\
+   FIRMWARE_STATUS__X(DATAFLOW_COMMON_STATUS_INVALID_EDGE_LAYER_INDEX)\
+   FIRMWARE_STATUS__X(DATAFLOW_COMMON_STATUS_INVALID_PARAMETER)\
+   FIRMWARE_STATUS__X(DATAFLOW_COMMON_STATUS_PADDING_NOT_SUPPORTED_FOR_ARCH)\
+   \
+   FIRMWARE_MODULE__X(FIRMWARE_MODULE__RESET_HANDLER)\
+   FIRMWARE_STATUS__X(RESET_HANDLER_CHIP_RESET_FAILED)\
+   \
+   FIRMWARE_MODULE__X(FIRMWARE_MODULE__TEMPERATURE_PROTECTION)\
+   FIRMWARE_STATUS__X(TEMPERATURE_PROTECTION_INVALID_TEMPERATURE_ZONE)\
+   FIRMWARE_STATUS__X(TEMPERATURE_PROTECTION_NULL_POINTER_PASSED)\
+   FIRMWARE_STATUS__X(TEMPERATURE_PROTECTION_TS0_TOO_HOT)\
+   FIRMWARE_STATUS__X(TEMPERATURE_PROTECTION_TS1_TOO_HOT)\
+   FIRMWARE_STATUS__X(TEMPERATURE_PROTECTION_READING_TEMPERATURE_FAILED)\
+   FIRMWARE_STATUS__X(TEMPERATURE_PROTECTION_THROTTLING_ACTIVATION_IS_ILLEGAL)\
+   FIRMWARE_STATUS__X(TEMPERATURE_PROTECTION_TEMPERATURE_ZONE_DECREASED)\
+   FIRMWARE_STATUS__X(TEMPERATURE_PROTECTION_TEMPERATURE_ZONE_DIDNT_CHANGE)\
+   \
+   FIRMWARE_MODULE__X(FIRMWARE_MODULE__DDR_BUFFER)\
+   FIRMWARE_STATUS__X(DDR_BUFFER_STATUS_TRYING_TO_ADD_DDR_PAIR_WHILE_NOT_IN_IDLE_STATE)\
+   FIRMWARE_STATUS__X(DDR_BUFFER_STATUS_TRYING_TO_CHANGE_STATE_TO_INFER_WHILE_ALREADY_DURING_INFER)\
+   FIRMWARE_STATUS__X(DDR_BUFFER_STATUS_NO_DDR_PAIRS_TO_RUN_INFER_ON)\
+   FIRMWARE_STATUS__X(DDR_BUFFER_STATUS_INFER_REACHED_TIMEOUT)\
+   \
+   FIRMWARE_MODULE__X(FIRMWARE_MODULE__PL320)\
+   FIRMWARE_STATUS__X(PL320_MAILBOX_BUSY)\
+   FIRMWARE_STATUS__X(PL320_MAILBOX_MAXIMUM_CHANNELS)\
+   \
+   FIRMWARE_MODULE__X(FIRMWARE_MODULE__PROCESS_MONITOR)\
+   FIRMWARE_STATUS__X(PROCESS_MONITOR_ACTIVATE_FAILED)\
+   FIRMWARE_STATUS__X(PROCESS_MONITOR_NULL_POINTER_PASSED)\
+   FIRMWARE_STATUS__X(PROCESS_MONITOR_INVALID_BUFFER_SIZE)\
+   FIRMWARE_STATUS__X(PROCESS_MONITOR_PVT_PD_UNINITIALIZED)\
+   FIRMWARE_STATUS__X(PROCESS_MONITOR_PVT_GET_PD_RESULT_FAILED_FOR_DELAY_CHAIN)\
+   FIRMWARE_STATUS__X(PROCESS_MONITOR_PVT_POWER_DOWN_FAILED)\
+   FIRMWARE_STATUS__X(PROCESS_MONITOR_INVALID_DELAY_CHAIN_INDEX)\
+   \
+   FIRMWARE_MODULE__X(FIRMWARE_MODULE__MBIST)\
+   FIRMWARE_STATUS__X(MBIST_STATUS_INVALID_ARGS)\
+   FIRMWARE_STATUS__X(MBIST_STATUS_TRYING_TO_READ_BISR_REQUIRED_BEFORE_MBIST_INIT_DONE)\
+   \
+   FIRMWARE_MODULE__X(FIRMWARE_MODULE__NN_CONFIG_SERVICE)\
+   FIRMWARE_STATUS__X(NN_CONFIG_SERVICE_STATUS_INVALID_CONFIG_STREAM_INDEX)\
+   FIRMWARE_STATUS__X(NN_CONFIG_SERVICE_STATUS_CSM_FETCH_ACTIONS_REQUEIRE_FW_TO_BE_COMPILED_WITH_CSM_SUPPORT)\
+   FIRMWARE_STATUS__X(NN_CONFIG_SERVICE_STATUS_CSM_FETCH_ACTIONS_REQUEIRE_CSM_IN_PRE_FETCH_MODE)\
+   FIRMWARE_STATUS__X(NN_CONFIG_SERVICE_STATUS_CSM_NOT_ENABLED_WHILE_TRYING_TO_FETCH_CONFIG)\
+   FIRMWARE_STATUS__X(NN_CONFIG_SERVICE_STATUS_CSM_BURST_COUNTER_IS_NOT_ZERO)\
+   FIRMWARE_STATUS__X(NN_CONFIG_SERVICE_STATUS_CSM_CREDIT_COUNTER_IS_NOT_ZERO)\
+   \
+   FIRMWARE_MODULE__X(FIRMWARE_MODULE__CONFIG_MANAGER_WRAPPER)\
+   FIRMWARE_STATUS__X(CONFIG_MANAGER_WRAPPER_STATUS_ACTION_TYPE_NOT_SUPPORTED)\
+   FIRMWARE_STATUS__X(CONFIG_MANAGER_WRAPPER_STATUS_NULL_ARG_PASSED)\
+   FIRMWARE_STATUS__X(CONFIG_MANAGER_WRAPPER_STATUS_WAIT_FOR_INTERRUPT_INTERRUPTED_BY_RESET_REQUEST)\
+   FIRMWARE_STATUS__X(CONFIG_MANAGER_WRAPPER_STATUS_RECEIVED_UNEXPECTED_INTERRUPT)\
+   \
+   FIRMWARE_MODULE__X(FIRMWARE_MODULE__CSM_CONFIG_MANAGER)\
+   FIRMWARE_STATUS__X(CSM_CONFIG_MANAGER_STATUS_NOT_IMPLEMENTED)\
+   FIRMWARE_STATUS__X(CSM_CONFIG_MANAGER_STATUS_INVALID_CONFIG_STREAM_INDEX)\
+   FIRMWARE_STATUS__X(CSM_CONFIG_MANAGER_STATUS_INVALID_CONFIG_MODULE_INDEX)\
+   FIRMWARE_STATUS__X(CSM_CONFIG_MANAGER_STATUS_NULL_ARG_PASSED)\
+   FIRMWARE_STATUS__X(CSM_CONFIG_MANAGER_STATUS_CSM_FETCH_ACTIONS_REQUEIRE_CSM_IN_PRE_FETCH_MODE)\
+   FIRMWARE_STATUS__X(CSM_CONFIG_MANAGER_STATUS_CSM_NOT_ENABLED_WHILE_TRYING_TO_FETCH_CONFIG)\
+   FIRMWARE_STATUS__X(CSM_CONFIG_MANAGER_STATUS_CSM_BURST_COUNTER_IS_NOT_ZERO)\
+   FIRMWARE_STATUS__X(CSM_CONFIG_MANAGER_STATUS_CSM_CREDIT_COUNTER_IS_NOT_ZERO)\
+   \
+   FIRMWARE_MODULE__X(FIRMWARE_MODULE__PCIE_CONFIG_MANAGER)\
+   FIRMWARE_STATUS__X(PCIE_CONFIG_MANAGER_STATUS_NOT_IMPLEMENTED)\
+   FIRMWARE_STATUS__X(PCIE_CONFIG_MANAGER_STATUS_INVALID_PCIE_CHANNEL_INDEX)\
+   FIRMWARE_STATUS__X(PCIE_CONFIG_MANAGER_STATUS_INVALID_MODULE_INDEX)\
+   FIRMWARE_STATUS__X(PCIE_CONFIG_MANAGER_STATUS_NULL_ARG_PASSED)\
+   \
+   FIRMWARE_MODULE__X(FIRMWARE_MODULE__VDMA_SERVICE)\
+   FIRMWARE_STATUS__X(VDMA_SERVICE_STATUS_INVALID_H2D_CHANNEL_INDEX)\
+   FIRMWARE_STATUS__X(VDMA_SERVICE_STATUS_INVALID_D2H_CHANNEL_INDEX)\
+   FIRMWARE_STATUS__X(VDMA_SERVICE_STATUS_NOT_SUPPORTED)\
+   FIRMWARE_STATUS__X(VDMA_SERVICE_STATUS_HOST_DESCRITPOR_BASE_ADDRESS_IS_NOT_64KB_ALIGNED)\
+   FIRMWARE_STATUS__X(VDMA_SERVICE_STATUS_INVALID_VDMA_CHANNEL_DEPTH)\
+   FIRMWARE_STATUS__X(VDMA_SERVICE_STATUS_CHANNEL_CANNOT_FETCH_DESCRITPROTS_WHILE_ABORTED)\
+   FIRMWARE_STATUS__X(VDMA_SERVICE_STATUS_DESCRIPTOR_COUNT_INDICATE_NOT_ALL_DATA_WAS_TRANSFERRED)\
+   FIRMWARE_STATUS__X(VDMA_SERVICE_STATUS_INVALID_CHANNEL_TYPE)\
+   FIRMWARE_STATUS__X(VDMA_SERVICE_STATUS_VDMA_MUST_BE_DISABLED_WHEN_CLEARING_CHANNEL_CREDITS)\
+   FIRMWARE_STATUS__X(VDMA_SERVICE_STATUS_CHANNEL_NOT_IDLE)\
+   FIRMWARE_STATUS__X(VDMA_SERVICE_STATUS_NOT_IMPLEMENTED)\
+   FIRMWARE_STATUS__X(VDMA_SERVICE_STATUS_INVALID_H2D_CREDITS)\
+   FIRMWARE_STATUS__X(VDMA_SERVICE_STATUS_INVALID_D2H_CREDITS)\
+   FIRMWARE_STATUS__X(VDMA_SERVICE_STATUS_NULL_ARG_PASSED)\
+   FIRMWARE_STATUS__X(VDMA_SERVICE_STATUS_CHANNEL_FAILED_TO_REACH_IDLE_STATE)\
+   FIRMWARE_STATUS__X(VDMA_SERVICE_STATUS_VDMA_MUST_BE_STOPPED_WHEN_CHECKING_IDLE)\
+   \
+   FIRMWARE_MODULE__X(FIRMWARE_MODULE__MEMORY_LOGGER)\
+   FIRMWARE_STATUS__X(MEMORY_LOGGER_STATUS_DEBUG_INSUFFICIENT_MEMORY)\
+   FIRMWARE_STATUS__X(MEMORY_LOGGER_STATUS_GOT_UNALIGNED_ADDRESS_FOR_PCIE_BUFFERS)\
+   FIRMWARE_STATUS__X(MEMORY_LOGGER_STATUS_INVALID_DEBUG_CHIP_OFFSET)\
+   \
+   FIRMWARE_MODULE__X(FIRMWARE_MODULE__UART_LOGGER)\
+   FIRMWARE_STATUS__X(UART_LOGGER_STATUS_QUEUE_SEND_FAILED)\
+   FIRMWARE_STATUS__X(UART_LOGGER_STATUS_FAILED_ACQUIRE_SEMAPHORE)\
+   FIRMWARE_STATUS__X(UART_LOGGER_STATUS_FAILED_RELEASE_SEMAPHORE)\
+   \
+   FIRMWARE_MODULE__X(FIRMWARE_MODULE__DRAM_DMA_SERVICE)\
+   FIRMWARE_STATUS__X(DRAM_DMA_SERVICE_STATUS_INVALID_PARAMETERS)\
+   FIRMWARE_STATUS__X(DRAM_DMA_SERVICE_STATUS_SETUP_INTERRUPT_HANDLER_FAILED)\
+   FIRMWARE_STATUS__X(DRAM_DMA_SERVICE_STATUS_BURST_CREDIT_SIZE_TOO_BIG)\
+   FIRMWARE_STATUS__X(DRAM_DMA_SERVICE_STATUS_INVALID_CHANNEL_DMA_ADDRESS)\
+   \
+   FIRMWARE_MODULE__X(FIRMWARE_MODULE__NN_CORE_SERVICE)\
+   FIRMWARE_STATUS__X(NN_CORE_SERVICE_STATUS_INVALID_ARG_PASSED)\
+   \
+   FIRMWARE_MODULE__X(FIRMWARE_MODULE__DATA_STREAM_MANAGER_WRAPPER)\
+   FIRMWARE_STATUS__X(DATA_STREAM_MANAGER_WRAPPER_STATUS_NOT_IMPLEMENTED)\
+   FIRMWARE_STATUS__X(DATA_STREAM_MANAGER_WRAPPER_STATUS_INVALID_EDGE_LAYER_INDEX)\
+   FIRMWARE_STATUS__X(DATA_STREAM_MANAGER_WRAPPER_STATUS_INVALID_DESC_PAGE_SIZE)\
+   FIRMWARE_STATUS__X(DATA_STREAM_MANAGER_WRAPPER_STATUS_INVALID_EDGE_LAYER_DIRECTION)\
+
+typedef enum {
+#define FIRMWARE_MODULE__X(module) module,
+#define FIRMWARE_STATUS__X(name)
+    FIRMWARE_STATUS__VARIABLES
+#undef FIRMWARE_STATUS__X
+#undef FIRMWARE_MODULE__X
+
+    /* Must be last! */
+    FIRMWARE_MODULE_COUNT
+} FIRMWARE_STATUS__modules_t;
+
+/* Validate each field at the status is in range range */
+CASSERT(FIRMWARE_STATUS__COMPONENT_ID<=FIRMWARE_STATUS__COMPONENT_MAX, firmware_status_h);
+CASSERT(FIRMWARE_MODULE_COUNT<FIRMWARE_STATUS__MODULE_INDEX_MAX, firmware_status_h);
+
+typedef enum {
+    HAILO_STATUS_SUCCESS = 0,
+
+#define FIRMWARE_MODULE__X(module) module##_START = FIRMWARE_STATUS__COMPONENT_SHIFT(FIRMWARE_STATUS__COMPONENT_ID) \
+    | FIRMWARE_STATUS__MODULE_INDEX_SHIFT(module),
+#define FIRMWARE_STATUS__X(name) name, 
+    FIRMWARE_STATUS__VARIABLES
+#undef FIRMWARE_STATUS__X
+#undef FIRMWARE_MODULE__X
+} FIRMWARE_STATUS_t;
+
+HAILO_COMMON_STATUS_t FIRMWARE_STATUS__get_textual(FIRMWARE_STATUS_t fw_status, const char **text);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __FIRMWARE_STATUS_H__ */
diff --git a/common/include/firmware_version.h b/common/include/firmware_version.h
new file mode 100644 (file)
index 0000000..6b30140
--- /dev/null
@@ -0,0 +1,25 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+**/
+/**
+ * @file firmware_version.h
+ * @brief Contains information regarding the firmware version.
+**/
+
+#ifndef __FIRMWARE_VERSION__
+#define __FIRMWARE_VERSION__
+#include <stdint.h>
+typedef struct {
+    uint32_t firmware_major;
+    uint32_t firmware_minor;
+    uint32_t firmware_revision;
+} firmware_version_t;
+
+#define PACK_FW_VERSION(major, minor, revision) {(major), (minor), (revision)}
+#define MINIMUM_SECURED_FW_VERSION (firmware_version_t)PACK_FW_VERSION(2, 6, 0)
+
+const firmware_version_t* FIRMWARE_VERSION__get_version(void);
+
+
+#endif /* __FIRMWARE_VERSION__ */
diff --git a/common/include/logger_level.h b/common/include/logger_level.h
new file mode 100644 (file)
index 0000000..8ccaa6d
--- /dev/null
@@ -0,0 +1,22 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+**/
+/**
+ * @file logger_level.h
+ * @brief Contains the possible logger level information.
+**/
+
+#ifndef __LOGGER_LEVEL__
+#define __LOGGER_LEVEL__
+
+typedef enum {
+    FW_LOGGER_LEVEL_TRACE = 0,
+    FW_LOGGER_LEVEL_DEBUG,
+    FW_LOGGER_LEVEL_INFO,
+    FW_LOGGER_LEVEL_WARN,
+    FW_LOGGER_LEVEL_ERROR,
+    FW_LOGGER_LEVEL_FATAL
+} FW_LOGGER_LEVEL_t;
+
+#endif //__LOGGER_LEVEL__
\ No newline at end of file
diff --git a/common/include/md5.h b/common/include/md5.h
new file mode 100644 (file)
index 0000000..d012b5d
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * This is an OpenSSL-compatible implementation of the RSA Data Security, Inc.
+ * MD5 Message-Digest Algorithm (RFC 1321).
+ *
+ * Homepage:
+ * http://openwall.info/wiki/people/solar/software/public-domain-source-code/md5
+ *
+ * Author:
+ * Alexander Peslyak, better known as Solar Designer <solar at openwall.com>
+ *
+ * This software was written by Alexander Peslyak in 2001.  No copyright is
+ * claimed, and the software is hereby placed in the public domain.
+ * In case this attempt to disclaim copyright and place the software in the
+ * public domain is deemed null and void, then the software is
+ * Copyright (c) 2001 Alexander Peslyak and it is hereby released to the
+ * general public under the following terms:
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted.
+ *
+ * There's ABSOLUTELY NO WARRANTY, express or implied.
+ *
+ * See md5.c for more information.
+ */
+
+#ifdef HAVE_OPENSSL
+#include <openssl/md5.h>
+#elif !defined(_MD5_H)
+#define _MD5_H
+
+/** Start of modifications for the open source file. **/
+#include "stdint.h"
+typedef uint8_t MD5_SUM_t[16];
+/* Any 32-bit or wider unsigned integer data type will do */
+typedef size_t MD5_u32plus;
+/** End of modifications. **/
+
+typedef struct {
+       MD5_u32plus lo, hi;
+       MD5_u32plus a, b, c, d;
+       unsigned char buffer[64];
+       MD5_u32plus block[16];
+} MD5_CTX;
+
+extern void MD5_Init(MD5_CTX *ctx);
+extern void MD5_Update(MD5_CTX *ctx, const void *data, size_t size);
+extern void MD5_Final(unsigned char *result, MD5_CTX *ctx);
+
+#endif
diff --git a/common/include/sensor_config_exports.h b/common/include/sensor_config_exports.h
new file mode 100644 (file)
index 0000000..e36dcd4
--- /dev/null
@@ -0,0 +1,68 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+**/
+/**
+ * @file sensor_config_exports.h
+ * @brief Sensor config exported defines.
+**/
+
+#ifndef __SENSOR_CONFIG_EXPORT__
+#define __SENSOR_CONFIG_EXPORT__
+
+
+#define MAX_CONFIG_NAME_LEN 100 
+
+#pragma pack(push, 1)
+typedef struct {
+    uint8_t operation;
+    uint8_t length; //how many bits of the register
+    uint8_t page;  //If SOC is limited to 8 bit addressing, or some array imager.
+    uint32_t address;
+    uint32_t bitmask;
+    uint32_t value; //8/16/32-bit register value
+} SENSOR_CONFIG__operation_cfg_t;
+#pragma pack(pop)
+
+typedef struct {
+    uint32_t sensor_type;
+    uint32_t config_size; 
+    uint16_t reset_config_size;
+    uint8_t is_free;
+    uint8_t no_reset_offset;
+    uint16_t config_height;
+    uint16_t config_width;
+    uint16_t config_fps;
+    uint16_t section_version;
+    uint8_t config_name[MAX_CONFIG_NAME_LEN];
+} SENSOR_CONFIG__section_info_t; 
+
+typedef enum {
+    SENSOR_CONFIG_OPCODES_WR = 0,
+    SENSOR_CONFIG_OPCODES_RD,
+    SENSOR_CONFIG_OPCODES_RMW,
+    SENSOR_CONFIG_OPCODES_DELAY,
+} SENSOR_CONFIG_OPCODES_t;
+
+typedef struct {
+    uint8_t bus_index;
+    uint16_t slave_address;
+    uint8_t register_address_size;
+    bool should_hold_bus;
+    uint8_t endianness;
+} SENSOR_I2C_SLAVE_INFO_t;
+
+typedef enum {
+    I2C_SLAVE_ENDIANNESS_BIG_ENDIAN = 0,
+    I2C_SLAVE_ENDIANNESS_LITTLE_ENDIAN = 1,
+
+    I2C_SLAVE_ENDIANNESS_COUNT
+} i2c_slave_endianness_t;
+
+#define SENSOR_CONFIG__TOTAL_SECTIONS_BLOCK_COUNT   (8)
+#define SENSOR_CONFIG__SENSOR_SECTION_BLOCK_COUNT   (7)
+#define SENSOR_CONFIG__ISP_SECTIONS_BLOCK_COUNT     (SENSOR_CONFIG__TOTAL_SECTIONS_BLOCK_COUNT - SENSOR_CONFIG__SENSOR_SECTION_BLOCK_COUNT)
+#define SENSOR_CONFIG__ISP_SECTION_INDEX            (SENSOR_CONFIG__TOTAL_SECTIONS_BLOCK_COUNT - SENSOR_CONFIG__ISP_SECTIONS_BLOCK_COUNT)
+#define SENSOR_SECTIONS_INFO_SIZE                   (SENSOR_CONFIG__TOTAL_SECTIONS_BLOCK_COUNT * sizeof(SENSOR_CONFIG__section_info_t))
+
+#endif /* __SENSOR_CONFIG_EXPORT__ */
\ No newline at end of file
diff --git a/common/include/status.h b/common/include/status.h
new file mode 100644 (file)
index 0000000..882fff8
--- /dev/null
@@ -0,0 +1,61 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+**/
+/**
+ * @file status.h
+ * @brief Declares status enum for hailo c platform.
+**/
+
+#ifndef __STATUS_H__
+#define __STATUS_H__
+
+/**
+ *  @brief The enumeration of all status codes.
+ */
+
+typedef enum {
+    /* global statuses */
+    HAILO_COMMON_STATUS__SUCCESS = 0,
+    HAILO_COMMON_STATUS__UNINITIALIZED,
+    
+    /* Control protocol module errors */
+    HAILO_STATUS__CONTROL_PROTOCOL__OVERRUN_BEFORE_PARAMETER = 0x1000,
+    HAILO_STATUS__CONTROL_PROTOCOL__NULL_ARGUMENT_PASSED,
+    HAILO_STATUS__CONTROL_PROTOCOL__OVERRUN_AT_PARAMETER,
+    HAILO_STATUS__CONTROL_PROTOCOL__UNEXPECTED_ACK_VALUE,
+    HAILO_STATUS__CONTROL_PROTOCOL__INVALID_VERSION,
+    HAILO_STATUS__CONTROL_PROTOCOL__PART_OF_THE_MESSAGE_NOT_PARSED,
+    HAILO_STATUS__CONTROL_PROTOCOL__INVALID_BUFFER_SIZE,
+    HAILO_STATUS__CONTROL_PROTOCOL__INVALID_ARGUMENT,
+
+    HAILO_STATUS__FIRMWARE_HEADER_UTILS__CERT_TOO_LARGE = 0x2000,
+    HAILO_STATUS__FIRMWARE_HEADER_UTILS__INVALID_FIRMWARE_HEADER_SIZE,
+    HAILO_STATUS__FIRMWARE_HEADER_UTILS__INCORRECT_FIRMWARE_HEADER_MAGIC,
+    HAILO_STATUS__FIRMWARE_HEADER_UTILS__UNSUPPORTED_FIRMWARE__HEADER_VERSION,
+    HAILO_STATUS__FIRMWARE_HEADER_UTILS__CODE_SIZE_BELOW_MINIMUM,
+    HAILO_STATUS__FIRMWARE_HEADER_UTILS__CODE_OVERRUNS_RAM_SIZE,
+    HAILO_STATUS__FIRMWARE_HEADER_UTILS__INVALID_FIRMWARE_CODE_SIZE,
+    HAILO_STATUS__FIRMWARE_HEADER_UTILS__INVALID_CERT_HEADER_SIZE,
+    HAILO_STATUS__FIRMWARE_HEADER_UTILS__INVALID_CERT_KEY_SIZE,
+    HAILO_STATUS__FIRMWARE_HEADER_UTILS__INVALID_CERT_CONTENT_SIZE,
+    HAILO_STATUS__FIRMWARE_HEADER_UTILS__INVALID_APP_CPU_FIRMWARE_HEADER,
+    HAILO_STATUS__FIRMWARE_HEADER_UTILS__INVALID_APP_CPU_FIRMWARE_CERTIFICATE_HEADER,
+    HAILO_STATUS__FIRMWARE_HEADER_UTILS__INVALID_CORE_CPU_FIRMWARE_HEADER,
+    HAILO_STATUS__FIRMWARE_HEADER_UTILS__INVALID_CORE_CPU_FIRMWARE_CERTIFICATE_HEADER,
+    HAILO_STATUS__FIRMWARE_HEADER_UTILS__LEFTOVER_DATA_AFTER_LAST_FIRMWARE_HEADER,
+    HAILO_STATUS__FIRMWARE_HEADER_UTILS__DETECTED_PROHIBITED_DOWNGRADE_ATTEMPT,
+    HAILO_STATUS__FIRMWARE_HEADER_UTILS__INVALID_BINARY_TYPE,
+    HAILO_STATUS__FIRMWARE_HEADER_UTILS__INVALID_FIRMWARE_TYPE,
+
+    HAILO_STATUS__D2H_EVENTS__INCORRECT_PARAMETER_COUNT = 0x3000,
+    HAILO_STATUS__D2H_EVENTS__INCORRECT_PARAMETER_LENGTH,
+    HAILO_STATUS__D2H_EVENTS__INVALID_ARGUMENT,
+
+    HAILO_STATUS__FIRMWARE_STATUS__NULL_ARGUMENT_PASSED = 0x4000,
+    HAILO_STATUS__FIRMWARE_STATUS__INVALID_COMPONENT_ID,
+    HAILO_STATUS__FIRMWARE_STATUS__INVALID_MODULE_ID,
+    HAILO_STATUS__FIRMWARE_STATUS__INVALID_STATUS_VALUE,
+} HAILO_COMMON_STATUS_t;
+
+#endif /* __STATUS_H__ */
diff --git a/common/include/stdfloat.h b/common/include/stdfloat.h
new file mode 100644 (file)
index 0000000..7e954fe
--- /dev/null
@@ -0,0 +1,16 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+**/
+/**
+ * @file stdfloat.h
+ * @brief Defines fixed-size float types.
+**/
+
+#ifndef _STDFLOAT_H
+#define _STDFLOAT_H
+
+typedef float  float32_t;
+typedef double float64_t;
+
+#endif /* _STDFLOAT_H */
diff --git a/common/include/user_config_common.h b/common/include/user_config_common.h
new file mode 100644 (file)
index 0000000..009bc4c
--- /dev/null
@@ -0,0 +1,101 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file user_config_common.h
+ * @brief Contains information regarding the firmware user config.
+**/
+
+#ifndef __USER_CONFIG_COMMON__
+#define __USER_CONFIG_COMMON__
+
+#include <stdint.h>
+#include "logger_level.h"
+
+#define USER_CONFIG_OVERCURRENT_UNINITIALIZED_VALUE    (0)
+
+#define USER_CONFIG_TEMPERATURE_DEFAULT_RED_ALARM_THRESHOLD (120.f)
+#define USER_CONFIG_TEMPERATURE_DEFAULT_RED_HYSTERESIS_ALARM_THRESHOLD (116.f)
+#define USER_CONFIG_TEMPERATURE_DEFAULT_ORANGE_ALARM_THRESHOLD (104.f)
+#define USER_CONFIG_TEMPERATURE_DEFAULT_ORANGE_HYSTERESIS_ALARM_THRESHOLD (99.f)
+
+#pragma pack(push, 1)
+#if defined(_MSC_VER)
+#pragma warning(push)
+#pragma warning(disable: 4200)
+#endif
+typedef struct {
+    uint32_t magic;
+    uint32_t version;
+    uint32_t entry_count;
+    uint8_t  entries[0];
+} USER_CONFIG_header_t;
+
+typedef struct {
+    uint16_t category;
+    uint16_t entry_id;
+    uint32_t entry_size;
+    uint8_t value[0];
+} USER_CONFIG_ENTRY_t;
+#if defined(_MSC_VER)
+#pragma warning(pop)
+#endif
+#pragma pack(pop)
+
+// Used by user config defaults
+typedef enum {
+    ASPM_DISABLED = 0,
+    ASPM_L1_ONLY,
+    ASPM_L0S_L1
+} PCIE_CONFIG_SUPPOPRTED_ASPM_STATES_t;
+
+typedef enum {
+    ASPM_L1_SUBSTATES_DISABLED = 0,
+    ASPM_L1_SUBSTATES_L11_ONLY,
+    ASPM_L1_SUBSTATES_L11_L12
+} PCIE_CONFIG_SUPPOPRTED_L1_ASPM_SUBSTATES_t;
+
+typedef enum {
+    SOC__NN_CLOCK_400MHz = 400 * 1000 * 1000,
+    SOC__NN_CLOCK_375MHz = 375 * 1000 * 1000,
+    SOC__NN_CLOCK_350MHz = 350 * 1000 * 1000,
+    SOC__NN_CLOCK_325MHz = 325 * 1000 * 1000,
+    SOC__NN_CLOCK_300MHz = 300 * 1000 * 1000,
+    SOC__NN_CLOCK_275MHz = 275 * 1000 * 1000,
+    SOC__NN_CLOCK_250MHz = 250 * 1000 * 1000,
+    SOC__NN_CLOCK_225MHz = 225 * 1000 * 1000,
+    SOC__NN_CLOCK_200MHz = 200 * 1000 * 1000,
+    SOC__NN_CLOCK_100MHz = 100 * 1000 * 1000
+} SOC__NN_CLOCK_HZ_t;
+
+typedef enum {
+    WD_SERVICE_MODE_HW_SW = 0,
+    WD_SERVICE_MODE_HW_ONLY,
+    WD_SERVICE_NUM_MODES
+} WD_SERVICE_wd_mode_t;
+
+typedef enum {
+    OVERCURRENT_PARAMETERS_SOURCE_FW_VALUES = 0,
+    OVERCURRENT_PARAMETERS_SOURCE_USER_CONFIG_VALUES,
+    OVERCURRENT_PARAMETERS_SOURCE_BOARD_CONFIG_VALUES,
+    OVERCURRENT_PARAMETERS_SOURCE_OVERCURRENT_DISABLED,
+} OVERCURRENT_parameters_source_t;
+
+typedef enum {
+    OVERCURRENT_CONVERSION_PERIOD_140US = 140,
+    OVERCURRENT_CONVERSION_PERIOD_204US = 204,
+    OVERCURRENT_CONVERSION_PERIOD_332US = 332,
+    OVERCURRENT_CONVERSION_PERIOD_588US = 588,
+    OVERCURRENT_CONVERSION_PERIOD_1100US = 1100,
+    OVERCURRENT_CONVERSION_PERIOD_2116US = 2116,
+    OVERCURRENT_CONVERSION_PERIOD_4156US = 4156,
+    OVERCURRENT_CONVERSION_PERIOD_8244US = 8244
+} OVERCURRENT_conversion_time_us_t;
+
+typedef enum {
+    TEMPERATURE_PROTECTION_PARAMETERS_SOURCE_FW_VALUES = 0,
+    TEMPERATURE_PROTECTION_PARAMETERS_SOURCE_USER_CONFIG_VALUES
+} TEMPERATURE_PROTECTION_parameters_source_t;
+
+#endif /* __USER_CONFIG_COMMON__ */
diff --git a/common/include/utils.h b/common/include/utils.h
new file mode 100644 (file)
index 0000000..8bdfec3
--- /dev/null
@@ -0,0 +1,114 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file utils.h
+ * @brief Defines common utilities.
+**/
+
+#ifndef __UTILS_H__
+#define __UTILS_H__
+
+/** A compile time assertion check.
+ *
+ *  Validate at compile time that the predicate is true without
+ *  generating code. This can be used at any point in a source file
+ *  where typedef is legal.
+ *
+ *  On success, compilation proceeds normally.
+ *
+ *  On failure, attempts to typedef an array type of negative size. The
+ *  offending line will look like
+ *      typedef assertion_failed_file_h_42[-1]
+ *  where file is the content of the second parameter which should
+ *  typically be related in some obvious way to the containing file
+ *  name, 42 is the line number in the file on which the assertion
+ *  appears, and -1 is the result of a calculation based on the
+ *  predicate failing.
+ *
+ *  \param predicate The predicate to test. It must evaluate to
+ *  something that can be coerced to a normal C boolean.
+ *
+ *  \param file A sequence of legal identifier characters that should
+ *  uniquely identify the source file in which this condition appears.
+ */
+#define CASSERT(predicate, file) \
+    _impl_CASSERT_LINE(predicate,__LINE__,file) \
+
+#define _impl_PASTE(a,b) a##b
+#define _impl_CASSERT_LINE(predicate, line, file) \
+        ATTR_UNUSED typedef char _impl_PASTE(assertion_failed_##file##_,line)[2*!!(predicate)-1];
+
+#define ARRAY_LENGTH(__array) (sizeof((__array)) / sizeof((__array)[0]))
+#ifndef MIN
+#define MIN(a,b) (((a) < (b)) ? (a) : (b))
+#endif
+#ifndef MAX
+#define MAX(a,b) (((a) > (b)) ? (a) : (b))
+#endif
+
+#ifndef DIV_ROUND_UP
+#define DIV_ROUND_UP(n,d) (((n) + (d) - 1) / (d))
+#endif
+
+#ifndef ROUND_UNSIGNED_FLOAT
+#define ROUND_UNSIGNED_FLOAT(n) ((n - (uint32_t)(n)) > 0.5) ? (uint32_t)(n + 1) : (uint32_t)(n)
+#endif
+
+#ifndef IS_POWEROF2
+#define IS_POWEROF2(v) ((v & (v - 1)) == 0)
+#endif
+
+#define CPU_CYCLES_NUMBER_IN_MS (configCPU_CLOCK_HZ / 1000)
+
+#define GET_MASK(width, shift) (((1U << (width)) - 1U) << (shift))
+
+/* Argument counter for variadic macros */
+#define GET_ARG_COUNT(...) INTERNAL_GET_ARG_COUNT_PRIVATE(0, ## __VA_ARGS__, 70, 69, 68, 67, 66, 65, 64, 63, 62, 61, 60, 59, 58, 57, 56, 55, 54, 53, 52, 51, 50, 49, 48, 47, 46, 45, 44, 43, 42, 41, 40, 39, 38, 37, 36, 35, 34, 33, 32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)
+#define INTERNAL_GET_ARG_COUNT_PRIVATE(_0, _1_, _2_, _3_, _4_, _5_, _6_, _7_, _8_, _9_, _10_, _11_, _12_, _13_, _14_, _15_, _16_, _17_, _18_, _19_, _20_, _21_, _22_, _23_, _24_, _25_, _26_, _27_, _28_, _29_, _30_, _31_, _32_, _33_, _34_, _35_, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52, _53, _54, _55, _56, _57, _58, _59, _60, _61, _62, _63, _64, _65, _66, _67, _68, _69, _70, count, ...) count
+
+#ifdef __GNUC__
+  #define ATTR_UNUSED __attribute__((unused))
+#else
+  #define ATTR_UNUSED
+#endif
+
+#define PP_ARG_N(_1,  _2,  _3,  _4,  _5,  _6, N, ...) N
+
+#define PP_RSEQ_N() 6,  5,  4,  3,  2,  1,  0
+
+#define PP_NARG_(...)    PP_ARG_N(__VA_ARGS__)
+
+#define PP_COMMASEQ_N()  1,  1,  1,  1,  1,  0
+
+#define PP_COMMA(...)    ,
+
+#define PP_NARG_HELPER3_01(N)    0
+#define PP_NARG_HELPER3_00(N)    1
+#define PP_NARG_HELPER3_11(N)    N
+#define PP_NARG_HELPER2(a, b, N)    PP_NARG_HELPER3_ ## a ## b(N)
+#define PP_NARG_HELPER1(a, b, N)    PP_NARG_HELPER2(a, b, N)
+
+#define PP_HASCOMMA(...) \
+        PP_NARG_(__VA_ARGS__, PP_COMMASEQ_N())
+
+#define PP_NARG(...)                             \
+        PP_NARG_HELPER1(                         \
+            PP_HASCOMMA(__VA_ARGS__),            \
+            PP_HASCOMMA(PP_COMMA __VA_ARGS__ ()),\
+            PP_NARG_(__VA_ARGS__, PP_RSEQ_N()))
+
+#define UNUSED0(...)
+#define UNUSED1(x) (void)(x)
+#define UNUSED2(x,y) (void)(x),(void)(y)
+#define UNUSED3(x,y,z) (void)(x),(void)(y),(void)(z)
+#define UNUSED4(a,x,y,z) (void)(a),(void)(x),(void)(y),(void)(z)
+#define UNUSED5(a,b,x,y,z) (void)(a),(void)(b),(void)(x),(void)(y),(void)(z)
+#define UNUSED6(a,b,x,y,z,c) (void)(a),(void)(b),(void)(x),(void)(y),(void)(z),(void)(c)
+
+#define ALL_UNUSED_IMPL_(nargs) UNUSED ## nargs
+#define ALL_UNUSED_IMPL(nargs) ALL_UNUSED_IMPL_(nargs)
+#define ALL_UNUSED(...) ALL_UNUSED_IMPL( PP_NARG(__VA_ARGS__))(__VA_ARGS__ )
+
+#endif /* __UTILS_H__ */
diff --git a/common/src/firmware_header_utils.c b/common/src/firmware_header_utils.c
new file mode 100644 (file)
index 0000000..51435ba
--- /dev/null
@@ -0,0 +1,279 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+**/
+/**
+ * @file firmware_header_utils.c
+ * @brief Utilities for working with the firmware header.
+**/
+
+#include <stdint.h>
+#include <stddef.h>
+#include <stdbool.h>
+
+#include "firmware_header.h"
+#include "firmware_version.h"
+#include "firmware_header_utils.h"
+#include "utils.h"
+#include "control_protocol.h"
+
+/* when reading the firmware we don't want to read past the firmware_size,
+   so we have a consumed_firmware_offset that is updated _before_ accessing data at that offset
+   of firmware_base_address */
+#define CONSUME_FIRMWARE(__size, __status) do {                                                 \
+        consumed_firmware_offset += (uint32_t) (__size);                                        \
+        if ((firmware_size < (__size)) || (firmware_size < consumed_firmware_offset)) {         \
+            status = __status;                                                                  \
+            goto exit;                                                                          \
+        }                                                                                       \
+    } while(0)
+
+static HAILO_COMMON_STATUS_t firmware_header_utils__validate_fw_header(uintptr_t firmware_base_address,
+                                                                       uint32_t firmware_size,
+                                                                       uint32_t max_code_size,
+                                                                       uint32_t *outer_consumed_firmware_offset,
+                                                                       firmware_header_t **out_firmware_header,
+                                                                       firmware_type_t firmware_type)
+{
+    HAILO_COMMON_STATUS_t status = HAILO_COMMON_STATUS__UNINITIALIZED;
+    firmware_header_t *firmware_header = NULL;
+    uint32_t consumed_firmware_offset = *outer_consumed_firmware_offset;
+    uint32_t firmware_magic = 0;
+
+    firmware_header = (firmware_header_t *) (firmware_base_address + consumed_firmware_offset);
+    CONSUME_FIRMWARE(sizeof(firmware_header_t), HAILO_STATUS__FIRMWARE_HEADER_UTILS__INVALID_FIRMWARE_HEADER_SIZE);
+
+    switch (firmware_type) {
+    case FIRMWARE_TYPE_HAILO8:
+        firmware_magic = FIRMWARE_HEADER_MAGIC_HAILO8; 
+        break;
+    case FIRMWARE_TYPE_MERCURY:
+        firmware_magic = FIRMWARE_HEADER_MAGIC_MERCURY; 
+        break;
+    default:
+        status = HAILO_STATUS__FIRMWARE_HEADER_UTILS__INVALID_FIRMWARE_TYPE;
+        goto exit;
+    }
+
+    if (firmware_magic != firmware_header->magic) {
+        status = HAILO_STATUS__FIRMWARE_HEADER_UTILS__INCORRECT_FIRMWARE_HEADER_MAGIC;
+        goto exit;
+    }
+
+    /* Validate that the firmware header version is supported */
+    switch(firmware_header->header_version) {
+        case FIRMWARE_HEADER_VERSION_INITIAL:
+            break;
+        default:
+            status = HAILO_STATUS__FIRMWARE_HEADER_UTILS__UNSUPPORTED_FIRMWARE__HEADER_VERSION;
+            goto exit;
+            break;
+    }
+
+    if (MINIMUM_FIRMWARE_CODE_SIZE > firmware_header->code_size) {
+        status = HAILO_STATUS__FIRMWARE_HEADER_UTILS__CODE_SIZE_BELOW_MINIMUM;
+        goto exit;
+    }
+
+    if (max_code_size < firmware_header->code_size) {
+        status = HAILO_STATUS__FIRMWARE_HEADER_UTILS__CODE_OVERRUNS_RAM_SIZE;
+        goto exit;
+    }
+
+    CONSUME_FIRMWARE(firmware_header->code_size, HAILO_STATUS__FIRMWARE_HEADER_UTILS__INVALID_FIRMWARE_CODE_SIZE);
+
+    *outer_consumed_firmware_offset = consumed_firmware_offset;
+    *out_firmware_header = firmware_header;
+    status = HAILO_COMMON_STATUS__SUCCESS;
+
+exit:
+    return status;
+}
+
+static HAILO_COMMON_STATUS_t firmware_header_utils__validate_cert_header(uintptr_t firmware_base_address,
+                                                                         uint32_t firmware_size,
+                                                                         uint32_t *outer_consumed_firmware_offset,
+                                                                         secure_boot_certificate_t **out_firmware_cert)
+{
+
+    secure_boot_certificate_t *firmware_cert = NULL;
+    HAILO_COMMON_STATUS_t status = HAILO_COMMON_STATUS__UNINITIALIZED;
+    uint32_t consumed_firmware_offset = *outer_consumed_firmware_offset;
+
+    firmware_cert = (secure_boot_certificate_t *) (firmware_base_address + consumed_firmware_offset);
+    CONSUME_FIRMWARE(sizeof(secure_boot_certificate_t), HAILO_STATUS__FIRMWARE_HEADER_UTILS__INVALID_CERT_HEADER_SIZE);
+
+    if ((MAXIMUM_FIRMWARE_CERT_KEY_SIZE < firmware_cert->key_size) ||
+        (MAXIMUM_FIRMWARE_CERT_CONTENT_SIZE < firmware_cert->content_size)) {
+        status = HAILO_STATUS__FIRMWARE_HEADER_UTILS__CERT_TOO_LARGE;
+        goto exit;
+    }
+
+    CONSUME_FIRMWARE(firmware_cert->key_size, HAILO_STATUS__FIRMWARE_HEADER_UTILS__INVALID_CERT_KEY_SIZE);
+    CONSUME_FIRMWARE(firmware_cert->content_size, HAILO_STATUS__FIRMWARE_HEADER_UTILS__INVALID_CERT_CONTENT_SIZE);
+
+    *outer_consumed_firmware_offset = consumed_firmware_offset;
+    *out_firmware_cert = firmware_cert;
+    status = HAILO_COMMON_STATUS__SUCCESS;
+
+exit:
+    return status;
+}
+
+HAILO_COMMON_STATUS_t FIRMWARE_HEADER_UTILS__validate_fw_headers(uintptr_t firmware_base_address,
+                                                                 uint32_t firmware_size,
+                                                                 bool is_firmware_size_unknown,
+                                                                 firmware_header_t **out_app_firmware_header,
+                                                                 firmware_header_t **out_core_firmware_header,
+                                                                 secure_boot_certificate_t **out_firmware_cert,
+                                                                 firmware_type_t firmware_type)
+{
+    firmware_header_t *app_firmware_header = NULL;
+    firmware_header_t *core_firmware_header = NULL;
+    secure_boot_certificate_t *firmware_cert = NULL;
+    HAILO_COMMON_STATUS_t status = HAILO_COMMON_STATUS__UNINITIALIZED;
+    uint32_t consumed_firmware_offset = 0;
+
+    status = firmware_header_utils__validate_fw_header(firmware_base_address, firmware_size, MAXIMUM_APP_FIRMWARE_CODE_SIZE,
+        &consumed_firmware_offset, &app_firmware_header, firmware_type);
+    if (HAILO_COMMON_STATUS__SUCCESS != status) {
+        status = HAILO_STATUS__FIRMWARE_HEADER_UTILS__INVALID_APP_CPU_FIRMWARE_HEADER;
+        goto exit;
+    }
+
+    status = firmware_header_utils__validate_cert_header(firmware_base_address, firmware_size,
+        &consumed_firmware_offset, &firmware_cert);
+    if (HAILO_COMMON_STATUS__SUCCESS != status) {
+        status = HAILO_STATUS__FIRMWARE_HEADER_UTILS__INVALID_APP_CPU_FIRMWARE_CERTIFICATE_HEADER;
+        goto exit;
+    }
+
+    status = firmware_header_utils__validate_fw_header(firmware_base_address, firmware_size, MAXIMUM_CORE_FIRMWARE_CODE_SIZE,
+        &consumed_firmware_offset, &core_firmware_header, firmware_type);
+    if (HAILO_COMMON_STATUS__SUCCESS != status) {
+        status = HAILO_STATUS__FIRMWARE_HEADER_UTILS__INVALID_CORE_CPU_FIRMWARE_HEADER;
+        goto exit;
+    }
+
+    if ((consumed_firmware_offset != firmware_size) && (!is_firmware_size_unknown)) {
+        /* it is an error if there is leftover data after the last firmware header */
+        status = HAILO_STATUS__FIRMWARE_HEADER_UTILS__LEFTOVER_DATA_AFTER_LAST_FIRMWARE_HEADER;
+        goto exit;
+    }
+
+    /* the out params are all optional */
+    if (NULL != out_app_firmware_header) {
+        *out_app_firmware_header = app_firmware_header;
+    }
+    if (NULL != out_firmware_cert) {
+        *out_firmware_cert = firmware_cert;
+    }
+    if (NULL != out_core_firmware_header) {
+        *out_core_firmware_header = core_firmware_header;
+    }
+    status = HAILO_COMMON_STATUS__SUCCESS;
+
+exit:
+    return status;
+}
+
+
+HAILO_COMMON_STATUS_t FIRMWARE_HEADER_UTILS__validate_second_stage_headers(uintptr_t second_stage_base_size,
+                                                                           uint32_t second_stage_size,
+                                                                           firmware_header_t **out_second_stage_header,
+                                                                           firmware_type_t firmware_type)
+{
+    firmware_header_t *second_stage_header = NULL;
+    secure_boot_certificate_t *second_stage_cert = NULL;
+    HAILO_COMMON_STATUS_t status = HAILO_COMMON_STATUS__UNINITIALIZED;
+    uint32_t consumed_second_stage_offset = 0;
+
+    status = firmware_header_utils__validate_fw_header(second_stage_base_size, second_stage_size, MAXIMUM_SECOND_STAGE_CODE_SIZE,
+        &consumed_second_stage_offset, &second_stage_header, firmware_type);
+    if (HAILO_COMMON_STATUS__SUCCESS != status) {
+        status = HAILO_STATUS__FIRMWARE_HEADER_UTILS__INVALID_APP_CPU_FIRMWARE_HEADER;
+        goto exit;
+    }
+
+    status = firmware_header_utils__validate_cert_header(second_stage_base_size, second_stage_size,
+        &consumed_second_stage_offset, &second_stage_cert);
+    if (HAILO_COMMON_STATUS__SUCCESS != status) {
+        status = HAILO_STATUS__FIRMWARE_HEADER_UTILS__INVALID_APP_CPU_FIRMWARE_CERTIFICATE_HEADER;
+        goto exit;
+    }
+
+    if (consumed_second_stage_offset != second_stage_size) {
+        /* it is an error if there is leftover data after the last firmware header */
+        status = HAILO_STATUS__FIRMWARE_HEADER_UTILS__LEFTOVER_DATA_AFTER_LAST_FIRMWARE_HEADER;
+        goto exit;
+    }
+
+    /* the out params are all optional */
+    if (NULL != out_second_stage_header) {
+        *out_second_stage_header = second_stage_header;
+    }
+
+    status = HAILO_COMMON_STATUS__SUCCESS;
+
+exit:
+    return status;
+}
+
+FW_BINARY_TYPE_t FIRMWARE_HEADER_UTILS__get_fw_binary_type(uint32_t binary_revision)
+{
+    FW_BINARY_TYPE_t fw_binary_type = FW_BINARY_TYPE_INVALID;
+    // Remove dev flag before checking binary type
+    binary_revision &= ~(REVISION_DEV_FLAG_BIT_MASK);
+
+    if (REVISION_SECOND_STAGE_FLAG_BIT_MASK == (binary_revision & REVISION_SECOND_STAGE_FLAG_BIT_MASK)) {
+        fw_binary_type = FW_BINARY_TYPE_SECOND_STAGE_BOOT;
+    } else if (0 == (binary_revision & (REVISION_APP_CORE_FLAG_BIT_MASK))) {
+        fw_binary_type = FW_BINARY_TYPE_APP_FIRMWARE;
+    } else if (REVISION_APP_CORE_FLAG_BIT_MASK == (binary_revision & (REVISION_APP_CORE_FLAG_BIT_MASK))) {
+        fw_binary_type = FW_BINARY_TYPE_CORE_FIRMWARE;
+    } else {
+        fw_binary_type = FW_BINARY_TYPE_INVALID;
+    }
+
+    return fw_binary_type;
+}
+
+HAILO_COMMON_STATUS_t FIRMWARE_HEADER_UTILS__is_binary_being_downgraded(const firmware_version_t *new_binary_version,
+                                                                        const firmware_version_t *minimum_allowed_binary_version)
+{
+    bool is_binary_being_downgraded = 
+            // Check if minimum allowed binary's major is greater than new binary's major
+             (minimum_allowed_binary_version->firmware_major >  new_binary_version->firmware_major) ||
+            // Check if minimum allowed binary's minor is greater than new binary's minor (If major is the same)
+            ((minimum_allowed_binary_version->firmware_major == new_binary_version->firmware_major) &&
+             (minimum_allowed_binary_version->firmware_minor >  new_binary_version->firmware_minor)) ||
+            // Check if minimum allowed binary's revision is greater than new binary's revision (If major and minor are the same)
+            ((minimum_allowed_binary_version->firmware_major == new_binary_version->firmware_major) &&
+             (minimum_allowed_binary_version->firmware_minor == new_binary_version->firmware_minor) &&
+             (GET_REVISION_NUMBER_VALUE(minimum_allowed_binary_version->firmware_revision) >
+             (GET_REVISION_NUMBER_VALUE(new_binary_version->firmware_revision))));
+    return is_binary_being_downgraded;
+}
+
+HAILO_COMMON_STATUS_t FIRMWARE_HEADER_UTILS__validate_binary_version(const firmware_version_t *new_binary_version,
+                                                                     const firmware_version_t *minimum_allowed_binary_version,
+                                                                     FW_BINARY_TYPE_t fw_binary_type)
+{
+    HAILO_COMMON_STATUS_t status = HAILO_COMMON_STATUS__UNINITIALIZED;
+    // Make sure downgrade is not executed
+    if (FIRMWARE_HEADER_UTILS__is_binary_being_downgraded(new_binary_version, minimum_allowed_binary_version)) {
+            status = HAILO_STATUS__FIRMWARE_HEADER_UTILS__DETECTED_PROHIBITED_DOWNGRADE_ATTEMPT;
+            goto exit;
+    }
+
+    if (FIRMWARE_HEADER_UTILS__get_fw_binary_type(new_binary_version->firmware_revision) != fw_binary_type) {
+        status = HAILO_STATUS__FIRMWARE_HEADER_UTILS__INVALID_BINARY_TYPE;
+        goto exit;
+    }
+
+    status = HAILO_COMMON_STATUS__SUCCESS;
+
+exit:
+    return status;
+
+}
\ No newline at end of file
diff --git a/common/src/firmware_status.c b/common/src/firmware_status.c
new file mode 100644 (file)
index 0000000..39f32d5
--- /dev/null
@@ -0,0 +1,135 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+**/
+/**
+ * @file firmware_status.c
+ * @brief Defines firmware status codes.
+**/
+
+#include <stdint.h>
+#include "firmware_status.h"
+#include <string.h>
+
+#ifdef FIRMWARE_ARCH
+
+#pragma pack(push, 1)
+typedef struct {
+    const char *status_name;
+    uint32_t status_id;
+} FIRMWARE_STATUS__status_record_t;
+
+typedef struct {
+    const char *module_name;
+    uint32_t module_id;
+} FIRMWARE_STATUS__module_record_t;
+#pragma pack(pop)
+
+#define FIRMWARE_STATUS_SECTION  __attribute__((section(".firmware_statuses")))
+
+#define FIRMWARE_MODULE__X(module) static const char module##_str[] FIRMWARE_STATUS_SECTION = #module;
+#define FIRMWARE_STATUS__X(name) static const char name##_str[] FIRMWARE_STATUS_SECTION = #name;
+FIRMWARE_STATUS__VARIABLES
+#undef FIRMWARE_STATUS__X
+#undef FIRMWARE_MODULE__X
+
+const FIRMWARE_STATUS__module_record_t FIRMWARE_STATUS__module_records[] FIRMWARE_STATUS_SECTION = {
+#define FIRMWARE_MODULE__X(module) { .module_id = module, .module_name = module##_str },
+#define FIRMWARE_STATUS__X(name)
+    FIRMWARE_STATUS__VARIABLES
+#undef FIRMWARE_STATUS__X
+#undef FIRMWARE_MODULE__X
+};
+
+const FIRMWARE_STATUS__status_record_t FIRMWARE_STATUS__status_records[] FIRMWARE_STATUS_SECTION = {
+#define FIRMWARE_MODULE__X(module)
+#define FIRMWARE_STATUS__X(name) { .status_id = name, .status_name = name##_str },
+    FIRMWARE_STATUS__VARIABLES
+#undef FIRMWARE_STATUS__X
+#undef FIRMWARE_MODULE__X
+};
+
+#endif
+
+#ifndef FIRMWARE_ARCH
+
+static const char *FIRMWARE_STATUS__textual_format[] = 
+{
+#define FIRMWARE_MODULE__X(module)
+#define FIRMWARE_STATUS__X(name) #name,
+    FIRMWARE_STATUS__VARIABLES
+#undef FIRMWARE_STATUS__X
+#undef FIRMWARE_MODULE__X
+};
+
+/* The FIRMWARE_STATUS__textual_format array stores the strings in "absolute" order.
+   In order for us to know the absolute index of each status we store an array that for each module stores 
+   the absolute index of it's first status.
+   This way we can compute the absolute index in O(1) time. */
+
+#define HELPER_INDEX_NAME(__name) __HELPER_FIRMWARE_STATUS__##__name
+
+/* the helper indices counts all module names and statuses.
+   the goal here is to be able to count how many statuses occured prior to each module.
+   we mark the module starts with the module__START elements, and in order 
+   to calculate the number of statuses prior to the current module, we take the
+   module's module__START value, and subtract the number of __START element
+   of previous modules, which is the enum value of the module. */
+typedef enum {
+#define FIRMWARE_MODULE__X(module) HELPER_INDEX_NAME(module##__START),
+#define FIRMWARE_STATUS__X(name) HELPER_INDEX_NAME(name), 
+    FIRMWARE_STATUS__VARIABLES
+#undef FIRMWARE_STATUS__X
+#undef FIRMWARE_MODULE__X
+} __FIRMWARE_STATUS__helper_indices_t;
+
+static const uint32_t FIRMWARE_STATUS__absolute_module_indices[] = {
+#define FIRMWARE_MODULE__X(module) HELPER_INDEX_NAME(module##__START) - module,
+#define FIRMWARE_STATUS__X(name)
+    FIRMWARE_STATUS__VARIABLES
+#undef FIRMWARE_STATUS__X
+#undef FIRMWARE_MODULE__X
+    ARRAY_LENGTH(FIRMWARE_STATUS__textual_format)
+};
+
+HAILO_COMMON_STATUS_t FIRMWARE_STATUS__get_textual(FIRMWARE_STATUS_t fw_status, const char **text)
+{
+    HAILO_COMMON_STATUS_t status = HAILO_COMMON_STATUS__UNINITIALIZED;
+    uint32_t module_id = 0;
+    uint32_t module_abs_index = 0;
+    uint32_t next_module_abs_index = 0;
+    uint32_t status_value = 0;
+
+    if (NULL == text) {
+        status = HAILO_STATUS__FIRMWARE_STATUS__NULL_ARGUMENT_PASSED;
+        goto exit;
+    }
+
+    if (FIRMWARE_STATUS__COMPONENT_ID != FIRMWARE_STATUS__COMPONENT_GET(fw_status)) {
+        status = HAILO_STATUS__FIRMWARE_STATUS__INVALID_COMPONENT_ID;
+        goto exit;
+    }
+
+    module_id = FIRMWARE_STATUS__MODULE_INDEX_GET(fw_status);
+    if (FIRMWARE_MODULE_COUNT <= module_id) {
+        status = HAILO_STATUS__FIRMWARE_STATUS__INVALID_MODULE_ID;
+        goto exit;
+    }
+
+    module_abs_index = FIRMWARE_STATUS__absolute_module_indices[module_id];
+    next_module_abs_index = FIRMWARE_STATUS__absolute_module_indices[module_id+1];
+    status_value = (uint32_t)FIRMWARE_STATUS__VALUE_GET(fw_status) - 1; /* status values start at 1 */
+    
+    /* check status value is in the correct range */
+    if (status_value >= next_module_abs_index - module_abs_index) {
+        status = HAILO_STATUS__FIRMWARE_STATUS__INVALID_STATUS_VALUE;
+        goto exit;
+    }
+    
+    *text = FIRMWARE_STATUS__textual_format[module_abs_index + status_value];
+    status = HAILO_COMMON_STATUS__SUCCESS;
+
+exit:
+    return status;
+}
+#endif
diff --git a/common/src/md5.c b/common/src/md5.c
new file mode 100644 (file)
index 0000000..a68a102
--- /dev/null
@@ -0,0 +1,291 @@
+/*
+ * This is an OpenSSL-compatible implementation of the RSA Data Security, Inc.
+ * MD5 Message-Digest Algorithm (RFC 1321).
+ *
+ * Homepage:
+ * http://openwall.info/wiki/people/solar/software/public-domain-source-code/md5
+ *
+ * Author:
+ * Alexander Peslyak, better known as Solar Designer <solar at openwall.com>
+ *
+ * This software was written by Alexander Peslyak in 2001.  No copyright is
+ * claimed, and the software is hereby placed in the public domain.
+ * In case this attempt to disclaim copyright and place the software in the
+ * public domain is deemed null and void, then the software is
+ * Copyright (c) 2001 Alexander Peslyak and it is hereby released to the
+ * general public under the following terms:
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted.
+ *
+ * There's ABSOLUTELY NO WARRANTY, express or implied.
+ *
+ * (This is a heavily cut-down "BSD license".)
+ *
+ * This differs from Colin Plumb's older public domain implementation in that
+ * no exactly 32-bit integer data type is required (any 32-bit or wider
+ * unsigned integer data type will do), there's no compile-time endianness
+ * configuration, and the function prototypes match OpenSSL's.  No code from
+ * Colin Plumb's implementation has been reused; this comment merely compares
+ * the properties of the two independent implementations.
+ *
+ * The primary goals of this implementation are portability and ease of use.
+ * It is meant to be fast, but not as fast as possible.  Some known
+ * optimizations are not included to reduce source code size and avoid
+ * compile-time configuration.
+ */
+
+#ifndef HAVE_OPENSSL
+
+#include <string.h>
+
+#include "md5.h"
+
+/*
+ * The basic MD5 functions.
+ *
+ * F and G are optimized compared to their RFC 1321 definitions for
+ * architectures that lack an AND-NOT instruction, just like in Colin Plumb's
+ * implementation.
+ */
+#define F(x, y, z)                     ((z) ^ ((x) & ((y) ^ (z))))
+#define G(x, y, z)                     ((y) ^ ((z) & ((x) ^ (y))))
+#define H(x, y, z)                     (((x) ^ (y)) ^ (z))
+#define H2(x, y, z)                    ((x) ^ ((y) ^ (z)))
+#define I(x, y, z)                     ((y) ^ ((x) | ~(z)))
+
+/*
+ * The MD5 transformation for all four rounds.
+ */
+#define STEP(f, a, b, c, d, x, t, s) \
+       (a) += f((b), (c), (d)) + (x) + (t); \
+       (a) = (((a) << (s)) | (((a) & 0xffffffff) >> (32 - (s)))); \
+       (a) += (b);
+
+/*
+ * SET reads 4 input bytes in little-endian byte order and stores them in a
+ * properly aligned word in host byte order.
+ *
+ * The check for little-endian architectures that tolerate unaligned memory
+ * accesses is just an optimization.  Nothing will break if it fails to detect
+ * a suitable architecture.
+ *
+ * Unfortunately, this optimization may be a C strict aliasing rules violation
+ * if the caller's data buffer has effective type that cannot be aliased by
+ * MD5_u32plus.  In practice, this problem may occur if these MD5 routines are
+ * inlined into a calling function, or with future and dangerously advanced
+ * link-time optimizations.  For the time being, keeping these MD5 routines in
+ * their own translation unit avoids the problem.
+ */
+#if defined(__i386__) || defined(__x86_64__) || defined(__vax__)
+#define SET(n) \
+       (*(MD5_u32plus *)&ptr[(n) * 4])
+#define GET(n) \
+       SET(n)
+#else
+#define SET(n) \
+       (ctx->block[(n)] = \
+       (MD5_u32plus)ptr[(n) * 4] | \
+       ((MD5_u32plus)ptr[(n) * 4 + 1] << 8) | \
+       ((MD5_u32plus)ptr[(n) * 4 + 2] << 16) | \
+       ((MD5_u32plus)ptr[(n) * 4 + 3] << 24))
+#define GET(n) \
+       (ctx->block[(n)])
+#endif
+
+/*
+ * This processes one or more 64-byte data blocks, but does NOT update the bit
+ * counters.  There are no alignment requirements.
+ */
+static const void *body(MD5_CTX *ctx, const void *data, size_t size)
+{
+       const unsigned char *ptr;
+       MD5_u32plus a, b, c, d;
+       MD5_u32plus saved_a, saved_b, saved_c, saved_d;
+
+       ptr = (const unsigned char *)data;
+
+       a = ctx->a;
+       b = ctx->b;
+       c = ctx->c;
+       d = ctx->d;
+
+       do {
+               saved_a = a;
+               saved_b = b;
+               saved_c = c;
+               saved_d = d;
+
+/* Round 1 */
+               STEP(F, a, b, c, d, SET(0), 0xd76aa478, 7)
+               STEP(F, d, a, b, c, SET(1), 0xe8c7b756, 12)
+               STEP(F, c, d, a, b, SET(2), 0x242070db, 17)
+               STEP(F, b, c, d, a, SET(3), 0xc1bdceee, 22)
+               STEP(F, a, b, c, d, SET(4), 0xf57c0faf, 7)
+               STEP(F, d, a, b, c, SET(5), 0x4787c62a, 12)
+               STEP(F, c, d, a, b, SET(6), 0xa8304613, 17)
+               STEP(F, b, c, d, a, SET(7), 0xfd469501, 22)
+               STEP(F, a, b, c, d, SET(8), 0x698098d8, 7)
+               STEP(F, d, a, b, c, SET(9), 0x8b44f7af, 12)
+               STEP(F, c, d, a, b, SET(10), 0xffff5bb1, 17)
+               STEP(F, b, c, d, a, SET(11), 0x895cd7be, 22)
+               STEP(F, a, b, c, d, SET(12), 0x6b901122, 7)
+               STEP(F, d, a, b, c, SET(13), 0xfd987193, 12)
+               STEP(F, c, d, a, b, SET(14), 0xa679438e, 17)
+               STEP(F, b, c, d, a, SET(15), 0x49b40821, 22)
+
+/* Round 2 */
+               STEP(G, a, b, c, d, GET(1), 0xf61e2562, 5)
+               STEP(G, d, a, b, c, GET(6), 0xc040b340, 9)
+               STEP(G, c, d, a, b, GET(11), 0x265e5a51, 14)
+               STEP(G, b, c, d, a, GET(0), 0xe9b6c7aa, 20)
+               STEP(G, a, b, c, d, GET(5), 0xd62f105d, 5)
+               STEP(G, d, a, b, c, GET(10), 0x02441453, 9)
+               STEP(G, c, d, a, b, GET(15), 0xd8a1e681, 14)
+               STEP(G, b, c, d, a, GET(4), 0xe7d3fbc8, 20)
+               STEP(G, a, b, c, d, GET(9), 0x21e1cde6, 5)
+               STEP(G, d, a, b, c, GET(14), 0xc33707d6, 9)
+               STEP(G, c, d, a, b, GET(3), 0xf4d50d87, 14)
+               STEP(G, b, c, d, a, GET(8), 0x455a14ed, 20)
+               STEP(G, a, b, c, d, GET(13), 0xa9e3e905, 5)
+               STEP(G, d, a, b, c, GET(2), 0xfcefa3f8, 9)
+               STEP(G, c, d, a, b, GET(7), 0x676f02d9, 14)
+               STEP(G, b, c, d, a, GET(12), 0x8d2a4c8a, 20)
+
+/* Round 3 */
+               STEP(H, a, b, c, d, GET(5), 0xfffa3942, 4)
+               STEP(H2, d, a, b, c, GET(8), 0x8771f681, 11)
+               STEP(H, c, d, a, b, GET(11), 0x6d9d6122, 16)
+               STEP(H2, b, c, d, a, GET(14), 0xfde5380c, 23)
+               STEP(H, a, b, c, d, GET(1), 0xa4beea44, 4)
+               STEP(H2, d, a, b, c, GET(4), 0x4bdecfa9, 11)
+               STEP(H, c, d, a, b, GET(7), 0xf6bb4b60, 16)
+               STEP(H2, b, c, d, a, GET(10), 0xbebfbc70, 23)
+               STEP(H, a, b, c, d, GET(13), 0x289b7ec6, 4)
+               STEP(H2, d, a, b, c, GET(0), 0xeaa127fa, 11)
+               STEP(H, c, d, a, b, GET(3), 0xd4ef3085, 16)
+               STEP(H2, b, c, d, a, GET(6), 0x04881d05, 23)
+               STEP(H, a, b, c, d, GET(9), 0xd9d4d039, 4)
+               STEP(H2, d, a, b, c, GET(12), 0xe6db99e5, 11)
+               STEP(H, c, d, a, b, GET(15), 0x1fa27cf8, 16)
+               STEP(H2, b, c, d, a, GET(2), 0xc4ac5665, 23)
+
+/* Round 4 */
+               STEP(I, a, b, c, d, GET(0), 0xf4292244, 6)
+               STEP(I, d, a, b, c, GET(7), 0x432aff97, 10)
+               STEP(I, c, d, a, b, GET(14), 0xab9423a7, 15)
+               STEP(I, b, c, d, a, GET(5), 0xfc93a039, 21)
+               STEP(I, a, b, c, d, GET(12), 0x655b59c3, 6)
+               STEP(I, d, a, b, c, GET(3), 0x8f0ccc92, 10)
+               STEP(I, c, d, a, b, GET(10), 0xffeff47d, 15)
+               STEP(I, b, c, d, a, GET(1), 0x85845dd1, 21)
+               STEP(I, a, b, c, d, GET(8), 0x6fa87e4f, 6)
+               STEP(I, d, a, b, c, GET(15), 0xfe2ce6e0, 10)
+               STEP(I, c, d, a, b, GET(6), 0xa3014314, 15)
+               STEP(I, b, c, d, a, GET(13), 0x4e0811a1, 21)
+               STEP(I, a, b, c, d, GET(4), 0xf7537e82, 6)
+               STEP(I, d, a, b, c, GET(11), 0xbd3af235, 10)
+               STEP(I, c, d, a, b, GET(2), 0x2ad7d2bb, 15)
+               STEP(I, b, c, d, a, GET(9), 0xeb86d391, 21)
+
+               a += saved_a;
+               b += saved_b;
+               c += saved_c;
+               d += saved_d;
+
+               ptr += 64;
+       } while (size -= 64);
+
+       ctx->a = a;
+       ctx->b = b;
+       ctx->c = c;
+       ctx->d = d;
+
+       return ptr;
+}
+
+void MD5_Init(MD5_CTX *ctx)
+{
+       ctx->a = 0x67452301;
+       ctx->b = 0xefcdab89;
+       ctx->c = 0x98badcfe;
+       ctx->d = 0x10325476;
+
+       ctx->lo = 0;
+       ctx->hi = 0;
+}
+
+void MD5_Update(MD5_CTX *ctx, const void *data, size_t size)
+{
+       MD5_u32plus saved_lo;
+       size_t used, available;
+
+       saved_lo = ctx->lo;
+       if ((ctx->lo = (saved_lo + size) & 0x1fffffff) < saved_lo)
+               ctx->hi++;
+       ctx->hi += size >> 29;
+
+       used = saved_lo & 0x3f;
+
+       if (used) {
+               available = 64 - used;
+
+               if (size < available) {
+                       memcpy(&ctx->buffer[used], data, size);
+                       return;
+               }
+
+               memcpy(&ctx->buffer[used], data, available);
+               data = (const unsigned char *)data + available;
+               size -= available;
+               body(ctx, ctx->buffer, 64);
+       }
+
+       if (size >= 64) {
+               data = body(ctx, data, size & ~(size_t)0x3f);
+               size &= 0x3f;
+       }
+
+       memcpy(ctx->buffer, data, size);
+}
+
+#define OUT(dst, src) \
+       (dst)[0] = (unsigned char)(src); \
+       (dst)[1] = (unsigned char)((src) >> 8); \
+       (dst)[2] = (unsigned char)((src) >> 16); \
+       (dst)[3] = (unsigned char)((src) >> 24);
+
+void MD5_Final(unsigned char *result, MD5_CTX *ctx)
+{
+       unsigned long used, available;
+
+       used = ctx->lo & 0x3f;
+
+       ctx->buffer[used++] = 0x80;
+
+       available = 64 - used;
+
+       if (available < 8) {
+               memset(&ctx->buffer[used], 0, available);
+               body(ctx, ctx->buffer, 64);
+               used = 0;
+               available = 64;
+       }
+
+       memset(&ctx->buffer[used], 0, available - 8);
+
+       ctx->lo <<= 3;
+       OUT(&ctx->buffer[56], ctx->lo)
+       OUT(&ctx->buffer[60], ctx->hi)
+
+       body(ctx, ctx->buffer, 64);
+
+       OUT(&result[0], ctx->a)
+       OUT(&result[4], ctx->b)
+       OUT(&result[8], ctx->c)
+       OUT(&result[12], ctx->d)
+
+       memset(ctx, 0, sizeof(*ctx));
+}
+
+#endif
diff --git a/hailort/CMakeLists.txt b/hailort/CMakeLists.txt
new file mode 100644 (file)
index 0000000..c189d11
--- /dev/null
@@ -0,0 +1,102 @@
+cmake_minimum_required(VERSION 3.0.0)
+
+# Set firmware version
+add_definitions( -DFIRMWARE_VERSION_MAJOR=4 )
+add_definitions( -DFIRMWARE_VERSION_MINOR=6 )
+add_definitions( -DFIRMWARE_VERSION_REVISION=0 )
+
+message(STATUS "Building pre_build")
+include(${CMAKE_CURRENT_LIST_DIR}/libhailort/cmake/execute_cmake.cmake)
+set(HAILO_EXTERNAL_DIR ${CMAKE_CURRENT_LIST_DIR}/external)
+execute_cmake(
+    SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR}/pre_build
+    BUILD_DIR ${CMAKE_CURRENT_LIST_DIR}/pre_build/build
+    CONFIGURE_ARGS
+        -DCMAKE_BUILD_TYPE=Release
+        -DCMAKE_INSTALL_PREFIX=${CMAKE_CURRENT_LIST_DIR}/pre_build/install
+        -DHAILO_EXTERNAL_DIR=${HAILO_EXTERNAL_DIR}
+    BUILD_ARGS
+        --config ${CMAKE_BUILD_TYPE} --target install ${CMAKE_EXTRA_BUILD_ARGS}
+    PARALLEL_BUILD
+)
+
+# BENCHMARK_ENABLE_TESTING can be used by other 3rd party projects, therefore we define it 
+# before adding projects 
+set(BENCHMARK_ENABLE_TESTING OFF CACHE BOOL "Enable testing of the benchmark library.")
+add_subdirectory(external/benchmark EXCLUDE_FROM_ALL)
+
+# Include host protobuf for protoc (https://stackoverflow.com/questions/53651181/cmake-find-protobuf-package-in-custom-directory)
+if(CMAKE_HOST_UNIX)
+    include(${CMAKE_CURRENT_LIST_DIR}/pre_build/install/lib/cmake/protobuf/protobuf-config.cmake)
+    include(${CMAKE_CURRENT_LIST_DIR}/pre_build/install/lib/cmake/protobuf/protobuf-module.cmake)
+else()
+    include(${CMAKE_CURRENT_LIST_DIR}/pre_build/install/cmake/protobuf-config.cmake)
+    include(${CMAKE_CURRENT_LIST_DIR}/pre_build/install/cmake/protobuf-module.cmake)
+endif()
+
+# Add target protobuf directory and exclude its targets from all
+# Disable protobuf tests, protoc and MSVC static runtime unless they are already defined
+# NOTE: we can also force - set(protobuf_BUILD_TESTS OFF CACHE BOOL "Build protobuf tests" FORCE)
+if(NOT protobuf_BUILD_TESTS)
+    set(protobuf_BUILD_TESTS OFF CACHE BOOL "Build protobuf tests")
+endif()
+if(NOT protobuf_BUILD_PROTOC_BINARIES)
+    set(protobuf_BUILD_PROTOC_BINARIES OFF CACHE BOOL "Build libprotoc and protoc compiler")
+endif()
+if(MSVC AND NOT protobuf_MSVC_STATIC_RUNTIME)
+    set(protobuf_MSVC_STATIC_RUNTIME OFF CACHE BOOL "Protobuf MSVC static runtime")
+endif()
+if(NOT protobuf_WITH_ZLIB)
+    set(protobuf_WITH_ZLIB OFF CACHE BOOL "Compile protobuf with zlib")
+endif()
+add_subdirectory(external/protobuf/cmake EXCLUDE_FROM_ALL)
+if(NOT MSVC)
+    set_target_properties(libprotobuf PROPERTIES POSITION_INDEPENDENT_CODE ON)
+endif()
+
+set(HAILORT_INC_DIR ${PROJECT_SOURCE_DIR}/hailort/libhailort/include)
+set(HAILORT_SRC_DIR ${PROJECT_SOURCE_DIR}/hailort/libhailort/src)
+set(HAILORT_COMMON_DIR ${PROJECT_SOURCE_DIR}/hailort/)
+set(COMMON_INC_DIR ${PROJECT_SOURCE_DIR}/common/include)
+set(DRIVER_INC_DIR ${PROJECT_SOURCE_DIR}/hailort/drivers/common)
+
+if(HAILO_BUILD_PYBIND)
+    add_subdirectory(external/pybind11 EXCLUDE_FROM_ALL)
+endif()
+add_subdirectory(external/Catch2 EXCLUDE_FROM_ALL)
+add_subdirectory(external/CLI11 EXCLUDE_FROM_ALL)
+add_subdirectory(external/json EXCLUDE_FROM_ALL)
+add_subdirectory(external/DotWriter EXCLUDE_FROM_ALL)
+add_subdirectory(external/spdlog EXCLUDE_FROM_ALL)
+set_target_properties(spdlog PROPERTIES POSITION_INDEPENDENT_CODE ON)
+add_subdirectory(common)
+add_subdirectory(libhailort)
+add_subdirectory(hailortcli)
+
+# copy files to venv
+if(HAILO_BUILD_PYBIND AND HAILO_BUILD_PYHAILORT_VENV)
+    set(VENV_DRIVERS_DIR ${CMAKE_SOURCE_DIR}/hailort/libhailort/bindings/python/platform/hailo_platform/drivers/hailort/)
+    set(PYHAILORT_FILES_TO_COPY
+        $<TARGET_FILE:_pyhailort>
+    )
+    set(VENV_PYHAILORT_INTERNAL_DIR ${CMAKE_SOURCE_DIR}/platform_internals/hailo_platform_internals/pyhailort/)
+    set(PYHAILORT_INTERNAL_FILES_TO_COPY
+        $<TARGET_FILE:_pyhailort_internal>
+    )
+    add_custom_target(
+        pyhailort_venv ALL
+        COMMAND ${CMAKE_COMMAND} -E copy ${PYHAILORT_FILES_TO_COPY} ${VENV_DRIVERS_DIR}
+        COMMAND ${CMAKE_COMMAND} -E copy ${PYHAILORT_INTERNAL_FILES_TO_COPY} ${VENV_PYHAILORT_INTERNAL_DIR}
+    )
+    add_dependencies(pyhailort_venv libhailort _pyhailort)
+endif()
+
+if(HAILO_WIN_DRIVER)
+    add_subdirectory(drivers/win)
+    add_subdirectory(packaging)
+endif()
+
+# Compile PCIe Driver if QNX
+if(CMAKE_SYSTEM_NAME STREQUAL QNX)
+    add_subdirectory(drivers/qnx)
+endif()
\ No newline at end of file
diff --git a/hailort/LICENSE b/hailort/LICENSE
new file mode 100644 (file)
index 0000000..aeed83c
--- /dev/null
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2020-2022 Hailo Technologies Ltd.
+All rights reserved.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
\ No newline at end of file
diff --git a/hailort/LICENSE-3RD-PARTY.md b/hailort/LICENSE-3RD-PARTY.md
new file mode 100644 (file)
index 0000000..c7e4576
--- /dev/null
@@ -0,0 +1,13 @@
+| Package                          | Copyright (c)                     | License            | Version        | Notes                                      | References                                                                    |
+|:---------------------------------|:----------------------------------|:-------------------|:---------------|:-------------------------------------------|:------------------------------------------------------------------------------|
+| CLI11                            | University of Cincinnati          | 3-Clause BSD       | 1.7            | Cloned entire package                      | https://github.com/CLIUtils/CLI11                                             |
+| Catch2                           | Catch2 Authors                    | BSL-1.0            | 2.13.7         | Cloned entire package                      | https://github.com/catchorg/Catch2                                            |
+| protobuf                         | Google Inc.                       | BSD                | 3.11.4         | Cloned entire package                      | https://github.com/protocolbuffers/protobuf                                   |
+| pybind11                         | Wenzel Jakob                      | BSD                | 2.3.0          | Cloned entire package                      | https://github.com/pybind/pybind11                                            |
+| spdlog                           | Gabi Melman                       | MIT                | 1.6.1          | Cloned entire package                      | https://github.com/gabime/spdlog                                              |
+| folly                            | Facebook, Inc. and its affiliates | Apache License 2.0 | v2020.08.17.00 | Copied only the file `folly/TokenBucket.h` | https://github.com/facebook/folly                                             |
+| nlohmann_json_cmake_fetchcontent | ArthurSonzogni                    | MIT License        | v3.9.1         | Cloned entire package                      | https://github.com/ArthurSonzogni/nlohmann_json_cmake_fetchcontent            |
+| readerwriterqueue                | Cameron Desrochers                | Simplified BSD     | 1.0.3          | Cloned entire package                      | https://github.com/cameron314/readerwriterqueue                               |
+| DotWriter                        | John Vilk                         | MIT License        | master         | Cloned entire package (forked)             | https://github.com/jvilk/DotWriter                                            |
+| benchmark                        | Google Inc.                       | Apache License 2.0 | 1.6.0          | Cloned entire package                      | https://github.com/google/benchmark.git                                       |
+| md5                              | Alexander Peslyak                 | cut-down BSD       | -              | Copied code from website                   | http://openwall.info/wiki/people/solar/software/public-domain-source-code/md5 |
\ No newline at end of file
diff --git a/hailort/common/CMakeLists.txt b/hailort/common/CMakeLists.txt
new file mode 100644 (file)
index 0000000..34427de
--- /dev/null
@@ -0,0 +1,35 @@
+cmake_minimum_required(VERSION 3.0.0)
+
+if(WIN32)
+    set(HAILORT_COMMON_OS_DIR "${CMAKE_CURRENT_SOURCE_DIR}/os/windows")
+elseif(UNIX)
+    set(HAILORT_COMMON_OS_DIR "${CMAKE_CURRENT_SOURCE_DIR}/os/posix")
+else()
+    message(FATAL_ERROR "Unexpeced host, stopping build")
+endif()
+set(HAILORT_COMMON_OS_DIR ${HAILORT_COMMON_OS_DIR} PARENT_SCOPE)
+
+set(SRC_FILES
+    ${HAILORT_COMMON_OS_DIR}/ethernet_utils.cpp
+    ${HAILORT_COMMON_OS_DIR}/filesystem.cpp
+    ${HAILORT_COMMON_OS_DIR}/socket.cpp
+    ${HAILORT_COMMON_OS_DIR}/process.cpp
+
+    ${CMAKE_CURRENT_SOURCE_DIR}/barrier.cpp
+    ${CMAKE_CURRENT_SOURCE_DIR}/file_utils.cpp
+    ${CMAKE_CURRENT_SOURCE_DIR}/string_utils.cpp
+)
+
+if(WIN32)
+    # Windows only modules:
+    set(SRC_FILES ${SRC_FILES}
+        ${HAILORT_COMMON_OS_DIR}/string_conversion.cpp
+    )
+elseif(UNIX)
+    # Unix only modules
+    set(SRC_FILES ${SRC_FILES}
+        ${HAILORT_COMMON_OS_DIR}/traffic_control.cpp
+    )
+endif()
+
+set(HAILORT_COMMON_CPP_SOURCES ${SRC_FILES} PARENT_SCOPE)
\ No newline at end of file
diff --git a/hailort/common/async_thread.hpp b/hailort/common/async_thread.hpp
new file mode 100644 (file)
index 0000000..fa53bfa
--- /dev/null
@@ -0,0 +1,62 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file async_thread.hpp
+ **/
+
+#ifndef _ASYNC_THREAD_HPP_
+#define _ASYNC_THREAD_HPP_
+
+#include <functional>
+#include <thread>
+#include <memory>
+#include <atomic>
+
+namespace hailort
+{
+
+/**
+ * Basic implementation of an async result of a function on some new thread. We use this class instead of `std::async`
+ * because std::async uses future object that store/throw exceptions, and we can't compile it to armv7l platform.
+ */
+template<typename T>
+class AsyncThread final {
+public:
+    explicit AsyncThread(std::function<T(void)> func) :
+        m_result(),
+        m_thread([this, func]() {
+            m_result.store(func());
+        })
+    {}
+
+    /**
+     * NOTE! this object is not moveable by purpose, on creation we create a lambda that take `this`, if we
+     * move the object `this` will change and the callback will be wrong. Use exeternal storage like std::unique_ptr
+     * to move the object (or to put it inside a container)
+     */
+    AsyncThread(const AsyncThread<T> &) = delete;
+    AsyncThread(AsyncThread<T> &&other) = delete;
+    AsyncThread<T>& operator=(const AsyncThread<T>&) = delete;
+    AsyncThread<T>& operator=(AsyncThread<T> &&) = delete;
+
+    T get()
+    {
+        if (m_thread.joinable()) {
+            m_thread.join();
+        }
+        return m_result.load();
+    }
+
+private:
+    std::atomic<T> m_result;
+    std::thread m_thread;
+};
+
+template<typename T>
+using AsyncThreadPtr = std::unique_ptr<AsyncThread<T>>;
+
+} /* namespace hailort */
+
+#endif /* _ASYNC_THREAD_HPP_ */
diff --git a/hailort/common/barrier.cpp b/hailort/common/barrier.cpp
new file mode 100644 (file)
index 0000000..4342170
--- /dev/null
@@ -0,0 +1,42 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file barrier.cpp
+ **/
+
+#include "common/barrier.hpp"
+
+namespace hailort
+{
+
+Barrier::Barrier(size_t count) :
+    m_original_count(count), m_count(count), m_generation(0), m_mutex(), m_cv(), m_is_activated(true)
+{}
+
+void Barrier::arrive_and_wait()
+{
+    // Consider adding timeout and returning HAILO_STATUS
+    if (!m_is_activated.load()) {
+        return;
+    }
+    std::unique_lock<std::mutex> lock(m_mutex);
+    size_t current_generation = m_generation;
+    if ((--m_count) == 0) {
+        m_generation++;
+        m_count = m_original_count;
+        m_cv.notify_all();
+    }
+    else {
+        m_cv.wait(lock, [this, current_generation] { return ((current_generation != m_generation) || !m_is_activated); });
+    }
+}
+
+void Barrier::terminate()
+{
+    m_is_activated.store(false);
+    m_cv.notify_all();
+}
+
+} /* namespace hailort */
diff --git a/hailort/common/barrier.hpp b/hailort/common/barrier.hpp
new file mode 100644 (file)
index 0000000..3062754
--- /dev/null
@@ -0,0 +1,56 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file barrier.hpp
+ **/
+
+#ifndef _BARRIER_HPP_
+#define _BARRIER_HPP_
+
+#include <mutex>
+#include <condition_variable>
+#include <atomic>
+
+namespace hailort
+{
+
+/**
+ * A barrier is a synchronization object that allows an expected number of threads to block until all of them
+ * arrive at the barrier.
+ *
+ * This class should be similar to std::barrier that will be released in c++20 (If we use c++20, please delete this class)
+ */
+class Barrier final {
+public:
+    explicit Barrier(size_t count);
+
+    Barrier(const Barrier &) = delete;
+    Barrier& operator=(const Barrier &) = delete;
+    Barrier(Barrier &&) = delete;
+    Barrier& operator=(Barrier &&) = delete;
+
+    /**
+     * Decreases the count by 1 and blocks until all threads arrives.
+     */
+    void arrive_and_wait();
+
+    /**
+     * Signal all blocking occurrences, and further calls to 'arrive_and_wait()' will return immediately
+     */
+    void terminate();
+
+private:
+    const size_t m_original_count;
+    size_t m_count;
+    size_t m_generation;
+
+    std::mutex m_mutex;
+    std::condition_variable m_cv;
+    std::atomic_bool m_is_activated;
+};
+
+} /* namespace hailort */
+
+#endif /* _BARRIER_HPP_ */
diff --git a/hailort/common/circular_buffer.hpp b/hailort/common/circular_buffer.hpp
new file mode 100644 (file)
index 0000000..c50b971
--- /dev/null
@@ -0,0 +1,107 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file circular_buffer.hpp
+ * @brief
+ *
+ **/
+
+#ifndef __CIRCULAR_BUFFER_HEADER__
+#define __CIRCULAR_BUFFER_HEADER__
+
+#include "hailo/platform.h"
+#include "common/utils.hpp"
+#include <array>
+
+namespace hailort
+{
+
+typedef struct {
+    volatile int head;
+    volatile int tail;
+    int size;
+    int size_mask;
+} circbuf_t;
+
+//TODO: Do not change the behavior of this module. see PLDA descs impl..
+//TODO: optimize macros
+#ifndef MIN
+#define MIN(x,y) (((x) < (y)) ? (x) : (y))
+#endif
+#ifdef _WIN32
+#define _CB_FETCH(x) (InterlockedOr((LONG volatile*)(&x), (LONG)0))
+#define _CB_SET(x, value) (InterlockedExchange((LONG volatile*)(&x), (LONG)(value)))
+#else
+#define _CB_FETCH(x) (__sync_fetch_and_or(&(x), 0))
+#define _CB_SET(x, value) ((void)__sync_lock_test_and_set(&(x), value))
+#endif
+
+#define CB_INIT(circbuf, s)                         \
+    (circbuf).head = 0;                             \
+    (circbuf).tail = 0;                             \
+    (circbuf).size = static_cast<int>(s);           \
+    (circbuf).size_mask = static_cast<int>((s) - 1)
+#define CB_RESET(circbuf)           \
+    (circbuf).head = 0;             \
+    (circbuf).tail = 0            
+#define CB_HEAD(x) _CB_FETCH((x).head)
+#define CB_TAIL(x) _CB_FETCH((x).tail)
+#define CB_SIZE(x) _CB_FETCH((x).size)
+#define CB_ENQUEUE(circbuf, value) _CB_SET((circbuf).head, ((circbuf).head + (value)) & ((circbuf).size_mask))
+#define CB_DEQUEUE(circbuf, value) _CB_SET((circbuf).tail, ((circbuf).tail + (value)) & ((circbuf).size_mask))
+#define CB_AVAIL(circbuf, head, tail) ((((circbuf).size)-1+(tail)-(head)) & ((circbuf).size_mask))
+#define CB_AVAIL_CONT(circbuf, head, tail) \
+    MIN(CB_AVAIL((circbuf), (head), (tail)), (circbuf).size - (head))
+#define CB_PROG(circbuf, head, tail) ((((circbuf).size)+(head)-(tail)) & ((circbuf).size_mask))
+#define CB_PROG_CONT(circbuf, head, tail) \
+    MIN(CB_PROG((circbuf), (head), (tail)), (circbuf).size - (tail))
+
+
+// TODO: implement more functionalities, better move semantic handle
+// TODO: support consts methods (front(), empty()), right now CB_* macros requires non const pointer to head+tail
+template<typename T>
+class CircularArray final
+{
+public:
+    static_assert(std::is_pod<T>::value, "CircularArray can be used only with POD type");
+
+    CircularArray(size_t storage_size)
+    {
+        // storage size must be a power of 2
+        assert(is_powerof2(storage_size));
+        CB_INIT(m_circ, storage_size);
+        m_array.resize(storage_size);
+    }
+
+    void push_back(const T& element)
+    {
+        // assert(CB_AVAIL(m_circ, CB_HEAD(m_circ), CB_TAIL(m_circ)));
+        m_array[CB_HEAD(m_circ)] = element;
+        CB_ENQUEUE(m_circ, 1);
+    }
+
+    void pop_front()
+    {
+        CB_DEQUEUE(m_circ, 1);
+    }
+
+    T& front()
+    {
+        return m_array[CB_TAIL(m_circ)];
+    }
+
+    bool empty()
+    {
+        return CB_HEAD(m_circ) == CB_TAIL(m_circ);
+    }
+
+private:
+    circbuf_t m_circ;
+    std::vector<T> m_array;
+};
+
+} /* namespace hailort */
+
+#endif /* __CIRCULAR_BUFFER_HEADER__ */
diff --git a/hailort/common/compiler_extensions_compat.hpp b/hailort/common/compiler_extensions_compat.hpp
new file mode 100644 (file)
index 0000000..0bd1652
--- /dev/null
@@ -0,0 +1,55 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file compiler_extensions_compat.hpp
+ * @brief Defines compiler extensions and preprocessor macros that are compatible across MSVC and GNU compilers
+ **/
+
+#ifndef __COMPILER_EXTENSIONS_COMPAT_HPP__
+#define __COMPILER_EXTENSIONS_COMPAT_HPP__
+
+
+// https://stackoverflow.com/questions/1113409/attribute-constructor-equivalent-in-vc
+// Initializer/finalizer sample for MSVC and GCC/Clang.
+// 2010-2016 Joe Lowe. Released into the public domain.
+#ifdef __cplusplus
+    #define COMPAT__INITIALIZER(f) \
+        static void f(void); \
+        struct f##_t_ { f##_t_(void) { f(); } }; static f##_t_ f##_; \
+        static void f(void)
+#elif defined(_MSC_VER)
+    #pragma section(".CRT$XCU",read)
+    #define INITIALIZER2_(f,p) \
+        static void f(void); \
+        __declspec(allocate(".CRT$XCU")) void (*f##_)(void) = f; \
+        __pragma(comment(linker,"/include:" p #f "_")) \
+        static void f(void)
+    #ifdef _WIN64
+        #define COMPAT__INITIALIZER(f) INITIALIZER2_(f,"")
+    #else
+        #define COMPAT__INITIALIZER(f) INITIALIZER2_(f,"_")
+    #endif
+#else
+    #define COMPAT__INITIALIZER(f) \
+        static void f(void) __attribute__((constructor)); \
+        static void f(void)
+#endif
+
+#if !defined(UNREFERENCED_PARAMETER) && !defined(_MSC_VER)
+    #define UNREFERENCED_PARAMETER(param)   \
+        do {                                \
+            (void)(param);                  \
+        } while(0)
+#endif
+
+// Used to capture consts in lambda expressions. On mscv constants must be captures, while in clang it
+// causes a warning
+#if _MSC_VER
+#define LAMBDA_CONSTANT(constant_name) constant_name
+#else
+#define LAMBDA_CONSTANT(constant_name) constant_name=constant_name
+#endif
+
+#endif /* __COMPILER_EXTENSIONS_COMPAT_HPP__ */
diff --git a/hailort/common/ethernet_utils.hpp b/hailort/common/ethernet_utils.hpp
new file mode 100644 (file)
index 0000000..05dfde7
--- /dev/null
@@ -0,0 +1,101 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file ethernet_utils.hpp
+ * @brief TODO
+ *
+ * TODO
+ **/
+
+#ifndef __OS_ETHERNET_UTILS_H__
+#define __OS_ETHERNET_UTILS_H__
+
+
+#include <hailo/hailort.h>
+#include "hailo/expected.hpp"
+
+#if defined(_MSC_VER)
+
+#include <unordered_map>
+#include <ifmib.h>
+
+namespace hailort
+{
+
+class NetworkInterface;
+using NetworkInterfaces = std::vector<NetworkInterface>;
+
+class NetworkInterface final
+{
+public:
+    NetworkInterface(uint32_t index, const std::string& name, const std::string& friendly_name, const std::string& ip);
+    ~NetworkInterface() = default;
+
+    uint32_t index() const;
+    std::string name() const;
+    std::string friendly_name() const;
+    std::string ip() const;
+    
+    static Expected<NetworkInterfaces> get_all_interfaces();
+
+private:
+    const uint32_t m_index;
+    const std::string m_name;
+    const std::string m_friendly_name;
+    const std::string m_ip;    
+};
+
+static const uint32_t MacAddressSize = 6;
+using MacAddress = std::array<uint8_t, MacAddressSize>;
+
+class ArpTable final
+{
+public:
+    ~ArpTable() = default;
+    Expected<MacAddress> get_mac_address(uint32_t ip) const;
+    
+    static Expected<ArpTable> create(uint32_t interface_index);
+
+private:
+    ArpTable(const std::unordered_map<uint32_t, MacAddress>& table);
+
+    std::unordered_map<uint32_t, MacAddress> m_table;
+};
+
+} /* namespace hailort */
+
+#else
+
+#include <net/if.h>
+
+#endif
+
+namespace hailort
+{
+
+class EthernetUtils final
+{
+public:
+    EthernetUtils() = delete;
+
+    #if defined(_MSC_VER)
+    static const uint32_t MAX_INTERFACE_SIZE = MAX_INTERFACE_NAME_LEN;
+    #else
+    static const uint32_t MAX_INTERFACE_SIZE = IFNAMSIZ;
+    #endif
+
+    static hailo_status get_interface_from_board_ip(const char *board_ip, char *interface_name, size_t interface_name_length);
+    static hailo_status get_ip_from_interface(const char *interface_name, char *ip, size_t ip_length);
+
+private:
+    #if defined(__GNUG__)
+    static hailo_status get_interface_from_arp_entry(char *arp_entry, char *interface_name,
+        size_t max_interface_name_length);
+    #endif
+};
+
+} /* namespace hailort */
+
+#endif /* __OS_ETHERNET_UTILS_H__ */
diff --git a/hailort/common/file_utils.cpp b/hailort/common/file_utils.cpp
new file mode 100644 (file)
index 0000000..90ff088
--- /dev/null
@@ -0,0 +1,54 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file file_utils.cpp
+ * @brief Utilities for file operations
+ **/
+
+#include "common/file_utils.hpp"
+#include "common/utils.hpp"
+#include <fstream>
+
+namespace hailort
+{
+
+Expected<size_t> get_istream_size(std::ifstream &s)
+{
+    auto beg_pos = s.tellg();
+    CHECK_AS_EXPECTED(-1 != beg_pos, HAILO_FILE_OPERATION_FAILURE, "ifstream::tellg() failed");
+
+    s.seekg(0, s.end);
+    CHECK_AS_EXPECTED(s.good(), HAILO_FILE_OPERATION_FAILURE, "ifstream::seekg() failed");
+
+    auto size = s.tellg();
+    CHECK_AS_EXPECTED(-1 != size, HAILO_FILE_OPERATION_FAILURE, "ifstream::tellg() failed");
+
+    s.seekg(beg_pos, s.beg);
+    CHECK_AS_EXPECTED(s.good(), HAILO_FILE_OPERATION_FAILURE, "ifstream::seekg() failed");
+
+    auto total_size = static_cast<uint64_t>(size - beg_pos);
+    CHECK_AS_EXPECTED(total_size <= std::numeric_limits<size_t>::max(), HAILO_FILE_OPERATION_FAILURE,
+        "File size {} is too big", total_size);
+    return Expected<size_t>(static_cast<size_t>(total_size));
+}
+
+Expected<Buffer> read_binary_file(const std::string &file_path)
+{
+    std::ifstream file(file_path, std::ios::in | std::ios::binary);
+    CHECK_AS_EXPECTED(file.good(), HAILO_OPEN_FILE_FAILURE, "Error opening file {}", file_path);
+
+    auto file_size = get_istream_size(file);
+    CHECK_EXPECTED(file_size, "Failed to get file size");
+
+    auto buffer =  Buffer::create(file_size.value());
+    CHECK_EXPECTED(buffer, "Failed to allocate file buffer ({} bytes}", file_size.value());
+
+    // Read the data
+    file.read(reinterpret_cast<char*>(buffer->data()), buffer->size());
+    CHECK_AS_EXPECTED(file.good(), HAILO_FILE_OPERATION_FAILURE, "Failed reading file {}", file_path);
+    return buffer.release();
+}
+
+} /* namespace hailort */
diff --git a/hailort/common/file_utils.hpp b/hailort/common/file_utils.hpp
new file mode 100644 (file)
index 0000000..0673774
--- /dev/null
@@ -0,0 +1,31 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file file_utils.hpp
+ * @brief Utilities for file operations
+ **/
+
+#ifndef _HAILO_FILE_UTILS_HPP_
+#define _HAILO_FILE_UTILS_HPP_
+
+#include "hailo/expected.hpp"
+#include "hailo/buffer.hpp"
+
+namespace hailort
+{
+
+/**
+ * Returns the amount of data left in the given file.
+ */
+Expected<size_t> get_istream_size(std::ifstream &s);
+
+/**
+ * Reads full file content into a `Buffer`
+ */
+Expected<Buffer> read_binary_file(const std::string &file_path);
+
+} /* namespace hailort */
+
+#endif /* _HAILO_FILE_UTILS_HPP_ */
diff --git a/hailort/common/filesystem.hpp b/hailort/common/filesystem.hpp
new file mode 100644 (file)
index 0000000..8f6a583
--- /dev/null
@@ -0,0 +1,97 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file filesystem.hpp
+ * @brief File system API
+ **/
+
+#ifndef _OS_FILESYSTEM_HPP_
+#define _OS_FILESYSTEM_HPP_
+
+#include "hailo/hailort.h"
+#include "hailo/platform.h"
+
+#include "hailo/expected.hpp"
+#include <vector>
+#include <string>
+
+#if defined(__GNUC__)
+#include <dirent.h>
+#endif
+
+namespace hailort
+{
+
+class Filesystem final {
+public:
+    Filesystem() = delete;
+
+    static Expected<std::vector<std::string>> get_files_in_dir_flat(const std::string &dir_path);
+    static Expected<bool> is_directory(const std::string &path);
+    static bool has_suffix(const std::string &file_name, const std::string &suffix)
+    {
+        return (file_name.size() >= suffix.size()) && equal(suffix.rbegin(), suffix.rend(), file_name.rbegin());    
+    }
+
+private:
+    // OS-specific filesystem directory separator char (i.e. backslash on Windows or forward slash on UNIX)
+    static const char *SEPARATOR;
+
+    #if defined(_MSC_VER)
+
+    struct FileInfo {
+        std::string path;
+        DWORD       attrs;
+    };
+    
+    static bool is_regular_or_readonly_file(DWORD attrs);
+    
+    // TODO: supoport unicode
+    class FindFile final {
+    public:
+        static Expected<FindFile> create(const std::string &dir_path);
+        ~FindFile();
+        FindFile(const FindFile &other) = delete;
+        FindFile &operator=(const FindFile &other) = delete;
+        FindFile &operator=(FindFile &&other) = delete;
+        FindFile(FindFile &&other);
+        
+        Filesystem::FileInfo get_cur_file_info();
+        // Will return HAILO_INVALID_OPERATION when the iteration is complete or HAILO_FILE_OPERATION_FAILURE upon failure
+        hailo_status next_file();
+
+    private:
+        FindFile(HANDLE find_hadle, const WIN32_FIND_DATAA &find_data);
+
+        HANDLE m_find_handle;
+        WIN32_FIND_DATAA m_find_data;
+    };
+    
+    #else
+    
+    class DirWalker final {
+    public:
+        static Expected<DirWalker> create(const std::string &dir_path);
+        ~DirWalker();
+        DirWalker(const DirWalker &other) = delete;
+        DirWalker &operator=(const DirWalker &other) = delete;
+        DirWalker &operator=(DirWalker &&other) = delete;
+        DirWalker(DirWalker &&other);
+        
+        dirent* next_file();
+
+    private:
+        DirWalker(DIR *dir, const std::string &dir_path);
+
+        DIR *m_dir;
+        const std::string m_path_string;
+    };
+    
+    #endif
+};
+
+} /* namespace hailort */
+
+#endif /* _OS_FILESYSTEM_HPP_ */
diff --git a/hailort/common/latency_meter.hpp b/hailort/common/latency_meter.hpp
new file mode 100644 (file)
index 0000000..c839a0f
--- /dev/null
@@ -0,0 +1,131 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file latency_meter.hpp
+ * @brief Calculate inference frame latency
+ **/
+
+#ifndef _HAILO_LATENCY_METER_HPP_
+#define _HAILO_LATENCY_METER_HPP_
+
+#include "hailo/expected.hpp"
+#include "common/circular_buffer.hpp"
+
+#include <set>
+#include <mutex>
+#include <unordered_map>
+
+namespace hailort
+{
+
+/**
+ * Used to measure latency of hailo datastream - the average amount of time between
+ * the start of the action to the end of the last stream.
+ */
+class LatencyMeter final {
+public:
+    using duration = std::chrono::nanoseconds;
+    using TimestampsArray = CircularArray<duration>;
+
+    explicit LatencyMeter(const std::set<uint32_t> &output_channels, size_t timestamps_list_length) :
+        m_start_timestamps(timestamps_list_length),
+        m_latency_count(0),
+        m_latency_sum(0)
+    {
+        for (uint32_t ch : output_channels) {
+            m_end_timestamps_per_channel.emplace(ch, TimestampsArray(timestamps_list_length));
+        }
+    }
+
+    /**
+     * Adds the given timestamp as a start.
+     * @note Assumes it is the only thread that is calling the function
+     */
+    void add_start_sample(duration timestamp)
+    {
+        m_start_timestamps.push_back(timestamp);
+        update_latency();
+    }
+
+    /*
+     * Adds the given timestamp as the end of the given channel. The operation is done
+     * after this function is called on all channels.
+     * @note Assumes that only one thread per channel is calling this function.
+     */  
+    void add_end_sample(uint32_t channel_index, duration timestamp)
+    {
+        // Safe to access from several threads (when each pass different channel) because the map cannot
+        // be changed in runtime.
+        assert(m_end_timestamps_per_channel.find(channel_index) != m_end_timestamps_per_channel.end());
+        m_end_timestamps_per_channel.at(channel_index).push_back(timestamp);
+        update_latency();
+    }
+
+    /**
+     * Queries average latency. One can clear measured latency by passing clear=true.
+     */
+    Expected<duration> get_latency(bool clear)
+    {
+        std::lock_guard<std::mutex> lock_guard(m_lock);
+
+        if (m_latency_count == 0) {
+            return make_unexpected(HAILO_NOT_AVAILABLE);
+        }
+
+        duration latency = (m_latency_sum / m_latency_count);
+        if (clear) {
+            m_latency_sum = duration();
+            m_latency_count = 0;
+        }
+
+        return latency;
+    }
+
+private:
+    void update_latency()
+    {
+        std::lock_guard<std::mutex> lock_guard(m_lock);
+
+        if (m_start_timestamps.empty()) {
+            // wait for begin sample
+            return;
+        }
+
+        duration start = m_start_timestamps.front();
+        duration end(0);
+        for (auto &end_timesatmps : m_end_timestamps_per_channel) {
+            if (end_timesatmps.second.empty()) {
+                // Wait for all channel samples
+                return;
+            }
+
+            end = std::max(end, end_timesatmps.second.front());
+        }
+
+        // calculate the latency
+        m_latency_sum += (end - start);
+        m_latency_count++;
+
+        // pop fronts
+        m_start_timestamps.pop_front();
+        for (auto &end_timesatmps : m_end_timestamps_per_channel) {
+            end_timesatmps.second.pop_front();
+        }
+    }
+
+    std::mutex m_lock;
+
+    TimestampsArray m_start_timestamps;
+    std::unordered_map<uint32_t, TimestampsArray> m_end_timestamps_per_channel;
+
+    size_t m_latency_count;
+    duration m_latency_sum;
+};
+
+using LatencyMeterPtr = std::shared_ptr<LatencyMeter>;
+
+} /* namespace hailort */
+
+#endif /* _HAILO_LATENCY_METER_HPP_ */
diff --git a/hailort/common/logger_macros.hpp b/hailort/common/logger_macros.hpp
new file mode 100644 (file)
index 0000000..bd31151
--- /dev/null
@@ -0,0 +1,75 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file logger_macros.hpp
+ * @brief Declares logger macros used by hailort components.
+ *        Assumes spdlog::set_default_logger was called (otherwise uses spdlog default logger)
+ **/
+
+#ifndef _LOGGER_MACROS_HPP_
+#define _LOGGER_MACROS_HPP_
+
+#include "hailo/hailort.h"
+
+#define SPDLOG_NO_EXCEPTIONS
+
+/* Minimum log level availble at compile time */
+#ifndef SPDLOG_ACTIVE_LEVEL
+#ifndef NDEBUG
+#define SPDLOG_ACTIVE_LEVEL (SPDLOG_LEVEL_DEBUG)
+#else
+#define SPDLOG_ACTIVE_LEVEL (SPDLOG_LEVEL_INFO)
+#endif
+#endif
+
+#include <spdlog/spdlog.h>
+#include <spdlog/fmt/ostr.h>
+
+inline std::ostream& operator<<(std::ostream& os, const hailo_status& status)
+{
+    auto status_str = hailo_get_status_message(status);
+    if (status_str == nullptr) {
+        return os << "<Invalid(" << static_cast<int>(status) << ")>";
+    }
+    return os << status_str << "(" << static_cast<int>(status) << ")";
+}
+
+namespace hailort
+{
+
+// Makes sure during compilation time that all strings in LOGGER__X macros are not in printf format, but in fmtlib format.
+constexpr bool string_not_printf_format(char const * str) {
+    int i = 0;
+
+    while (str[i] != '\0') {
+        if (str[i] == '%' && ((str[i+1] >= 'a' && str[i+1] <= 'z') || (str[i+1] >= 'A' && str[i+1] <= 'Z'))) {
+            return false;
+        }
+        i++;
+    }
+
+    return true;
+}
+
+#define EXPAND(x) x
+#define ASSERT_NOT_PRINTF_FORMAT(fmt, ...) static_assert(string_not_printf_format(fmt), "Error - Log string is in printf format and not in fmtlib format!")
+
+#define LOGGER_TO_SPDLOG(level, ...)\
+do{\
+    EXPAND(ASSERT_NOT_PRINTF_FORMAT(__VA_ARGS__));\
+    level(__VA_ARGS__);\
+} while(0) // NOLINT: clang complains about this code never executing
+
+#define LOGGER__TRACE(...)  LOGGER_TO_SPDLOG(SPDLOG_TRACE, __VA_ARGS__)
+#define LOGGER__DEBUG(...)  LOGGER_TO_SPDLOG(SPDLOG_DEBUG, __VA_ARGS__)
+#define LOGGER__INFO(...)  LOGGER_TO_SPDLOG(SPDLOG_INFO, __VA_ARGS__)
+#define LOGGER__WARN(...)  LOGGER_TO_SPDLOG(SPDLOG_WARN, __VA_ARGS__)
+#define LOGGER__WARNING  LOGGER__WARN
+#define LOGGER__ERROR(...)  LOGGER_TO_SPDLOG(SPDLOG_ERROR, __VA_ARGS__)
+#define LOGGER__CRITICAL(...)  LOGGER_TO_SPDLOG(SPDLOG_CRITICAL, __VA_ARGS__)
+
+} /* namespace hailort */
+
+#endif /* _LOGGER_MACROS_HPP_ */
\ No newline at end of file
diff --git a/hailort/common/os/posix/ethernet_utils.cpp b/hailort/common/os/posix/ethernet_utils.cpp
new file mode 100644 (file)
index 0000000..0908c4d
--- /dev/null
@@ -0,0 +1,161 @@
+#include <stdio.h>
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
+#include <errno.h>
+#include "utils.h"
+
+#include <hailo/hailort.h>
+#include "common/utils.hpp"
+#include "common/logger_macros.hpp"
+#include "common/ethernet_utils.hpp"
+
+namespace hailort
+{
+
+#define ETHERNET_UTILS__ARP_FILE ("/proc/net/arp")
+#define ETHERNET_UTILS__ARP_ENTRY_DELIMIETERS (" ,\n")
+#define ETHERNET_UTILS__ARP_MAX_ENTRY_LENGTH (512)
+#define ETHERNET_UTILS__ARP_DEVICE_NAME_INDEX (4)
+
+
+hailo_status EthernetUtils::get_interface_from_arp_entry(char *arp_entry, char *interface_name,
+        size_t max_interface_name_length)
+{
+    /* This function parses the interface name out from the arp entry
+     * Each entry is built as follows:
+     *     IP address       HW type     Flags       HW address            Mask     Device 
+     *
+     * For example:
+     *     10.0.0.163       0x1         0x2         80:00:de:ad:be:3f     *        enp1s0
+     * */
+    hailo_status status = HAILO_UNINITIALIZED;
+    size_t token_counter = 0;
+    char* token = NULL;
+
+    /* Start splitting the arp entry into tokens according to the delimiter */
+    token = strtok(arp_entry, ETHERNET_UTILS__ARP_ENTRY_DELIMIETERS);
+    if (NULL == token) {
+        LOGGER__ERROR("Invalid arp entry, could not split it to tokens");
+        status = HAILO_ETH_FAILURE;
+        goto l_exit;
+    }
+
+    /* Iterate over the tokens until the device name is found */
+    while (NULL != token) {
+        token = strtok(NULL, ETHERNET_UTILS__ARP_ENTRY_DELIMIETERS);
+        if (ETHERNET_UTILS__ARP_DEVICE_NAME_INDEX == token_counter) {
+            LOGGER__DEBUG("Interface name: {}", token);
+            strncpy(interface_name, token, max_interface_name_length);
+            break;
+        }
+        token_counter++;
+    } 
+
+    status = HAILO_SUCCESS;
+l_exit:
+    return status;
+}
+
+hailo_status EthernetUtils::get_interface_from_board_ip(const char *board_ip, char *interface_name, size_t interface_name_length)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    FILE* arp_file = NULL;
+    int fclose_rc = -1;
+    char buffer[ETHERNET_UTILS__ARP_MAX_ENTRY_LENGTH] = {};
+
+    CHECK_ARG_NOT_NULL(interface_name);
+    CHECK_ARG_NOT_NULL(board_ip);
+
+    /* Open arp file */
+    arp_file = fopen(ETHERNET_UTILS__ARP_FILE, "r");
+    if (NULL == arp_file) {
+        LOGGER__ERROR("Cannot open file {}. Errno: {:#x}", ETHERNET_UTILS__ARP_FILE, errno);
+        status = HAILO_OPEN_FILE_FAILURE;
+        goto l_exit;
+    }
+
+    /* Go over all of the lines at the file */
+    while(fgets(buffer, ARRAY_LENGTH(buffer), arp_file)) {
+        /* Check if the arp line contains the board_ip */
+        if (strstr(buffer, board_ip)) {
+            status = get_interface_from_arp_entry(buffer, interface_name, interface_name_length);
+            if (HAILO_SUCCESS != status) {
+                goto l_exit;
+            }
+            break;
+        }
+    }
+
+    status = HAILO_SUCCESS;
+l_exit:
+    if (NULL != arp_file) {
+        fclose_rc = fclose(arp_file);
+        if (0 != fclose_rc) {
+            LOGGER__ERROR("Cannot close arp file {} ", ETHERNET_UTILS__ARP_FILE);
+            if (HAILO_SUCCESS == status) {
+                status = HAILO_CLOSE_FAILURE;
+            } else {
+                LOGGER__ERROR("Did not override status. Left status value at: {} (not assigned {}",
+                        status,
+                        HAILO_CLOSE_FAILURE);
+            }
+        }
+    }
+
+    return status;
+}
+
+hailo_status EthernetUtils::get_ip_from_interface(const char *interface_name, char *ip, size_t ip_length)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    struct ifreq ifr = {};
+    int fd = 0;
+    int posix_rc = 0;
+
+    CHECK_ARG_NOT_NULL(interface_name);
+    CHECK_ARG_NOT_NULL(ip);
+
+    /* Create socket */
+    fd = socket(AF_INET, SOCK_DGRAM, 0);
+    if (fd < 0) {
+        LOGGER__ERROR("Failed to create socket. Errno: {:#x}", errno);
+        status = HAILO_ETH_FAILURE;
+        goto l_exit;
+    }
+
+    /* Convert interface name to ip address */
+    ifr.ifr_addr.sa_family = AF_INET;
+    (void)strncpy(ifr.ifr_name, interface_name, IFNAMSIZ-1);
+    posix_rc = ioctl(fd, SIOCGIFADDR, &ifr);
+    if (0 > posix_rc) {
+        LOGGER__ERROR("Interface was not found. ioctl with SIOCGIFADDR has failed. Errno: {:#x}", errno);
+        status = HAILO_ETH_INTERFACE_NOT_FOUND;
+        goto l_exit;
+    }
+    (void)strncpy(ip, inet_ntoa(((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr), ip_length);
+    LOGGER__DEBUG("Interface {} | IP: {}", interface_name, ip);
+
+    status = HAILO_SUCCESS;
+l_exit:
+    /* Close the socket if it was created */
+    if (0 < fd) {
+        posix_rc = close(fd);
+        if (0 != posix_rc) {
+            LOGGER__ERROR("Failed closing socket. Errno: {:#x}", errno);
+            /* Update status if only in case there was not previous error */
+            if (HAILO_SUCCESS == status) {
+                status = HAILO_CLOSE_FAILURE;
+            } else {
+                LOGGER__ERROR("Did not override status. Left status value at: {} (not assigned {}",
+                        status,
+                        HAILO_CLOSE_FAILURE);
+            }
+        }
+    }
+
+    return status;
+}
+
+} /* namespace hailort */
diff --git a/hailort/common/os/posix/filesystem.cpp b/hailort/common/os/posix/filesystem.cpp
new file mode 100644 (file)
index 0000000..00fce95
--- /dev/null
@@ -0,0 +1,97 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file filesystem.cpp
+ * @brief Filesystem wrapper for Linux
+ **/
+
+#include "common/filesystem.hpp"
+#include "common/logger_macros.hpp"
+#include "common/utils.hpp"
+
+#include <errno.h>
+#include <sys/stat.h>
+
+namespace hailort
+{
+
+const char *Filesystem::SEPARATOR = "/";
+
+Expected<Filesystem::DirWalker> Filesystem::DirWalker::create(const std::string &dir_path)
+{
+    DIR *dir = opendir(dir_path.c_str());
+    CHECK(nullptr != dir, make_unexpected(HAILO_FILE_OPERATION_FAILURE),
+        "Could not open directory \"{}\" with errno {}", dir_path, errno);
+    return DirWalker(dir, dir_path);
+}
+
+Filesystem::DirWalker::DirWalker(DIR *dir, const std::string &dir_path) :
+    m_dir(dir),
+    m_path_string(dir_path)
+{}
+
+Filesystem::DirWalker::~DirWalker()
+{
+    if (nullptr != m_dir) {
+        const auto result = closedir(m_dir);
+        if (-1 == result) {
+            LOGGER__ERROR("closedir on directory \"{}\" failed with errno {}", m_path_string.c_str(), errno);
+        }
+    }
+}
+
+Filesystem::DirWalker::DirWalker(DirWalker &&other) :
+    m_dir(std::exchange(other.m_dir, nullptr)),
+    m_path_string(other.m_path_string)
+{}
+        
+dirent* Filesystem::DirWalker::next_file()
+{
+    return readdir(m_dir);
+}
+
+#if defined(__unix__)
+
+Expected<std::vector<std::string>> Filesystem::get_files_in_dir_flat(const std::string &dir_path)
+{
+    const std::string dir_path_with_sep = has_suffix(dir_path, SEPARATOR) ? dir_path : dir_path + SEPARATOR;
+    
+    auto dir = DirWalker::create(dir_path_with_sep);
+    CHECK_EXPECTED(dir);
+    
+    std::vector<std::string> files;
+    struct dirent *entry = nullptr;
+    while ((entry = dir->next_file()) != nullptr) {
+        if (entry->d_type != DT_REG) {
+            continue;
+        }
+        const std::string file_name = entry->d_name;
+        files.emplace_back(dir_path_with_sep + file_name);
+    }
+
+    return files;
+}
+// QNX
+#elif defined(__QNX__)
+Expected<std::vector<std::string>> Filesystem::get_files_in_dir_flat(const std::string &dir_path)
+{
+    (void) dir_path;
+    return make_unexpected(HAILO_NOT_IMPLEMENTED);
+}
+// Unsupported Platform
+#else
+static_assert(false, "Unsupported Platform!");
+#endif
+
+Expected<bool> Filesystem::is_directory(const std::string &path)
+{
+    struct stat path_stat{};
+    CHECK(0 == stat(path.c_str(), &path_stat), make_unexpected(HAILO_FILE_OPERATION_FAILURE),
+        "stat() on path \"{}\" failed. errno {}", path.c_str(), errno);
+
+   return S_ISDIR(path_stat.st_mode);
+}
+
+} /* namespace hailort */
diff --git a/hailort/common/os/posix/process.cpp b/hailort/common/os/posix/process.cpp
new file mode 100644 (file)
index 0000000..808a2e1
--- /dev/null
@@ -0,0 +1,96 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file process.cpp
+ * @brief Process wrapper for Linux
+ **/
+
+#include "common/process.hpp"
+#include "hailo/hailort.h"
+#include "common/utils.hpp"
+#include "hailo/buffer.hpp"
+
+namespace hailort
+{
+
+Expected<std::pair<int32_t, std::string>> Process::create_and_wait_for_output(const std::string &command_line, uint32_t max_output_size)
+{
+    auto popen_expected = PopenWrapper::create(command_line);
+    CHECK_EXPECTED(popen_expected);
+    const auto output_expected = popen_expected->read_stdout(max_output_size);
+    CHECK_EXPECTED(output_expected);
+    const auto process_exit_code = popen_expected->close();
+    return std::make_pair(process_exit_code, output_expected.value());
+}
+
+Expected<Process::PopenWrapper> Process::PopenWrapper::create(const std::string &command_line)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    PopenWrapper popen(command_line, status);
+    CHECK_SUCCESS_AS_EXPECTED(status);
+    return popen;
+}
+
+Process::PopenWrapper::PopenWrapper(const std::string &command_line, hailo_status &status) :
+    m_command_line(command_line)
+{
+    static const char* READONLY_MODE = "r";
+    m_pipe = popen(command_line.c_str(), READONLY_MODE);
+    if (nullptr == m_pipe) {
+        LOGGER__ERROR("popen(\"{}\") failed with errno={}", command_line.c_str(), errno);
+        status = HAILO_INTERNAL_FAILURE;
+    } else {
+        status = HAILO_SUCCESS;
+    }
+}
+
+Process::PopenWrapper::~PopenWrapper()
+{
+    if (nullptr != m_pipe) {
+        (void) close();
+    }
+}
+
+Process::PopenWrapper::PopenWrapper(PopenWrapper &&other) :
+    m_pipe(std::exchange(other.m_pipe, nullptr))
+{}
+
+Expected<std::string> Process::PopenWrapper::read_stdout(uint32_t max_output_size)
+{
+    assert (nullptr != m_pipe);
+
+    // We zero out the bufer so that output won't contain junk from the heap
+    auto output = Buffer::create(max_output_size, 0);
+    CHECK_EXPECTED(output);
+    
+    const auto num_read = fread(reinterpret_cast<char*>(output->data()), sizeof(uint8_t), output->size(), m_pipe);
+    if (num_read != output->size()) {
+        if (feof(m_pipe)) {
+            // We remove the trailing newline we get from fread
+            const auto output_as_str = output->to_string();
+            if (output_as_str[output_as_str.length() - 1] == '\n') {
+                return output_as_str.substr(0, num_read - 1);
+            }
+            return output_as_str.substr(0, num_read);
+        } else {
+            LOGGER__ERROR("fread failed with ferror={}", ferror(m_pipe));
+            return make_unexpected(HAILO_INTERNAL_FAILURE);
+        }
+    } else {
+        // Truncate output
+        LOGGER__TRACE("Truncating output to {} chars long", max_output_size);
+        return output->to_string();
+    }
+}
+
+int32_t Process::PopenWrapper::close()
+{
+    assert (nullptr != m_pipe);
+    const auto return_code = pclose(m_pipe);
+    m_pipe = nullptr; // We only close the handle once
+    return return_code;
+}
+
+} /* namespace hailort */
diff --git a/hailort/common/os/posix/socket.cpp b/hailort/common/os/posix/socket.cpp
new file mode 100644 (file)
index 0000000..2f4ac8f
--- /dev/null
@@ -0,0 +1,304 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file socket.cpp
+ * @brief Socket wrapper for Unix
+ **/
+
+#include "common/socket.hpp"
+
+#include <arpa/inet.h>
+#include <unistd.h>
+#include <poll.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <array>
+
+namespace hailort
+{
+
+#define LINUX_RMEM_MAX_PATH "/proc/sys/net/core/rmem_max"
+
+hailo_status Socket::SocketModuleWrapper::init_module()
+{
+    return HAILO_SUCCESS;
+}
+
+hailo_status Socket::SocketModuleWrapper::free_module()
+{
+    return HAILO_SUCCESS;
+}
+
+Expected<Socket> Socket::create(int af, int type, int protocol)
+{
+    auto module_wrapper = SocketModuleWrapper::create();
+    CHECK_EXPECTED(module_wrapper);
+    
+    auto socket_fd = create_socket_fd(af, type, protocol);
+    CHECK_EXPECTED(socket_fd);
+
+    auto obj = Socket(module_wrapper.release(), socket_fd.release());
+    return obj;
+}
+
+Socket::Socket(SocketModuleWrapper &&module_wrapper, const socket_t socket_fd) :
+  m_module_wrapper(std::move(module_wrapper)), m_socket_fd(socket_fd)
+{
+}
+
+Socket::~Socket()
+{
+    auto status = close_socket_fd();
+    if (HAILO_SUCCESS != status) {
+        LOGGER__ERROR("Failed to free socket fd with status {}", status);
+    }
+}
+
+Expected<socket_t> Socket::create_socket_fd(int af, int type, int protocol)
+{
+    socket_t local_socket = INVALID_SOCKET;
+
+    local_socket = socket(af, type, protocol);
+    CHECK_VALID_SOCKET_AS_EXPECTED(local_socket);
+    
+    return local_socket;
+}
+
+hailo_status Socket::close_socket_fd()
+{
+    if (INVALID_SOCKET != m_socket_fd) {
+        int socket_rc = close(m_socket_fd);
+        CHECK(0 == socket_rc, HAILO_ETH_FAILURE, "Failed to close socket. errno={}", errno);
+    }
+
+    return HAILO_SUCCESS;
+}
+
+hailo_status Socket::abort()
+{
+    int socket_rc = shutdown(m_socket_fd, SHUT_RDWR);
+    CHECK((0 == socket_rc) || ((-1 == socket_rc) && (ENOTCONN == errno)), HAILO_ETH_FAILURE, "Failed to shutdown (abort) socket. errno={}", errno);
+
+    return HAILO_SUCCESS;
+}
+
+hailo_status Socket::socket_bind(const sockaddr *addr, socklen_t len)
+{
+    int socket_rc = SOCKET_ERROR;
+
+    CHECK_ARG_NOT_NULL(addr);
+
+    socket_rc = bind(m_socket_fd, addr, len);
+    CHECK(0 == socket_rc, HAILO_ETH_FAILURE, "Failed to bind socket. errno={}", errno);
+
+    return HAILO_SUCCESS;
+}
+
+hailo_status Socket::get_sock_name(sockaddr *addr, socklen_t *len)
+{
+    int socket_rc = SOCKET_ERROR;
+
+    CHECK_ARG_NOT_NULL(addr);
+    CHECK_ARG_NOT_NULL(len);
+
+    socket_rc = getsockname(m_socket_fd, addr, len);
+    CHECK(0 == socket_rc, HAILO_ETH_FAILURE, "Failed getsockname. errno={}", errno);
+
+    return HAILO_SUCCESS;
+}
+
+hailo_status Socket::ntop(int af, const void *src, char *dst, socklen_t size)
+{ 
+    CHECK_ARG_NOT_NULL(src);
+    CHECK_ARG_NOT_NULL(dst);
+
+    CHECK(NULL != inet_ntop(af, src, dst, size), HAILO_ETH_FAILURE,
+        "Could not convert sockaddr struct to string ip address");
+
+    return HAILO_SUCCESS;
+}
+
+hailo_status Socket::pton(int af, const char *src, void *dst)
+{
+    int inet_rc = 0;
+
+    CHECK_ARG_NOT_NULL(src);
+    CHECK_ARG_NOT_NULL(dst);
+
+    inet_rc = inet_pton(af, reinterpret_cast<const char*>(src), dst);
+    CHECK(1 == inet_rc, HAILO_ETH_FAILURE, "Could not convert string ip address to sockaddr struct");
+
+    return HAILO_SUCCESS;
+}
+
+hailo_status Socket::set_recv_buffer_size_max()
+{
+    int socket_rc = SOCKET_ERROR;
+    FILE *rmem_max_file = NULL;
+    uint8_t rmem_max_buffer[20] = {};
+    uint64_t rmem_max = 0;
+    int file_status = 0;
+    size_t bytes_read = 0;
+   
+    rmem_max_file = fopen(LINUX_RMEM_MAX_PATH, "r");
+    if (NULL != rmem_max_file) {
+        bytes_read = fread(rmem_max_buffer, sizeof(rmem_max_buffer), sizeof(*rmem_max_buffer), rmem_max_file);
+        if ((0 != bytes_read) || (feof(rmem_max_file))) {
+            rmem_max = strtoul((char *)rmem_max_buffer, NULL, 10);
+        }
+
+        if (0 == rmem_max) {
+            LOGGER__WARN("Could not read rmem_max value from file '{}'", LINUX_RMEM_MAX_PATH);
+            rmem_max = UINT64_MAX;
+        }
+
+        file_status = fclose(rmem_max_file);
+        if (0 != file_status) {
+            LOGGER__WARN("Could not close file '{}' errno - {}.", LINUX_RMEM_MAX_PATH, errno);
+        }
+    } else {
+        LOGGER__WARN("Could not open file '{}' to read rmem_max value.", LINUX_RMEM_MAX_PATH);
+    }
+    socket_rc = setsockopt(m_socket_fd, SOL_SOCKET, SO_RCVBUF, &rmem_max, sizeof(rmem_max));
+    CHECK(0 == socket_rc, HAILO_ETH_FAILURE,  "Cannot set the rcv socket buffer to {}", rmem_max);
+
+    return HAILO_SUCCESS;
+}
+
+hailo_status Socket::set_timeout(std::chrono::milliseconds timeout_ms, timeval_t *timeout)
+{
+    int socket_rc = SOCKET_ERROR;
+    time_t seconds = 0;
+    suseconds_t microseconds = 0;
+    auto timeout_value = static_cast<uint32_t>(timeout_ms.count());
+
+    /* Validate arguments */
+    CHECK_ARG_NOT_NULL(timeout);
+
+    seconds = (timeout_value / MILLISECONDS_IN_SECOND);
+    microseconds = (timeout_value % MILLISECONDS_IN_SECOND) * MICROSECONDS_IN_MILLISECOND;
+
+    timeout->tv_sec = seconds;
+    timeout->tv_usec = microseconds;
+
+    socket_rc = setsockopt(m_socket_fd, SOL_SOCKET, SO_RCVTIMEO, timeout, sizeof(*timeout));
+    CHECK(0 == socket_rc, HAILO_ETH_FAILURE, "Cannot set receive timeout. Seconds: {}, microseconds {}", seconds,
+        microseconds);
+
+    socket_rc = setsockopt(m_socket_fd, SOL_SOCKET, SO_SNDTIMEO, timeout, sizeof(*timeout));
+    CHECK(0 == socket_rc, HAILO_ETH_FAILURE, "Cannot set send timeout. Seconds: {}, microseconds {}", seconds,
+        microseconds);
+
+    return HAILO_SUCCESS;
+}
+
+hailo_status Socket::enable_broadcast()
+{
+    int socket_rc = SOCKET_ERROR;
+    int enable_broadcast = 1;
+    
+    socket_rc = setsockopt(m_socket_fd, SOL_SOCKET, SO_BROADCAST, &enable_broadcast, sizeof(enable_broadcast));
+    CHECK(0 == socket_rc, HAILO_ETH_FAILURE, "Cannot set socket to be broadcast");
+
+    return HAILO_SUCCESS;
+}
+
+hailo_status Socket::send_to(const uint8_t *src_buffer, size_t src_buffer_size, int flags,
+    const sockaddr *dest_addr, socklen_t dest_addr_size, size_t *bytes_sent)
+{
+    ssize_t number_of_sent_bytes = 0;
+
+    /* Validate arguments */
+    CHECK_ARG_NOT_NULL(src_buffer);
+    CHECK_ARG_NOT_NULL(dest_addr);
+    CHECK_ARG_NOT_NULL(bytes_sent);
+    
+    number_of_sent_bytes = sendto(m_socket_fd, src_buffer, src_buffer_size, flags,
+        dest_addr,  dest_addr_size);
+    if (-1 == number_of_sent_bytes) {
+        if ((EWOULDBLOCK == errno) || (EAGAIN == errno)) {
+            LOGGER__ERROR("Udp send timeout");
+            return HAILO_TIMEOUT;
+        } else if (EINTR == errno) {
+            LOGGER__ERROR("Udp send interrupted!");
+            return HAILO_INTERRUPTED_BY_SIGNAL;
+        } else if (EPIPE == errno) {
+            // When socket is aborted from another thread sendto will return errno EPIPE
+            LOGGER__INFO("Udp send aborted!");
+            return HAILO_STREAM_INTERNAL_ABORT;
+        } else {
+            LOGGER__ERROR("Udp failed to send data, errno:{}.", errno);
+            return HAILO_ETH_SEND_FAILURE;
+        }
+    }
+
+    *bytes_sent = (size_t)number_of_sent_bytes;
+    return HAILO_SUCCESS;
+}
+
+hailo_status Socket::recv_from(uint8_t *dest_buffer, size_t dest_buffer_size, int flags,
+    sockaddr *src_addr, socklen_t src_addr_size, size_t *bytes_received, bool log_timeouts_in_debug)
+{
+    ssize_t number_of_received_bytes = 0;
+    socklen_t result_src_addr_size = src_addr_size;
+
+    /* Validate arguments */
+    CHECK_ARG_NOT_NULL(dest_buffer);
+    CHECK_ARG_NOT_NULL(src_addr);
+    CHECK_ARG_NOT_NULL(bytes_received);
+
+    number_of_received_bytes = recvfrom(m_socket_fd, dest_buffer, dest_buffer_size, flags,
+        src_addr, &result_src_addr_size);
+    if (-1 == number_of_received_bytes) {
+        if ((EWOULDBLOCK == errno) || (EAGAIN == errno)) {
+            if (log_timeouts_in_debug) {
+                LOGGER__DEBUG("Udp recvfrom failed with timeout");
+            } else {
+                LOGGER__ERROR("Udp recvfrom failed with timeout");
+            }
+            return HAILO_TIMEOUT;
+        } else if (EINTR == errno) {
+            LOGGER__ERROR("Udp recv interrupted!");
+            return HAILO_INTERRUPTED_BY_SIGNAL;
+        } else {
+            LOGGER__ERROR("Udp failed to recv data");
+            return HAILO_ETH_RECV_FAILURE;
+        }
+    }
+    else if ((0 == number_of_received_bytes) && (0 != dest_buffer_size)) {
+        LOGGER__INFO("Udp socket was aborted");
+        return HAILO_STREAM_INTERNAL_ABORT;
+    }
+
+    if (result_src_addr_size > src_addr_size) {
+        LOGGER__ERROR("src_addr size invalid");
+        return HAILO_ETH_RECV_FAILURE;
+    }
+
+    *bytes_received = (size_t)number_of_received_bytes;
+    return HAILO_SUCCESS;
+}
+
+hailo_status Socket::has_data(sockaddr *src_addr, socklen_t src_addr_size, bool log_timeouts_in_debug)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    static const size_t DEST_BUFFER_SIZE = 1;
+    std::array<uint8_t, DEST_BUFFER_SIZE> dest_buffer{};
+    size_t number_of_received_bytes = 0;
+
+    status = recv_from(dest_buffer.data(), dest_buffer.size(), 0, src_addr, src_addr_size, &number_of_received_bytes, log_timeouts_in_debug);
+    if ((status == HAILO_TIMEOUT) && log_timeouts_in_debug) {
+        LOGGER__DEBUG("recv_from failed with timeout");
+        return HAILO_TIMEOUT;
+    } else {
+        CHECK_SUCCESS(status);
+        assert(number_of_received_bytes > 0);
+    }
+    
+    return HAILO_SUCCESS;
+}
+
+} /* namespace hailort */
diff --git a/hailort/common/os/posix/traffic_control.cpp b/hailort/common/os/posix/traffic_control.cpp
new file mode 100644 (file)
index 0000000..fccbbc9
--- /dev/null
@@ -0,0 +1,284 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file traffic_control.cpp
+ * @brief Traffic Control wrapper
+ **/
+
+#include "traffic_control.hpp"
+#include "common/process.hpp"
+#include "common/ethernet_utils.hpp"
+#include "common/utils.hpp"
+#include "hailo/buffer.hpp"
+#include "byte_order.h"
+
+#include <sstream>
+#include <thread>
+
+namespace hailort
+{
+
+Expected<TrafficControlUtil> TrafficControlUtil::create(const std::string &ip, uint16_t port, uint32_t rate_bytes_per_sec)
+{
+    auto interface_name = get_interface_name(ip);
+    CHECK_EXPECTED(interface_name, "get_interface_name failed with status {}", interface_name.status());
+
+    auto board_id = ip_to_board_id(ip);
+    CHECK_EXPECTED(board_id, "ip_to_board_id failed with status {}", board_id.status());
+
+    auto is_sudo_needed = check_is_sudo_needed();
+    CHECK_EXPECTED(is_sudo_needed, "check_is_sudo_needed failed with status {}", is_sudo_needed.status());
+
+    return TrafficControlUtil(ip, interface_name.release(), board_id.release(), port, port_to_port_id(port),
+        rate_bytes_per_sec, is_sudo_needed.release());
+}
+
+TrafficControlUtil::TrafficControlUtil(const std::string& board_address, const std::string& interface_name,
+        uint32_t board_id, uint16_t board_port, uint16_t port_id, uint32_t rate_bytes_per_sec, bool is_sudo_needed) :
+    m_board_address(board_address),
+    m_interface_name(interface_name),
+    m_board_id(board_id),
+    m_board_port(board_port),
+    m_port_id(port_id),
+    m_rate_bytes_per_sec(rate_bytes_per_sec),
+    m_is_sudo_needed(is_sudo_needed)
+{}
+
+hailo_status TrafficControlUtil::set_rate_limit()
+{
+    LOGGER__INFO("Setting UDP rate to {} Byte/sec for {}:{}", m_rate_bytes_per_sec, m_board_address, m_board_port);
+    auto status = add_board_to_interface();
+    CHECK_SUCCESS(status, "add_board_to_interface failed with status {}", status);
+    status = add_input_to_inteface();
+    CHECK_SUCCESS(status, "add_input_to_inteface failed with status {}", status);
+    return status;
+}
+
+hailo_status TrafficControlUtil::reset_rate_limit()
+{
+    LOGGER__INFO("Resetting UDP rate for {}:{}", m_board_address, m_board_port);
+    // Best effort
+    const auto del_input_status = del_input();
+    const auto del_board_status = del_board();
+
+    CHECK_SUCCESS(del_input_status);
+    CHECK_SUCCESS(del_board_status);
+    return HAILO_SUCCESS;
+}
+
+hailo_status TrafficControlUtil::add_board_to_interface()
+{
+    CHECK_SUCCESS(tc_qdisc_add_dev(m_interface_name));
+    CHECK_SUCCESS(tc_class_add_dev_for_board(m_interface_name, m_board_id));
+    return HAILO_SUCCESS;
+}
+
+hailo_status TrafficControlUtil::add_input_to_inteface()
+{
+    CHECK_SUCCESS(tc_class_add_dev_for_input(m_interface_name, m_board_id, m_port_id, m_rate_bytes_per_sec));
+    CHECK_SUCCESS(tc_filter_add_dev_for_input(m_interface_name, m_board_address, m_port_id, m_board_port));
+    return HAILO_SUCCESS;
+}
+
+hailo_status TrafficControlUtil::del_input()
+{
+    static const auto SLEEP_BETWEEN_DELS = std::chrono::milliseconds(200);
+    CHECK_SUCCESS(tc_filter_del_dev_for_input(m_interface_name, m_board_address, m_port_id, m_board_port));
+    std::this_thread::sleep_for(SLEEP_BETWEEN_DELS);
+    CHECK_SUCCESS(tc_class_del_dev_for_input(m_interface_name, m_board_id, m_port_id));
+    return HAILO_SUCCESS;
+}
+
+hailo_status TrafficControlUtil::del_board()
+{
+    return tc_class_del_dev_for_board(m_interface_name, m_board_id);
+}
+
+hailo_status TrafficControlUtil::tc_qdisc_add_dev(const std::string &interface_name)
+{
+    // Right now we do not delete the qdisc itself on cleanup
+    // Hence, it's OK if the QDISC configuration already exists
+    // On 18.04 the error is a bit different so adding another allowed error
+    std::stringstream cmd;
+    cmd << "tc qdisc add dev " << interface_name << " root handle 1: htb default 10 direct_qlen 2000";
+    return run_command(cmd.str(), m_is_sudo_needed,
+        {"RTNETLINK answers: File exists", "Error: Exclusivity flag on, cannot modify."});
+}
+
+hailo_status TrafficControlUtil::tc_class_add_dev_for_board(const std::string &interface_name, uint32_t board_id)
+{
+    std::stringstream cmd;
+    cmd << "tc class add dev " << interface_name << " parent 1: classid 1:" << board_id << " htb rate 1Gbit";
+    return run_command(cmd.str(), m_is_sudo_needed,
+        {"RTNETLINK answers: File exists", "Error: Exclusivity flag on, cannot modify."});
+}
+
+hailo_status TrafficControlUtil::tc_class_add_dev_for_input(const std::string &interface_name, uint32_t board_id,
+    uint16_t port_id, uint32_t rate_bytes_per_sec)
+{
+    std::stringstream cmd;
+    cmd << "tc class add dev " << interface_name << " parent 1:" << board_id << " classid 1:" << port_id
+        << " htb rate " << rate_bytes_per_sec << "bps ceil " << rate_bytes_per_sec << "bps maxburst 1kb";
+    return run_command(cmd.str(), m_is_sudo_needed);
+}
+
+hailo_status TrafficControlUtil::tc_filter_add_dev_for_input(const std::string &interface_name,
+    const std::string &board_ip, uint16_t port_id, uint16_t board_port)
+{
+    std::stringstream cmd;
+    cmd << "tc filter add dev " << interface_name << " protocol ip parent 1:0 prio 1 u32 match ip dst " << board_ip
+        << " match ip dport " << board_port << " 0xffff flowid 1:" << port_id;
+    return run_command(cmd.str(), m_is_sudo_needed);
+}
+
+hailo_status TrafficControlUtil::tc_filter_del_dev_for_input(const std::string &interface_name,
+    const std::string &board_ip, uint16_t port_id, uint16_t board_port)
+{
+    std::stringstream cmd;
+    cmd << "tc filter del dev " << interface_name << " protocol ip parent 1:0 prio 1 u32 match ip dst "
+        << board_ip << " match ip dport " << board_port << " 0xffff flowid 1:" << port_id;
+    return run_command(cmd.str(), m_is_sudo_needed, {}, true);
+}
+
+hailo_status TrafficControlUtil::tc_class_del_dev_for_input(const std::string &interface_name,
+    uint32_t board_id, uint16_t port_id)
+{
+    std::stringstream cmd;
+    cmd << "tc class del dev " << interface_name << " parent 1:" << board_id << " classid 1:" << port_id;
+    return run_command(cmd.str(), m_is_sudo_needed, {}, true);
+}
+
+hailo_status TrafficControlUtil::tc_class_del_dev_for_board(const std::string &interface_name,
+    uint32_t board_id)
+{
+    std::stringstream cmd;
+    cmd << "tc class del dev " << interface_name << " parent 1: classid 1:" << board_id;
+    return run_command(cmd.str(), m_is_sudo_needed, {}, true);
+}
+
+Expected<std::string> TrafficControlUtil::get_interface_name(const std::string &ip)
+{
+    auto interface_name = Buffer::create(EthernetUtils::MAX_INTERFACE_SIZE, 0);
+    CHECK_EXPECTED(interface_name);
+
+    CHECK_SUCCESS_AS_EXPECTED(EthernetUtils::get_interface_from_board_ip(ip.c_str(),
+        interface_name->as_pointer<char>(), interface_name->size()));
+    
+    return interface_name->to_string();
+}
+
+Expected<uint32_t> TrafficControlUtil::ip_to_board_id(const std::string &ip)
+{
+    // Takes last digit from 3 octet + the whole 4th octet
+    // We try to get a unique id
+    const auto last_dot_loc = ip.find_last_of('.');
+    if (std::string::npos == last_dot_loc) {
+        LOGGER__ERROR("\".\" character not found in ip=\"{}\"", ip.c_str());
+        return make_unexpected(HAILO_INVALID_ARGUMENT);
+    }
+    std::string board_id_str = ip[last_dot_loc - 1] + ip.substr(last_dot_loc + 1);
+    uint32_t board_id = std::atoi(board_id_str.c_str());
+    if (0 == board_id) {
+        LOGGER__ERROR("atoi failed parsing \"{}\"", board_id_str.c_str());
+        return make_unexpected(HAILO_INVALID_ARGUMENT);
+    }
+
+    return board_id;
+}
+
+uint16_t TrafficControlUtil::port_to_port_id(uint16_t port)
+{
+    // We use % to make the id unique (hopefully)
+    return port % 1000;
+}
+
+Expected<bool> TrafficControlUtil::check_is_sudo_needed()
+{
+    const auto result = Process::create_and_wait_for_output("id -u", MAX_COMMAND_OUTPUT_LENGTH);
+    CHECK_EXPECTED(result);
+    
+    // If the user id is zero then we don't need to add `sudo` to our commands
+    return std::move(result->second != "0");
+}
+
+hailo_status TrafficControlUtil::run_command(const std::string &commnad, bool add_sudo,
+    const std::vector<std::string> &allowed_errors, bool ignore_fails)
+{
+    // Note: we redirect stderr to stdout
+    const auto result = Process::create_and_wait_for_output(
+        add_sudo ? "sudo " + commnad + " 2>&1" : commnad + " 2>&1",
+        MAX_COMMAND_OUTPUT_LENGTH);
+    CHECK_EXPECTED_AS_STATUS(result);
+
+    const uint32_t exit_code = result->first;
+    if (0 == exit_code) {
+        return HAILO_SUCCESS;
+    }
+
+    std::string cmd_output = result->second;
+    // No output = everything was OK
+    bool is_output_valid = cmd_output.empty();
+    if ((!is_output_valid) && (!allowed_errors.empty())) {
+        is_output_valid = (std::find(allowed_errors.cbegin(), allowed_errors.cend(), cmd_output) != allowed_errors.cend());
+    }
+
+    if (is_output_valid || ignore_fails) {
+        LOGGER__TRACE("Commnad \"{}\" returned a non-zero exit code ({});"
+                      "ignoring (ignore_fails={}, is_output_valid={}).",
+                      cmd_output.c_str(), exit_code, is_output_valid, ignore_fails);
+        return HAILO_SUCCESS;
+    }
+    LOGGER__ERROR("Commnad \"{}\" returned a non-zero exit code ({}), failing.",  cmd_output.c_str(), exit_code);
+    return HAILO_TRAFFIC_CONTROL_FAILURE;
+}
+
+Expected<TrafficControl> TrafficControl::create(const std::string &ip, uint16_t port, uint32_t rate_bytes_per_sec)
+{
+    auto tc_util = TrafficControlUtil::create(ip, port, rate_bytes_per_sec);
+    CHECK_EXPECTED(tc_util);
+    
+    hailo_status rate_set_status = HAILO_UNINITIALIZED;
+    TrafficControl tc(tc_util.release(), rate_set_status);
+    CHECK_SUCCESS_AS_EXPECTED(rate_set_status, "Failed setting rate limit with status {}", rate_set_status);
+
+    return tc;
+}
+
+TrafficControl::~TrafficControl()
+{
+    if (m_call_reset) {
+        const auto status = m_tc_util.reset_rate_limit();
+        if (HAILO_SUCCESS != status) {
+            LOGGER__ERROR("reset_rate_limit failed with status={}", status);
+        }
+    }
+}
+
+TrafficControl::TrafficControl(TrafficControl &&other) :
+    m_tc_util(std::move(other.m_tc_util)),
+    m_call_reset(std::exchange(other.m_call_reset, false))
+{}
+
+TrafficControl::TrafficControl(TrafficControlUtil &&tc, hailo_status &rate_set_status) :
+    m_tc_util(std::move(tc)),
+    m_call_reset(false)
+{
+    rate_set_status = m_tc_util.reset_rate_limit();
+    if (HAILO_SUCCESS != rate_set_status) {
+        // This should succeed if there were previous TC rules set or if there weren't any
+        // Hence, we won't continue
+        LOGGER__ERROR("reset_rate_limit failed with status={}", rate_set_status);
+        return;
+    }
+    // We want to call reset in the dtor even if set_rate_limit fails
+    m_call_reset = true;
+    rate_set_status = m_tc_util.set_rate_limit();
+    if (HAILO_SUCCESS != rate_set_status) {
+        LOGGER__ERROR("set_rate_limit failed with status={}", rate_set_status);
+        return;
+    }
+}
+
+} /* namespace hailort */
diff --git a/hailort/common/os/posix/traffic_control.hpp b/hailort/common/os/posix/traffic_control.hpp
new file mode 100644 (file)
index 0000000..cbca2e8
--- /dev/null
@@ -0,0 +1,95 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file traffic_control.hpp
+ * @brief Traffic Control wrapper
+ *
+ * 
+ **/
+#ifndef _TRAFFIC_CONTROL_HPP_
+#define _TRAFFIC_CONTROL_HPP_
+
+#include "common/utils.hpp"
+#include "hailo/hailort.h"
+#include "hailo/expected.hpp"
+
+namespace hailort
+{
+
+// Contains helper functions to work with the Traffic control command line tool
+class TrafficControlUtil final
+{
+public:
+    static Expected<TrafficControlUtil> create(const std::string &ip, uint16_t port, uint32_t rate_bytes_per_sec);
+    static Expected<std::string> get_interface_name(const std::string &ip);
+    ~TrafficControlUtil() = default;
+    TrafficControlUtil(TrafficControlUtil&) = delete;
+    TrafficControlUtil &operator=(const TrafficControlUtil &) = delete;
+    TrafficControlUtil &operator=(TrafficControlUtil &&) = delete;
+    TrafficControlUtil(TrafficControlUtil &&other) = default;
+    
+    hailo_status set_rate_limit();
+    hailo_status reset_rate_limit();
+
+private:
+    TrafficControlUtil(const std::string& board_address, const std::string& interface_name,
+        uint32_t board_id, uint16_t board_port, uint16_t port_id, uint32_t rate_bytes_per_sec, bool is_sudo_needed);
+
+    hailo_status add_board_to_interface();
+    hailo_status add_input_to_inteface();
+    hailo_status del_input();
+    hailo_status del_board();
+
+    hailo_status tc_qdisc_add_dev(const std::string &interface_name);
+    hailo_status tc_class_add_dev_for_board(const std::string &interface_name, uint32_t board_id);
+    hailo_status tc_class_add_dev_for_input(const std::string &interface_name, uint32_t board_id,
+        uint16_t port_id, uint32_t rate_bytes_per_sec);
+    hailo_status tc_filter_add_dev_for_input(const std::string &interface_name, const std::string &board_ip,
+        uint16_t port_id, uint16_t board_port);
+    hailo_status tc_filter_del_dev_for_input(const std::string &interface_name, const std::string &board_ip,
+        uint16_t port_id, uint16_t board_port);
+    hailo_status tc_class_del_dev_for_input(const std::string &interface_name, uint32_t board_id,
+        uint16_t port_id);
+    hailo_status tc_class_del_dev_for_board(const std::string &interface_name, uint32_t board_id);
+
+    static const uint32_t MAX_COMMAND_OUTPUT_LENGTH = 100;
+    static Expected<std::string> get_interface_address(const struct in_addr *addr);
+
+    static Expected<uint32_t> ip_to_board_id(const std::string &ip);
+    static uint16_t port_to_port_id(uint16_t port);
+    static Expected<bool> check_is_sudo_needed();
+    static hailo_status run_command(const std::string &commnad, bool add_sudo,
+        const std::vector<std::string> &allowed_errors = {}, bool ignore_fails = false);
+
+    const std::string m_board_address;
+    const std::string m_interface_name;
+    const uint32_t m_board_id;
+    const uint16_t m_board_port;
+    const uint16_t m_port_id;
+    const uint32_t m_rate_bytes_per_sec;
+    const bool m_is_sudo_needed;
+};
+
+// RAII for TrafficControlUtil
+class TrafficControl final 
+{
+public:
+    static Expected<TrafficControl> create(const std::string &ip, uint16_t port, uint32_t rate_bytes_per_sec);
+    ~TrafficControl();
+    TrafficControl(TrafficControl&) = delete;
+    TrafficControl &operator=(const TrafficControl &) = delete;
+    TrafficControl &operator=(TrafficControl &&) = delete;
+    TrafficControl(TrafficControl &&other);
+
+private:
+    TrafficControl(TrafficControlUtil &&tc, hailo_status &rate_set_status);
+
+    TrafficControlUtil m_tc_util;
+    bool m_call_reset;
+};
+
+} /* namespace hailort */
+
+#endif  /* _TRAFFIC_CONTROL_HPP_ */
diff --git a/hailort/common/os/windows/ethernet_utils.cpp b/hailort/common/os/windows/ethernet_utils.cpp
new file mode 100644 (file)
index 0000000..70ddc0a
--- /dev/null
@@ -0,0 +1,207 @@
+
+#include "common/ethernet_utils.hpp"
+
+#include "common/socket.hpp"
+#include "common/os/windows/string_conversion.hpp"
+#include "hailo/hailort.h"
+#include "common/logger_macros.hpp"
+#include "hailo/buffer.hpp"
+
+#include <array>
+#include <iphlpapi.h>
+
+namespace hailort
+{
+
+NetworkInterface::NetworkInterface(uint32_t index, const std::string& name, const std::string& friendly_name, const std::string& ip) :
+    m_index(index),
+    m_name(name),
+    m_friendly_name(friendly_name),
+    m_ip(ip)
+{}
+
+uint32_t NetworkInterface::index() const
+{
+    return m_index;
+}
+
+std::string NetworkInterface::name() const
+{
+    return m_name;
+}
+
+std::string NetworkInterface::friendly_name() const
+{
+    return m_friendly_name;
+}
+
+std::string NetworkInterface::ip() const
+{
+    return m_ip;
+}
+
+Expected<NetworkInterfaces> NetworkInterface::get_all_interfaces()
+{
+    static const ULONG IPV4 = AF_INET;
+    static const ULONG UNICAST_ONLY = GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_DNS_SERVER;
+    static const PVOID RESERVED = nullptr;
+    static const PIP_ADAPTER_ADDRESSES NO_INTERFACE_INFO = nullptr;
+    ULONG required_size = 0;
+    ULONG ret_value = GetAdaptersAddresses(IPV4, UNICAST_ONLY, RESERVED, NO_INTERFACE_INFO, &required_size);
+    if (ret_value != ERROR_BUFFER_OVERFLOW) {
+        LOGGER__ERROR("Failed calculating necessary size for IP_ADAPTER_ADDRESSES. "
+                      "Expected ret_value=ERROR_BUFFER_OVERFLOW, received={}", ret_value);
+        return make_unexpected(HAILO_UNEXPECTED_INTERFACE_INFO_FAILURE);
+    }
+
+    auto interface_info_buffer = Buffer::create(required_size, 0);
+    CHECK_EXPECTED(interface_info_buffer);
+    ret_value = GetAdaptersAddresses(IPV4, UNICAST_ONLY, RESERVED,
+        interface_info_buffer->as_pointer<IP_ADAPTER_ADDRESSES>(), &required_size);
+    if (ret_value == ERROR_NO_DATA) {
+        LOGGER__ERROR("No IPv4 interfaces found");
+        return make_unexpected(HAILO_NO_IPV4_INTERFACES_FOUND);
+    } else if (ret_value != NO_ERROR) {
+        LOGGER__ERROR("GetInterfaceInfo failed with error: {}", ret_value);
+        return make_unexpected(HAILO_UNEXPECTED_INTERFACE_INFO_FAILURE);
+    }
+
+    NetworkInterfaces interfaces;
+    PIP_ADAPTER_ADDRESSES interface_info = interface_info_buffer->as_pointer<IP_ADAPTER_ADDRESSES>();
+
+    while (interface_info != nullptr) {
+        PIP_ADAPTER_UNICAST_ADDRESS first_unicast_address = interface_info->FirstUnicastAddress;
+        // TODO: keep a vector of all addresses
+        if (first_unicast_address == nullptr) {
+            LOGGER__DEBUG("first_unicast_address is null. Skipping.");
+            continue;
+        }
+
+        const auto address_struct = first_unicast_address->Address.lpSockaddr;
+        if ((address_struct == nullptr) || (address_struct->sa_family != AF_INET)) {
+            LOGGER__DEBUG("Unicast address is invalid. Skipping.");
+            continue;
+        }
+
+        auto ip = Buffer::create(IPV4_STRING_MAX_LENGTH);
+        CHECK_EXPECTED(ip);
+        const auto result = Socket::ntop(AF_INET, &(reinterpret_cast<sockaddr_in *>(address_struct)->sin_addr),
+            ip->as_pointer<char>(), EthernetUtils::MAX_INTERFACE_SIZE);
+        if (result != HAILO_SUCCESS) {
+            LOGGER__DEBUG("Failed converting unicast address to string (result={}). Skipping.", result);
+            continue;
+        }
+        const auto friendly_name_ansi = StringConverter::utf16_to_ansi(interface_info->FriendlyName);
+        if (!friendly_name_ansi.has_value()) {
+            LOGGER__DEBUG("Failed converting the interface's friendly_name to ansi (result={}). Skipping.",
+                friendly_name_ansi.status());
+            continue;
+        }
+        interfaces.emplace_back(interface_info->IfIndex, interface_info->AdapterName,
+            friendly_name_ansi.value(), ip->to_string());
+        
+        interface_info = interface_info->Next;
+    }
+    return interfaces;
+}
+
+ArpTable::ArpTable(const std::unordered_map<uint32_t, MacAddress>& table) :
+    m_table(table)
+{}
+
+Expected<MacAddress> ArpTable::get_mac_address(uint32_t ip) const
+{
+    auto search = m_table.find(ip);
+    if (search == m_table.end()) {
+        return make_unexpected(HAILO_MAC_ADDRESS_NOT_FOUND);
+    }
+    
+    return Expected<MacAddress>(m_table.at(ip));
+}
+    
+Expected<ArpTable> ArpTable::create(uint32_t interface_index)
+{
+    static const PMIB_IPNETTABLE NO_NETTABLE = nullptr;
+    static const BOOL SORTED = true;
+    ULONG required_size = 0;
+    ULONG ret_value = GetIpNetTable(NO_NETTABLE, &required_size, SORTED);
+    if (ret_value != ERROR_INSUFFICIENT_BUFFER) {
+        LOGGER__ERROR("Failed calculating necessary size for MIB_IPNETTABLE. Expected ret_value=ERROR_INSUFFICIENT_BUFFER, received={}", ret_value);
+        return make_unexpected(HAILO_UNEXPECTED_ARP_TABLE_FAILURE);
+    }
+
+    auto ip_net_table_buffer = Buffer::create(required_size, 0);
+    CHECK_EXPECTED(ip_net_table_buffer);
+    ret_value = GetIpNetTable(ip_net_table_buffer->as_pointer<MIB_IPNETTABLE>(), &required_size, SORTED);
+    if (ret_value == ERROR_NO_DATA) {
+        LOGGER__ERROR("No IPv4 interfaces found");
+        return make_unexpected(HAILO_NO_IPV4_INTERFACES_FOUND);
+    } else if (ret_value != NO_ERROR) {
+        LOGGER__ERROR("GetIpNetTable failed with error: {}", ret_value);
+        return make_unexpected(HAILO_UNEXPECTED_ARP_TABLE_FAILURE);
+    }
+
+    std::unordered_map<uint32_t, MacAddress> result;
+    const PMIB_IPNETTABLE ip_net_table = ip_net_table_buffer->as_pointer<MIB_IPNETTABLE>();
+    for (uint32_t i = 0; i < ip_net_table->dwNumEntries; i++) {
+        if (ip_net_table->table[i].dwIndex != interface_index) {
+            continue;
+        }
+
+        if (ip_net_table->table[i].dwPhysAddrLen != MacAddressSize) {
+            continue;
+        }
+
+        const uint32_t ip = ip_net_table->table[i].dwAddr;
+        MacAddress mac{};
+        memcpy(mac.data(), ip_net_table->table[i].bPhysAddr, MacAddressSize);
+        result[ip] = mac;
+    }
+    return result;
+}
+
+hailo_status EthernetUtils::get_interface_from_board_ip(const char *board_ip, char *interface_name, size_t interface_name_length)
+{
+    CHECK_ARG_NOT_NULL(interface_name);
+    CHECK_ARG_NOT_NULL(board_ip);
+    
+    auto network_interfaces = NetworkInterface::get_all_interfaces();
+    CHECK_EXPECTED_AS_STATUS(network_interfaces);
+
+    struct in_addr board_ip_struct{};
+    auto status = Socket::pton(AF_INET, board_ip, &board_ip_struct);
+    CHECK_SUCCESS(status);
+
+    for (const auto& network_interface : network_interfaces.value()) {
+        auto arp_table = ArpTable::create(network_interface.index());
+        CHECK_EXPECTED_AS_STATUS(arp_table);
+
+        const auto mac_address = arp_table->get_mac_address(static_cast<uint32_t>(board_ip_struct.S_un.S_addr));
+        if (mac_address) {
+            (void)strncpy(interface_name, network_interface.friendly_name().c_str(), interface_name_length); 
+            return HAILO_SUCCESS;
+        }
+    }
+
+    return HAILO_ETH_INTERFACE_NOT_FOUND;
+}
+
+hailo_status EthernetUtils::get_ip_from_interface(const char *interface_name, char *ip, size_t ip_length)
+{
+    CHECK_ARG_NOT_NULL(interface_name);
+    CHECK_ARG_NOT_NULL(ip);
+
+    auto network_interfaces = NetworkInterface::get_all_interfaces();
+    CHECK_EXPECTED_AS_STATUS(network_interfaces);
+
+    for (const auto& network_interface : network_interfaces.value()) {
+        if (network_interface.friendly_name() == interface_name) {
+            (void)strncpy(ip, network_interface.ip().c_str(), ip_length); 
+            return HAILO_SUCCESS;
+        }
+    }
+
+    return HAILO_ETH_INTERFACE_NOT_FOUND;
+}
+
+} /* namespace hailort */
diff --git a/hailort/common/os/windows/filesystem.cpp b/hailort/common/os/windows/filesystem.cpp
new file mode 100644 (file)
index 0000000..caf19e1
--- /dev/null
@@ -0,0 +1,132 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file filesystem.cpp
+ * @brief Filesystem wrapper for Windows
+ **/
+
+#include "common/filesystem.hpp"
+#include "common/logger_macros.hpp"
+#include "common/utils.hpp"
+
+#include <shlwapi.h>
+
+namespace hailort
+{
+
+const char *Filesystem::SEPARATOR = "\\";
+
+Expected<Filesystem::FindFile> Filesystem::FindFile::create(const std::string &dir_path)
+{
+    static const char *DIR_WALK_WILD_CARD = "*";
+    const std::string dir_path_with_sep = has_suffix(dir_path, SEPARATOR) ? dir_path + DIR_WALK_WILD_CARD :
+                                                                            dir_path + SEPARATOR + DIR_WALK_WILD_CARD;
+
+    WIN32_FIND_DATAA find_data{};
+    auto find_handle = FindFirstFileA(dir_path_with_sep.c_str(), &find_data);
+    if (INVALID_HANDLE_VALUE == find_handle) {
+        const auto last_error = GetLastError();
+        if (last_error == ERROR_FILE_NOT_FOUND) {
+            LOGGER__ERROR("No matching files could be found \"{}\"", dir_path_with_sep.c_str());
+        } else {
+            LOGGER__ERROR("FindFirstFileA(\"{}\") failed with LE={}", dir_path_with_sep.c_str(), last_error);
+        }
+        return make_unexpected(HAILO_FILE_OPERATION_FAILURE);
+    }
+
+    // Note: find_data will be copied into the m_find_data member (it doesn't contain pointers)
+    return std::move(FindFile(find_handle, find_data));
+}
+
+Filesystem::FindFile::FindFile(HANDLE find_hadle, const WIN32_FIND_DATAA &find_data) :
+    m_find_handle(find_hadle),
+    m_find_data(find_data)
+{}
+
+Filesystem::FindFile::~FindFile()
+{
+    if (INVALID_HANDLE_VALUE != m_find_handle) {
+        const auto result = FindClose(m_find_handle);
+        if (0 == result) {
+            LOGGER__ERROR("FindClose on handle={} failed with LE={}", m_find_handle, GetLastError());
+        }
+    }
+}
+
+Filesystem::FindFile::FindFile(FindFile &&other) :
+    m_find_handle(std::exchange(other.m_find_handle, INVALID_HANDLE_VALUE)),
+    m_find_data(other.m_find_data)
+{}
+
+Filesystem::FileInfo Filesystem::FindFile::get_cur_file_info()
+{
+    return {m_find_data.cFileName, m_find_data.dwFileAttributes};
+}
+
+hailo_status Filesystem::FindFile::next_file()
+{
+    const auto result = FindNextFileA(m_find_handle, &m_find_data);
+    if (result) {
+        return HAILO_SUCCESS;
+    }
+    const auto last_error = GetLastError();
+    if (last_error == ERROR_NO_MORE_FILES) {
+        // No more files
+        return HAILO_INVALID_OPERATION;
+    }
+    
+    LOGGER__ERROR("FindNextFileA() failed with LE={}", last_error);
+    return HAILO_FILE_OPERATION_FAILURE;
+}
+
+bool Filesystem::is_regular_or_readonly_file(DWORD attrs)
+{
+    return (attrs & FILE_ATTRIBUTE_DIRECTORY) == 0;
+}
+
+Expected<std::vector<std::string>> Filesystem::get_files_in_dir_flat(const std::string &dir_path)
+{
+    const std::string dir_path_with_sep = has_suffix(dir_path, SEPARATOR) ? dir_path : dir_path + SEPARATOR;
+    
+    auto dir = FindFile::create(dir_path_with_sep);
+    CHECK_EXPECTED(dir);
+    
+    std::vector<std::string> files;
+    auto file_info = dir->get_cur_file_info();
+    if (is_regular_or_readonly_file(file_info.attrs)) {
+        files.emplace_back(file_info.path);
+    }
+    
+    hailo_status status = HAILO_UNINITIALIZED;
+    while (true) {
+        status = dir->next_file();
+        if (HAILO_INVALID_OPERATION == status) {
+            // We're done
+            break;
+        }
+        if (HAILO_SUCCESS != status) {
+            // Best effort
+            LOGGER__ERROR("next_file failed with status {}; skipping", status);
+            continue;
+        }
+
+        file_info = dir->get_cur_file_info();
+        if (is_regular_or_readonly_file(file_info.attrs)) {
+            files.emplace_back(dir_path_with_sep + file_info.path);
+        }
+    }
+    return files;
+}
+
+Expected<bool> Filesystem::is_directory(const std::string &path)
+{
+    if (path.length() > MAX_PATH) {
+        LOGGER__ERROR("path is too long (MAX_PATH={}, received length={}", MAX_PATH, path.length());
+        return make_unexpected(HAILO_INVALID_ARGUMENT);
+    }
+    return PathIsDirectoryA(path.c_str());
+}
+
+} /* namespace hailort */
diff --git a/hailort/common/os/windows/process.cpp b/hailort/common/os/windows/process.cpp
new file mode 100644 (file)
index 0000000..c57641d
--- /dev/null
@@ -0,0 +1,25 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file process.cpp
+ * @brief Process wrapper for Windows
+ **/
+
+#include "common/process.hpp"
+#include "hailo/hailort.h"
+#include "common/utils.hpp"
+
+namespace hailort
+{
+
+Expected<std::pair<int32_t, std::string>> Process::create_and_wait_for_output(const std::string &command_line, uint32_t max_output_size)
+{
+    // TODO: Add windows impl (HRT-2510)
+    command_line;
+    max_output_size;
+    return make_unexpected(HAILO_NOT_IMPLEMENTED);
+}
+
+} /* namespace hailort */
diff --git a/hailort/common/os/windows/socket.cpp b/hailort/common/os/windows/socket.cpp
new file mode 100644 (file)
index 0000000..42cc79d
--- /dev/null
@@ -0,0 +1,288 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file socket.cpp
+ * @brief Socket wrapper for Windows
+ **/
+
+#include "common/socket.hpp"
+
+#include <array>
+
+namespace hailort
+{
+
+#define WSA_VERSION MAKEWORD(2, 2)
+
+hailo_status Socket::SocketModuleWrapper::init_module()
+{
+    uint16_t wsa_version = WSA_VERSION;
+    WSADATA wsa_data{};
+    int wsa_rt = SOCKET_ERROR;
+
+    wsa_rt = WSAStartup(wsa_version, &wsa_data);
+    CHECK(0 == wsa_rt, HAILO_ETH_FAILURE, "WSAStartup failed. rt={}", wsa_rt);
+
+    return HAILO_SUCCESS;
+}
+
+hailo_status Socket::SocketModuleWrapper::free_module()
+{
+    int wsa_rt = SOCKET_ERROR;
+
+    wsa_rt = WSACleanup();
+    CHECK(0 == wsa_rt, HAILO_ETH_FAILURE, "WSACleanup failed. LE={}", WSAGetLastError());
+
+    return HAILO_SUCCESS;
+}
+
+Expected<Socket> Socket::create(int af, int type, int protocol)
+{
+    auto module_wrapper = SocketModuleWrapper::create();
+    CHECK_EXPECTED(module_wrapper);
+    
+    auto socket_fd = create_socket_fd(af, type, protocol);
+    CHECK_EXPECTED(socket_fd);
+
+    auto obj = Socket(module_wrapper.release(), socket_fd.release());
+    return std::move(obj);
+}
+
+Socket::Socket(SocketModuleWrapper &&module_wrapper, const socket_t socket_fd) :
+  m_module_wrapper(std::move(module_wrapper)), m_socket_fd(socket_fd)
+{
+}
+
+Socket::~Socket()
+{
+    auto status = close_socket_fd();
+    if (HAILO_SUCCESS != status) {
+        LOGGER__ERROR("Failed to free socket fd with status {:X}");
+    }
+}
+
+Expected<socket_t> Socket::create_socket_fd(int af, int type, int protocol)
+{
+    socket_t local_socket = INVALID_SOCKET;
+
+    local_socket = socket(af, type, protocol);
+    CHECK_VALID_SOCKET_AS_EXPECTED(local_socket);
+  
+    return local_socket;
+}
+
+hailo_status Socket::abort()
+{
+    return HAILO_NOT_IMPLEMENTED;
+}
+
+hailo_status Socket::close_socket_fd()
+{
+    if (INVALID_SOCKET != m_socket_fd) {
+        int socket_rc = closesocket(m_socket_fd);
+        CHECK(0 == socket_rc, HAILO_ETH_FAILURE, "Failed to close socket. WSALE={}", WSAGetLastError());
+    }
+
+    return HAILO_SUCCESS;
+}
+
+hailo_status Socket::socket_bind(const sockaddr *addr, socklen_t len)
+{
+    int socket_rc = SOCKET_ERROR;
+
+    CHECK_ARG_NOT_NULL(addr);
+
+    socket_rc = bind(m_socket_fd, addr, len);
+    CHECK(0 == socket_rc, HAILO_ETH_FAILURE, "Failed to bind socket. WSALE={}", WSAGetLastError());
+    
+    return HAILO_SUCCESS;
+}
+
+hailo_status Socket::get_sock_name(sockaddr *addr, socklen_t *len)
+{
+    int socket_rc = SOCKET_ERROR;
+    
+    CHECK_ARG_NOT_NULL(addr);
+    CHECK_ARG_NOT_NULL(len);
+
+    socket_rc = getsockname(m_socket_fd, addr, len);
+    CHECK(0 == socket_rc, HAILO_ETH_FAILURE, "Failed getsockname. WSALE={}", WSAGetLastError());
+    
+    return HAILO_SUCCESS;
+}
+
+hailo_status Socket::ntop(int af, const void *src, char *dst, socklen_t size)
+{
+    CHECK_ARG_NOT_NULL(src);
+    CHECK_ARG_NOT_NULL(dst);
+
+    auto module_wrapper = SocketModuleWrapper::create();
+    CHECK_EXPECTED_AS_STATUS(module_wrapper);
+
+    const char *inet_result = inet_ntop(af, src, dst, size);
+    CHECK(nullptr != inet_result, HAILO_ETH_FAILURE, "Failed inet_ntop. WSALE={}", WSAGetLastError());
+
+    return HAILO_SUCCESS;
+}
+
+hailo_status Socket::pton(int af, const char *src, void *dst)
+{
+    int inet_result = SOCKET_ERROR;
+
+    CHECK_ARG_NOT_NULL(src);
+    CHECK_ARG_NOT_NULL(dst);
+
+    auto module_wrapper = SocketModuleWrapper::create();
+    CHECK_EXPECTED_AS_STATUS(module_wrapper);
+
+    inet_result = inet_pton(af, src, dst);
+    CHECK(1 == inet_result, HAILO_ETH_FAILURE, "Failed inet_pton. WSALE={}", WSAGetLastError());
+
+    return HAILO_SUCCESS;
+}
+
+hailo_status Socket::set_recv_buffer_size_max()
+{
+    int socket_rc = SOCKET_ERROR;
+    // TOOD: MAX_SIZE?? https://docs.microsoft.com/en-us/windows/win32/winsock/sol-socket-socket-options
+    const int MAX_RECV_BUFFER_SIZE = 52428800;
+    socket_rc = setsockopt(m_socket_fd, SOL_SOCKET, SO_RCVBUF,
+        reinterpret_cast<const char*>(&MAX_RECV_BUFFER_SIZE), sizeof(MAX_RECV_BUFFER_SIZE));
+    CHECK(0 == socket_rc, HAILO_ETH_FAILURE, "Failed setsockopt(SOL_SOCKET, SO_RCVBUF). WSALE={}", WSAGetLastError());
+
+    return HAILO_SUCCESS;
+}
+
+hailo_status Socket::set_timeout(const std::chrono::milliseconds timeout_ms, timeval_t *timeout)
+{
+    int socket_rc = SOCKET_ERROR;
+    auto timeout_value = static_cast<uint32_t>(timeout_ms.count());
+    
+    /* Validate arguments */
+    CHECK_ARG_NOT_NULL(timeout);
+    
+    // From https://docs.microsoft.com/en-us/windows/win32/winsock/sol-socket-socket-options (SO_RCVTIMEO):
+    // If the socket is created using the WSASocket function, then the dwFlags parameter must have the
+    // WSA_FLAG_OVERLAPPED attribute set for the timeout to function properly. Otherwise the timeout never takes effect.
+    socket_rc = setsockopt(m_socket_fd, SOL_SOCKET, SO_RCVTIMEO,
+        reinterpret_cast<const char*>(&timeout_value), sizeof(timeout_value));
+    CHECK(0 == socket_rc, HAILO_ETH_FAILURE, "Failed setsockopt(SOL_SOCKET, SO_RCVTIMEO). WSALE={}", WSAGetLastError());
+
+    socket_rc = setsockopt(m_socket_fd, SOL_SOCKET, SO_SNDTIMEO,
+        reinterpret_cast<const char*>(&timeout_value), sizeof(timeout_value));
+    CHECK(0 == socket_rc, HAILO_ETH_FAILURE, "Failed setsockopt(SOL_SOCKET, SO_SNDTIMEO). WSALE={}", WSAGetLastError());
+
+    timeout->tv_sec = timeout_value / MILLISECONDS_IN_SECOND;
+    timeout->tv_usec = (timeout_value % MILLISECONDS_IN_SECOND) * MICROSECONDS_IN_MILLISECOND;
+
+    return HAILO_SUCCESS;
+}
+
+hailo_status Socket::enable_broadcast()
+{
+    int socket_rc = SOCKET_ERROR;
+    int enable_broadcast = 1;
+
+    socket_rc = setsockopt(m_socket_fd, SOL_SOCKET, SO_BROADCAST, 
+        reinterpret_cast<const char*>(&enable_broadcast), sizeof(enable_broadcast));
+    CHECK(0 == socket_rc, HAILO_ETH_FAILURE, "Failed setsockopt(SOL_SOCKET, SO_BROADCAST). WSALE={}", WSAGetLastError());
+
+    return HAILO_SUCCESS;
+}
+
+hailo_status Socket::send_to(const uint8_t *src_buffer, size_t src_buffer_size, int flags,
+    const sockaddr *dest_addr, socklen_t dest_addr_size, size_t *bytes_sent)
+{
+    int number_of_sent_bytes = SOCKET_ERROR;
+
+    /* Validate arguments */
+    CHECK_ARG_NOT_NULL(src_buffer);
+    CHECK_ARG_NOT_NULL(dest_addr);
+    CHECK_ARG_NOT_NULL(bytes_sent);
+    
+    number_of_sent_bytes = sendto(m_socket_fd, reinterpret_cast<const char*>(src_buffer),
+        static_cast<int>(src_buffer_size), flags, dest_addr, dest_addr_size);
+    if (SOCKET_ERROR == number_of_sent_bytes) {
+        const int wsale = WSAGetLastError();
+        if (WSAETIMEDOUT == errno) {
+            LOGGER__ERROR("Udp send timeout");
+            return HAILO_TIMEOUT;
+        } else {
+            LOGGER__ERROR("Udp failed to send data, WSALE={}.", wsale);
+            return HAILO_ETH_SEND_FAILURE;
+        }
+    }
+
+    *bytes_sent = (size_t)number_of_sent_bytes;
+    return HAILO_SUCCESS;
+}
+
+hailo_status Socket::recv_from(uint8_t *dest_buffer, size_t dest_buffer_size, int flags,
+    sockaddr *src_addr, socklen_t src_addr_size, size_t *bytes_recieved, bool log_timeouts_in_debug)
+{
+    int number_of_received_bytes = SOCKET_ERROR;
+    socklen_t result_src_addr_size = src_addr_size;
+
+    /* Validate arguments */
+    CHECK_ARG_NOT_NULL(dest_buffer);
+    CHECK_ARG_NOT_NULL(src_addr);
+    CHECK_ARG_NOT_NULL(bytes_recieved);
+
+    number_of_received_bytes = recvfrom(m_socket_fd, reinterpret_cast<char*>(dest_buffer),
+        static_cast<int>(dest_buffer_size), flags, src_addr, &result_src_addr_size); 
+    if (SOCKET_ERROR == number_of_received_bytes) {
+        const int wsale = WSAGetLastError();
+        if (WSAETIMEDOUT == wsale) {
+            if (log_timeouts_in_debug) {
+                LOGGER__DEBUG("Udp recvfrom failed with timeout");
+            } else { 
+                LOGGER__ERROR("Udp recvfrom failed with timeout");
+            }
+            return HAILO_TIMEOUT;
+        } else {
+            LOGGER__ERROR("Udp failed to recv data. WSALE={}.", wsale);
+            return HAILO_ETH_RECV_FAILURE;
+        }
+    }
+
+    *bytes_recieved = static_cast<size_t>(number_of_received_bytes);
+    return HAILO_SUCCESS;
+}
+
+hailo_status Socket::has_data(sockaddr *src_addr, socklen_t src_addr_size, bool log_timeouts_in_debug)
+{
+    int number_of_received_bytes = SOCKET_ERROR;
+    socklen_t result_src_addr_size = src_addr_size;
+    static const size_t DEST_BUFFER_SIZE = 1;
+    std::array<char, DEST_BUFFER_SIZE> dest_buffer{};
+
+    /* Validate arguments */
+    CHECK_ARG_NOT_NULL(src_addr);
+
+    static const int NO_FLAGS = 0;
+    number_of_received_bytes = recvfrom(m_socket_fd, dest_buffer.data(), static_cast<int>(dest_buffer.size()), NO_FLAGS,
+        src_addr, &result_src_addr_size);
+    if (SOCKET_ERROR == number_of_received_bytes) {
+        const int wsale = WSAGetLastError();
+        if (WSAETIMEDOUT == wsale) {
+            if (log_timeouts_in_debug) {
+                LOGGER__DEBUG("Udp recvfrom failed with timeout");
+            } else {
+                LOGGER__ERROR("Udp recvfrom failed with timeout");
+            }
+            return HAILO_TIMEOUT;
+        }
+        // The message may be bigger than DEST_BUFFER_SIZE bytes, leading to WSAEMSGSIZE. This is ok
+        if (WSAEMSGSIZE != wsale) {
+            LOGGER__ERROR("Udp failed to recv data. WSALE={}.", wsale);
+            return HAILO_ETH_RECV_FAILURE;
+        }
+    }
+
+    return HAILO_SUCCESS;
+}
+
+} /* namespace hailort */
diff --git a/hailort/common/os/windows/string_conversion.cpp b/hailort/common/os/windows/string_conversion.cpp
new file mode 100644 (file)
index 0000000..2ca1255
--- /dev/null
@@ -0,0 +1,63 @@
+#include <vector>
+
+#include "common/os/windows/string_conversion.hpp"
+#include "common/utils.hpp"
+
+namespace hailort
+{
+
+Expected<std::wstring> StringConverter::ansi_to_utf16(const std::string& ansi_string)
+{
+    static const UINT ANSI_CODE_PAGE = CP_ACP;
+    static const DWORD FAIL_ON_INVALID_CHARS = MB_ERR_INVALID_CHARS;
+    static const int CALCULATE_INPUT_LENGTH = -1;
+    static const LPWSTR NO_WIDE_CHAR_BUFFER = nullptr;
+    static const int CALCULATE_RESULT_LENGTH = 0;
+    const int required_length = MultiByteToWideChar(ANSI_CODE_PAGE, FAIL_ON_INVALID_CHARS, ansi_string.c_str(),
+        CALCULATE_INPUT_LENGTH, NO_WIDE_CHAR_BUFFER, CALCULATE_RESULT_LENGTH);
+    if (0 == required_length) {
+        LOGGER__ERROR("Failed calculating necessary length for '{}' as unicode string. LE={}", ansi_string, GetLastError());
+        return make_unexpected(HAILO_ANSI_TO_UTF16_CONVERSION_FAILED     );
+    }
+
+    std::vector<wchar_t> result_buffer(required_length, L'\0');
+    const int allocated_length = MultiByteToWideChar(ANSI_CODE_PAGE, FAIL_ON_INVALID_CHARS, ansi_string.c_str(),
+        CALCULATE_INPUT_LENGTH, result_buffer.data(), required_length);
+    if (0 == allocated_length || allocated_length != required_length) {
+        LOGGER__ERROR("Failed converting '{}' to unicode string. LE={}", ansi_string, GetLastError());
+        return make_unexpected(HAILO_ANSI_TO_UTF16_CONVERSION_FAILED     );
+    }
+
+    // result_buffer includes the terminating null
+    return std::wstring(result_buffer.data());
+}
+
+Expected<std::string> StringConverter::utf16_to_ansi(const std::wstring& utf16_string)
+{
+    static const UINT ANSI_CODE_PAGE = CP_ACP;
+    static const DWORD NO_FLAGS = 0;
+    static const int CALCULATE_INPUT_LENGTH = -1;
+    static const LPSTR NO_UTF8_BUFFER = nullptr;
+    static const int CALCULATE_RESULT_LENGTH = 0;
+    static const LPCCH USE_SYSTEM_DEFAULT_CHAR = nullptr;
+    static const LPBOOL NO_CUSTOM_DEFAULT_CHAR = nullptr;
+    const int required_length = WideCharToMultiByte(ANSI_CODE_PAGE, NO_FLAGS, utf16_string.c_str(),
+        CALCULATE_INPUT_LENGTH, NO_UTF8_BUFFER, CALCULATE_RESULT_LENGTH, USE_SYSTEM_DEFAULT_CHAR, NO_CUSTOM_DEFAULT_CHAR);
+    if (0 == required_length) {
+        LOGGER__ERROR("Failed calculating necessary length for utf16_string as ansi string. LE={}", GetLastError());
+        return make_unexpected(HAILO_UTF16_TO_ANSI_CONVERSION_FAILED);
+    }
+
+    std::vector<char> result_buffer(required_length, '\0');
+    const int allocated_length = WideCharToMultiByte(ANSI_CODE_PAGE, NO_FLAGS, utf16_string.c_str(),
+        CALCULATE_INPUT_LENGTH, result_buffer.data(), required_length, USE_SYSTEM_DEFAULT_CHAR, NO_CUSTOM_DEFAULT_CHAR);
+    if (0 == allocated_length || allocated_length != required_length) {
+        LOGGER__ERROR("Failed converting utf16_string to ansi string. LE={}", GetLastError());
+        return make_unexpected(HAILO_UTF16_TO_ANSI_CONVERSION_FAILED);
+    }
+
+    // result_buffer includes the terminating null
+    return std::string(result_buffer.data());
+}
+
+} /* namespace hailort */
diff --git a/hailort/common/os/windows/string_conversion.hpp b/hailort/common/os/windows/string_conversion.hpp
new file mode 100644 (file)
index 0000000..aa7f621
--- /dev/null
@@ -0,0 +1,34 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file string_conversion.hpp
+ * @brief Safe string encoding conversions.
+ **/
+
+#ifndef _OS_STRING_CONVERSION_HPP_
+#define _OS_STRING_CONVERSION_HPP_
+
+#include <string>
+
+#include <hailo/platform.h>
+#include <hailo/hailort.h>
+#include "hailo/expected.hpp"
+
+namespace hailort
+{
+
+class StringConverter final
+{
+public:
+    StringConverter() = delete;
+
+    static Expected<std::wstring> ansi_to_utf16(const std::string& ansi_string);
+    static Expected<std::string> utf16_to_ansi(const std::wstring& utf16_string);
+};
+
+
+} /* namespace hailort */
+
+#endif /* _OS_STRING_CONVERSION_HPP_ */
diff --git a/hailort/common/process.hpp b/hailort/common/process.hpp
new file mode 100644 (file)
index 0000000..014be18
--- /dev/null
@@ -0,0 +1,58 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file process.hpp
+ * @brief Create shell processes and retrieve output
+ **/
+
+#ifndef _OS_PROCESS_HPP_
+#define _OS_PROCESS_HPP_
+
+#include "hailo/hailort.h"
+#include "hailo/platform.h"
+
+#include "hailo/expected.hpp"
+
+#include <string>
+
+namespace hailort
+{
+
+class Process final {
+public:
+    // Note:
+    // * This function will block!
+    // * If the process' output size exceeds max_output_size, the output will be truncated to max_output_size
+    // * We remove the trailing newline if it's the last char
+    static Expected<std::pair<int32_t, std::string>> create_and_wait_for_output(const std::string &command_line, uint32_t max_output_size);
+    Process() = delete;
+
+private:
+    #if defined(__GNUC__)
+    class PopenWrapper final {
+    public:
+        static Expected<PopenWrapper> create(const std::string &command_line);
+        ~PopenWrapper();
+        PopenWrapper(const PopenWrapper &other) = delete;
+        PopenWrapper &operator=(const PopenWrapper &other) = delete;
+        PopenWrapper &operator=(PopenWrapper &&other) = delete;
+        PopenWrapper(PopenWrapper &&other);
+        
+        Expected<std::string> read_stdout(uint32_t max_output_size);
+        // This function is to be called only once!
+        int32_t close();
+
+    private:
+        PopenWrapper(const std::string &command_line, hailo_status &status);
+
+        const std::string m_command_line;
+        FILE* m_pipe;
+    };
+    #endif
+};
+
+} /* namespace hailort */
+
+#endif /* _OS_PROCESS_HPP_ */
diff --git a/hailort/common/runtime_statistics_internal.hpp b/hailort/common/runtime_statistics_internal.hpp
new file mode 100644 (file)
index 0000000..cee5785
--- /dev/null
@@ -0,0 +1,217 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file runtime_statistics_internal.hpp
+ * @brief Implementation of Accumulator<T> interface
+ **/
+
+#ifndef _HAILO_RUNTIME_STATISTICS_INTERNAL_HPP_
+#define _HAILO_RUNTIME_STATISTICS_INTERNAL_HPP_
+
+#include "hailo/runtime_statistics.hpp"
+
+#include <cmath>
+#include <mutex>
+#include <limits>
+
+namespace hailort
+{
+
+template<typename T, std::enable_if_t<std::is_arithmetic<T>::value, int> = 0>
+class FullAccumulator : public Accumulator<T>
+{
+public:
+    // Creation isn't thread safe
+    FullAccumulator(const std::string& data_type) :
+        Accumulator<T>(data_type),
+        m_lock(),
+        m_count(0),
+        m_min(static_cast<double>(std::numeric_limits<T>::max())),
+        m_max(static_cast<double>(std::numeric_limits<T>::min())),
+        m_mean(0),
+        m_M2(0)
+    {}
+    FullAccumulator(FullAccumulator &&) = default;
+    FullAccumulator(const FullAccumulator &) = delete;
+    FullAccumulator &operator=(FullAccumulator &&) = delete;
+    FullAccumulator &operator=(const FullAccumulator &) = delete;
+    virtual ~FullAccumulator() = default;
+
+    virtual void add_data_point(T data) override
+    {
+        std::lock_guard<std::recursive_mutex> lock_guard(m_lock);
+        m_min = std::min(m_min, static_cast<double>(data));
+        m_max = std::max(m_max, static_cast<double>(data));
+        m_count++;
+        
+        // mean, variance, sd and mean_sd are calculated using Welford's_online_algorithm.
+        // See: https://en.wikipedia.org/wiki/Algorithms_for_calculating_variance#Welford's_online_algorithm
+        const auto delta = static_cast<double>(data) - m_mean;
+        m_mean += delta / static_cast<double>(m_count);
+        m_M2 += delta * (static_cast<double>(data) - m_mean);
+    }
+
+    virtual AccumulatorResults get() const override
+    {
+        std::lock_guard<std::recursive_mutex> lock_guard(m_lock);
+        return AccumulatorResults(count(), min(), max(), mean(), var(), sd(), mean_sd());
+    }
+
+    virtual AccumulatorResults get_and_clear() override
+    {
+        std::lock_guard<std::recursive_mutex> lock_guard(m_lock);
+        auto result = get();
+        clear();
+        return result;
+    }
+
+    virtual Expected<size_t> count() const override
+    {
+        std::lock_guard<std::recursive_mutex> lock_guard(m_lock);
+        return Expected<size_t>(m_count);
+    }
+
+    virtual Expected<double> min() const override
+    {
+        std::lock_guard<std::recursive_mutex> lock_guard(m_lock);
+        if (m_count < 1) {
+            return make_unexpected(HAILO_UNINITIALIZED);
+        }
+        return Expected<double>(m_min);
+    }
+
+    virtual Expected<double> max() const override
+    {
+        std::lock_guard<std::recursive_mutex> lock_guard(m_lock);
+        if (m_count < 1) {
+            return make_unexpected(HAILO_UNINITIALIZED);
+        }
+        return Expected<double>(m_max);
+    }
+
+    virtual Expected<double> mean() const override
+    {
+        std::lock_guard<std::recursive_mutex> lock_guard(m_lock);
+        if (m_count < 1) {
+            // Otherwise we'll divide by zero
+            return make_unexpected(HAILO_UNINITIALIZED);
+        }
+        return Expected<double>(m_mean);
+    }
+
+    // Sample variance
+    virtual Expected<double> var() const override
+    {
+        std::lock_guard<std::recursive_mutex> lock_guard(m_lock);
+        if (m_count < 2) {
+            // Otherwise we'll divide by zero
+            return make_unexpected(HAILO_UNINITIALIZED);
+        }
+        return Expected<double>(var_impl());
+    }
+
+    // Sample sd
+    virtual Expected<double> sd() const override
+    {
+        std::lock_guard<std::recursive_mutex> lock_guard(m_lock);
+        if (m_count < 2) {
+            // Otherwise we'll divide by zero
+            return make_unexpected(HAILO_UNINITIALIZED);
+        }
+        return Expected<double>(sd_impl());
+    }
+
+    // Sample mean sd
+    virtual Expected<double> mean_sd() const override
+    {
+        std::lock_guard<std::recursive_mutex> lock_guard(m_lock);
+        if (m_count < 2) {
+            // Otherwise we'll divide by zero
+            return make_unexpected(HAILO_UNINITIALIZED);
+        }
+        // Calculation based on: https://en.wikipedia.org/wiki/Standard_deviation#Standard_deviation_of_the_mean
+        return Expected<double>(sd_impl() / std::sqrt(m_count));
+    }
+
+protected:
+    mutable std::recursive_mutex m_lock;
+    size_t m_count;
+    double m_min;
+    double m_max;
+    double m_mean;
+    double m_M2; // Sum {i=1...n} (x_i-x_mean)^2
+
+    // These functions are to be called after acquiring the mutex
+    virtual void clear()
+    {
+        m_count = 0;
+        m_min = static_cast<double>(std::numeric_limits<T>::max());
+        m_max = static_cast<double>(std::numeric_limits<T>::min());
+        m_mean = 0;
+        m_M2 = 0;
+    }
+
+    double var_impl() const
+    {
+        return m_M2 / static_cast<double>(m_count - 1);
+    }
+
+    double sd_impl() const
+    {
+        return std::sqrt(var_impl());
+    }
+};
+
+template<typename T, std::enable_if_t<std::is_arithmetic<T>::value, int> = 0>
+class AverageFPSAccumulator : public FullAccumulator<T>
+{
+public:
+    // Creation isn't thread safe
+    AverageFPSAccumulator(const std::string& data_type) : 
+        FullAccumulator<T>(data_type),
+        m_sum(0)
+    {}
+    AverageFPSAccumulator(AverageFPSAccumulator &&) = default;
+    AverageFPSAccumulator(const AverageFPSAccumulator &) = delete;
+    AverageFPSAccumulator &operator=(AverageFPSAccumulator &&) = delete;
+    AverageFPSAccumulator &operator=(const AverageFPSAccumulator &) = delete;
+    virtual ~AverageFPSAccumulator() = default;
+
+
+    // data is a duration of time.
+    // However, the statistics collected will be in frames per seconds (i.e. time^-1).
+    virtual void add_data_point(T data) override
+    {
+        assert(0 != data);
+
+        std::lock_guard<std::recursive_mutex> lock_guard(this->m_lock);
+        m_sum += data;
+        const double data_inverse = 1.0 / static_cast<double>(data);
+        // Note: 'this' is needed to access protected members of a template base class
+        this->m_min = std::min(this->m_min, data_inverse);
+        this->m_max = std::max(this->m_max, data_inverse);
+        this->m_count++;
+        
+        // mean, variance, sd and mean_sd are calculated using Welford's_online_algorithm.
+        // See: https://en.wikipedia.org/wiki/Algorithms_for_calculating_variance#Welford's_online_algorithm
+        const auto delta = data_inverse - this->m_mean;
+        // We calculate the arithmatic mean
+        this->m_mean = static_cast<double>(this->m_count) / static_cast<double>(m_sum);
+        this->m_M2 += delta * (data_inverse - this->m_mean);
+    }
+
+    virtual void clear() override
+    {
+        m_sum = 0;
+        FullAccumulator<T>::clear();
+    }
+
+private:
+    T m_sum; // the sum of data added with add_data_point, before inversion
+};
+
+} /* namespace hailort */
+
+#endif /* _HAILO_RUNTIME_STATISTICS_INTERNAL_HPP_ */
diff --git a/hailort/common/socket.hpp b/hailort/common/socket.hpp
new file mode 100644 (file)
index 0000000..8df9daf
--- /dev/null
@@ -0,0 +1,108 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file socket.hpp
+ * @brief TODO
+ **/
+
+#ifndef __OS_SOCKET_H__
+#define __OS_SOCKET_H__
+
+#include <hailo/platform.h>
+#include <hailo/hailort.h>
+#include "common/utils.hpp"
+#include "hailo/expected.hpp"
+
+namespace hailort
+{
+
+// 12 for the octets (3 * 4, each octet<=255)
+// 3 for the dots (".")
+// 1 for the terminating null
+#define IPV4_STRING_MAX_LENGTH (16)
+
+#define PADDING_BYTES_SIZE (6)
+#define PADDING_ALIGN_BYTES (8 - PADDING_BYTES_SIZE)
+#define MIN_UDP_PAYLOAD_SIZE (24)
+#define MAX_UDP_PAYLOAD_SIZE (1456)
+#define MAX_UDP_PADDED_PAYLOAD_SIZE (MAX_UDP_PAYLOAD_SIZE - PADDING_BYTES_SIZE - PADDING_ALIGN_BYTES)
+
+#define CHECK_VALID_SOCKET_AS_EXPECTED(sock) CHECK((sock) != INVALID_SOCKET, make_unexpected(HAILO_ETH_FAILURE), "Invalid socket")
+
+class Socket final {
+public:
+    static Expected<Socket> create(int af, int type, int protocol);
+    ~Socket();
+    Socket(const Socket &other) = delete;
+    Socket &operator=(const Socket &other) = delete;
+    Socket &operator=(Socket &&other) = delete;
+    Socket(Socket &&other) noexcept :
+      m_module_wrapper(std::move(other.m_module_wrapper)), m_socket_fd(std::exchange(other.m_socket_fd, INVALID_SOCKET))
+        {};
+
+    static hailo_status ntop(int af, const void *src, char *dst, socklen_t size);
+    static hailo_status pton(int af, const char *src, void *dst);
+
+    hailo_status socket_bind(const sockaddr *addr, socklen_t len);
+    hailo_status get_sock_name(sockaddr *addr, socklen_t *len);
+
+    hailo_status set_recv_buffer_size_max();
+    hailo_status set_timeout(const std::chrono::milliseconds timeout_ms, timeval_t *timeout);
+    hailo_status enable_broadcast();
+    hailo_status abort();
+
+    // TODO: Should these be in udp.cpp?
+    // TODO: Work with const Buffer& instead of uint8_t*
+    hailo_status send_to(const uint8_t *src_buffer, size_t src_buffer_size, int flags,
+        const sockaddr *dest_addr, socklen_t dest_addr_size, size_t *bytes_sent);
+    hailo_status recv_from(uint8_t *dest_buffer, size_t dest_buffer_size, int flags,
+        sockaddr *src_addr, socklen_t src_addr_size, size_t *bytes_received, bool log_timeouts_in_debug = false);
+    hailo_status has_data(sockaddr *src_addr, socklen_t src_addr_size, bool log_timeouts_in_debug = false);
+
+private:
+    class SocketModuleWrapper final {
+    public:
+        static Expected<SocketModuleWrapper> create()
+        {
+            auto status = HAILO_UNINITIALIZED;
+            auto obj = SocketModuleWrapper(status);
+            CHECK_SUCCESS_AS_EXPECTED(status);
+            return obj;
+        }
+
+        SocketModuleWrapper(hailo_status &status)
+        {
+            status = init_module();
+        }
+
+        SocketModuleWrapper(const SocketModuleWrapper &other) = delete;
+        SocketModuleWrapper &operator=(const SocketModuleWrapper &other) = delete;
+        SocketModuleWrapper &operator=(SocketModuleWrapper &&other) = delete;
+        SocketModuleWrapper(SocketModuleWrapper &&other) noexcept = default;
+
+        ~SocketModuleWrapper()
+        {
+            auto status = free_module();
+            if (HAILO_SUCCESS != status) {
+                LOGGER__ERROR("Failed to free socket module.");
+            }
+        }
+    private:
+        static hailo_status init_module();
+        static hailo_status free_module();
+    };
+
+    Socket(SocketModuleWrapper &&module_wrapper, const socket_t socket_fd);
+    static Expected<socket_t> create_socket_fd(int af, int type, int protocol);
+    hailo_status close_socket_fd();
+
+    // Itialization dependency
+    SocketModuleWrapper m_module_wrapper;
+    socket_t m_socket_fd;
+};
+
+} /* namespace hailort */
+
+#endif /* __OS_SOCKET_H__ */
diff --git a/hailort/common/string_utils.cpp b/hailort/common/string_utils.cpp
new file mode 100644 (file)
index 0000000..78440de
--- /dev/null
@@ -0,0 +1,74 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file string_utils.cpp
+ * @brief Utilities for string
+ **/
+
+#include "common/string_utils.hpp"
+#include "common/utils.hpp"
+
+#include <stdlib.h>
+#include <errno.h>
+
+namespace hailort
+{
+
+// TODO: make it templated
+Expected<uint32_t> StringUtils::to_uint32(const std::string &str, int base)
+{
+    errno = 0;
+    char *end_pointer = nullptr;
+
+    auto value = strtoul(str.c_str(), &end_pointer, base);
+    static_assert(sizeof(value) >= sizeof(uint32_t), "Size of value must be equal or greater than size of uint32_t");
+    CHECK_AS_EXPECTED(errno == 0, HAILO_INVALID_ARGUMENT, "Failed to convert string {} to uint32_t. strtoul failed with errno {}", str, errno);
+    CHECK_AS_EXPECTED(((*end_pointer == '\0') || (*end_pointer == '\n')  || (*end_pointer == ' ') || (*end_pointer == '\r')),
+        HAILO_INVALID_ARGUMENT, "Failed to convert string {} to uint32_t. strtoul failed with errno {}", str, errno);
+    if ((value == 0) && (end_pointer == str)) {
+        LOGGER__ERROR("Failed to convert string {} to uint32_t.", str);
+        return make_unexpected(HAILO_INVALID_ARGUMENT);
+    }
+
+    CHECK_AS_EXPECTED(((value >= std::numeric_limits<uint32_t>::min()) && (value <= std::numeric_limits<uint32_t>::max())), 
+        HAILO_INVALID_ARGUMENT, "Failed to convert string {} to uint32_t.", str);
+
+    return static_cast<uint32_t>(value);
+}
+
+// TODO: make it templated
+Expected<int32_t> StringUtils::to_int32(const std::string &str, int base)
+{
+    errno = 0;
+    char *end_pointer = nullptr;
+
+    auto value = strtol(str.c_str(), &end_pointer, base);
+    static_assert(sizeof(value) >= sizeof(int32_t), "Size of value must be equal or greater than size of int32_t");
+    CHECK_AS_EXPECTED(errno == 0, HAILO_INVALID_ARGUMENT, "Failed to convert string {} to int32_t. strtol failed with errno {}", str, errno);
+    CHECK_AS_EXPECTED(((*end_pointer == '\0') || (*end_pointer == '\n')  || (*end_pointer == ' ') || (*end_pointer == '\r')),
+        HAILO_INVALID_ARGUMENT, "Failed to convert string {} to int32_t. strtoul failed with errno {}", str, errno);
+    if ((value == 0) && (end_pointer == str)) {
+        LOGGER__ERROR("Failed to convert string {} to int32_t.", str);
+        return make_unexpected(HAILO_INVALID_ARGUMENT);
+    }
+
+    CHECK_AS_EXPECTED(((value >= std::numeric_limits<int32_t>::min()) && (value <= std::numeric_limits<int32_t>::max())), 
+        HAILO_INVALID_ARGUMENT, "Failed to convert string {} to int32.", str);
+
+    return static_cast<int32_t>(value);
+}
+
+Expected<uint8_t> StringUtils::to_uint8(const std::string &str, int base)
+{
+    auto number = to_uint32(str, base);
+    CHECK_EXPECTED(number);
+
+    CHECK_AS_EXPECTED(((number.value() >= std::numeric_limits<uint8_t>::min()) && (number.value() <= std::numeric_limits<uint8_t>::max())), 
+        HAILO_INVALID_ARGUMENT, "Failed to convert string {} to uint8_t.", str);
+
+    return static_cast<uint8_t>(number.value());
+}
+
+} /* namespace hailort */
diff --git a/hailort/common/string_utils.hpp b/hailort/common/string_utils.hpp
new file mode 100644 (file)
index 0000000..120c7a8
--- /dev/null
@@ -0,0 +1,28 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file string_utils.hpp
+ * @brief Defines utilities methods for string.
+ **/
+
+#ifndef _HAILO_STRING_UTILS_HPP_
+#define _HAILO_STRING_UTILS_HPP_
+
+#include "hailo/expected.hpp"
+#include <string>
+
+namespace hailort
+{
+
+class StringUtils {
+public:
+    static Expected<int32_t> to_int32(const std::string &str, int base);
+    static Expected<uint8_t> to_uint8(const std::string &str, int base);
+    static Expected<uint32_t> to_uint32(const std::string &str, int base);
+};
+
+} /* namespace hailort */
+
+#endif /* _HAILO_STRING_UTILS_HPP_ */
diff --git a/hailort/common/utils.hpp b/hailort/common/utils.hpp
new file mode 100644 (file)
index 0000000..f4f74d7
--- /dev/null
@@ -0,0 +1,246 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file utils.hpp
+ * @brief TODO: brief
+ *
+ * TODO: doc
+ **/
+
+#ifndef HAILO_UTILS_H_
+#define HAILO_UTILS_H_
+
+#include <assert.h>
+#include <hailo/hailort.h>
+#include "common/logger_macros.hpp"
+#include <spdlog/fmt/bundled/core.h>
+#include <map>
+#include <set>
+
+namespace hailort
+{
+
+#define IS_FIT_IN_UINT8(number) ((std::numeric_limits<uint8_t>::max() >= ((int32_t)(number))) && (std::numeric_limits<uint8_t>::min() <= ((int32_t)(number))))
+#define IS_FIT_IN_UINT16(number) ((std::numeric_limits<uint16_t>::max() >= ((int32_t)(number))) && (std::numeric_limits<uint16_t>::min() <= ((int32_t)(number))))
+
+#define IS_FIT_IN_UINT16(number) ((std::numeric_limits<uint16_t>::max() >= ((int32_t)(number))) && (std::numeric_limits<uint16_t>::min() <= ((int32_t)(number))))
+
+
+template <typename T>
+static inline bool contains(const std::vector<T> &container, const T &value)
+{
+    return std::find(container.begin(), container.end(), value) != container.end();
+}
+
+template <typename T, typename Q>
+static inline bool contains(const std::map<Q, T> &container, Q value)
+{
+    return (container.find(value) != container.end());
+}
+
+template <typename T, typename Q>
+static inline bool contains(const std::unordered_map<Q, T> &container, Q value)
+{
+    return (container.find(value) != container.end());
+}
+
+template <typename T>
+static inline bool contains(const std::set<T> &container, T value)
+{
+    return (container.find(value) != container.end());
+}
+
+// From https://stackoverflow.com/questions/57092289/do-stdmake-shared-and-stdmake-unique-have-a-nothrow-version
+template <class T, class... Args>
+static inline std::unique_ptr<T> make_unique_nothrow(Args&&... args)
+    noexcept(noexcept(T(std::forward<Args>(args)...)))
+{
+#ifndef NDEBUG
+    auto ptr = std::unique_ptr<T>(new (std::nothrow) T(std::forward<Args>(args)...));
+    if (nullptr == ptr) {
+        LOGGER__ERROR("make_unique failed, pointer is null!");
+    }
+    return ptr;
+#else
+    return std::unique_ptr<T>(new (std::nothrow) T(std::forward<Args>(args)...));
+#endif
+}
+
+template <class T, class... Args>
+static inline std::shared_ptr<T> make_shared_nothrow(Args&&... args)
+    noexcept(noexcept(T(std::forward<Args>(args)...)))
+{
+#ifndef NDEBUG
+    auto ptr = std::shared_ptr<T>(new (std::nothrow) T(std::forward<Args>(args)...));
+    if (nullptr == ptr) {
+        LOGGER__ERROR("make_shared failed, pointer is null!");
+    }
+    return ptr;
+#else
+    return std::shared_ptr<T>(new (std::nothrow) T(std::forward<Args>(args)...));
+#endif
+}
+
+#define ASSERT assert
+
+#define ARRAY_ENTRIES(x) (sizeof(x) / sizeof((x)[0]))
+
+#define RETURN_IF_ARG_NULL(arg)                         \
+    do {                                                \
+        if (NULL == (arg)) {                            \
+            LOGGER__ERROR("Invalid argument: "#arg);    \
+            return HAILO_INVALID_ARGUMENT;              \
+        }                                               \
+    } while(0)
+
+#define _FREE(var, invalid_value, func)     \
+    do {                                    \
+        if ((invalid_value) != (var)) {     \
+            free(var);                      \
+            var = (invalid_value);          \
+        }                                   \
+    } while(0)
+
+#define FREE(p) _FREE(p, NULL, free)
+
+#define _CLOSE(var, invalid_var_value, func, invalid_func_result, status)                                       \
+    do {                                                                                                        \
+        if ((invalid_var_value) != (var)) {                                                                     \
+            if ((invalid_func_result) == func(var)) {                                                           \
+                LOGGER__ERROR("CLOSE failed");                                                                  \
+                if (HAILO_SUCCESS == (status)) {                                                                \
+                    status = HAILO_CLOSE_FAILURE;                                                               \
+                }                                                                                               \
+                else {                                                                                          \
+                    LOGGER__ERROR("Not setting status to HAILO_CLOSE_FAILURE since it is not HAILO_SUCCESS");   \
+                }                                                                                               \
+            }                                                                                                   \
+            var = (invalid_var_value);                                                                          \
+        }                                                                                                       \
+    } while(0)
+// TODO: Add tests in tests/utils/main.cpp
+
+#define CLOSE(fd, status) _CLOSE(fd, -1, close, -1, status)
+#define FCLOSE(file, status) _CLOSE(file, NULL, fclose, 0, status)
+
+
+// Detect empty macro arguments
+// https://gustedt.wordpress.com/2010/06/08/detect-empty-macro-arguments/
+#define _ARG16(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, ...) _15
+#define HAS_COMMA(...) _ARG16(__VA_ARGS__, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0)
+#define _TRIGGER_PARENTHESIS_(...) ,
+#define ISEMPTY(...)                                                    \
+_ISEMPTY(                                                               \
+          /* test if there is just one argument, eventually an empty    \
+             one */                                                     \
+          HAS_COMMA(__VA_ARGS__),                                       \
+          /* test if _TRIGGER_PARENTHESIS_ together with the argument   \
+             adds a comma */                                            \
+          HAS_COMMA(_TRIGGER_PARENTHESIS_ __VA_ARGS__),                 \
+          /* test if the argument together with a parenthesis           \
+             adds a comma */                                            \
+          HAS_COMMA(__VA_ARGS__ (/*empty*/)),                           \
+          /* test if placing it between _TRIGGER_PARENTHESIS_ and the   \
+             parenthesis adds a comma */                                \
+          HAS_COMMA(_TRIGGER_PARENTHESIS_ __VA_ARGS__ (/*empty*/))      \
+          )
+#define PASTE5(_0, _1, _2, _3, _4) _0 ## _1 ## _2 ## _3 ## _4
+#define _ISEMPTY(_0, _1, _2, _3) HAS_COMMA(PASTE5(_IS_EMPTY_CASE_, _0, _1, _2, _3))
+#define _IS_EMPTY_CASE_0001 ,
+//
+
+#define __CONSTRUCT_MSG_1(dft_fmt, usr_fmt, ...) dft_fmt, ##__VA_ARGS__
+#define __CONSTRUCT_MSG_0(dft_fmt, usr_fmt, ...) dft_fmt " - " usr_fmt, ##__VA_ARGS__
+#define __CONSTRUCT_MSG(is_dft, dft_fmt, usr_fmt, ...) __CONSTRUCT_MSG_##is_dft(dft_fmt, usr_fmt, ##__VA_ARGS__)
+#define _CONSTRUCT_MSG(is_dft, dft_fmt, usr_fmt, ...) __CONSTRUCT_MSG(is_dft, dft_fmt, usr_fmt, ##__VA_ARGS__)
+#define CONSTRUCT_MSG(dft_fmt, ...) _CONSTRUCT_MSG(ISEMPTY(__VA_ARGS__), dft_fmt, "" __VA_ARGS__)
+
+
+#define _CHECK(cond, ret_val, ...)      \
+    do {                                \
+        if (!(cond)) {                  \
+            LOGGER__ERROR(__VA_ARGS__); \
+            return (ret_val);           \
+        }                               \
+    } while(0)
+
+/** Returns ret_val when cond is false */
+#define CHECK(cond, ret_val, ...) _CHECK((cond), (ret_val), CONSTRUCT_MSG("CHECK failed", ##__VA_ARGS__))
+#define CHECK_AS_EXPECTED(cond, ret_val, ...) \
+    _CHECK((cond), (make_unexpected(ret_val)), CONSTRUCT_MSG("CHECK_AS_EXPECTED failed", ##__VA_ARGS__))
+
+#define CHECK_ARG_NOT_NULL(arg) _CHECK(nullptr != (arg), HAILO_INVALID_ARGUMENT, "CHECK_ARG_NOT_NULL for {} failed", #arg)
+
+#define CHECK_ARG_NOT_NULL_AS_EXPECTED(arg) _CHECK(nullptr != (arg), make_unexpected(HAILO_INVALID_ARGUMENT), "CHECK_ARG_NOT_NULL_AS_EXPECTED for {} failed", #arg)
+
+#define CHECK_NOT_NULL(arg, status) _CHECK(nullptr != (arg), status, "CHECK_NOT_NULL for {} failed", #arg)
+
+#define CHECK_NOT_NULL_AS_EXPECTED(arg, status) _CHECK(nullptr != (arg), make_unexpected(status), "CHECK_NOT_NULL_AS_EXPECTED for {} failed", #arg)
+
+#define _CHECK_SUCCESS(status, is_default, fmt, ...)                                                                            \
+    do {                                                                                                                        \
+        const auto &__check_success_status = (status);                                                                          \
+        _CHECK(                                                                                                                 \
+            HAILO_SUCCESS == __check_success_status,                                                                            \
+            __check_success_status,                                                                                             \
+            _CONSTRUCT_MSG(is_default, "CHECK_SUCCESS failed with status={}", fmt, __check_success_status, ##__VA_ARGS__)       \
+        );                                                                                                                      \
+    } while(0)
+#define CHECK_SUCCESS(status, ...) _CHECK_SUCCESS(status, ISEMPTY(__VA_ARGS__), "" __VA_ARGS__)
+
+#define _CHECK_SUCCESS_AS_EXPECTED(status, is_default, fmt, ...)                                                                       \
+    do {                                                                                                                               \
+        const auto &__check_success_status = (status);                                                                                 \
+        _CHECK(                                                                                                                        \
+            HAILO_SUCCESS == __check_success_status,                                                                                   \
+            make_unexpected(__check_success_status),                                                                                   \
+            _CONSTRUCT_MSG(is_default, "CHECK_SUCCESS_AS_EXPECTED failed with status={}", fmt, __check_success_status, ##__VA_ARGS__)  \
+        );                                                                                                                             \
+    } while(0)
+#define CHECK_SUCCESS_AS_EXPECTED(status, ...) _CHECK_SUCCESS_AS_EXPECTED(status, ISEMPTY(__VA_ARGS__), "" __VA_ARGS__)
+
+#define _CHECK_EXPECTED(obj, is_default, fmt, ...)                                                                                      \
+    do {                                                                                                                                \
+        const auto &__check_expected_obj = (obj);                                                                                       \
+        _CHECK(                                                                                                                         \
+            __check_expected_obj.has_value(),                                                                                           \
+            make_unexpected(__check_expected_obj.status()),                                                                             \
+            _CONSTRUCT_MSG(is_default, "CHECK_EXPECTED failed with status={}", fmt, __check_expected_obj.status(), ##__VA_ARGS__)       \
+        );                                                                                                                              \
+    } while(0)
+#define CHECK_EXPECTED(obj, ...) _CHECK_EXPECTED(obj, ISEMPTY(__VA_ARGS__), "" __VA_ARGS__)
+
+
+#define _CHECK_EXPECTED_AS_STATUS(obj, is_default, fmt, ...)                                                                                      \
+    do {                                                                                                                                          \
+        const auto &__check_expected_obj = (obj);                                                                                                 \
+        _CHECK(                                                                                                                                   \
+            __check_expected_obj.has_value(),                                                                                                     \
+            __check_expected_obj.status(),                                                                                                        \
+            _CONSTRUCT_MSG(is_default, "CHECK_EXPECTED_AS_STATUS failed with status={}", fmt, __check_expected_obj.status(), ##__VA_ARGS__)       \
+        );                                                                                                                                        \
+    } while(0)
+#define CHECK_EXPECTED_AS_STATUS(obj, ...) _CHECK_EXPECTED_AS_STATUS(obj, ISEMPTY(__VA_ARGS__), "" __VA_ARGS__)
+
+constexpr bool is_powerof2(size_t v) {
+    // bit trick
+    return (v & (v - 1)) == 0;
+}
+
+constexpr uint32_t get_nearest_powerof_2(uint32_t value, uint32_t min_power_of_2)
+{
+    assert(value <= 0x80000000);
+    uint32_t power_of_2 = min_power_of_2;
+    while (value > power_of_2) {
+        power_of_2 <<=  1;
+    }
+    return power_of_2;
+}
+
+} /* namespace hailort */
+
+#endif /* HAILO_UTILS_H_ */
\ No newline at end of file
diff --git a/hailort/drivers/common/hailo_ioctl_common.h b/hailort/drivers/common/hailo_ioctl_common.h
new file mode 100644 (file)
index 0000000..1177130
--- /dev/null
@@ -0,0 +1,349 @@
+// SPDX-License-Identifier: (GPL-2.0 WITH Linux-syscall-note) AND MIT
+/**
+ * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved.
+ **/
+
+#ifndef _HAILO_IOCTL_COMMON_H_
+#define _HAILO_IOCTL_COMMON_H_
+
+// This value is not easily changeable.
+// For example: the channel interrupts ioctls assume we have up to 32 channels
+#define MAX_VDMA_CHANNELS           (32)
+#define SIZE_OF_VDMA_DESCRIPTOR     (16)
+#define VDMA_DEST_CHANNELS_START    (16)
+
+#define CHANNEL_IRQ_TIMESTAMPS_SIZE (128 * 2) // Should be same as MAX_IRQ_TIMESTAMPS_SIZE (hailort_driver.hpp)
+#define CHANNEL_IRQ_TIMESTAMPS_SIZE_MASK (CHANNEL_IRQ_TIMESTAMPS_SIZE - 1)
+
+#define INVALID_CHANNEL_HANDLE_VALUE    ((uint64_t)-1)
+#define INVALID_DRIVER_HANDLE_VALUE     ((uintptr_t)-1)
+
+// Used by windows and unix driver to raise the right CPU control handle to the FW. The same as in pcie_service FW 
+#define FW_ACCESS_CORE_CPU_CONTROL_SHIFT (1)
+#define FW_ACCESS_CORE_CPU_CONTROL_MASK  (1 << FW_ACCESS_CORE_CPU_CONTROL_SHIFT) 
+#define FW_ACCESS_CONTROL_INTERRUPT_SHIFT (0)
+#define FW_ACCESS_APP_CPU_CONTROL_MASK (1 << FW_ACCESS_CONTROL_INTERRUPT_SHIFT)
+
+#define INVALID_VDMA_CHANNEL (0xff)
+
+#ifdef _MSC_VER
+#if !defined(bool) && !defined(__cplusplus)
+typedef uint8_t bool;
+#endif
+#if !defined(INT_MAX)
+#define INT_MAX 0x7FFFFFFF
+#endif
+#else
+#ifndef __KERNEL__
+// include the userspace headers only if this file is included by user space program
+// It is discourged to include them when compiling the driver (https://lwn.net/Articles/113349/)
+#include <stdint.h>
+#include <sys/types.h>
+#else
+#include <linux/types.h>
+#include <linux/limits.h>
+#include <linux/kernel.h>
+#endif
+
+#if defined(__unix__)
+#include <linux/ioctl.h>
+#endif
+
+#define _IOW_       _IOW
+#define _IOR_       _IOR
+#define _IOWR_      _IOWR
+#define _IO_        _IO
+
+#define HAILO_GENERAL_IOCTL_MAGIC 'g'
+#define HAILO_VDMA_IOCTL_MAGIC 'v'
+#define HAILO_WINDOWS_IOCTL_MAGIC 'w'
+#endif
+
+#pragma pack(push, 1)
+
+struct hailo_channel_interrupt_timestamp {
+    uint64_t timestamp_ns;
+    uint16_t desc_num_processed;
+};
+
+// This struct is the same as `enum dma_data_direction` (defined in linux/dma-direction)
+enum hailo_dma_data_direction {
+    HAILO_DMA_BIDIRECTIONAL = 0,
+    HAILO_DMA_TO_DEVICE = 1,
+    HAILO_DMA_FROM_DEVICE = 2,
+    HAILO_DMA_NONE = 3,
+
+    /** Max enum value to maintain ABI Integrity */
+    HAILO_DMA_MAX_ENUM = INT_MAX,
+};
+
+// Enum that determines if buffer should be allocated from user space or from driver
+enum hailo_allocation_mode {
+    HAILO_ALLOCATION_MODE_USERSPACE = 0,
+    HAILO_ALLOCATION_MODE_DRIVER    = 1,
+
+    /** Max enum value to maintain ABI Integrity */
+    HAILO_ALLOCATION_MODE_MAX_ENUM = INT_MAX,
+};
+
+/* structure used in ioctl HAILO_VDMA_BUFFER_MAP */
+struct hailo_vdma_buffer_map_params {
+    void* user_address;                             // in
+    size_t size;                                    // in
+    enum hailo_dma_data_direction data_direction;   // in
+    uintptr_t allocated_buffer_handle;              // in
+    size_t mapped_handle;                           // out
+};
+
+/* structure used in ioctl HAILO_DESC_LIST_CREATE */
+struct hailo_desc_list_create_params {
+    size_t desc_count;          // in
+    uintptr_t desc_handle;      // out
+    // Note: The dma address is required for CONTEXT_SWITCH firmware controls
+    uint64_t dma_address;    // out
+};
+
+/* structure used in ioctl HAILO_WINDOWS_DESC_LIST_MMAP */
+struct hailo_windows_desc_list_mmap_params {
+    uintptr_t desc_handle;  // in
+    size_t size;            // in
+    void* user_address;     // out
+};
+
+/* structure used in ioctl HAILO_DESC_LIST_BIND_VDMA_BUFFER */
+struct hailo_desc_list_bind_vdma_buffer_params {
+    size_t buffer_handle;       // in
+    uintptr_t desc_handle;      // in
+    uint16_t desc_page_size;    // in
+    uint8_t channel_index;      // in
+};
+
+/* structure used in ioctl HAILO_VDMA_CHANNEL_ENABLE */
+struct hailo_vdma_channel_enable_params {
+    uint32_t channel_index;                     // in
+    enum hailo_dma_data_direction direction;    // in
+    // If desc_list_handle is set to valid handle (different than INVALID_DRIVER_HANDLE_VALUE),
+    // the driver will start the channel with the given descriptors list.
+    uintptr_t desc_list_handle;                 // in
+    bool enable_timestamps_measure;             // in
+    uint64_t channel_handle;                    // out
+};
+
+/* structure used in ioctl HAILO_VDMA_CHANNEL_DISABLE */
+struct hailo_vdma_channel_disable_params {
+    uint32_t channel_index;  // in
+    uint64_t channel_handle; // in
+};
+
+/* structure used in ioctl HAILO_VDMA_CHANNEL_WAIT_INT */
+struct hailo_vdma_channel_wait_params {
+    uint32_t channel_index;                                 // in
+    uint64_t channel_handle;                                // in
+    uint64_t timeout_ms;                                    // in
+    struct hailo_channel_interrupt_timestamp *timestamps;   // out
+    uint32_t timestamps_count;                              // inout
+};
+
+/* structure used in ioctl HAILO_VDMA_CHANNEL_ABORT */
+struct hailo_vdma_channel_abort_params {
+    uint32_t channel_index;     // in
+    uint64_t channel_handle;    // in
+};
+
+/* structure used in ioctl HAILO_VDMA_CHANNEL_CLEAR_ABORT */
+struct hailo_vdma_channel_clear_abort_params {
+    uint32_t channel_index;     // in
+    uint64_t channel_handle;    // in
+};
+
+/* structure used in ioctl HAILO_FW_CONTROL */
+#define MAX_CONTROL_LENGTH  (1500)
+#define PCIE_EXPECTED_MD5_LENGTH (16)
+
+
+/* structure used in ioctl     HAILO_FW_CONTROL and HAILO_READ_LOG */
+enum hailo_cpu_id {
+    HAILO_CPU_ID_CPU0 = 0,
+    HAILO_CPU_ID_CPU1,
+    HAILO_CPU_ID_NONE,
+
+    /** Max enum value to maintain ABI Integrity */
+    HAILO_CPU_MAX_ENUM = INT_MAX,
+};
+
+struct hailo_fw_control {
+    // expected_md5+buffer_len+buffer must be in this order at the start of the struct
+    uint8_t   expected_md5[PCIE_EXPECTED_MD5_LENGTH];
+    uint32_t  buffer_len;
+    uint8_t   buffer[MAX_CONTROL_LENGTH];
+    uint32_t timeout_ms;
+    enum hailo_cpu_id cpu_id;
+};
+
+/* structure used in ioctl HAILO_BAR_TRANSFER */
+enum hailo_transfer_direction {
+    TRANSFER_READ = 0,
+    TRANSFER_WRITE,
+
+    /** Max enum value to maintain ABI Integrity */
+    TRANSFER_MAX_ENUM = INT_MAX,
+};
+
+struct hailo_bar_transfer_params {
+    enum hailo_transfer_direction transfer_direction;   // in
+    uint32_t bar_index;                                 // in
+    off_t offset;                                       // in
+    size_t count;                                       // in
+    void* buffer;                                       // in/out
+};
+
+/* structure used in ioctl HAILO_VDMA_CHANNEL_REGISTERS */
+struct hailo_channel_registers_params {
+    enum hailo_transfer_direction transfer_direction;  // in
+    off_t offset;                                      // in
+    size_t size;                                       // in
+    uint32_t data;                                     // in/out
+};
+
+/* structure used in ioctl HAILO_VDMA_BUFFER_SYNC */
+enum hailo_vdma_buffer_sync_type {
+    HAILO_SYNC_FOR_HOST,
+    HAILO_SYNC_FOR_DEVICE,
+
+    /** Max enum value to maintain ABI Integrity */
+    HAILO_SYNC_MAX_ENUM = INT_MAX,
+};
+
+struct hailo_vdma_buffer_sync_params {
+    size_t handle;                                      // in
+    enum hailo_vdma_buffer_sync_type  sync_type;        // in
+    void*                             buffer_address;   // in
+    uint64_t                          buffer_size;      // in
+};
+
+/* structure used in ioctl HAILO_READ_NOTIFICATION */
+struct hailo_d2h_notification {
+    size_t buffer_len;                  // out
+    uint8_t buffer[MAX_CONTROL_LENGTH]; // out
+};
+
+enum hailo_board_type {
+    HAILO8 = 0,
+    HAILO_MERCURY,
+    HAILO_BOARD_COUNT,
+    HAILO_INVALID_BOARD = 0xFFFFFFFF,
+};
+
+enum hailo_dma_type {
+    HAILO_DMA_TYPE_PCIE,
+    HAILO_DMA_TYPE_DRAM,
+
+    /** Max enum value to maintain ABI Integrity */
+    HAILO_DMA_TYPE_MAX_ENUM = INT_MAX,
+};
+
+struct hailo_device_properties {
+    uint16_t                     desc_max_page_size;
+    enum hailo_board_type        board_type;
+    enum hailo_allocation_mode   allocation_mode;
+    enum hailo_dma_type          dma_type;
+};
+
+struct hailo_driver_info {
+    uint32_t major_version;
+    uint32_t minor_version;
+    uint32_t revision_version;
+};
+struct hailo_read_log_params {
+    enum hailo_cpu_id cpu_id;   // in
+    uint8_t *buffer;            // out
+    size_t buffer_size;         // in
+    size_t read_bytes;          // out
+};
+
+struct hailo_allocate_buffer_params {
+    size_t      buffer_size;    // in
+    uintptr_t   buffer_handle;  // out
+};
+
+struct hailo_mark_as_in_use_params {
+    bool in_use;           // out
+};
+
+#pragma pack(pop)
+
+enum hailo_general_ioctl_code {
+    HAILO_BAR_TRANSFER_CODE,
+    HAILO_FW_CONTROL_CODE,
+    HAILO_READ_NOTIFICATION_CODE,
+    HAILO_DISABLE_NOTIFICATION_CODE,
+    HAILO_QUERY_DEVICE_PROPERTIES_CODE,
+    HAILO_QUERY_DRIVER_INFO_CODE,
+    HAILO_READ_LOG_CODE,
+    HAILO_RESET_NN_CORE_CODE,
+
+    // Must be last
+    HAILO_GENERAL_IOCTL_MAX_NR,
+};
+
+#define HAILO_BAR_TRANSFER              _IOW_(HAILO_GENERAL_IOCTL_MAGIC,   HAILO_BAR_TRANSFER_CODE,               struct hailo_bar_transfer_params)
+#define HAILO_FW_CONTROL                _IOWR_(HAILO_GENERAL_IOCTL_MAGIC,  HAILO_FW_CONTROL_CODE,                 struct hailo_fw_control)
+#define HAILO_READ_NOTIFICATION         _IOW_(HAILO_GENERAL_IOCTL_MAGIC,   HAILO_READ_NOTIFICATION_CODE,          struct hailo_d2h_notification)
+#define HAILO_DISABLE_NOTIFICATION      _IO_(HAILO_GENERAL_IOCTL_MAGIC,    HAILO_DISABLE_NOTIFICATION_CODE)
+#define HAILO_QUERY_DEVICE_PROPERTIES   _IOW_(HAILO_GENERAL_IOCTL_MAGIC,   HAILO_QUERY_DEVICE_PROPERTIES_CODE,    struct hailo_device_properties)
+#define HAILO_QUERY_DRIVER_INFO         _IOW_(HAILO_GENERAL_IOCTL_MAGIC,   HAILO_QUERY_DRIVER_INFO_CODE,          struct hailo_driver_info)
+#define HAILO_READ_LOG                  _IOWR_(HAILO_GENERAL_IOCTL_MAGIC,  HAILO_READ_LOG_CODE,                   struct hailo_read_log_params)
+#define HAILO_RESET_NN_CORE             _IO_(HAILO_GENERAL_IOCTL_MAGIC,    HAILO_RESET_NN_CORE_CODE)
+
+enum hailo_vdma_ioctl_code {
+    HAILO_VDMA_CHANNEL_ENABLE_CODE,
+    HAILO_VDMA_CHANNEL_DISABLE_CODE,
+    HAILO_VDMA_CHANNEL_WAIT_INT_CODE,
+    HAILO_VDMA_CHANNEL_ABORT_CODE,
+    HAILO_VDMA_CHANNEL_CLEAR_ABORT_CODE,
+    HAILO_VDMA_CHANNEL_REGISTERS_CODE,
+    HAILO_VDMA_BUFFER_MAP_CODE,
+    HAILO_VDMA_BUFFER_UNMAP_CODE,
+    HAILO_VDMA_BUFFER_SYNC_CODE,
+    HAILO_DESC_LIST_CREATE_CODE,
+    HAILO_DESC_LIST_RELEASE_CODE,
+    HAILO_DESC_LIST_BIND_VDMA_BUFFER_CODE,
+    HAILO_VDMA_LOW_MEMORY_BUFFER_ALLOC_CODE,
+    HAILO_VDMA_LOW_MEMORY_BUFFER_FREE_CODE,
+    HAILO_MARK_AS_IN_USE_CODE,
+
+    // Must be last
+    HAILO_VDMA_IOCTL_MAX_NR,
+};
+
+#define HAILO_VDMA_CHANNEL_ENABLE           _IOR_(HAILO_VDMA_IOCTL_MAGIC,  HAILO_VDMA_CHANNEL_ENABLE_CODE,        struct hailo_vdma_channel_enable_params)
+#define HAILO_VDMA_CHANNEL_DISABLE          _IOR_(HAILO_VDMA_IOCTL_MAGIC,  HAILO_VDMA_CHANNEL_DISABLE_CODE,       struct hailo_vdma_channel_disable_params)
+#define HAILO_VDMA_CHANNEL_WAIT_INT         _IOR_(HAILO_VDMA_IOCTL_MAGIC,  HAILO_VDMA_CHANNEL_WAIT_INT_CODE,      struct hailo_vdma_channel_wait_params)
+#define HAILO_VDMA_CHANNEL_ABORT            _IOR_(HAILO_VDMA_IOCTL_MAGIC,  HAILO_VDMA_CHANNEL_ABORT_CODE,         struct hailo_vdma_channel_abort_params)
+#define HAILO_VDMA_CHANNEL_CLEAR_ABORT      _IOR_(HAILO_VDMA_IOCTL_MAGIC,  HAILO_VDMA_CHANNEL_CLEAR_ABORT_CODE,   struct hailo_vdma_channel_clear_abort_params)
+#define HAILO_VDMA_CHANNEL_REGISTERS        _IOWR_(HAILO_VDMA_IOCTL_MAGIC, HAILO_VDMA_CHANNEL_REGISTERS_CODE,     struct hailo_channel_registers_params)
+
+#define HAILO_VDMA_BUFFER_MAP               _IOR_(HAILO_VDMA_IOCTL_MAGIC,  HAILO_VDMA_BUFFER_MAP_CODE,            struct hailo_vdma_buffer_map_params)
+#define HAILO_VDMA_BUFFER_UNMAP             _IO_(HAILO_VDMA_IOCTL_MAGIC,   HAILO_VDMA_BUFFER_UNMAP_CODE)
+#define HAILO_VDMA_BUFFER_SYNC              _IOR_(HAILO_VDMA_IOCTL_MAGIC,  HAILO_VDMA_BUFFER_SYNC_CODE,           struct hailo_vdma_buffer_sync_params)
+
+#define HAILO_DESC_LIST_CREATE              _IOWR_(HAILO_VDMA_IOCTL_MAGIC, HAILO_DESC_LIST_CREATE_CODE,           struct hailo_desc_list_create_params)
+#define HAILO_DESC_LIST_RELEASE             _IO_(HAILO_VDMA_IOCTL_MAGIC,   HAILO_DESC_LIST_RELEASE_CODE)
+#define HAILO_DESC_LIST_BIND_VDMA_BUFFER    _IOR_(HAILO_VDMA_IOCTL_MAGIC,  HAILO_DESC_LIST_BIND_VDMA_BUFFER_CODE, struct hailo_desc_list_bind_vdma_buffer_params)
+
+#define HAILO_VDMA_LOW_MEMORY_BUFFER_ALLOC  _IOWR_(HAILO_VDMA_IOCTL_MAGIC, HAILO_VDMA_LOW_MEMORY_BUFFER_ALLOC_CODE, struct hailo_allocate_buffer_params)
+#define HAILO_VDMA_LOW_MEMORY_BUFFER_FREE   _IO_(HAILO_VDMA_IOCTL_MAGIC,   HAILO_VDMA_LOW_MEMORY_BUFFER_FREE_CODE)
+
+#define HAILO_MARK_AS_IN_USE                _IOW_(HAILO_VDMA_IOCTL_MAGIC,  HAILO_MARK_AS_IN_USE_CODE,             struct hailo_mark_as_in_use_params)
+
+enum hailo_windows_ioctl_code {
+    HAILO_WINDOWS_DESC_LIST_MMAP_CODE,
+
+    // Must be last
+    HAILO_WINDOWS_IOCTL_MAX_NR,
+};
+
+#define HAILO_WINDOWS_DESC_LIST_MMAP _IOWR_(HAILO_WINDOWS_IOCTL_MAGIC, HAILO_WINDOWS_DESC_LIST_MMAP_CODE, struct hailo_windows_desc_list_mmap_params)
+
+
+#endif /* _HAILO_IOCTL_COMMON_H_ */
diff --git a/hailort/hailortcli/CMakeLists.txt b/hailort/hailortcli/CMakeLists.txt
new file mode 100644 (file)
index 0000000..4fb14ec
--- /dev/null
@@ -0,0 +1,69 @@
+cmake_minimum_required(VERSION 3.0.0)
+
+set(HAILORTCLI_CPP_FILES
+    hailortcli.cpp
+    command.cpp
+    scan_command.cpp
+    run_command.cpp
+    inference_progress.cpp
+    power_measurement_command.cpp
+    fw_control_command.cpp
+    fw_update_command.cpp
+    ssb_update_command.cpp
+    infer_stats_printer.cpp
+    sensor_config_command.cpp
+    board_config_command.cpp
+    fw_config_command.cpp
+    fw_logger_command.cpp
+    fw_config_serializer.cpp
+    common.cpp
+    benchmark_command.cpp
+    temp_measurement.cpp
+    parse_hef_command.cpp
+    graph_printer.cpp
+    )
+    
+if(UNIX)
+    # Unix only modules
+    set(HAILORTCLI_CPP_FILES ${HAILORTCLI_CPP_FILES}
+        udp_rate_limiter_command.cpp
+        # TODO: We dont compile download_action_list_command on windows, as it uses packed enums (HRT-5919)
+        download_action_list_command.cpp
+
+    )
+endif()
+
+# 'config_definitions_json_file' is used in generate_definitions_json_str.in for configure_file()
+file(READ "${PROJECT_SOURCE_DIR}/common/config_definitions.json" config_definitions_json_file)
+set(config_definitions_header ${CMAKE_CURRENT_BINARY_DIR}/definitions_json.auto.hpp)
+set(CONFIG_DEFENITIONS_IN ${PROJECT_SOURCE_DIR}/hailort/hailortcli/generate_definitions_json_str.in)
+configure_file(${CONFIG_DEFENITIONS_IN} ${config_definitions_header})
+
+add_executable(hailortcli
+    ${config_definitions_header}
+    ${HAILORTCLI_CPP_FILES}
+    ${HAILORT_COMMON_CPP_SOURCES}
+    ${PROJECT_SOURCE_DIR}/common/src/firmware_header_utils.c
+    ${PROJECT_SOURCE_DIR}/common/src/md5.c
+    ${HAILORT_SRC_DIR}/pipeline.cpp
+    ${HAILO_FULL_OS_DIR}/event.cpp
+)
+target_compile_options(hailortcli PRIVATE ${HAILORT_COMPILE_OPTIONS})
+set_property(TARGET hailortcli PROPERTY CXX_STANDARD 14)
+set_property(TARGET hailortcli PROPERTY INSTALL_RPATH "$ORIGIN") # Need to add "${CMAKE_INSTALL_LIBDIR}" when installing with cmake to /usr/local
+target_link_libraries(hailortcli libhailort CLI11::CLI11 nlohmann_json spdlog::spdlog readerwriterqueue)
+target_link_libraries(hailortcli DotWriter)
+if(WIN32)
+    target_link_libraries(hailortcli Ws2_32 Iphlpapi Shlwapi)
+endif()
+target_include_directories(hailortcli
+    PRIVATE
+    ${CMAKE_CURRENT_BINARY_DIR} # CMAKE_CURRENT_BINARY_DIR is necessary for config_definitions_header
+    ${HAILORT_COMMON_DIR}
+    ${COMMON_INC_DIR}
+    ${HAILORT_SRC_DIR}
+)
+
+install(TARGETS hailortcli
+   RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+   CONFIGURATIONS Release)
\ No newline at end of file
diff --git a/hailort/hailortcli/benchmark_command.cpp b/hailort/hailortcli/benchmark_command.cpp
new file mode 100644 (file)
index 0000000..7b17de5
--- /dev/null
@@ -0,0 +1,137 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file benchmark_command.cpp
+ * @brief measure basic performance on compiled network
+ **/
+
+#include "benchmark_command.hpp"
+#include "hailortcli.hpp"
+#include "infer_stats_printer.hpp"
+
+#include <iostream>
+
+
+BenchmarkCommand::BenchmarkCommand(CLI::App &parent_app) :
+    Command(parent_app.add_subcommand("benchmark", "Measure basic performance on compiled network")),
+    m_params({})
+{
+    add_device_options(m_app, m_params.device_params);
+    add_vdevice_options(m_app, m_params.device_params);
+    m_params.measure_overall_latency = false;
+    m_params.power_measurement.measure_current = false;
+    m_params.show_progress = true;
+    m_params.transform.format_type = HAILO_FORMAT_TYPE_AUTO;
+
+    m_app->add_option("hef", m_params.hef_path, "Path of the HEF to load")
+        ->check(CLI::ExistingFile)
+        ->required();
+     m_app->add_option("-t, --time-to-run", m_time, "Measurement time in seconds per hw_only/streaming/latency measurement mode")
+        ->check(CLI::PositiveNumber)
+        ->default_val(15);
+    m_app->add_option("--no-power", m_not_measure_power, "Skip power measurement, even if the platform supports it. The default value is False")
+        ->default_val("false");
+    m_app->add_option("--batch-size", m_params.batch_size, "Inference batch size (default is 1)")
+        ->default_val(1);
+    m_app->add_option("--input-files", m_params.inputs_name_and_file_path, "  The input files need to be in UINT8 before transformations.")
+        ->check(InputNameToFileMap);
+    m_app->add_option("--csv", m_csv_file_path, "If set print the output as csv to the specified path");
+    
+    auto measure_power_group = m_app->add_option_group("Measure Power");
+    CLI::Option *power_sampling_period = measure_power_group->add_option("--sampling-period",
+        m_params.power_measurement.sampling_period, "Sampling Period");
+    CLI::Option *power_averaging_factor = measure_power_group->add_option("--averaging-factor",
+        m_params.power_measurement.averaging_factor, "Averaging Factor");
+    PowerMeasurementSubcommand::init_sampling_period_option(power_sampling_period);
+    PowerMeasurementSubcommand::init_averaging_factor_option(power_averaging_factor);
+
+    // TODO HRT-5363 support multiple devices
+    m_app->parse_complete_callback([this]() {
+        PARSE_CHECK((this->m_params.device_params.vdevice_params.device_count == 1) || this->m_csv_file_path.empty() || this->m_not_measure_power,
+            "Writing power measurements in csv format is not supported for multiple devices");
+    });
+}
+
+hailo_status BenchmarkCommand::execute()
+{   
+    std::cout << "Starting Measurements..." << std::endl;
+    
+    std::cout << "Measuring FPS in hw_only mode" << std::endl;
+    auto hw_only_mode_info = hw_only_mode();
+    CHECK_EXPECTED_AS_STATUS(hw_only_mode_info, "hw_only measuring failed");
+    
+    std::cout << "Measuring FPS " << (!m_not_measure_power ? "and Power " : "") << "in streaming mode" << std::endl; 
+    auto streaming_mode_info = fps_streaming_mode();
+    CHECK_EXPECTED_AS_STATUS(streaming_mode_info, "FPS in streaming mode failed");
+
+    std::cout << "Measuring HW Latency" << std::endl;
+    auto latency_info = latency();
+    CHECK_EXPECTED_AS_STATUS(latency_info, "Latency measuring failed");
+
+    std::cout << std::endl;
+    std::cout << "=======" << std::endl;
+    std::cout << "Summary" << std::endl;
+    std::cout << "=======" << std::endl;
+    std::cout << "FPS     (hw_only)                 = " << hw_only_mode_info->fps().value() <<std::endl;
+    std::cout << "        (streaming)               = " << streaming_mode_info->fps().value() <<std::endl;
+    if (auto hw_latency = latency_info->hw_latency()) {
+        std::cout << "Latency (hw)                      = " << InferResultsFormatUtils::latency_result_to_ms(hw_latency.value()) << " ms" << std::endl;
+    }
+    if (auto overall_latency = latency_info->overall_latency()) {
+        std::cout << "        (overall)                 = " << InferResultsFormatUtils::latency_result_to_ms(overall_latency.value()) << " ms" << std::endl;
+    }
+    if (!m_not_measure_power) {
+        for (const auto &pair : streaming_mode_info->m_power_measurements) {
+            std::cout << "Device " << pair.first << ":" << std::endl;
+            const auto &data = pair.second->data();
+            const auto &power_units = pair.second->power_units();
+            std::cout << "  Power in streaming mode (average) = " << data.average_value << " " << power_units << std::endl;
+            std::cout << "                          (max)     = " << data.max_value << " " << power_units << std::endl;
+        }
+
+    }
+
+    if (!m_csv_file_path.empty()){
+        m_params.csv_output = m_csv_file_path;
+        auto printer = InferStatsPrinter::create(m_params, false);
+        CHECK_EXPECTED_AS_STATUS(printer, "Failed to initialize infer stats printer");
+        printer->print_benchmark_csv_header();
+        printer->print_benchmark_csv(m_params.hef_path, hw_only_mode_info.release(), 
+            streaming_mode_info.release(), latency_info.release());
+    }
+    return HAILO_SUCCESS;
+}
+
+Expected<NetworkGroupInferResult> BenchmarkCommand::hw_only_mode()
+{
+    m_params.transform.transform = (m_params.inputs_name_and_file_path.size() > 0);
+    m_params.power_measurement.measure_power = false;
+    m_params.measure_latency = false;
+    m_params.mode = InferMode::HW_ONLY;
+    m_params.time_to_run = m_time;
+    return run_command_hef(m_params);
+}
+
+Expected<NetworkGroupInferResult> BenchmarkCommand::fps_streaming_mode()
+{
+    m_params.power_measurement.measure_power = !m_not_measure_power;
+    m_params.mode = InferMode::STREAMING;
+    m_params.measure_latency = false;
+    m_params.transform.transform = true;
+    m_params.transform.quantized = false;
+    m_params.time_to_run = m_time;
+    return run_command_hef(m_params);
+}
+
+Expected<NetworkGroupInferResult> BenchmarkCommand::latency()
+{
+    m_params.power_measurement.measure_power = false;
+    m_params.measure_latency = true;
+    m_params.mode = InferMode::STREAMING;
+    m_params.transform.transform = true;
+    m_params.transform.quantized = false;
+    m_params.time_to_run = m_time;
+    return run_command_hef(m_params);
+}
\ No newline at end of file
diff --git a/hailort/hailortcli/benchmark_command.hpp b/hailort/hailortcli/benchmark_command.hpp
new file mode 100644 (file)
index 0000000..bffd401
--- /dev/null
@@ -0,0 +1,34 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file benchmarks_command.hpp
+ * @brief measure basic performance on compiled network
+ **/
+
+#ifndef _HAILO_BENCHMARK_COMMAND_HPP_
+#define _HAILO_BENCHMARK_COMMAND_HPP_
+
+#include "hailortcli.hpp"
+#include "command.hpp"
+#include "run_command.hpp"
+#include "CLI/CLI.hpp"
+
+class BenchmarkCommand : public Command {
+public:
+    explicit BenchmarkCommand(CLI::App &parent_app);
+    hailo_status execute() override;
+
+private:
+    Expected<NetworkGroupInferResult> hw_only_mode();
+    Expected<NetworkGroupInferResult> fps_streaming_mode();
+    Expected<NetworkGroupInferResult> latency();
+    
+    inference_runner_params m_params;
+    bool m_not_measure_power;
+    uint32_t m_time;
+    std::string m_csv_file_path;
+};
+
+#endif /*_HAILO_BENCHMARK_COMMAND_HPP_*/
\ No newline at end of file
diff --git a/hailort/hailortcli/board_config_command.cpp b/hailort/hailortcli/board_config_command.cpp
new file mode 100644 (file)
index 0000000..d9a0012
--- /dev/null
@@ -0,0 +1,63 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file board_config_command.cpp
+ * @brief Board configuration command (fw static configuration).
+ **/
+
+#include "board_config_command.hpp"
+#include "common/file_utils.hpp"
+
+#include <fstream>
+
+BoardConfigCommand::BoardConfigCommand(CLI::App &parent_app) :
+    ContainerCommand(parent_app.add_subcommand("board-config", "Board configuration tool"))
+{
+    // This will make the board-config command to be hidden in the --help print in the command line.
+    m_app->group("");
+
+    add_subcommand<BoardConfigReadSubcommand>();
+    add_subcommand<BoardConfigWriteSubcommand>();
+}
+
+BoardConfigReadSubcommand::BoardConfigReadSubcommand(CLI::App &parent_app) :
+    DeviceCommand(parent_app.add_subcommand("read", "Read board configuration from device"))
+{
+    m_app->add_option("output_file", m_output_file_path, "File path to dump board configuration into.")
+        ->required();
+}
+
+hailo_status BoardConfigReadSubcommand::execute_on_device(Device &device)
+{
+    auto buffer = device.read_board_config();
+    CHECK_EXPECTED_AS_STATUS(buffer, "Failed reading board config from device");
+
+    auto output_file = std::ofstream(m_output_file_path, std::ios::out | std::ios::binary);
+    CHECK(output_file.is_open(), HAILO_OPEN_FILE_FAILURE, "Failed opening output file {} with errno: {}", m_output_file_path, errno);
+
+    output_file.write(reinterpret_cast<char*>(buffer->data()), buffer->size());
+    CHECK(output_file.good(), HAILO_FILE_OPERATION_FAILURE, "Failed writing board config into file {}.", m_output_file_path);
+
+    return HAILO_SUCCESS;
+}
+
+BoardConfigWriteSubcommand::BoardConfigWriteSubcommand(CLI::App &parent_app) :
+    DeviceCommand(parent_app.add_subcommand("write", "Write board configuration to device"))
+{
+    m_app->add_option("input_file", m_input_file_path, "Board config binary file path.")
+        ->check(CLI::ExistingFile)
+        ->required();
+}
+
+hailo_status BoardConfigWriteSubcommand::execute_on_device(Device &device)
+{
+    auto buffer = read_binary_file(m_input_file_path);
+    CHECK_EXPECTED_AS_STATUS(buffer);
+
+    hailo_status status = device.write_board_config(MemoryView(buffer.value()));
+    CHECK_SUCCESS(status, "Failed writing board config to device.");
+
+    return HAILO_SUCCESS;
+}
diff --git a/hailort/hailortcli/board_config_command.hpp b/hailort/hailortcli/board_config_command.hpp
new file mode 100644 (file)
index 0000000..8f8231b
--- /dev/null
@@ -0,0 +1,47 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file board_config_command.hpp
+ * @brief Firmware configuration command.
+ **/
+
+#ifndef _HAILO_BOARD_CONFIG_COMMAND_HPP_
+#define _HAILO_BOARD_CONFIG_COMMAND_HPP_
+
+#include "hailortcli.hpp"
+#include "command.hpp"
+
+#include "hailo/hailort.h"
+#include "hailo/device.hpp"
+#include "CLI/CLI.hpp"
+
+class BoardConfigReadSubcommand final : public DeviceCommand {
+public:
+    explicit BoardConfigReadSubcommand(CLI::App &parent_app);
+
+protected:
+    virtual hailo_status execute_on_device(Device &device) override;
+
+private:
+    std::string m_output_file_path;
+};
+
+class BoardConfigWriteSubcommand final : public DeviceCommand {
+public:
+    explicit BoardConfigWriteSubcommand(CLI::App &parent_app);
+
+protected:
+    virtual hailo_status execute_on_device(Device &device) override;
+
+private:
+    std::string m_input_file_path;
+};
+
+class BoardConfigCommand final : public ContainerCommand {
+public:
+    explicit BoardConfigCommand(CLI::App &parent_app);
+};
+
+#endif /* _HAILO_BOARD_CONFIG_COMMAND_HPP_ */
\ No newline at end of file
diff --git a/hailort/hailortcli/command.cpp b/hailort/hailortcli/command.cpp
new file mode 100644 (file)
index 0000000..59ad875
--- /dev/null
@@ -0,0 +1,71 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file command.cpp
+ * @brief Base classes for hailortcli commands.
+ **/
+
+
+#include "command.hpp"
+
+Command::Command(CLI::App *app) :
+    m_app(app)
+{
+}
+
+ContainerCommand::ContainerCommand(CLI::App *app) :
+    Command(app)
+{
+    m_app->require_subcommand(1);
+}
+
+hailo_status ContainerCommand::execute()
+{
+    for (auto &command : m_subcommands) {
+        if (command->parsed()) {
+            return command->execute();
+        }
+    }
+
+    LOGGER__ERROR("No subommand found..");
+    return HAILO_NOT_FOUND;
+}
+
+DeviceCommand::DeviceCommand(CLI::App *app) :
+    Command(app)
+{
+    add_device_options(m_app, m_device_params);
+}
+
+hailo_status DeviceCommand::execute()
+{
+    auto device = create_device(m_device_params);
+    if (!device) {
+        return device.status();
+    }
+
+    return execute_on_device(*device.value());
+}
+
+PcieDeviceCommand::PcieDeviceCommand(CLI::App *app) :
+    Command(app)
+{
+    auto group = app->add_option_group("PCIE Device Options");
+
+    // PCIe options
+    group->add_option("-s,--bdf", m_pcie_device_params.pcie_bdf,
+        "Device id ([<domain>]:<bus>:<device>.<func>, same as in lspci command)")
+        ->default_val("");
+}
+
+hailo_status PcieDeviceCommand::execute()
+{
+    auto device = create_pcie_device(m_pcie_device_params);
+    if (!device) {
+        return device.status();
+    }
+
+    return execute_on_device(*device.value());
+}
diff --git a/hailort/hailortcli/command.hpp b/hailort/hailortcli/command.hpp
new file mode 100644 (file)
index 0000000..e92e999
--- /dev/null
@@ -0,0 +1,90 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file command.hpp
+ * @brief Base classes for hailortcli commands.
+ **/
+
+#ifndef _HAILO_COMMAND_HPP_
+#define _HAILO_COMMAND_HPP_
+
+#include "hailortcli.hpp"
+#include "CLI/CLI.hpp"
+
+
+class Command {
+public:
+    explicit Command(CLI::App *app);
+    virtual ~Command() = default;
+
+    virtual hailo_status execute() = 0;
+
+    bool parsed() const
+    {
+        return m_app->parsed();
+    }
+
+    void set_description(const std::string &new_desc)
+    {
+        m_app->description(new_desc);
+    }
+
+    void set_footer(const std::string &new_footer)
+    {
+        m_app->footer(new_footer);
+    }
+
+protected:
+    CLI::App *m_app;
+};
+
+// Command that only contains list of subcommand
+class ContainerCommand : public Command {
+public:
+    explicit ContainerCommand(CLI::App *app);
+    virtual hailo_status execute() override final;
+
+protected:
+
+    template<typename CommandType>
+    CommandType &add_subcommand(bool hidden = false)
+    {
+        // Unnamed "option groups" hide subcommands/options from the help message
+        // (see https://github.com/CLIUtils/CLI11/blob/main/README.md)
+        auto *parent = hidden ? m_app->add_option_group("") : m_app;
+        auto command = std::make_shared<CommandType>(*parent);
+        m_subcommands.push_back(command);
+        return *command;
+    }
+
+private:
+    std::vector<std::shared_ptr<Command>> m_subcommands;
+};
+
+class DeviceCommand : public Command {
+public:
+    explicit DeviceCommand(CLI::App *app);
+    virtual hailo_status execute() override final;
+
+protected:
+    virtual hailo_status execute_on_device(Device &device) = 0;
+
+private:
+    hailo_device_params m_device_params;
+};
+
+class PcieDeviceCommand : public Command {
+public:
+    explicit PcieDeviceCommand(CLI::App *app);
+    virtual hailo_status execute() override final;
+
+protected:
+    virtual hailo_status execute_on_device(Device &device) = 0;
+
+private:
+    hailo_pcie_params m_pcie_device_params;
+};
+
+#endif /* _HAILO_COMMAND_HPP_ */
\ No newline at end of file
diff --git a/hailort/hailortcli/common.cpp b/hailort/hailortcli/common.cpp
new file mode 100644 (file)
index 0000000..27b45ec
--- /dev/null
@@ -0,0 +1,74 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file common.cpp
+ * @brief Common functions.
+ **/
+#include "common.hpp"
+#include "common/utils.hpp"
+
+#include <iostream>
+#include <sstream>
+#include <chrono>
+#include <ctime>
+
+std::string CliCommon::duration_to_string(std::chrono::seconds secs)
+{
+    using namespace std::chrono;
+    using namespace std::chrono_literals;
+
+    bool neg = (secs < 0s);
+    if (neg) {
+        secs = -secs;
+    }
+
+    auto h = duration_cast<hours>(secs);
+    secs -= h;
+    auto m = duration_cast<minutes>(secs);
+    secs -= m;
+
+    std::stringstream result;
+    if (neg) {
+        result << '-';
+    }
+
+    if (h < 10h) {
+        result << '0';
+    }
+    result << (h/1h) << ':';
+
+    if (m < 10min) {
+        result << '0';
+    }
+    result << m/1min << ':';
+
+    if (secs < 10s) {
+        result << '0';
+    }
+    result << secs/1s;
+
+    return result.str();
+}
+
+
+Expected<std::string> CliCommon::current_time_to_string()
+{
+    const auto curr_time = std::time(nullptr);
+    CHECK_AS_EXPECTED(static_cast<std::time_t>(-1) != curr_time, HAILO_INTERNAL_FAILURE, "std::time failed");
+    const auto *local_time = std::localtime(&curr_time);
+    CHECK_AS_EXPECTED(nullptr != local_time, HAILO_INTERNAL_FAILURE, "std::localtime failed");
+
+    std::stringstream result;
+    // Standard date and time string (see: https://en.cppreference.com/w/cpp/io/manip/put_time)
+    result << std::put_time(local_time, "%c");
+    return result.str();
+}
+
+void CliCommon::reset_cursor(size_t lines_count)
+{
+    for (size_t i = 0; i < lines_count; i++) {
+        std::cout << FORMAT_CURSOR_UP_LINE;
+    }
+}
\ No newline at end of file
diff --git a/hailort/hailortcli/common.hpp b/hailort/hailortcli/common.hpp
new file mode 100644 (file)
index 0000000..54179f7
--- /dev/null
@@ -0,0 +1,53 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file common.hpp
+ * @brief Common functions.
+ **/
+
+#ifndef _HAILO_HAILORTCLI_COMMON_HPP_
+#define _HAILO_HAILORTCLI_COMMON_HPP_
+
+#include "CLI/CLI.hpp"
+
+#include "common/filesystem.hpp"
+
+#include <chrono>
+#include <sstream>
+
+using namespace hailort;
+
+// http://www.climagic.org/mirrors/VT100_Escape_Codes.html
+#define FORMAT_CLEAR_LINE "\033[2K\r"
+#define FORMAT_CURSOR_UP_LINE "\033[F"
+
+class CliCommon final
+{
+public:
+    CliCommon() = delete;
+
+    static std::string duration_to_string(std::chrono::seconds secs);
+    static Expected<std::string> current_time_to_string();
+    static void reset_cursor(size_t number_of_lines);
+};
+
+// Validators
+struct FileSuffixValidator : public CLI::Validator {
+    FileSuffixValidator(const std::string &suffix) {
+        name_ = "FILE_SUFFIX";
+        func_ = [suffix](const std::string &filename) {
+            if (!Filesystem::has_suffix(filename, suffix)) {
+                std::stringstream error_message;
+                error_message << "File '" << filename << "' does not end with suffix '"
+                    << suffix << "'." << std::endl;
+                return error_message.str();
+            }
+            // Success
+            return std::string();
+        };
+    }
+};
+
+#endif /* _HAILO_HAILORTCLI_COMMON_HPP_ */
\ No newline at end of file
diff --git a/hailort/hailortcli/download_action_list_command.cpp b/hailort/hailortcli/download_action_list_command.cpp
new file mode 100644 (file)
index 0000000..3832338
--- /dev/null
@@ -0,0 +1,429 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file download_action_list_command.cpp
+ * @brief Download action list command implementation
+ **/
+
+#include "download_action_list_command.hpp"
+#include "common.hpp"
+#include "common/file_utils.hpp"
+#include "md5.h"
+
+#include <iostream>
+
+constexpr int DownloadActionListCommand::INVALID_NUMERIC_VALUE;
+
+DownloadActionListCommand::DownloadActionListCommand(CLI::App &parent_app) :
+    DeviceCommand(parent_app.add_subcommand("action-list", "Download action list data, for run time profiler"))
+{
+    static const char *JSON_SUFFIX = ".json";
+    m_app->add_option("--output-file", m_output_file_path, "Output file path")
+        ->default_val("context_action_list.json")
+        ->check(FileSuffixValidator(JSON_SUFFIX));
+}
+
+hailo_status DownloadActionListCommand::execute(Device &device, const std::string &output_file_path,
+    const ConfiguredNetworkGroupVector &network_groups, const std::string &hef_file_path)
+{
+    std::cout << "> Writing action list to '" << output_file_path << "'... ";
+
+    auto curr_time = CliCommon::current_time_to_string();
+    CHECK_EXPECTED_AS_STATUS(curr_time);
+
+    // TODO: Get real clock rate (HRT-5998)
+    static const uint32_t CLOCK_CYCLE = 200;
+    ordered_json action_list_json = {
+        {"version", ACTION_LIST_FORMAT_VERSION()},
+        {"creation_time", curr_time.release()},
+        {"clock_cycle_MHz", CLOCK_CYCLE},
+        {"hef", json({})}
+    };
+
+    if (!hef_file_path.empty()) {
+        auto hef_info = parse_hef_metadata(hef_file_path);
+        CHECK_EXPECTED_AS_STATUS(hef_info);
+        action_list_json["hef"] = hef_info.release();
+    }
+
+    auto network_groups_list_json = parse_network_groups(device, network_groups);
+    CHECK_EXPECTED_AS_STATUS(network_groups_list_json);
+    action_list_json["network_groups"] = network_groups_list_json.release();
+
+    CHECK_SUCCESS(write_json(action_list_json, output_file_path));
+
+    std::cout << "done." << std::endl;
+
+    return HAILO_SUCCESS;
+}
+
+hailo_status DownloadActionListCommand::execute_on_device(Device &device)
+{
+    return execute(device, m_output_file_path);
+}
+
+Expected<ordered_json> DownloadActionListCommand::parse_hef_metadata(const std::string &hef_file_path)
+{
+    CHECK_AS_EXPECTED(is_valid_hef(hef_file_path), HAILO_INTERNAL_FAILURE,
+        "Hef '{}' is not valid", hef_file_path);
+
+    auto hef_md5 = calc_md5_hexdigest(hef_file_path);
+    CHECK_EXPECTED(hef_md5);
+
+    ordered_json hef_info_json = {
+        {"path", hef_file_path},
+        {"file_hash", hef_md5.release()}
+    };
+    
+    return hef_info_json;
+}
+
+bool DownloadActionListCommand::is_valid_hef(const std::string &hef_file_path)
+{
+    // Open hef, to check that it's valid
+    const auto hef = Hef::create(hef_file_path);
+    return hef.has_value();
+}
+
+Expected<std::string> DownloadActionListCommand::calc_md5_hexdigest(const std::string &hef_file_path)
+{
+    auto hef_bin = read_binary_file(hef_file_path);
+    CHECK_EXPECTED(hef_bin);
+
+    MD5_CTX md5_ctx{};
+    MD5_SUM_t md5_sum{};
+    MD5_Init(&md5_ctx);
+    MD5_Update(&md5_ctx, hef_bin->data(), hef_bin->size());
+    MD5_Final(md5_sum, &md5_ctx);
+
+    std::stringstream hexdigest;
+    for (uint32_t i = 0; i < ARRAY_ENTRIES(md5_sum); i++) {
+        // cast to int needed for proper formatting
+        hexdigest << std::hex << static_cast<int>(md5_sum[i]);
+    }
+
+    return hexdigest.str();
+}
+
+hailo_status DownloadActionListCommand::write_json(const ordered_json &json_obj, const std::string &output_file_path,
+    int tab_width)
+{
+    std::ofstream output_file(output_file_path);
+    CHECK(output_file, HAILO_INTERNAL_FAILURE, "Failed opening file '{}'", output_file_path);
+    
+    output_file << std::setw(tab_width) << json_obj << std::endl;
+    CHECK(!output_file.bad() && !output_file.fail(), HAILO_INTERNAL_FAILURE,
+        "Failed writing to file '{}'", output_file_path);
+
+    return HAILO_SUCCESS;
+}
+
+Expected<ordered_json> DownloadActionListCommand::parse_action_data(uint32_t base_address, uint8_t *action,
+    uint32_t current_buffer_offset, uint32_t *action_length, CONTEXT_SWITCH_DEFS__ACTION_TYPE_t action_type,
+    uint32_t timestamp, uint8_t sub_action_index, bool sub_action_index_set, bool *is_repeated, uint8_t *num_repeated,
+    CONTEXT_SWITCH_DEFS__ACTION_TYPE_t *sub_action_type)
+{
+    ordered_json action_json {
+        {"address", base_address + current_buffer_offset},
+        {"timestamp", timestamp},
+        {"type", action_type}
+    };
+
+    if (sub_action_index_set) {
+        action_json["sub_action_index"] = sub_action_index;
+    }
+
+    size_t action_length_local = 0;
+    json data_json;
+    switch (action_type) {
+        case CONTEXT_SWITCH_DEFS__ACTION_TYPE_REPEATED_ACTION:
+        {
+            data_json = *reinterpret_cast<CONTEXT_SWITCH_DEFS__repeated_action_header_t *>(action);
+            action_length_local = sizeof(CONTEXT_SWITCH_DEFS__repeated_action_header_t);
+            const auto *repeated_header = reinterpret_cast<CONTEXT_SWITCH_DEFS__repeated_action_header_t *>(action);
+            *is_repeated = true;
+            *num_repeated = repeated_header->count;
+            *sub_action_type = repeated_header->sub_action_type;
+            break;
+        }
+        case CONTEXT_SWITCH_DEFS__ACTION_TYPE_LCU_INTERRUPT:
+            data_json = *reinterpret_cast<CONTEXT_SWITCH_DEFS__lcu_interrupt_data_t *>(action);
+            action_length_local = sizeof(CONTEXT_SWITCH_DEFS__lcu_interrupt_data_t);
+            break;
+        case CONTEXT_SWITCH_DEFS__ACTION_TYPE_SEQUENCER_DONE_INTERRUPT:
+            data_json = *reinterpret_cast<CONTEXT_SWITCH_DEFS__sequencer_interrupt_data_t *>(action);
+            action_length_local = sizeof(CONTEXT_SWITCH_DEFS__sequencer_interrupt_data_t);
+            break;
+        case CONTEXT_SWITCH_DEFS__ACTION_TYPE_INPUT_CHANNEL_TRANSFER_DONE_INTERRUPT:
+            data_json = *reinterpret_cast<CONTEXT_SWITCH_DEFS__vdma_dataflow_interrupt_data_t *>(action);
+            action_length_local = sizeof(CONTEXT_SWITCH_DEFS__vdma_dataflow_interrupt_data_t);
+            break;
+        case CONTEXT_SWITCH_DEFS__ACTION_TYPE_WAIT_FOR_DMA_IDLE_ACTION:
+            data_json = *reinterpret_cast<CONTEXT_SWITCH_DEFS__wait_dma_idle_data_t *>(action);
+            action_length_local = sizeof(CONTEXT_SWITCH_DEFS__wait_dma_idle_data_t);
+            break;
+        case CONTEXT_SWITCH_DEFS__ACTION_TYPE_WAIT_FOR_NMS_IDLE:
+            data_json = *reinterpret_cast<CONTEXT_SWITCH_DEFS__wait_nms_idle_data_t *>(action);
+            action_length_local = sizeof(CONTEXT_SWITCH_DEFS__wait_nms_idle_data_t);
+            break;
+        case CONTEXT_SWITCH_DEFS__ACTION_TYPE_OUTPUT_CHANNEL_TRANSFER_DONE_INTERRUPT:
+            data_json = *reinterpret_cast<CONTEXT_SWITCH_DEFS__vdma_dataflow_interrupt_data_t *>(action);
+            action_length_local = sizeof(CONTEXT_SWITCH_DEFS__vdma_dataflow_interrupt_data_t);
+            break;
+        case CONTEXT_SWITCH_DEFS__ACTION_TYPE_MODULE_CONFIG_DONE_INTERRUPT:
+            data_json = *reinterpret_cast<CONTEXT_SWITCH_DEFS__module_config_done_interrupt_data_t *>(action);
+            action_length_local = sizeof(CONTEXT_SWITCH_DEFS__module_config_done_interrupt_data_t);
+            break;
+        case CONTEXT_SWITCH_DEFS__ACTION_TYPE_APPLICATION_CHANGE_INTERRUPT:
+            data_json = *reinterpret_cast<CONTEXT_SWITCH_DEFS__application_change_interrupt_data_t *>(action);
+            action_length_local = sizeof(CONTEXT_SWITCH_DEFS__application_change_interrupt_data_t);
+            break;
+        case CONTEXT_SWITCH_DEFS__ACTION_TYPE_FETCH_VDMA_DESCRIPTORS:
+            data_json = *reinterpret_cast<CONTEXT_SWITCH_DEFS__read_vdma_action_data_t *>(action);
+            action_length_local = sizeof(CONTEXT_SWITCH_DEFS__read_vdma_action_data_t);
+            break;
+        case CONTEXT_SWITCH_DEFS__ACTION_TYPE_FETCH_CCW_BURSTS:
+            data_json = *reinterpret_cast<CONTEXT_SWITCH_DEFS__fetch_ccw_bursts_action_data_t *>(action);
+            action_length_local = sizeof(CONTEXT_SWITCH_DEFS__fetch_ccw_bursts_action_data_t);
+            break;
+        case CONTEXT_SWITCH_DEFS__ACTION_TYPE_TRIGGER_SEQUENCER:
+            data_json = *reinterpret_cast<CONTEXT_SWITCH_DEFS__trigger_sequencer_action_data_t *>(action);
+            action_length_local = sizeof(CONTEXT_SWITCH_DEFS__trigger_sequencer_action_data_t);
+            break;
+        case CONTEXT_SWITCH_DEFS__ACTION_TYPE_FETCH_DATA_FROM_VDMA_CHANNEL:
+            data_json = *reinterpret_cast<CONTEXT_SWITCH_DEFS__fetch_data_action_data_t *>(action);
+            action_length_local = sizeof(CONTEXT_SWITCH_DEFS__fetch_data_action_data_t);
+            break;
+        case CONTEXT_SWITCH_DEFS__ACTION_TYPE_DEACTIVATE_VDMA_CHANNEL:
+            data_json = *reinterpret_cast<CONTEXT_SWITCH_DEFS__deactivate_vdma_channel_action_data_t *>(action);
+            action_length_local = sizeof(CONTEXT_SWITCH_DEFS__deactivate_vdma_channel_action_data_t);
+            break;
+        case CONTEXT_SWITCH_DEFS__ACTION_TYPE_ENABLE_LCU_DEFAULT:
+            data_json = *reinterpret_cast<CONTEXT_SWITCH_DEFS__enable_lcu_action_default_data_t *>(action);
+            action_length_local = sizeof(CONTEXT_SWITCH_DEFS__enable_lcu_action_default_data_t);
+            break;
+        case CONTEXT_SWITCH_DEFS__ACTION_TYPE_ENABLE_LCU_NON_DEFAULT:
+            data_json = *reinterpret_cast<CONTEXT_SWITCH_DEFS__enable_lcu_action_non_default_data_t *>(action);
+            action_length_local = sizeof(CONTEXT_SWITCH_DEFS__enable_lcu_action_non_default_data_t);
+            break;
+        case CONTEXT_SWITCH_DEFS__ACTION_TYPE_DISABLE_LCU:
+            data_json = *reinterpret_cast<CONTEXT_SWITCH_DEFS__disable_lcu_action_data_t *>(action);
+            action_length_local = sizeof(CONTEXT_SWITCH_DEFS__disable_lcu_action_data_t);
+            break;
+        case CONTEXT_SWITCH_DEFS__ACTION_TYPE_ACTIVATE_BOUNDARY_INPUT:
+            data_json = *reinterpret_cast<CONTEXT_SWITCH_DEFS__activate_boundary_input_data_t *>(action);
+            action_length_local = sizeof(CONTEXT_SWITCH_DEFS__activate_boundary_input_data_t);
+            break;
+        case CONTEXT_SWITCH_DEFS__ACTION_TYPE_ACTIVATE_BOUNDARY_OUTPUT:
+            data_json = *reinterpret_cast<CONTEXT_SWITCH_DEFS__activate_boundary_output_data_t *>(action);
+            action_length_local = sizeof(CONTEXT_SWITCH_DEFS__activate_boundary_output_data_t);
+            break;
+        case CONTEXT_SWITCH_DEFS__ACTION_TYPE_ACTIVATE_INTER_CONTEXT_INPUT:
+            data_json = *reinterpret_cast<CONTEXT_SWITCH_DEFS__activate_inter_context_input_data_t *>(action);
+            action_length_local = sizeof(CONTEXT_SWITCH_DEFS__activate_inter_context_input_data_t);
+            break;
+        case CONTEXT_SWITCH_DEFS__ACTION_TYPE_ACTIVATE_INTER_CONTEXT_OUTPUT:
+            data_json = *reinterpret_cast<CONTEXT_SWITCH_DEFS__activate_inter_context_output_data_t *>(action);
+            action_length_local = sizeof(CONTEXT_SWITCH_DEFS__activate_inter_context_output_data_t);
+            break;
+        case CONTEXT_SWITCH_DEFS__ACTION_TYPE_ACTIVATE_DDR_BUFFER_INPUT:
+            data_json = *reinterpret_cast<CONTEXT_SWITCH_DEFS__activate_ddr_buffer_input_data_t *>(action);
+            action_length_local = sizeof(CONTEXT_SWITCH_DEFS__activate_ddr_buffer_input_data_t);
+            break;
+        case CONTEXT_SWITCH_DEFS__ACTION_TYPE_ACTIVATE_DDR_BUFFER_OUTPUT:
+            data_json = *reinterpret_cast<CONTEXT_SWITCH_DEFS__activate_ddr_buffer_output_data_t *>(action);
+            action_length_local = sizeof(CONTEXT_SWITCH_DEFS__activate_ddr_buffer_output_data_t);
+            break;
+        case CONTEXT_SWITCH_DEFS__ACTION_TYPE_CHANGE_VDMA_TO_STREAM_MAPPING:
+            data_json = *reinterpret_cast<CONTEXT_SWITCH_DEFS__change_vdma_to_stream_mapping_data_t *>(action);
+            action_length_local = sizeof(CONTEXT_SWITCH_DEFS__change_vdma_to_stream_mapping_data_t);
+            break;
+        case CONTEXT_SWITCH_DEFS__ACTION_TYPE_ADD_DDR_PAIR_INFO:
+            data_json = *reinterpret_cast<CONTEXT_SWITCH_DEFS__add_ddr_pair_info_action_data_t *>(action);
+            action_length_local = sizeof(CONTEXT_SWITCH_DEFS__add_ddr_pair_info_action_data_t);
+            break;
+        case CONTEXT_SWITCH_DEFS__ACTION_TYPE_DDR_BUFFERING_START:
+            data_json = json({});
+            action_length_local = 0;
+            break;
+        case CONTEXT_SWITCH_DEFS__ACTION_TYPE_ACTIVATE_CFG_CHANNEL:
+            data_json = *reinterpret_cast<CONTEXT_SWITCH_DEFS__activate_cfg_channel_t *>(action);
+            action_length_local = sizeof(CONTEXT_SWITCH_DEFS__activate_cfg_channel_t);
+            break;
+        case CONTEXT_SWITCH_DEFS__ACTION_TYPE_DEACTIVATE_CFG_CHANNEL:
+            data_json = *reinterpret_cast<CONTEXT_SWITCH_DEFS__deactivate_cfg_channel_t *>(action);
+            action_length_local = sizeof(CONTEXT_SWITCH_DEFS__deactivate_cfg_channel_t);
+            break;
+        default:
+            std::cerr << "PARSING ERROR ! unknown action main type" << std::endl;
+            return make_unexpected(HAILO_INTERNAL_FAILURE);
+    }
+    action_json["data"] = data_json;
+    *action_length = static_cast<uint32_t>(action_length_local);
+    return action_json;
+}
+
+Expected<ordered_json> DownloadActionListCommand::parse_single_repeated_action(uint32_t base_address,
+    uint8_t *action, uint32_t current_buffer_offset, uint32_t *action_length,
+    CONTEXT_SWITCH_DEFS__ACTION_TYPE_t action_type, uint32_t timestamp, uint8_t index_in_repeated_block)
+{
+    static const bool SET_SUB_ACTION_INDEX = true;
+    return parse_action_data(base_address, action, current_buffer_offset, action_length,
+        action_type, timestamp, index_in_repeated_block, SET_SUB_ACTION_INDEX);
+}
+
+Expected<ordered_json> DownloadActionListCommand::parse_single_action(uint32_t base_address,
+    uint8_t *context_action_list, uint32_t current_buffer_offset, uint32_t *action_length, bool *is_repeated,
+    uint8_t *num_repeated, CONTEXT_SWITCH_DEFS__ACTION_TYPE_t *sub_action_type, uint32_t *time_stamp)
+{
+    const auto action_length_local = sizeof(CONTEXT_SWITCH_DEFS__common_action_header_t);
+    const auto *action_header = reinterpret_cast<CONTEXT_SWITCH_DEFS__common_action_header_t *>(&context_action_list[current_buffer_offset]);
+    const auto time_stamp_local = CONTEXT_SWITCH_DEFS__TIMESTAMP_INIT_VALUE - action_header->time_stamp;
+    current_buffer_offset += static_cast<uint32_t>(sizeof(CONTEXT_SWITCH_DEFS__common_action_header_t));
+
+    static const bool DONT_SET_SUB_ACTION_INDEX = false;
+    uint32_t action_data_length = 0;
+    auto json = parse_action_data(base_address, &context_action_list[current_buffer_offset], current_buffer_offset, &action_data_length,
+        action_header->action_type, time_stamp_local, 0, DONT_SET_SUB_ACTION_INDEX, is_repeated, num_repeated, sub_action_type);
+    CHECK_EXPECTED(json);
+    *action_length = static_cast<uint32_t>(action_length_local + action_data_length);
+    *time_stamp = time_stamp_local;
+    return json.release();
+}
+
+Expected<ordered_json> DownloadActionListCommand::parse_context(uint32_t action_list_base_address,
+    uint8_t *context_action_list, uint16_t action_list_size, uint32_t batch_counter) 
+{
+    ordered_json context_json {
+        {"action_list_base_address", action_list_base_address},
+        {"action_list_size", action_list_size },
+        {"batch_counter", batch_counter}
+    };
+
+    ordered_json action_list_json;
+    uint16_t current_buffer_offset = 0;
+    while (current_buffer_offset < action_list_size) {
+        bool is_repeated = false;
+        uint8_t num_repeated = 0;
+        CONTEXT_SWITCH_DEFS__ACTION_TYPE_t sub_action_type = CONTEXT_SWITCH_DEFS__ACTION_TYPE_COUNT;
+        uint32_t single_action_length = 0;
+        uint32_t timestamp = 0;
+        auto action_json = parse_single_action(action_list_base_address, context_action_list,
+            current_buffer_offset, &single_action_length, &is_repeated, &num_repeated, &sub_action_type, &timestamp);
+        CHECK_EXPECTED(action_json);
+        current_buffer_offset = (uint16_t)(current_buffer_offset + single_action_length);
+        action_list_json.emplace_back(action_json.release());
+
+        if (is_repeated) {
+            for (uint8_t index_in_repeated_block = 0; index_in_repeated_block < num_repeated; index_in_repeated_block++) {
+                uint32_t sub_action_length = 0;
+                auto repeated_action_json = parse_single_repeated_action(action_list_base_address,
+                    context_action_list + current_buffer_offset, current_buffer_offset, &sub_action_length,
+                    sub_action_type, timestamp, index_in_repeated_block);
+                CHECK_EXPECTED(repeated_action_json);
+                current_buffer_offset = (uint16_t)(current_buffer_offset + sub_action_length);
+                action_list_json.emplace_back(repeated_action_json.release());
+            }
+        }
+    }
+    CHECK_AS_EXPECTED(current_buffer_offset == action_list_size, HAILO_INTERNAL_FAILURE,
+        "PARSING ERROR ! Reached forbidden memory space");
+
+    context_json["actions"] = action_list_json;
+
+    return context_json;
+}
+
+double DownloadActionListCommand::get_accumulator_mean_value(const AccumulatorPtr &accumulator, double default_value)
+{
+    auto mean_value = accumulator->mean();
+    return mean_value ? mean_value.value() : default_value;
+}
+
+Expected<ordered_json> DownloadActionListCommand::parse_network_groups(Device &device, const ConfiguredNetworkGroupVector &network_groups)
+{
+    const auto number_of_contexts_per_network_group = device.get_number_of_contexts_per_network_group();
+    CHECK_EXPECTED(number_of_contexts_per_network_group);
+
+    ordered_json network_group_list_json;
+    uint8_t global_context_index = 0;
+    for (uint32_t network_group_index = 0; network_group_index < number_of_contexts_per_network_group->size(); network_group_index++) {
+        // TODO: network_group_name via Hef::get_network_groups_names (HRT-5997)
+        ordered_json network_group_json = {
+            {"mean_activation_time_ms", INVALID_NUMERIC_VALUE},
+            {"mean_deactivation_time_ms", INVALID_NUMERIC_VALUE},
+            {"contexts", json::array()}
+        };
+        // We assume the the order of the network_groups in the ConfiguredNetworkGroupVector and in the action_list
+        // downloaded from the fw is the same. If the received ConfiguredNetworkGroupVector is empty, we leave the 
+        // mean_de/activation_time_ms with their default values (INVALID_NUMERIC_VALUE).
+        if (network_groups.size() > network_group_index) {
+            network_group_json["mean_activation_time_ms"] = get_accumulator_mean_value(
+                network_groups[network_group_index]->get_activation_time_accumulator());
+            network_group_json["mean_deactivation_time_ms"] = get_accumulator_mean_value(
+                network_groups[network_group_index]->get_deactivation_time_accumulator());
+        }
+
+        const auto num_contexts_in_network_group = number_of_contexts_per_network_group.value()[network_group_index];
+        for (uint8_t i = 0; i < num_contexts_in_network_group; i++) {
+            uint32_t batch_counter = 0;
+            uint32_t base_address = 0;
+            auto action_list = device.download_context_action_list(global_context_index, &base_address, &batch_counter);
+            CHECK_EXPECTED(action_list);
+            // Needs to fit in 2 bytes due to firmware limitation of action list size
+            CHECK_AS_EXPECTED(IS_FIT_IN_UINT16(action_list->size()), HAILO_INTERNAL_FAILURE,
+                "Action list size is expected to fit in 2B. actual size is {}", action_list->size());
+
+            auto context_json = parse_context(base_address, action_list->data(),
+                static_cast<uint16_t>(action_list->size()), batch_counter);
+            CHECK_EXPECTED(context_json);
+
+            network_group_json["contexts"].emplace_back(context_json.release());
+            
+            // Device::get_number_of_contexts_per_network_group guarantees no overflow
+            global_context_index++;
+        }
+        network_group_list_json.emplace_back(network_group_json);
+    }
+
+    return network_group_list_json;
+}
+
+void to_json(json& j, const CONTEXT_SWITCH_DEFS__read_vdma_action_data_t& data) {
+    j = json{{"descriptors_count", data.descriptors_count}, {"channel_index", data.cfg_channel_number}};
+}
+
+void to_json(json& j, const CONTEXT_SWITCH_DEFS__fetch_ccw_bursts_action_data_t& data) {
+    j = json{{"channel_index", data.cfg_channel_number}};
+}
+
+void to_json(json& j, const CONTEXT_SWITCH_DEFS__enable_lcu_action_non_default_data_t& data) {
+    const auto cluster_index = CONTEXT_SWITCH_DEFS__PACKED_LCU_ID_CLUSTER_INDEX_READ(data.packed_lcu_id);
+    const auto lcu_index = CONTEXT_SWITCH_DEFS__PACKED_LCU_ID_LCU_INDEX_READ(data.packed_lcu_id);
+    j = json{{"cluster_index", cluster_index}, {"lcu_index", lcu_index}};
+}
+
+void to_json(json& j, const CONTEXT_SWITCH_DEFS__enable_lcu_action_default_data_t& data) {
+    const auto cluster_index = CONTEXT_SWITCH_DEFS__PACKED_LCU_ID_CLUSTER_INDEX_READ(data.packed_lcu_id);
+    const auto lcu_index = CONTEXT_SWITCH_DEFS__PACKED_LCU_ID_LCU_INDEX_READ(data.packed_lcu_id);
+    j = json{{"cluster_index", cluster_index}, {"lcu_index", lcu_index}};
+}
+
+void to_json(json& j, const CONTEXT_SWITCH_DEFS__disable_lcu_action_data_t& data) {
+    const auto cluster_index = CONTEXT_SWITCH_DEFS__PACKED_LCU_ID_CLUSTER_INDEX_READ(data.packed_lcu_id);
+    const auto lcu_index = CONTEXT_SWITCH_DEFS__PACKED_LCU_ID_LCU_INDEX_READ(data.packed_lcu_id);
+    j = json{{"cluster_index", cluster_index}, {"lcu_index", lcu_index}};
+}
+
+void to_json(json& j, const CONTEXT_SWITCH_DEFS__change_vdma_to_stream_mapping_data_t& data) {
+    j = json{{"vdma_channel_index", data.vdma_channel_index}, {"stream_index", data.stream_index},
+        {"type", data.is_dummy_stream ? "dummy" : "active"}};
+}
+
+void to_json(json& j, const CONTEXT_SWITCH_DEFS__lcu_interrupt_data_t& data) {
+    const auto cluster_index = CONTEXT_SWITCH_DEFS__PACKED_LCU_ID_CLUSTER_INDEX_READ(data.packed_lcu_id);
+    const auto lcu_index = CONTEXT_SWITCH_DEFS__PACKED_LCU_ID_LCU_INDEX_READ(data.packed_lcu_id);
+    j = json{{"cluster_index", cluster_index}, {"lcu_index", lcu_index}};
+}
diff --git a/hailort/hailortcli/download_action_list_command.hpp b/hailort/hailortcli/download_action_list_command.hpp
new file mode 100644 (file)
index 0000000..bbbc330
--- /dev/null
@@ -0,0 +1,126 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file download_action_list_command.hpp
+ * @brief Download action list command
+ **/
+
+#ifndef _HAILO_DOWNLOAD_ACTION_LIST_COMMAND_HPP_
+#define _HAILO_DOWNLOAD_ACTION_LIST_COMMAND_HPP_
+
+#include "hailortcli.hpp"
+#include "command.hpp"
+
+#include "context_switch_defs.h"
+
+#include <nlohmann/json.hpp>
+
+using json = nlohmann::json;
+using ordered_json = nlohmann::ordered_json;
+
+class DownloadActionListCommand : public DeviceCommand
+{
+public:
+    explicit DownloadActionListCommand(CLI::App &parent_app);
+    // To be used from external commands
+    static hailo_status execute(Device &device, const std::string &output_file_path,
+        const ConfiguredNetworkGroupVector &network_groups={}, const std::string &hef_file_path="");
+
+protected:
+    virtual hailo_status execute_on_device(Device &device) override;
+
+private:
+    std::string m_output_file_path;
+    static constexpr int DEFAULT_JSON_TAB_WIDTH = 4;
+    static constexpr int INVALID_NUMERIC_VALUE = -1;
+    static std::string ACTION_LIST_FORMAT_VERSION() { return "1.0"; }
+
+    static Expected<ordered_json> parse_hef_metadata(const std::string &hef_file_path);
+    static bool is_valid_hef(const std::string &hef_file_path);
+    static Expected<std::string> calc_md5_hexdigest(const std::string &hef_file_path);
+    static hailo_status write_json(const ordered_json &json_obj, const std::string &output_file_path,
+        int tab_width = DEFAULT_JSON_TAB_WIDTH);
+    static Expected<ordered_json> parse_action_data(uint32_t base_address, uint8_t *action,
+        uint32_t current_buffer_offset, uint32_t *action_length, CONTEXT_SWITCH_DEFS__ACTION_TYPE_t action_type,
+        uint32_t timestamp, uint8_t sub_action_index = 0, bool sub_action_index_set = false,
+        bool *is_repeated = nullptr, uint8_t *num_repeated = nullptr,
+        CONTEXT_SWITCH_DEFS__ACTION_TYPE_t *sub_action_type = nullptr);
+    static Expected<ordered_json> parse_single_repeated_action(uint32_t base_address, uint8_t *action,
+        uint32_t current_buffer_offset, uint32_t *action_length, CONTEXT_SWITCH_DEFS__ACTION_TYPE_t action_type,
+        uint32_t timestamp, uint8_t index_in_repeated_block);
+    static Expected<ordered_json> parse_single_action(uint32_t base_address, uint8_t *context_action_list,
+        uint32_t current_buffer_offset, uint32_t *action_length, bool *is_repeated, uint8_t *num_repeated,
+        CONTEXT_SWITCH_DEFS__ACTION_TYPE_t *sub_action_type, uint32_t *time_stamp);
+    static Expected<ordered_json> parse_context(uint32_t action_list_base_address, uint8_t *context_action_list,
+        uint16_t action_list_size, uint32_t batch_counter);
+    static double get_accumulator_mean_value(const AccumulatorPtr &accumulator, double default_value = INVALID_NUMERIC_VALUE);
+    static Expected<ordered_json> parse_network_groups(Device &device, const ConfiguredNetworkGroupVector &network_groups);
+};
+
+// JSON serialization
+
+NLOHMANN_JSON_SERIALIZE_ENUM(CONTEXT_SWITCH_DEFS__ACTION_TYPE_t, {
+    {CONTEXT_SWITCH_DEFS__ACTION_TYPE_FETCH_VDMA_DESCRIPTORS, "fetch_vdma_descriptors"},
+    {CONTEXT_SWITCH_DEFS__ACTION_TYPE_TRIGGER_SEQUENCER, "trigger_sequencer"},
+    {CONTEXT_SWITCH_DEFS__ACTION_TYPE_FETCH_DATA_FROM_VDMA_CHANNEL, "fetch_data_from_vdma_channel"},
+    {CONTEXT_SWITCH_DEFS__ACTION_TYPE_ENABLE_LCU_DEFAULT, "enable_lcu_default"},
+    {CONTEXT_SWITCH_DEFS__ACTION_TYPE_ENABLE_LCU_NON_DEFAULT, "enable_lcu_non_default"},
+    {CONTEXT_SWITCH_DEFS__ACTION_TYPE_DISABLE_LCU, "disable_lcu"},
+    {CONTEXT_SWITCH_DEFS__ACTION_TYPE_ACTIVATE_BOUNDARY_INPUT, "activate_boundary_input"},
+    {CONTEXT_SWITCH_DEFS__ACTION_TYPE_ACTIVATE_BOUNDARY_OUTPUT, "activate_boundary_output"},
+    {CONTEXT_SWITCH_DEFS__ACTION_TYPE_ACTIVATE_INTER_CONTEXT_INPUT, "activate_inter_context_input"},
+    {CONTEXT_SWITCH_DEFS__ACTION_TYPE_ACTIVATE_INTER_CONTEXT_OUTPUT, "activate_inter_context_output"},
+    {CONTEXT_SWITCH_DEFS__ACTION_TYPE_ACTIVATE_DDR_BUFFER_INPUT, "activate_ddr_buffer_input"},
+    {CONTEXT_SWITCH_DEFS__ACTION_TYPE_ACTIVATE_DDR_BUFFER_OUTPUT, "activate_ddr_buffer_output"},
+    {CONTEXT_SWITCH_DEFS__ACTION_TYPE_DEACTIVATE_VDMA_CHANNEL, "deactivate_vdma_channel"},
+    {CONTEXT_SWITCH_DEFS__ACTION_TYPE_CHANGE_VDMA_TO_STREAM_MAPPING, "change_vdma_to_stream_mapping"},
+    {CONTEXT_SWITCH_DEFS__ACTION_TYPE_ADD_DDR_PAIR_INFO, "add_ddr_pair_info"},
+    {CONTEXT_SWITCH_DEFS__ACTION_TYPE_DDR_BUFFERING_START, "ddr_buffering_start"},
+    {CONTEXT_SWITCH_DEFS__ACTION_TYPE_LCU_INTERRUPT, "lcu_interrupt"},
+    {CONTEXT_SWITCH_DEFS__ACTION_TYPE_SEQUENCER_DONE_INTERRUPT, "sequencer_done_interrupt"},
+    {CONTEXT_SWITCH_DEFS__ACTION_TYPE_INPUT_CHANNEL_TRANSFER_DONE_INTERRUPT, "input_channel_transfer_done_interrupt"},
+    {CONTEXT_SWITCH_DEFS__ACTION_TYPE_OUTPUT_CHANNEL_TRANSFER_DONE_INTERRUPT, "output_channel_transfer_done_interrupt"},
+    {CONTEXT_SWITCH_DEFS__ACTION_TYPE_MODULE_CONFIG_DONE_INTERRUPT, "module_config_done_interrupt"},
+    {CONTEXT_SWITCH_DEFS__ACTION_TYPE_APPLICATION_CHANGE_INTERRUPT, "application_change_interrupt"},
+    {CONTEXT_SWITCH_DEFS__ACTION_TYPE_ACTIVATE_CFG_CHANNEL, "activate_cfg_channel"},
+    {CONTEXT_SWITCH_DEFS__ACTION_TYPE_DEACTIVATE_CFG_CHANNEL, "deactivate_cfg_channel"},
+    {CONTEXT_SWITCH_DEFS__ACTION_TYPE_REPEATED_ACTION, "repeated_action"},
+    {CONTEXT_SWITCH_DEFS__ACTION_TYPE_WAIT_FOR_DMA_IDLE_ACTION, "wait_for_dma_idle_action"},
+    {CONTEXT_SWITCH_DEFS__ACTION_TYPE_WAIT_FOR_NMS_IDLE, "wait_for_nms_idle"},
+    {CONTEXT_SWITCH_DEFS__ACTION_TYPE_FETCH_CCW_BURSTS, "fetch_ccw_bursts"},
+    {CONTEXT_SWITCH_DEFS__ACTION_TYPE_COUNT, nullptr},
+});
+
+// Default implementions
+NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(CONTEXT_SWITCH_DEFS__repeated_action_header_t, count, last_executed, sub_action_type);
+NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(CONTEXT_SWITCH_DEFS__trigger_sequencer_action_data_t, cluster_index);
+NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(CONTEXT_SWITCH_DEFS__deactivate_vdma_channel_action_data_t, vdma_channel_index);
+NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(CONTEXT_SWITCH_DEFS__fetch_data_action_data_t, vdma_channel_index, stream_index);
+NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(CONTEXT_SWITCH_DEFS__add_ddr_pair_info_action_data_t, h2d_vdma_channel_index, d2h_vdma_channel_index);
+NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(CONTEXT_SWITCH_DEFS__vdma_dataflow_interrupt_data_t, vdma_channel_index);
+NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(CONTEXT_SWITCH_DEFS__sequencer_interrupt_data_t, sequencer_index);
+NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(CONTEXT_SWITCH_DEFS__wait_nms_idle_data_t, aggregator_index);
+NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(CONTEXT_SWITCH_DEFS__wait_dma_idle_data_t, vdma_channel_index, stream_index);
+NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(CONTEXT_SWITCH_DEFS__module_config_done_interrupt_data_t, module_index);
+NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(CONTEXT_SWITCH_DEFS__application_change_interrupt_data_t, application_index);
+NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(CONTEXT_SWITCH_DEFS__activate_boundary_input_data_t, vdma_channel_index, stream_index);
+NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(CONTEXT_SWITCH_DEFS__activate_inter_context_input_data_t, vdma_channel_index, stream_index);
+NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(CONTEXT_SWITCH_DEFS__activate_ddr_buffer_input_data_t, vdma_channel_index, stream_index);
+NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(CONTEXT_SWITCH_DEFS__activate_boundary_output_data_t, vdma_channel_index, stream_index);
+NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(CONTEXT_SWITCH_DEFS__activate_inter_context_output_data_t, vdma_channel_index, stream_index);
+NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(CONTEXT_SWITCH_DEFS__activate_ddr_buffer_output_data_t, vdma_channel_index, stream_index);
+NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(CONTEXT_SWITCH_DEFS__activate_cfg_channel_t, channel_index);
+NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(CONTEXT_SWITCH_DEFS__deactivate_cfg_channel_t, channel_index);
+
+// Non-default implementations
+void to_json(json& j, const CONTEXT_SWITCH_DEFS__enable_lcu_action_default_data_t& data);
+void to_json(json& j, const CONTEXT_SWITCH_DEFS__enable_lcu_action_non_default_data_t& data);
+void to_json(json& j, const CONTEXT_SWITCH_DEFS__disable_lcu_action_data_t& data);
+void to_json(json& j, const CONTEXT_SWITCH_DEFS__read_vdma_action_data_t& data);
+void to_json(json& j, const CONTEXT_SWITCH_DEFS__fetch_ccw_bursts_action_data_t& data);
+void to_json(json& j, const CONTEXT_SWITCH_DEFS__change_vdma_to_stream_mapping_data_t& data);
+void to_json(json& j, const CONTEXT_SWITCH_DEFS__lcu_interrupt_data_t& data);
+
+#endif /* _HAILO_DOWNLOAD_ACTION_LIST_COMMAND_HPP_ */
diff --git a/hailort/hailortcli/fw_config_command.cpp b/hailort/hailortcli/fw_config_command.cpp
new file mode 100644 (file)
index 0000000..f30f111
--- /dev/null
@@ -0,0 +1,96 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file fw_config_command.cpp
+ * @brief User Firmware configuration (persistent config) command.
+ **/
+
+#include "fw_config_command.hpp"
+
+FwConfigReadSubcommand::FwConfigReadSubcommand(CLI::App &parent_app) :
+    DeviceCommand(parent_app.add_subcommand("read", "Read firmware configuration from device"))
+{
+    m_app->add_option("--output-file", m_output_file, "File path to write user firmware configuration into.\n"
+        "If not given the data will be printed to stdout.");
+}
+
+hailo_status FwConfigReadSubcommand::execute_on_device(Device &device)
+{
+    auto user_config_buffer = device.read_user_config();
+    CHECK_EXPECTED_AS_STATUS(user_config_buffer, "Failed reading user config from device");
+
+    auto status = FwConfigJsonSerializer::deserialize_config(
+        *reinterpret_cast<USER_CONFIG_header_t*>(user_config_buffer->data()),
+        user_config_buffer->size(), m_output_file);
+    CHECK_SUCCESS(status);
+    
+    return HAILO_SUCCESS;   
+}
+
+FwConfigWriteSubcommand::FwConfigWriteSubcommand(CLI::App &parent_app) :
+    DeviceCommand(parent_app.add_subcommand("write", "Write firmware configuration to device"))
+{
+    m_app->add_option("input_file", m_input_file, "User firmware configuration file path.")
+        ->check(CLI::ExistingFile)
+        ->required();
+}
+
+hailo_status FwConfigWriteSubcommand::execute_on_device(Device &device)
+{
+    auto config_buffer = Buffer::create(FLASH_USER_CONFIG_SECTION_SIZE);
+    CHECK_EXPECTED_AS_STATUS(config_buffer);
+
+    auto config_size = FwConfigJsonSerializer::serialize_config(
+        *reinterpret_cast<USER_CONFIG_header_t*>(config_buffer->data()), config_buffer->size(), m_input_file);
+    CHECK_EXPECTED_AS_STATUS(config_size);
+    
+    // We only need to write config_size.value() bytes from config_buffer, so we "resize" the buffer
+    CHECK(config_buffer->size() >= config_size.value(), HAILO_INTERNAL_FAILURE,
+        "Unexpected config size {} (max_size={})", config_size.value(), config_buffer->size());
+    auto resized_config_buffer = Buffer::create(config_buffer->data(), config_size.value());
+    CHECK_EXPECTED_AS_STATUS(resized_config_buffer);
+
+    hailo_status status = device.write_user_config(MemoryView(resized_config_buffer.value()));
+    CHECK_SUCCESS(status, "Failed writing user firmware configuration to device");
+
+    return HAILO_SUCCESS;
+}
+
+FwConfigSerializeSubcommand::FwConfigSerializeSubcommand(CLI::App &parent_app) :
+    Command(parent_app.add_subcommand("serialize", "Serialize firmware configuration json to a binary file"))
+{
+    m_app->add_option("input_file", m_input_file, "File path to firmware configuration json")
+        ->check(CLI::ExistingFile)
+        ->required();
+    m_app->add_option("output_file", m_output_file, "File path to write binary firmware configuration into")
+        ->required();
+}
+
+hailo_status FwConfigSerializeSubcommand::execute()
+{
+    auto config_buffer = Buffer::create(FLASH_USER_CONFIG_SECTION_SIZE);
+    CHECK_EXPECTED_AS_STATUS(config_buffer);
+
+    USER_CONFIG_header_t *config_header = reinterpret_cast<USER_CONFIG_header_t*>(config_buffer->data());
+    auto config_size = FwConfigJsonSerializer::serialize_config(*config_header, config_buffer->size(), m_input_file);
+    CHECK_EXPECTED_AS_STATUS(config_size);
+
+    std::ofstream ofs(m_output_file, std::ios::out | std::ios::binary);
+    CHECK(ofs.good(), HAILO_OPEN_FILE_FAILURE, "Failed opening file: {}, with errno: {}", m_output_file, errno);
+
+    ofs.write(reinterpret_cast<char*>(config_header), config_size.value());
+    CHECK(ofs.good(), HAILO_FILE_OPERATION_FAILURE,
+        "Failed writing binary firmware configuration to file: {}, with errno: {}", m_output_file, errno);
+
+    return HAILO_SUCCESS;
+}
+
+FwConfigCommand::FwConfigCommand(CLI::App &parent_app) :
+    ContainerCommand(parent_app.add_subcommand("fw-config", "User firmware configuration tool"))
+{
+    add_subcommand<FwConfigReadSubcommand>();
+    add_subcommand<FwConfigWriteSubcommand>();
+    add_subcommand<FwConfigSerializeSubcommand>();
+}
diff --git a/hailort/hailortcli/fw_config_command.hpp b/hailort/hailortcli/fw_config_command.hpp
new file mode 100644 (file)
index 0000000..23abeb1
--- /dev/null
@@ -0,0 +1,59 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file fw_config_command.hpp
+ * @brief User firmware configuration command.
+ **/
+
+#ifndef _HAILO_FW_CONFIG_COMMAND_HPP_
+#define _HAILO_FW_CONFIG_COMMAND_HPP_
+
+#include "hailortcli.hpp"
+#include "command.hpp"
+
+#include "fw_config_serializer.hpp"
+#include "hailo/device.hpp"
+#include "CLI/CLI.hpp"
+
+#define FLASH_USER_CONFIG_SECTION_SIZE (0x008000)
+
+class FwConfigReadSubcommand final : public DeviceCommand {
+public:
+    explicit FwConfigReadSubcommand(CLI::App &parent_app);
+
+protected:
+    virtual hailo_status execute_on_device(Device &device) override;
+
+private:
+    std::string m_output_file;
+};
+
+class FwConfigWriteSubcommand final : public DeviceCommand {
+public:
+    explicit FwConfigWriteSubcommand(CLI::App &parent_app);
+
+protected:
+    virtual hailo_status execute_on_device(Device &device) override;
+
+private:
+    std::string m_input_file;
+};
+
+class FwConfigSerializeSubcommand final : public Command {
+public:
+    explicit FwConfigSerializeSubcommand(CLI::App &parent_app);
+    hailo_status execute() override;
+
+private:
+    std::string m_input_file;
+    std::string m_output_file;
+};
+
+class FwConfigCommand final : public ContainerCommand {
+public:
+    explicit FwConfigCommand(CLI::App &parent_app);
+};
+
+#endif /* _HAILO_FW_CONFIG_COMMAND_HPP_ */
diff --git a/hailort/hailortcli/fw_config_serializer.cpp b/hailort/hailortcli/fw_config_serializer.cpp
new file mode 100644 (file)
index 0000000..81230e9
--- /dev/null
@@ -0,0 +1,904 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file fw_config_serializer.cpp
+ * @brief User firmware configuration serializer.
+ **/
+
+#include "fw_config_serializer.hpp"
+#include "definitions_json.auto.hpp"
+#include "hailo/hailort.h"
+#include "user_config_common.h"
+#include "common/file_utils.hpp"
+
+#include <fstream>
+#include <sstream>
+#include <iomanip>
+
+Expected<ordered_json> FwConfigJsonSerializer::read_json_file(const std::string &file_path)
+{
+    std::ifstream ifs(file_path);
+    CHECK_AS_EXPECTED(ifs.good(), HAILO_OPEN_FILE_FAILURE, "Failed opening json file: {} with errno: {}", file_path, errno);
+
+    ordered_json j;
+    ifs >> j;
+    CHECK_AS_EXPECTED(ifs.good(), HAILO_FILE_OPERATION_FAILURE, "Failed reading json file {}", file_path);
+    
+    return j;
+}
+
+Expected<std::map<std::string, std::map<std::string, ordered_json>>> FwConfigJsonSerializer::get_serialize_map()
+{
+    ordered_json json_definitions = ordered_json::parse(g_fw_config_str_definitions);
+    std::map<std::string, std::map<std::string, ordered_json>> definitions;
+
+    auto version = json_definitions.find("version");
+    CHECK_AS_EXPECTED(version != json_definitions.end(), HAILO_INTERNAL_FAILURE,
+        "Failed to find version in definitions file");
+    definitions["version"]["value"] = version.value();
+
+    auto categories = json_definitions.find("categories");
+    CHECK_AS_EXPECTED(categories != json_definitions.end(), HAILO_INTERNAL_FAILURE,
+        "Failed to find categories in definitions file");
+
+    uint32_t category_id = 0;
+    for (auto &category : categories->items()) {
+        auto entries = category.value().find("entries");
+        CHECK_AS_EXPECTED(entries != category.value().end(), HAILO_INTERNAL_FAILURE,
+            "Failed to find entries of category {} in definition file", category.key());
+
+        std::map<std::string, ordered_json> entries_map;
+        uint32_t entry_id = 0;
+        for (auto &entry : entries.value().items()) {
+            entry.value()["category_id"] = category_id;
+            entry.value()["entry_id"] = entry_id;
+            entries_map[entry.key()] = entry.value();
+            entry_id++;
+        }
+        definitions[category.key()] = entries_map;
+        category_id++;
+    }
+    return definitions;
+}
+
+Expected<std::vector<std::vector<ordered_json>>> FwConfigJsonSerializer::get_deserialize_vector()
+{
+    ordered_json json_definitions = ordered_json::parse(g_fw_config_str_definitions);
+
+    auto categories = json_definitions.find("categories");
+    CHECK_AS_EXPECTED(categories != json_definitions.end(), HAILO_INTERNAL_FAILURE,
+        "Failed to find categories in definitions file");
+
+    std::vector<std::vector<ordered_json>> categories_vector;
+    for (auto &category : categories.value().items()) {
+        auto entries = category.value().find("entries");
+        CHECK_AS_EXPECTED(entries != category.value().end(), HAILO_INTERNAL_FAILURE,
+            "Failed to find entries of category {} in definitions file", category.key());
+
+        std::vector<ordered_json> entries_vector;
+        for (auto &entry : entries.value().items()) {
+            entry.value()["category_name"] = category.key();
+            entry.value()["entry_name"] = entry.key();
+            entries_vector.emplace_back(entry.value());
+        }
+        categories_vector.emplace_back(entries_vector);
+    }  
+    return categories_vector;
+}
+
+hailo_status FwConfigJsonSerializer::dump_config(const ordered_json &config_json, const std::string &file_path)
+{
+    if (file_path.empty()) {
+        std::cout << config_json.dump(JSON_PRINT_INDENTATION) << std::endl; 
+        CHECK(std::cout.good(), HAILO_FILE_OPERATION_FAILURE, "Failed writing config to stdout");
+    }
+    else {
+        std::ofstream ofs(file_path);
+        CHECK(ofs.good(), HAILO_OPEN_FILE_FAILURE, "Failed opening output file: {} with errno: {}", file_path, errno);
+        
+        ofs << config_json.dump(JSON_PRINT_INDENTATION) << std::endl;
+        CHECK(ofs.good(), HAILO_FILE_OPERATION_FAILURE, "Failed writing firmware configuration into file: {} with errno: {}", file_path, errno);
+    }
+
+    return HAILO_SUCCESS;
+}
+
+/* Deserialization */
+hailo_status FwConfigJsonSerializer::deserialize_config(const USER_CONFIG_header_t &user_config_header, size_t config_size, const std::string &file_path)
+{
+    try {
+        auto categories = get_deserialize_vector();
+        CHECK_EXPECTED_AS_STATUS(categories); 
+
+        ordered_json config_json;
+        size_t current_deserialized_data_size = 0;
+        uintptr_t current_entry_offset = (uintptr_t)(&(user_config_header.entries));
+        for (size_t i = 0; i < user_config_header.entry_count; i++) {
+            USER_CONFIG_ENTRY_t *config_entry = reinterpret_cast<USER_CONFIG_ENTRY_t*>(current_entry_offset);
+            CHECK(config_entry->category < categories->size(), HAILO_INTERNAL_FAILURE,
+                "Category id is out of bounds. Category id = {}, Max category id = {}", config_entry->category, (categories->size()-1));
+            
+            auto category = categories.value()[config_entry->category];
+            CHECK(config_entry->entry_id < category.size(), HAILO_INTERNAL_FAILURE,
+                "Entry id is out of bounds. Entry id = {}, Max entry id = {}", config_entry->entry_id, (category.size() - 1));
+
+            current_deserialized_data_size += sizeof(USER_CONFIG_ENTRY_t) + config_entry->entry_size;
+            CHECK((current_deserialized_data_size <= config_size), HAILO_INVALID_OPERATION,
+                "Overrun configuration size. Deserialized data size = {}, configuration data size is: {}", current_deserialized_data_size, config_size);
+
+            hailo_status status = FwConfigJsonSerializer::deserialize_entry(config_json,
+                category[config_entry->entry_id], reinterpret_cast<uint8_t*>(&(config_entry->value)));
+            CHECK_SUCCESS(status);
+            
+            current_entry_offset += sizeof(USER_CONFIG_ENTRY_t) + config_entry->entry_size;
+        }
+
+        hailo_status status = dump_config(config_json, file_path);
+        CHECK_SUCCESS(status, "Failed writing firmware configuration");
+    }
+    catch (json::exception &e) {
+        LOGGER__ERROR("Exception caught: {}.\n Please check json definition file format", e.what());
+        return HAILO_INTERNAL_FAILURE;
+    }
+    return HAILO_SUCCESS;
+}
+
+hailo_status FwConfigJsonSerializer::deserialize_entry(ordered_json &config_json, const ordered_json &entry_definition, uint8_t *entry_value)
+{
+    std::string category_name = entry_definition["category_name"].get<std::string>();
+    std::string entry_name = entry_definition["entry_name"].get<std::string>();
+    std::string deserialize_as = entry_definition["deserialize_as"].get<std::string>();
+    uint32_t size = entry_definition.contains("length") ?
+        (entry_definition["length"].get<uint32_t>() * entry_definition["size"].get<uint32_t>()) :
+        entry_definition["size"].get<uint32_t>();
+
+    if (deserialize_as == "str") {
+        auto str_val = deserialize_str(entry_value, size);
+        CHECK_EXPECTED_AS_STATUS(str_val);
+        config_json[category_name][entry_name] = str_val.value();
+    }
+    else if (deserialize_as == "bool") {
+        auto bool_val = deserialize_bool(entry_value, size);
+        CHECK_EXPECTED_AS_STATUS(bool_val);
+        config_json[category_name][entry_name] = bool_val.value();
+    }
+    else if (deserialize_as == "int") {
+        auto int_val = deserialize_int(entry_value, size);
+        CHECK_EXPECTED_AS_STATUS(int_val);
+        config_json[category_name][entry_name] = int_val.value();
+    }
+    else if (deserialize_as == "i2c_speed") {
+        auto i2c_speed_val = deserialize_i2c_speed(entry_value, size);
+        CHECK_EXPECTED_AS_STATUS(i2c_speed_val);
+        config_json[category_name][entry_name]["value"] = i2c_speed_val.value();
+    }
+    else if (deserialize_as == "supported_aspm_states") {
+        auto supported_aspm_states_val = deserialize_supported_aspm_states(entry_value, size);
+        CHECK_EXPECTED_AS_STATUS(supported_aspm_states_val);
+        config_json[category_name][entry_name]["value"] = supported_aspm_states_val.value();
+    }
+    else if (deserialize_as == "supported_aspm_l1_substates") {
+        auto supported_aspm_l1_substates_val = deserialize_supported_aspm_l1_substates(entry_value, size);
+        CHECK_EXPECTED_AS_STATUS(supported_aspm_l1_substates_val);
+        config_json[category_name][entry_name]["value"] = supported_aspm_l1_substates_val.value();
+    }
+    else if (deserialize_as == "ipv4") {
+        auto ipv4_val = deserialize_ipv4(entry_value, size);
+        CHECK_EXPECTED_AS_STATUS(ipv4_val);
+        config_json[category_name][entry_name]["value"] = ipv4_val.value();
+    }
+    else if (deserialize_as == "mac_address") {
+        auto mac_address_val = deserialize_mac_address(entry_value, size);
+        CHECK_EXPECTED_AS_STATUS(mac_address_val);
+        config_json[category_name][entry_name]["value"] = mac_address_val.value();
+    }
+    else if (deserialize_as == "clock_frequency") {
+        auto clock_frequency_val = deserialize_clock_frequency(entry_value, size);
+        CHECK_EXPECTED_AS_STATUS(clock_frequency_val);
+        config_json[category_name][entry_name]["value"] = clock_frequency_val.value();
+    }
+    else if (deserialize_as == "logger_level") {
+        auto logger_level = deserialize_logger_level(entry_value, size);
+        CHECK_EXPECTED_AS_STATUS(logger_level);
+        config_json[category_name][entry_name]["value"] = logger_level.value();
+    }
+    else if (deserialize_as == "watchdog_mode") {
+        auto watchdog_mode_val = deserialize_watchdog_mode(entry_value, size);
+        CHECK_EXPECTED_AS_STATUS(watchdog_mode_val);
+        config_json[category_name][entry_name]["value"] = watchdog_mode_val.value();
+    }
+    else if (deserialize_as == "overcurrent_parameters_source") {
+        auto overcurrent_parameters_source_val = deserialize_overcurrent_parameters_source(entry_value, size);
+        CHECK_EXPECTED_AS_STATUS(overcurrent_parameters_source_val);
+        config_json[category_name][entry_name]["value"] = overcurrent_parameters_source_val.value();
+    }
+    else if (deserialize_as == "temperature_parameters_source") {
+        auto temperature_parameters_source_val = deserialize_temperature_parameters_source(entry_value, size);
+        CHECK_EXPECTED_AS_STATUS(temperature_parameters_source_val);
+        config_json[category_name][entry_name]["value"] = temperature_parameters_source_val.value();
+    }
+    else if (deserialize_as == "conversion_time") {
+        auto conversion_time_val = deserialize_conversion_time(entry_value, size);
+        CHECK_EXPECTED_AS_STATUS(conversion_time_val);
+        config_json[category_name][entry_name]["value"] = conversion_time_val.value();
+    }
+    else {
+        LOGGER__ERROR("Failed deserializing entry. Serialization format {} not found", deserialize_as);
+        return HAILO_NOT_FOUND;
+    }
+
+    return HAILO_SUCCESS;
+}
+
+Expected<json> FwConfigJsonSerializer::deserialize_str(uint8_t *entry_value, uint32_t size)
+{
+    return json(std::string((char*)entry_value, strnlen((char*)entry_value, size)));
+}
+
+Expected<json> FwConfigJsonSerializer::deserialize_bool(uint8_t *entry_value, uint32_t size)
+{
+    auto bool_val = get_int_value<uint8_t>(entry_value, size);
+    CHECK_EXPECTED(bool_val);
+    json bool_str = bool_val.value() ? true : false;
+    return bool_str;
+}
+
+Expected<json> FwConfigJsonSerializer::deserialize_ipv4(uint8_t *entry_value, uint32_t size)
+{
+    CHECK_AS_EXPECTED((IPV4_ADDRESS_LENGTH == size), HAILO_INTERNAL_FAILURE,
+        "IPv4 address length is invalid. Length recieved is: {}, length expected: {}", size, IPV4_ADDRESS_LENGTH);
+
+    std::stringstream ss;
+    for (size_t i = 0; i < IPV4_ADDRESS_LENGTH; i++) {
+        if (i != 0) {
+            ss << '.';
+        }
+        //TODO: HRT-3045 - Support big-endian.
+        ss << (int)(entry_value[IPV4_ADDRESS_LENGTH-i-1]);
+    }
+    return json(ss.str());
+}
+
+Expected<json> FwConfigJsonSerializer::deserialize_mac_address(uint8_t *entry_value, uint32_t size)
+{
+    CHECK_AS_EXPECTED((MAC_ADDRESS_LENGTH == size), HAILO_INTERNAL_FAILURE,
+        "Mac address length is invalid. Length recieved is: {}, length expected: {}", size, MAC_ADDRESS_LENGTH);
+    
+    std::stringstream ss;
+    for (size_t i = 0; i < MAC_ADDRESS_LENGTH; i++) {
+        if (i != 0) {
+            ss << ':';
+        }
+        ss.width(2); 
+        ss.fill('0');
+        ss << std::uppercase << std::hex << (int)(entry_value[i]);
+    }
+    return json(ss.str());
+}
+
+Expected<json> FwConfigJsonSerializer::deserialize_supported_aspm_states(uint8_t *entry_value, uint32_t size)
+{
+    auto aspm_state = get_int_value<uint8_t>(entry_value, size);
+    CHECK_EXPECTED(aspm_state);
+
+    switch (static_cast<PCIE_CONFIG_SUPPOPRTED_ASPM_STATES_t>(aspm_state.value())) {
+    case ASPM_DISABLED:
+        return json("ASPM DISABLED");
+    case ASPM_L1_ONLY:
+        return json("ASPM L1 ONLY");
+    case ASPM_L0S_L1:
+        return json("ASPM L0S L1");
+    default:
+        LOGGER__ERROR("Failed deserializing supported aspm states.");
+        return make_unexpected(HAILO_NOT_FOUND);
+    }
+}
+
+Expected<json> FwConfigJsonSerializer::deserialize_supported_aspm_l1_substates(uint8_t *entry_value, uint32_t size)
+{
+    auto aspm_l1_substate = get_int_value<uint8_t>(entry_value, size);
+    CHECK_EXPECTED(aspm_l1_substate);
+
+    switch (static_cast<PCIE_CONFIG_SUPPOPRTED_L1_ASPM_SUBSTATES_t>(aspm_l1_substate.value())) {
+    case ASPM_L1_SUBSTATES_DISABLED:
+        return json("ASPM L1 SUBSTATES DISABLED");
+    case ASPM_L1_SUBSTATES_L11_ONLY:
+        return json("ASPM L1 SUBSTATES L1.1 ONLY");
+    case ASPM_L1_SUBSTATES_L11_L12:
+        return json("ASPM L1 SUBSTATES L1.1 L1.2");
+    default:
+        LOGGER__ERROR("Failed deserializing supported aspm l1 substates.");
+        return make_unexpected(HAILO_NOT_FOUND);
+    }
+}
+
+Expected<json> FwConfigJsonSerializer::deserialize_clock_frequency(uint8_t *entry_value, uint32_t size)
+{
+    auto clock_frequency = get_int_value<uint32_t>(entry_value, size);
+    CHECK_EXPECTED(clock_frequency);
+
+    switch (clock_frequency.value()) {
+    case SOC__NN_CLOCK_400MHz:
+        return json("400MHZ");
+    case SOC__NN_CLOCK_375MHz:
+        return json("375MHZ");
+    case SOC__NN_CLOCK_350MHz:
+        return json("350MHZ");
+    case SOC__NN_CLOCK_325MHz:
+        return json("325MHZ");
+    case SOC__NN_CLOCK_300MHz:
+        return json("300MHZ");
+    case SOC__NN_CLOCK_275MHz:
+        return json("275MHZ");
+    case SOC__NN_CLOCK_250MHz:
+        return json("250MHZ");
+    case SOC__NN_CLOCK_225MHz:
+        return json("225MHZ");
+    case SOC__NN_CLOCK_200MHz:
+        return json("200MHZ");
+    case SOC__NN_CLOCK_100MHz:
+        return json("100MHZ");
+    default:
+        LOGGER__ERROR("Failed deserializing clock_frequency.");
+        return make_unexpected(HAILO_NOT_FOUND);
+    }
+}
+
+Expected<json> FwConfigJsonSerializer::deserialize_watchdog_mode(uint8_t *entry_value, uint32_t size)
+{
+    auto watchdog_mode = get_int_value<uint8_t>(entry_value, size);
+    CHECK_EXPECTED(watchdog_mode);
+
+    switch (static_cast<WD_SERVICE_wd_mode_t>(watchdog_mode.value())) {
+    case WD_SERVICE_MODE_HW_SW:
+        return json("WD MODE HW SW");
+    case WD_SERVICE_MODE_HW_ONLY:
+        return json("WD MODE HW ONLY");
+    default:
+        LOGGER__ERROR("Failed deserializing watchdog_mode.");
+        return make_unexpected(HAILO_NOT_FOUND);
+    }
+}
+
+Expected<json> FwConfigJsonSerializer::deserialize_i2c_speed(uint8_t *entry_value, uint32_t size)
+{
+    auto i2c_speed = get_int_value<uint8_t>(entry_value, size);
+    CHECK_EXPECTED(i2c_speed);
+
+    switch (static_cast<i2c_speed_mode_t>(i2c_speed.value())) {
+    case I2C_SPEED_STANDARD:
+        return json("I2C SPEED STANDARD");
+    case I2C_SPEED_FAST:
+        return json("I2C SPEED FAST");
+    default:
+        LOGGER__ERROR("Failed deserializing i2c speed");
+        return make_unexpected(HAILO_NOT_FOUND);
+    }
+}
+
+Expected<json> FwConfigJsonSerializer::deserialize_logger_level(uint8_t *entry_value, uint32_t size)
+{
+    auto logger_level = get_int_value<uint8_t>(entry_value, size);
+    CHECK_EXPECTED(logger_level);
+
+    switch (static_cast<FW_LOGGER_LEVEL_t>(logger_level.value())) {
+    case FW_LOGGER_LEVEL_TRACE:
+        return json("TRACE");
+    case FW_LOGGER_LEVEL_DEBUG:
+        return json("DEBUG");
+    case FW_LOGGER_LEVEL_INFO:
+        return json("INFO");
+    case FW_LOGGER_LEVEL_WARN:
+        return json("WARNING");
+    case FW_LOGGER_LEVEL_ERROR:
+        return json("ERROR");
+    case FW_LOGGER_LEVEL_FATAL:
+        return json("FATAL");
+    default:
+        LOGGER__ERROR("Failed deserializing logger_level");
+        return make_unexpected(HAILO_NOT_FOUND);
+    }
+}
+
+Expected<json> FwConfigJsonSerializer::deserialize_overcurrent_parameters_source(uint8_t *entry_value, uint32_t size)
+{
+    auto overcurrent_parameters_source = get_int_value<uint8_t>(entry_value, size);
+    CHECK_EXPECTED(overcurrent_parameters_source);
+
+    switch (static_cast<OVERCURRENT_parameters_source_t>(overcurrent_parameters_source.value())) {
+    case OVERCURRENT_PARAMETERS_SOURCE_FW_VALUES:
+        return json("FW VALUES");
+    case OVERCURRENT_PARAMETERS_SOURCE_USER_CONFIG_VALUES:
+        return json("USER CONFIG VALUES");
+    case OVERCURRENT_PARAMETERS_SOURCE_BOARD_CONFIG_VALUES:
+        return json("BOARD CONFIG VALUES");
+    case OVERCURRENT_PARAMETERS_SOURCE_OVERCURRENT_DISABLED:
+        return json("OVERCURRENT DISABLED");
+    default:
+        LOGGER__ERROR("Failed deserializing overcurrent thresholds source");
+        return make_unexpected(HAILO_NOT_FOUND);
+    }
+}
+
+Expected<json> FwConfigJsonSerializer::deserialize_temperature_parameters_source(uint8_t *entry_value, uint32_t size)
+{
+    auto temperature_parameters_source = get_int_value<uint8_t>(entry_value, size);
+    CHECK_EXPECTED(temperature_parameters_source);
+
+    switch (static_cast<TEMPERATURE_PROTECTION_parameters_source_t>(temperature_parameters_source.value())) {
+    case TEMPERATURE_PROTECTION_PARAMETERS_SOURCE_FW_VALUES:
+        return json("FW VALUES");
+    case TEMPERATURE_PROTECTION_PARAMETERS_SOURCE_USER_CONFIG_VALUES:
+        return json("USER CONFIG VALUES");
+    default:
+        LOGGER__ERROR("Failed deserializing overcurrent thresholds source");
+        return make_unexpected(HAILO_NOT_FOUND);
+    }
+}
+
+Expected<json> FwConfigJsonSerializer::deserialize_conversion_time(uint8_t *entry_value, uint32_t size)
+{
+    auto conversion_time = get_int_value<uint32_t>(entry_value, size);
+    CHECK_EXPECTED(conversion_time);
+    auto conversion_time_value = static_cast<OVERCURRENT_conversion_time_us_t>(conversion_time.value());
+
+    if (conversion_time_value == OVERCURRENT_CONVERSION_PERIOD_140US ||
+        conversion_time_value == OVERCURRENT_CONVERSION_PERIOD_204US ||
+        conversion_time_value == OVERCURRENT_CONVERSION_PERIOD_332US ||
+        conversion_time_value == OVERCURRENT_CONVERSION_PERIOD_588US ||
+        conversion_time_value == OVERCURRENT_CONVERSION_PERIOD_1100US ||
+        conversion_time_value == OVERCURRENT_CONVERSION_PERIOD_2116US ||
+        conversion_time_value == OVERCURRENT_CONVERSION_PERIOD_4156US ||
+        conversion_time_value == OVERCURRENT_CONVERSION_PERIOD_8244US) {
+
+        return json(conversion_time_value);
+    }
+    else {
+        LOGGER__ERROR("Got unvalid valid option for conversion_time.");
+        return make_unexpected(HAILO_NOT_FOUND);
+    }
+}
+
+Expected<json> FwConfigJsonSerializer::deserialize_int(uint8_t *entry_value, uint32_t size)
+{
+    switch (size) {
+    case sizeof(uint8_t):
+    {
+        auto uint8_val = get_int_value<uint8_t>(entry_value, size);
+        CHECK_EXPECTED(uint8_val);
+        return json(uint8_val.value());
+    }
+    case sizeof(uint16_t):
+    {
+        auto uint16_val = get_int_value<uint16_t>(entry_value, size);
+        CHECK_EXPECTED(uint16_val);
+        return json(uint16_val.value());
+    }
+    case sizeof(uint32_t):
+    {
+        auto uint32_val = get_int_value<uint32_t>(entry_value, size);
+        CHECK_EXPECTED(uint32_val);
+        return json(uint32_val.value());
+    }
+    default:
+        LOGGER__ERROR("Failed deserializing int value");
+        return make_unexpected(HAILO_NOT_FOUND);
+    }
+}
+
+/* Serialization */
+Expected<uint32_t> FwConfigJsonSerializer::serialize_config(USER_CONFIG_header_t &user_config_header, size_t config_size, const std::string &file_path)
+{
+    size_t data_size = sizeof(USER_CONFIG_header_t);
+
+    try {
+        auto config_json = FwConfigJsonSerializer::read_json_file(file_path);
+        CHECK_EXPECTED(config_json);
+
+        auto definitions = FwConfigJsonSerializer::get_serialize_map();
+        CHECK_EXPECTED(definitions);
+
+        user_config_header.version = definitions.value()["version"]["value"].get<uint32_t>();
+        user_config_header.magic = USER_CONFIG_MAGIC;
+        user_config_header.entry_count = 0;
+
+        uintptr_t current_entry_offset = (uintptr_t)(&(user_config_header.entries));
+        for (auto &config_category : config_json->items()) {
+            for (auto &config_entry : config_category.value().items()) {
+                ordered_json entry_definition = definitions.value()[config_category.key()][config_entry.key()];
+                USER_CONFIG_ENTRY_t *curr_entry = (USER_CONFIG_ENTRY_t *)current_entry_offset;
+                curr_entry->entry_size = entry_definition.contains("length") ?
+                    (entry_definition["length"].get<uint32_t>() * entry_definition["size"].get<uint32_t>()) :
+                    entry_definition["size"].get<uint32_t>();
+
+                data_size += sizeof(USER_CONFIG_ENTRY_t) + curr_entry->entry_size;
+                CHECK_AS_EXPECTED((data_size <= config_size), HAILO_INVALID_OPERATION,
+                    "User config overrun! Configuration is too big, data size {}, firmware configuration size: {}",
+                    data_size, config_size);
+
+                hailo_status status = serialize_entry(*curr_entry, config_entry.value(), entry_definition);
+                CHECK_SUCCESS_AS_EXPECTED(status, "Failed serializing json config category: {}, entry: {}",
+                    config_category.key(), config_entry.key());
+                    
+                user_config_header.entry_count++;
+                current_entry_offset += sizeof(USER_CONFIG_ENTRY_t) + curr_entry->entry_size;
+            }
+        }
+    }
+    catch (json::exception &e) {
+        LOGGER__ERROR("Exception caught: {}.\n Please check json files format", e.what());
+        return make_unexpected(HAILO_INTERNAL_FAILURE);
+    }
+
+    CHECK_AS_EXPECTED(((std::numeric_limits<uint32_t>::min() <= data_size) && (data_size <= std::numeric_limits<uint32_t>::max())), 
+        HAILO_INTERNAL_FAILURE, "Firmware configuration data size is out of bounds. data_size = {}", data_size);
+    
+    return static_cast<uint32_t>(data_size);
+}
+
+hailo_status FwConfigJsonSerializer::serialize_entry(USER_CONFIG_ENTRY_t &entry, const ordered_json &config_entry, const ordered_json &entry_definition)
+{
+    ordered_json inner_config_entry = config_entry;
+    entry.category = entry_definition["category_id"].get<uint16_t>();
+    entry.entry_id = entry_definition["entry_id"].get<uint16_t>();
+
+    std::string deserialize_as = entry_definition["deserialize_as"].get<std::string>();
+
+    if (deserialize_as == "str") {
+        return serialize_str(entry, inner_config_entry);
+    }
+    else if (deserialize_as == "bool") {
+        return serialize_bool(entry, inner_config_entry);
+    }
+    else if (deserialize_as == "int") {
+        return serialize_int_by_size(entry, inner_config_entry);
+    }
+    else {
+        if (!config_entry.contains("value"))
+        {
+            inner_config_entry = {{"value", config_entry}};
+        }
+        if (deserialize_as == "ipv4") {
+            return serialize_ipv4(entry, inner_config_entry);
+        }
+        else if (deserialize_as == "i2c_speed") {
+            return serialize_i2c_speed(entry, inner_config_entry);
+        }
+        else if (deserialize_as == "supported_aspm_states") {
+            return serialize_supported_aspm_states(entry, inner_config_entry);
+        }
+        else if (deserialize_as == "supported_aspm_l1_substates") {
+            return serialize_supported_aspm_l1_substates(entry, inner_config_entry);
+        }
+        else if (deserialize_as == "mac_address") {
+            return serialize_mac_address(entry, inner_config_entry);
+        }
+        else if (deserialize_as == "clock_frequency") {
+            return serialize_clock_frequency(entry, inner_config_entry);
+        }
+        else if (deserialize_as == "logger_level") {
+            return serialize_logger_level(entry, inner_config_entry);
+        }
+        else if (deserialize_as == "watchdog_mode") {
+            return serialize_watchdog_mode(entry, inner_config_entry);
+        }
+        else if (deserialize_as == "overcurrent_parameters_source") {
+            return serialize_overcurrent_parameters_source(entry, inner_config_entry);
+        }
+        else if (deserialize_as == "temperature_parameters_source") {
+            return serialize_temperature_parameters_source(entry, inner_config_entry);
+        }
+        else if (deserialize_as == "conversion_time") {
+            return serialize_conversion_time(entry, inner_config_entry);
+        }
+        else {
+            LOGGER__ERROR("Failed serializing entry. Serialization format {} not found", deserialize_as);
+            return HAILO_NOT_FOUND;
+        }
+    }
+
+}
+
+hailo_status FwConfigJsonSerializer::serialize_str(USER_CONFIG_ENTRY_t &entry, const ordered_json &config_entry)
+{
+    std::string str = config_entry.get<std::string>();
+
+    CHECK(0 != str.length(), HAILO_INVALID_ARGUMENT, "Failed serializing string. String length can't be 0.");
+
+    CHECK(entry.entry_size >= str.length(), HAILO_INVALID_ARGUMENT,
+        "Failed serializing string value {}. String length must be equal or shorter than {}", str, entry.entry_size);
+    
+    memcpy(&(entry.value), str.c_str(), str.length());
+
+    return HAILO_SUCCESS;
+}
+
+hailo_status FwConfigJsonSerializer::serialize_bool(USER_CONFIG_ENTRY_t &entry, const ordered_json &config_entry)
+{
+    uint8_t bool_value = config_entry.get<uint8_t>();
+    return serialize_int(entry, bool_value);    
+}
+
+hailo_status FwConfigJsonSerializer::serialize_ipv4(USER_CONFIG_ENTRY_t &entry, const ordered_json &config_entry)
+{
+    CHECK(IPV4_ADDRESS_LENGTH == entry.entry_size, HAILO_INVALID_ARGUMENT,
+        "IPv4 entry size is incompatible. Size recieved: {}, size expected: {}", entry.entry_size, IPV4_ADDRESS_LENGTH);
+    
+    std::string ipv4_str = config_entry["value"].get<std::string>();
+    uint8_t ip[IPV4_ADDRESS_LENGTH];
+
+    auto length = sscanf(ipv4_str.c_str(), "%hhd.%hhd.%hhd.%hhd", &ip[3], &ip[2], &ip[1], &ip[0]);
+    CHECK(IPV4_ADDRESS_LENGTH == length, HAILO_INVALID_ARGUMENT, "Failed serializing ipv4: {}", ipv4_str);
+    
+    memcpy(&(entry.value), &ip, entry.entry_size);
+
+    return HAILO_SUCCESS;
+}
+
+hailo_status FwConfigJsonSerializer::serialize_i2c_speed(USER_CONFIG_ENTRY_t &entry, const ordered_json &config_entry)
+{
+    std::string i2c_speed = config_entry["value"].get<std::string>();
+    uint8_t val = 0;
+    if (("I2C_SPEED_STANDARD" == i2c_speed) || ("I2C SPEED STANDARD" == i2c_speed)) {
+        val = I2C_SPEED_STANDARD;
+    }
+    else if (("I2C_SPEED_FAST" == i2c_speed) || ("I2C SPEED FAST" == i2c_speed)) {
+        val = I2C_SPEED_FAST;
+    }
+    else {
+        LOGGER__ERROR("Failed serializing i2c speed {}", i2c_speed);
+        return HAILO_NOT_FOUND;
+    }
+
+    return serialize_int(entry, val);    
+}
+
+hailo_status FwConfigJsonSerializer::serialize_logger_level(USER_CONFIG_ENTRY_t &entry, const ordered_json &config_entry)
+{
+    std::string logger_level = config_entry["value"].get<std::string>();
+    uint8_t val = 0;
+
+    if ("TRACE" == logger_level) {
+        val = FW_LOGGER_LEVEL_TRACE;
+    }
+    else if ("DEBUG" == logger_level) {
+        val = FW_LOGGER_LEVEL_DEBUG;
+    }
+    else if ("INFO" == logger_level) {
+        val = FW_LOGGER_LEVEL_INFO;
+    }
+    else if ("WARNING" == logger_level) {
+        val = FW_LOGGER_LEVEL_WARN;
+    }
+    else if ("ERROR" == logger_level) {
+        val = FW_LOGGER_LEVEL_ERROR;
+    }
+    else if ("FATAL" == logger_level) {
+        val = FW_LOGGER_LEVEL_FATAL;
+    }
+    else {
+        LOGGER__ERROR("Failed serializing logger_level {}", logger_level);
+        return HAILO_NOT_FOUND;
+    }
+
+    return serialize_int(entry, val);
+}
+
+hailo_status FwConfigJsonSerializer::serialize_supported_aspm_states(USER_CONFIG_ENTRY_t &entry, const ordered_json &config_entry)
+{
+    std::string aspm_state = config_entry["value"].get<std::string>();
+
+    uint8_t val = 0;
+    if (("ASPM_DISABLED" == aspm_state) || ("ASPM DISABLED" == aspm_state)) {
+        val = ASPM_DISABLED;
+    }
+    else if (("ASPM_L1_ONLY" == aspm_state) || ("ASPM L1 ONLY" == aspm_state)) {
+        val = ASPM_L1_ONLY;
+    }
+    else if (("ASPM_L0S_L1" == aspm_state) || ("ASPM L0S L1" == aspm_state)) {
+        val = ASPM_L0S_L1;
+    }
+    else {
+        LOGGER__ERROR("Failed serializing supported aspm state {}.", aspm_state);
+        return HAILO_NOT_FOUND;
+    }
+
+    return serialize_int(entry, val);    
+}
+
+hailo_status FwConfigJsonSerializer::serialize_supported_aspm_l1_substates(USER_CONFIG_ENTRY_t &entry, const ordered_json &config_entry)
+{
+    std::string aspm_l1_substate = config_entry["value"].get<std::string>();
+
+    uint8_t val = 0;
+    if (("ASPM_L1_SUBSTATES_DISABLED" == aspm_l1_substate) || ("ASPM L1 SUBSTATES DISABLED" == aspm_l1_substate)) {
+        val = ASPM_L1_SUBSTATES_DISABLED;
+    }
+    else if (("ASPM_L1_SUBSTATES_L11_ONLY" == aspm_l1_substate) || ("ASPM L1 SUBSTATES L1.1 ONLY" == aspm_l1_substate)) {
+        val = ASPM_L1_SUBSTATES_L11_ONLY;
+    }
+    else if (("ASPM_L1_SUBSTATES_L11_L12" == aspm_l1_substate) || ("ASPM L1 SUBSTATES L1.1 L1.2" == aspm_l1_substate)) {
+        val = ASPM_L1_SUBSTATES_L11_L12;
+    }
+    else {
+        LOGGER__ERROR("Failed serializing supported aspm l1 substate {}", aspm_l1_substate);
+        return HAILO_NOT_FOUND;
+    }
+
+    return serialize_int(entry, val);    
+}
+
+hailo_status FwConfigJsonSerializer::serialize_mac_address(USER_CONFIG_ENTRY_t &entry, const ordered_json &config_entry)
+{
+    std::string mac_addr_str = config_entry["value"].get<std::string>();
+
+    uint8_t mac_address[MAC_ADDRESS_LENGTH];
+    auto length = std::sscanf(mac_addr_str.c_str(),
+                    "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx",
+                    &mac_address[0], &mac_address[1], &mac_address[2],
+                    &mac_address[3], &mac_address[4], &mac_address[5]);
+    CHECK(MAC_ADDRESS_LENGTH == length, HAILO_INVALID_ARGUMENT, "Failed serializing mac address {}", mac_addr_str);
+
+    memcpy(&(entry.value), &mac_address, entry.entry_size);
+
+    return HAILO_SUCCESS;
+}
+
+hailo_status FwConfigJsonSerializer::serialize_clock_frequency(USER_CONFIG_ENTRY_t &entry, const ordered_json &config_entry)
+{
+    std::string clock_frequency = config_entry["value"].get<std::string>();
+
+    uint32_t val = 0;
+    if ("400MHZ" == clock_frequency) {
+        val = SOC__NN_CLOCK_400MHz;
+    }
+    else if ("375MHZ" == clock_frequency) {
+        val = SOC__NN_CLOCK_375MHz;
+    }
+    else if ("350MHZ" == clock_frequency) {
+        val = SOC__NN_CLOCK_350MHz;
+    }
+    else if ("325MHZ" == clock_frequency) {
+        val = SOC__NN_CLOCK_325MHz;
+    }
+    else if ("300MHZ" == clock_frequency) {
+        val = SOC__NN_CLOCK_300MHz;
+    }
+    else if ("275MHZ" == clock_frequency) {
+        val = SOC__NN_CLOCK_275MHz;
+    }
+    else if ("250MHZ" == clock_frequency) {
+        val = SOC__NN_CLOCK_250MHz;
+    }
+    else if ("225MHZ" == clock_frequency) {
+        val = SOC__NN_CLOCK_225MHz;
+    }
+    else if ("200MHZ" == clock_frequency) {
+        val = SOC__NN_CLOCK_200MHz;
+    }
+    else if ("100MHZ" == clock_frequency) {
+        val = SOC__NN_CLOCK_100MHz;
+    }
+    else {
+        LOGGER__ERROR("Failed serializing clock_frequency {}", clock_frequency);
+        return HAILO_NOT_FOUND;
+    }
+
+    return serialize_int(entry, val);    
+}
+
+hailo_status FwConfigJsonSerializer::serialize_watchdog_mode(USER_CONFIG_ENTRY_t &entry, const ordered_json &config_entry)
+{
+    std::string watchdog_mode = config_entry["value"].get<std::string>();
+
+    uint8_t val = 0;
+    if (("WD_MODE_HW_SW" == watchdog_mode) || ("WD MODE HW SW" == watchdog_mode)) {
+        val = 0;
+    }
+    else if (("WD_MODE_HW_ONLY" == watchdog_mode) || ("WD MODE HW ONLY" == watchdog_mode)) {
+        val = 1;
+    }
+    else {
+        LOGGER__ERROR("Failed serializing watchdog_mode {}", watchdog_mode);
+        return HAILO_NOT_FOUND;
+    }
+
+    return serialize_int(entry, val);
+}
+
+hailo_status FwConfigJsonSerializer::serialize_overcurrent_parameters_source(USER_CONFIG_ENTRY_t &entry, const ordered_json &config_entry)
+{
+    std::string overcurrent_parameters_source = config_entry["value"].get<std::string>();
+
+    uint8_t val = 0;
+    if (("FW_VALUES" == overcurrent_parameters_source) || ("FW VALUES" == overcurrent_parameters_source)) {
+        val = OVERCURRENT_PARAMETERS_SOURCE_FW_VALUES;
+    }
+    else if (("USER_CONFIG_VALUES" == overcurrent_parameters_source) || ("USER CONFIG VALUES" == overcurrent_parameters_source)) {
+        val = OVERCURRENT_PARAMETERS_SOURCE_USER_CONFIG_VALUES;
+    }
+    else if (("BOARD_CONFIG_VALUES" == overcurrent_parameters_source) || ("BOARD CONFIG VALUES" == overcurrent_parameters_source)) {
+        val = OVERCURRENT_PARAMETERS_SOURCE_BOARD_CONFIG_VALUES;
+    }
+    else if (("OVERCURRENT_DISABLED" == overcurrent_parameters_source) || ("OVERCURRENT DISABLED" == overcurrent_parameters_source)) {
+        val = OVERCURRENT_PARAMETERS_SOURCE_OVERCURRENT_DISABLED;
+    }
+    else {
+        LOGGER__ERROR("Failed serializing overcurrent_parameters_source {}", overcurrent_parameters_source);
+        return HAILO_NOT_FOUND;
+    }
+
+    return serialize_int(entry, val);
+}
+
+hailo_status FwConfigJsonSerializer::serialize_temperature_parameters_source(USER_CONFIG_ENTRY_t &entry, const ordered_json &config_entry)
+{
+    std::string temperature_parameters_source = config_entry["value"].get<std::string>();
+
+    uint8_t val = 0;
+    if (("FW_VALUES" == temperature_parameters_source) || ("FW VALUES" == temperature_parameters_source)) {
+        val = TEMPERATURE_PROTECTION_PARAMETERS_SOURCE_FW_VALUES;
+    }
+    else if (("USER_CONFIG_VALUES" == temperature_parameters_source) || ("USER CONFIG VALUES" == temperature_parameters_source)) {
+        val = TEMPERATURE_PROTECTION_PARAMETERS_SOURCE_USER_CONFIG_VALUES;
+    }
+    else {
+        LOGGER__ERROR("Failed serializing temperature_parameters_source {}", temperature_parameters_source);
+        return HAILO_NOT_FOUND;
+    }
+
+    return serialize_int(entry, val);
+}
+
+hailo_status FwConfigJsonSerializer::serialize_conversion_time(USER_CONFIG_ENTRY_t &entry, const ordered_json &config_entry)
+{
+    uint32_t conversion_time = config_entry["value"].get<uint32_t>();
+    uint32_t val = 0;
+
+    if (conversion_time == OVERCURRENT_CONVERSION_PERIOD_140US ||
+        conversion_time == OVERCURRENT_CONVERSION_PERIOD_204US ||
+        conversion_time == OVERCURRENT_CONVERSION_PERIOD_332US ||
+        conversion_time == OVERCURRENT_CONVERSION_PERIOD_588US ||
+        conversion_time == OVERCURRENT_CONVERSION_PERIOD_1100US ||
+        conversion_time == OVERCURRENT_CONVERSION_PERIOD_2116US ||
+        conversion_time == OVERCURRENT_CONVERSION_PERIOD_4156US ||
+        conversion_time == OVERCURRENT_CONVERSION_PERIOD_8244US) {
+
+        val = conversion_time;
+    }
+    else {
+        LOGGER__ERROR("Failed serializing conversion_time {}", conversion_time);
+        return HAILO_NOT_FOUND;
+    }
+
+    return serialize_int(entry, val);    
+}
+
+hailo_status FwConfigJsonSerializer::serialize_int_by_size(USER_CONFIG_ENTRY_t &entry, const ordered_json &config_entry)
+{
+    int64_t value = config_entry.get<int64_t>();
+    if (sizeof(uint8_t) == entry.entry_size) {
+        CHECK(((std::numeric_limits<uint8_t>::min() <= value) && (value <= std::numeric_limits<uint8_t>::max())),
+            HAILO_INVALID_ARGUMENT, "Failed serializing uint8_t value: {}. Value is out of numeric limits", value);
+
+        return serialize_int<uint8_t>(entry, static_cast<uint8_t>(value));
+    }
+    else if (sizeof(uint16_t) == entry.entry_size) {
+        CHECK(((std::numeric_limits<uint16_t>::min() <= value) && (value <= std::numeric_limits<uint16_t>::max())),
+            HAILO_INVALID_ARGUMENT, "Failed serializing uint16_t value: {}. Value is out of numeric limits", value);
+        
+        return serialize_int<uint16_t>(entry, static_cast<uint16_t>(value));
+    }
+    else if (sizeof(uint32_t)  == entry.entry_size) {
+        CHECK(((std::numeric_limits<uint32_t>::min() <= value) && (value <= std::numeric_limits<uint32_t>::max())),
+            HAILO_INVALID_ARGUMENT, "Failed serializing uint32_t value: {}. Value is out of numeric limits", value);
+        
+        return serialize_int<uint32_t>(entry, static_cast<uint32_t>(value));
+    }
+    else {
+        LOGGER__ERROR("Failed serializing int value. Invalid size {}", entry.entry_size);
+        return HAILO_NOT_FOUND;
+    }
+}
\ No newline at end of file
diff --git a/hailort/hailortcli/fw_config_serializer.hpp b/hailort/hailortcli/fw_config_serializer.hpp
new file mode 100644 (file)
index 0000000..4e3ecba
--- /dev/null
@@ -0,0 +1,100 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file fw_config_serializer.hpp
+ * @brief User firmware configuration serializer.
+ **/
+
+#ifndef _HAILO_FW_CONFIG_SERIALIZER_HPP_
+#define _HAILO_FW_CONFIG_SERIALIZER_HPP_
+
+#include "hailortcli.hpp"
+#include "hailo/hailort.h"
+#include "hailo/device.hpp"
+#include "user_config_common.h"
+
+#include <nlohmann/json.hpp>
+
+using json = nlohmann::json;
+using ordered_json = nlohmann::ordered_json;
+
+#define MAC_ADDRESS_LENGTH (6)
+#define IPV4_ADDRESS_LENGTH (4)
+#define JSON_PRINT_INDENTATION (4)
+#define USER_CONFIG_MAGIC (0x1FF6A40B)
+
+/* NOTE: Tightly coupled with I2C_speed_mode_t defined in "i2c_handler.h"
+ Configuring I2c_speed_high is not supported. */
+typedef enum {
+    I2C_SPEED_STANDARD = 1,
+    I2C_SPEED_FAST = 2,
+} i2c_speed_mode_t;
+
+// TODO: HRT-3045 - Add support for big-endian serialization
+class FwConfigJsonSerializer {
+public:
+    FwConfigJsonSerializer() = delete;
+
+    static Expected<ordered_json> read_json_file(const std::string &file_path);
+    static Expected<std::vector<std::vector<ordered_json>>> get_deserialize_vector();
+    static Expected<std::map<std::string, std::map<std::string, ordered_json>>> get_serialize_map();
+    static hailo_status dump_config(const ordered_json &config_json, const std::string &file_path);
+
+    static hailo_status deserialize_config(const USER_CONFIG_header_t &user_config_header, size_t config_size, const std::string &file_path);
+    static hailo_status deserialize_entry(ordered_json &config_json, const ordered_json &entry_definition, uint8_t *entry_value);
+    static Expected<json> deserialize_str(uint8_t *entry_value,  uint32_t size);
+    static Expected<json> deserialize_bool(uint8_t *entry_value, uint32_t size);
+    static Expected<json> deserialize_supported_aspm_states(uint8_t *entry_value, uint32_t size);
+    static Expected<json> deserialize_supported_aspm_l1_substates(uint8_t *entry_value, uint32_t size);
+    static Expected<json> deserialize_mac_address(uint8_t *entry_value, uint32_t size);
+    static Expected<json> deserialize_i2c_speed(uint8_t *entry_value, uint32_t size);
+    static Expected<json> deserialize_logger_level(uint8_t *entry_value, uint32_t size);
+    static Expected<json> deserialize_ipv4(uint8_t *entry_value, uint32_t size);
+    static Expected<json> deserialize_clock_frequency(uint8_t *entry_value, uint32_t size);
+    static Expected<json> deserialize_watchdog_mode(uint8_t *entry_value, uint32_t size);
+    static Expected<json> deserialize_overcurrent_parameters_source(uint8_t *entry_value, uint32_t size);
+    static Expected<json> deserialize_temperature_parameters_source(uint8_t *entry_value, uint32_t size);
+    static Expected<json> deserialize_conversion_time(uint8_t *entry_value, uint32_t size);
+    static Expected<json> deserialize_int(uint8_t *entry_value, uint32_t size);
+
+    static Expected<uint32_t> serialize_config(USER_CONFIG_header_t &user_config_header, size_t config_size, const std::string &file_path);
+    static hailo_status serialize_entry(USER_CONFIG_ENTRY_t &entry, const ordered_json &config_entry, const ordered_json &entry_definition);
+    static hailo_status serialize_str(USER_CONFIG_ENTRY_t &entry, const ordered_json &config_entry);
+    static hailo_status serialize_bool(USER_CONFIG_ENTRY_t &entry, const ordered_json &config_entry);
+    static hailo_status serialize_ipv4(USER_CONFIG_ENTRY_t &entry, const ordered_json &config_entry);
+    static hailo_status serialize_i2c_speed(USER_CONFIG_ENTRY_t &entry, const ordered_json &config_entry);
+    static hailo_status serialize_logger_level(USER_CONFIG_ENTRY_t &entry, const ordered_json &config_entry);
+    static hailo_status serialize_supported_aspm_states(USER_CONFIG_ENTRY_t &entry, const ordered_json &config_entry);
+    static hailo_status serialize_supported_aspm_l1_substates(USER_CONFIG_ENTRY_t &entry, const ordered_json &config_entry);
+    static hailo_status serialize_mac_address(USER_CONFIG_ENTRY_t &entry, const ordered_json &config_entry);
+    static hailo_status serialize_clock_frequency(USER_CONFIG_ENTRY_t &entry, const ordered_json &config_entry);
+    static hailo_status serialize_watchdog_mode(USER_CONFIG_ENTRY_t &entry, const ordered_json &config_entry);
+    static hailo_status serialize_overcurrent_parameters_source(USER_CONFIG_ENTRY_t &entry, const ordered_json &config_entry);
+    static hailo_status serialize_temperature_parameters_source(USER_CONFIG_ENTRY_t &entry, const ordered_json &config_entry);
+    static hailo_status serialize_conversion_time(USER_CONFIG_ENTRY_t &entry, const ordered_json &config_entry);
+    static hailo_status serialize_int_by_size(USER_CONFIG_ENTRY_t &entry, const ordered_json &config_entry);
+
+    template<typename IntegerType>
+    static Expected<IntegerType> get_int_value(uint8_t *entry_value, uint32_t size)
+    {
+        CHECK_AS_EXPECTED((sizeof(IntegerType) == size), HAILO_INTERNAL_FAILURE, "Entry size is incompatible with integer type");
+        auto val = *((IntegerType*)entry_value);
+        return val;
+    }
+
+    template<typename IntegerType>
+    static hailo_status serialize_int(USER_CONFIG_ENTRY_t &entry, IntegerType value)
+    {
+        CHECK((sizeof(IntegerType) == entry.entry_size), HAILO_INVALID_ARGUMENT,
+            "Entry size {} is incompatible with size of integer type {}", entry.entry_size, sizeof(IntegerType));
+
+        auto entry_value_ptr = (IntegerType*)(&entry.value);
+        *entry_value_ptr = value;
+
+        return HAILO_SUCCESS;
+    }
+};
+
+#endif /* _HAILO_FW_CONFIG_SERIALIZER_HPP_ */
diff --git a/hailort/hailortcli/fw_control_command.cpp b/hailort/hailortcli/fw_control_command.cpp
new file mode 100644 (file)
index 0000000..3be6e9a
--- /dev/null
@@ -0,0 +1,248 @@
+/**\r
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.\r
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)\r
+ **/\r
+/**\r
+ * @file fw_control.cpp\r
+ * @brief Several controls that can be sent to the firware\r
+ **/\r
+\r
+#include "fw_control_command.hpp"\r
+#include "firmware_header_utils.h"\r
+\r
+\r
+static const char *NOT_CONFIGURED_ATTR = "<Not Configured>";\r
+#define MHz (1000 * 1000)\r
+\r
+\r
+static std::string extended_device_information_boot_string(hailo_device_boot_source_t boot_source)\r
+{\r
+    switch (boot_source) {\r
+    case HAILO_DEVICE_BOOT_SOURCE_PCIE:\r
+        return "PCIE";\r
+    case HAILO_DEVICE_BOOT_SOURCE_FLASH:\r
+        return "FLASH";\r
+    default:\r
+        return "Unknown";\r
+    }\r
+}\r
+\r
+static std::string extended_device_information_supported_features(hailo_device_supported_features_t supported_features)\r
+{\r
+    std::string supported_features_str;\r
+\r
+    if(supported_features.current_monitoring) {\r
+        supported_features_str.append("Current Monitoring, ");\r
+    }\r
+    if(supported_features.ethernet) {\r
+        supported_features_str.append("Ethernet, ");\r
+    }\r
+    if(supported_features.mipi) {\r
+        supported_features_str.append("MIPI, ");\r
+    }\r
+    if(supported_features.mdio) {\r
+        supported_features_str.append("MDIO, ");\r
+    }\r
+    if(supported_features.pcie) {\r
+        supported_features_str.append("PCIE, ");\r
+    }\r
+\r
+    std::size_t last_comma_location = supported_features_str.find_last_of(",");\r
+    supported_features_str = supported_features_str.substr(0,last_comma_location);\r
+\r
+    return supported_features_str;\r
+}\r
+\r
+static void extended_device_information_print_array(uint8_t *array_for_print, size_t array_length, std::string splitter)\r
+{\r
+    uint32_t i = 0;\r
+    for(i = 0; i < array_length; i++) {\r
+        std::cout << std::setfill('0') << std::setw(2) << std::uppercase << std::hex << static_cast<int>(array_for_print[i]);\r
+        if(array_length != (i+1)) {\r
+           std::cout << splitter;\r
+        }\r
+    }\r
+    std::cout << std::endl;\r
+}\r
+\r
+static bool extended_device_information_is_array_not_empty(uint8_t *array_for_print, size_t array_length)\r
+{\r
+    uint32_t i = 0;\r
+    for(i = 0; i < array_length; i++) {\r
+        if(array_for_print[i] != 0){\r
+            return true;\r
+        }\r
+    }\r
+    return false;\r
+}\r
+\r
+static hailo_status print_extended_device_information(Device &device)\r
+{\r
+    auto extended_info_expected = device.get_extended_device_information();\r
+    CHECK_EXPECTED_AS_STATUS(extended_info_expected, "Failed identify");\r
+    auto device_info = extended_info_expected.release();\r
+\r
+    // Print Board Extended information\r
+    std::cout << "Boot source: " << extended_device_information_boot_string(device_info.boot_source) << std::endl;\r
+    std::cout << "Neural Network Core Clock Rate: " << (device_info.neural_network_core_clock_rate/MHz) <<"MHz" <<std::endl;\r
+\r
+    std::string supported_features_str = extended_device_information_supported_features(device_info.supported_features);\r
+    if(supported_features_str.length() > 0) {\r
+        std::cout << "Device supported features: " << supported_features_str << std::endl;\r
+    }\r
+    std::cout << "LCS: " << static_cast<int>(device_info.lcs) << std::endl;\r
+\r
+    if(extended_device_information_is_array_not_empty(device_info.soc_id, sizeof(device_info.soc_id))){\r
+        std::cout << "SoC ID: ";\r
+        extended_device_information_print_array(device_info.soc_id, sizeof(device_info.soc_id), "");\r
+    }\r
+\r
+    if(extended_device_information_is_array_not_empty(device_info.eth_mac_address, sizeof(device_info.eth_mac_address))){\r
+        std::cout << "MAC Address: ";\r
+        extended_device_information_print_array(device_info.eth_mac_address, sizeof(device_info.eth_mac_address), ":");\r
+    }\r
+\r
+    if(extended_device_information_is_array_not_empty(device_info.unit_level_tracking_id, sizeof(device_info.unit_level_tracking_id))){\r
+        std::cout << "ULT ID: ";\r
+        extended_device_information_print_array(device_info.unit_level_tracking_id, sizeof(device_info.unit_level_tracking_id), "");\r
+    }\r
+\r
+    if(extended_device_information_is_array_not_empty(device_info.soc_pm_values, sizeof(device_info.soc_pm_values))){\r
+        std::cout << "PM Values: ";\r
+        extended_device_information_print_array(device_info.soc_pm_values, sizeof(device_info.soc_pm_values), "");\r
+    }\r
+\r
+    return HAILO_SUCCESS;\r
+}\r
+\r
+static std::string fw_version_string(const hailo_device_identity_t &identity)\r
+{\r
+    std::stringstream os;\r
+    const auto fw_mode = ((identity.is_release) ? "release" : "develop");\r
+    // Currently will always return FW_BINARY_TYPE_APP_FIRMWARE as version bit is cleared in HailoRT\r
+    FW_BINARY_TYPE_t fw_binary_type = FIRMWARE_HEADER_UTILS__get_fw_binary_type(identity.fw_version.revision);\r
+    auto fw_type = "invalid";\r
+    if (FW_BINARY_TYPE_CORE_FIRMWARE == fw_binary_type) {\r
+        fw_type = "core";\r
+    } else if (FW_BINARY_TYPE_APP_FIRMWARE == fw_binary_type) {\r
+        fw_type = "app";\r
+    }\r
+    os << identity.fw_version.major << "." << identity.fw_version.minor << "."\r
+       << identity.fw_version.revision << " (" << fw_mode << "," << fw_type << ")";\r
+    return os.str();\r
+}\r
+\r
+static std::string identity_arch_string(const hailo_device_identity_t &identity)\r
+{\r
+    switch (identity.device_architecture) {\r
+    case HAILO_ARCH_HAILO8_B0:\r
+        return "HAILO8_B0";\r
+    case HAILO_ARCH_MERCURY_CA:\r
+        return "MERCURY_CA";\r
+    case HAILO_ARCH_MERCURY_VPU:\r
+        return "MERCURY_VPU";\r
+    default:\r
+        return "Unknown";\r
+    }\r
+}\r
+\r
+static std::string identity_attr_string(const char *attr, size_t attr_max_len)\r
+{\r
+    size_t actual_len = strnlen(attr, attr_max_len);\r
+    if (actual_len == 0) {\r
+        return  NOT_CONFIGURED_ATTR;\r
+    }\r
+    return std::string(attr, actual_len);\r
+}\r
+\r
+FwControlIdentifyCommand::FwControlIdentifyCommand(CLI::App &parent_app) :\r
+    DeviceCommand(parent_app.add_subcommand("identify", "Displays general information about the device")),\r
+    m_is_extended(false)\r
+{\r
+    m_app->add_flag("--extended", m_is_extended, "Print device extended information");\r
+}\r
+\r
+hailo_status FwControlIdentifyCommand::execute_on_device(Device &device)\r
+{\r
+    auto identity_expected = device.identify();\r
+    CHECK_EXPECTED_AS_STATUS(identity_expected, "Failed identify");\r
+    auto identity = identity_expected.release();\r
+\r
+    // Print board information\r
+    std::cout << "Identifying board" << std::endl;\r
+    std::cout << "Control Protocol Version: " << identity.protocol_version << std::endl;\r
+    std::cout << "Firmware Version: " << fw_version_string(identity) << std::endl;\r
+    std::cout << "Logger Version: " << identity.logger_version << std::endl;\r
+    std::cout << "Board Name: " << std::string(identity.board_name, identity.board_name_length) << std::endl;\r
+    std::cout << "Device Architecture: " << identity_arch_string(identity) << std::endl;\r
+    std::cout << "Serial Number: " <<\r
+        identity_attr_string(identity.serial_number, identity.serial_number_length) << std::endl;\r
+    std::cout << "Part Number: " <<\r
+        identity_attr_string(identity.part_number, identity.part_number_length) << std::endl;\r
+    std::cout << "Product Name: " <<\r
+        identity_attr_string(identity.product_name, identity.product_name_length) << std::endl;\r
+\r
+    if (m_is_extended) {\r
+        print_extended_device_information(device);\r
+    }\r
+\r
+    std::cout << std::endl;\r
+    return HAILO_SUCCESS;\r
+}\r
+\r
+FwControlResetCommand::FwControlResetCommand(CLI::App &parent_app) :\r
+    DeviceCommand(parent_app.add_subcommand("reset", "Resets the device"))\r
+{\r
+    m_app->add_option("--reset-type", m_reset_mode, "Reset type")\r
+        ->required()\r
+        ->transform(HailoCheckedTransformer<hailo_reset_device_mode_t>({\r
+            { "chip", HAILO_RESET_DEVICE_MODE_CHIP },\r
+            { "nn_core", HAILO_RESET_DEVICE_MODE_NN_CORE },\r
+            { "soft", HAILO_RESET_DEVICE_MODE_SOFT },\r
+            { "forced_soft", HAILO_RESET_DEVICE_MODE_FORCED_SOFT },\r
+        }));\r
+}\r
+\r
+hailo_status FwControlResetCommand::execute_on_device(Device &device)\r
+{\r
+    auto status = device.reset(m_reset_mode);\r
+    CHECK_SUCCESS(status, "Failed reset device");\r
+\r
+    std::cout << "Board has been reset successfully" << std::endl;\r
+    return HAILO_SUCCESS;\r
+}\r
+\r
+FwControlTestMemoriesCommand::FwControlTestMemoriesCommand(CLI::App &parent_app) :\r
+    DeviceCommand(parent_app.add_subcommand("test-memories", "Run a test of the chip's memories"))\r
+{}\r
+\r
+hailo_status FwControlTestMemoriesCommand::execute_on_device(Device &device)\r
+{\r
+    auto status = device.test_chip_memories();\r
+    CHECK_SUCCESS(status, "Failed memory test");\r
+\r
+    std::cout << "Memory test has completed succesfully" << std::endl;\r
+    return HAILO_SUCCESS;\r
+}\r
+\r
+FwControlCommand::FwControlCommand(CLI::App &parent_app) :\r
+    ContainerCommand(parent_app.add_subcommand("fw-control", "Useful firmware control operations"))\r
+{\r
+    add_subcommand<FwControlIdentifyCommand>();\r
+    add_subcommand<FwControlResetCommand>();\r
+\r
+    // TODO: Remove scan as a subcommand of fw_control_subcommand (HRT-2676)\r
+    //       Can also remove Command::set_description function after this, and the return value of `add_subcommand`\r
+    auto &scan = add_subcommand<ScanSubcommand>();\r
+    scan.set_description("Alias for root-level 'scan' command (i.e. 'hailortcli scan...')\n"\r
+        "Note: 'scan' as a sub-command of 'fw-control' is deprecated; use 'hailortcli scan' instead\n"\r
+        "       (or 'hailo scan' when using the 'hailo' command).");\r
+\r
+    add_subcommand<FwControlTestMemoriesCommand>();\r
+    // TODO: Support on windows (HRT-5919)\r
+    #if defined(__GNUC__)\r
+    // TODO: Unhide (HRT-6035)\r
+    static const bool HIDDEN = true;\r
+    add_subcommand<DownloadActionListCommand>(HIDDEN);\r
+    #endif\r
+}\r
diff --git a/hailort/hailortcli/fw_control_command.hpp b/hailort/hailortcli/fw_control_command.hpp
new file mode 100644 (file)
index 0000000..7ffbcd4
--- /dev/null
@@ -0,0 +1,58 @@
+/**\r
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.\r
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)\r
+ **/\r
+/**\r
+ * @file fw_control.hpp\r
+ * @brief Several controls that can be sent to the firware\r
+ **/\r
+\r
+#ifndef _HAILO_FW_CONTROL_COMMAND_HPP_\r
+#define _HAILO_FW_CONTROL_COMMAND_HPP_\r
+\r
+#include "hailortcli.hpp"\r
+#include "command.hpp"\r
+// TODO: Remove scan as a subcommand of fw_control_subcommand (HRT-2676)\r
+#include "scan_command.hpp"\r
+#if defined(__GNUC__)\r
+// TODO: Support on windows (HRT-5919)\r
+#include "download_action_list_command.hpp"\r
+#endif\r
+\r
+class FwControlIdentifyCommand : public DeviceCommand {\r
+public:\r
+    explicit FwControlIdentifyCommand(CLI::App &parent_app);\r
+\r
+protected:\r
+    virtual hailo_status execute_on_device(Device &device) override;\r
+\r
+private:\r
+    bool m_is_extended;\r
+};\r
+\r
+class FwControlResetCommand : public DeviceCommand {\r
+public:\r
+    explicit FwControlResetCommand(CLI::App &parent_app);\r
+\r
+protected:\r
+    virtual hailo_status execute_on_device(Device &device) override;\r
+\r
+private:\r
+    hailo_reset_device_mode_t m_reset_mode;\r
+};\r
+\r
+class FwControlTestMemoriesCommand : public DeviceCommand {\r
+public:\r
+    explicit FwControlTestMemoriesCommand(CLI::App &parent_app);\r
+\r
+protected:\r
+    virtual hailo_status execute_on_device(Device &device) override;\r
+};\r
+\r
+class FwControlCommand : public ContainerCommand {\r
+public:\r
+    explicit FwControlCommand(CLI::App &parent_app);\r
+};\r
+\r
+\r
+#endif /* _HAILO_FW_CONTROL_COMMAND_HPP_ */\r
diff --git a/hailort/hailortcli/fw_logger_command.cpp b/hailort/hailortcli/fw_logger_command.cpp
new file mode 100644 (file)
index 0000000..c7fc5dc
--- /dev/null
@@ -0,0 +1,81 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file fw_logger_command.cpp
+ * @brief Write fw log to output file
+ **/
+
+#include "fw_logger_command.hpp"
+#include "common/file_utils.hpp"
+
+#define AMOUNT_OF_BYTES_TO_READ 256
+
+FwLoggerCommand::FwLoggerCommand(CLI::App &parent_app) :
+    DeviceCommand(parent_app.add_subcommand("fw-logger", "Download fw logs to a file")),
+    m_should_overwrite(false)
+{
+    m_app->add_option("output_file", m_output_file, "File path to write binary firmware log into")
+        ->required();
+    m_app->add_flag("--overwrite", m_should_overwrite, "Should overwrite the file or not");
+}
+
+hailo_status write_logs_to_file(Device &device, std::ofstream &ofs, hailo_cpu_id_t cpu_id){
+    auto still_has_logs = true;
+    static const auto buffer_size = AMOUNT_OF_BYTES_TO_READ;
+    
+    auto expected_buffer = Buffer::create(buffer_size);
+    CHECK_EXPECTED_AS_STATUS(expected_buffer);
+    Buffer buffer = expected_buffer.release();
+
+    while(still_has_logs) {
+        MemoryView response_view(buffer);
+        auto response_size_expected = device.read_log(response_view, cpu_id);
+        CHECK_EXPECTED_AS_STATUS(response_size_expected);
+
+        auto response_size = response_size_expected.release();
+        if (response_size == 0) {
+            still_has_logs = false;
+        }
+        else {
+            ofs.write((char *)buffer.data(), response_size);
+            CHECK(ofs.good(), HAILO_FILE_OPERATION_FAILURE,
+                "Failed writing firmware logger to output file, with errno: {}", errno);
+        }
+    }
+    return HAILO_SUCCESS;
+}
+
+hailo_status FwLoggerCommand::execute_on_device(Device &device)
+{
+    auto ofs_flags = std::ios::out | std::ios::binary;
+    hailo_status status = HAILO_UNINITIALIZED;
+
+    if (!m_should_overwrite){
+        ofs_flags |= std::ios::app;
+    }
+
+    std::ofstream ofs(m_output_file, ofs_flags);
+    CHECK(ofs.good(), HAILO_OPEN_FILE_FAILURE, "Failed opening file: {}, with errno: {}", m_output_file, errno);
+
+    if (Device::Type::ETH == device.get_type()) {
+        LOGGER__ERROR("Read FW log is not supported over Eth device");
+        return HAILO_INVALID_OPERATION;
+    }
+    
+    if (Device::Type::CORE != device.get_type()) {
+        status = write_logs_to_file(device, ofs, HAILO_CPU_ID_0);
+        if (status != HAILO_SUCCESS){
+            return status;
+        }
+    }
+
+    status = write_logs_to_file(device, ofs, HAILO_CPU_ID_1);
+    if (status != HAILO_SUCCESS){
+        return status;
+    }
+
+    return HAILO_SUCCESS;
+}
+
diff --git a/hailort/hailortcli/fw_logger_command.hpp b/hailort/hailortcli/fw_logger_command.hpp
new file mode 100644 (file)
index 0000000..8ed82e6
--- /dev/null
@@ -0,0 +1,34 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file fw_logger_command.hpp
+ * @brief Write fw log to output file
+ **/
+
+#ifndef _HAILO_FW_LOGGER_COMMAND_COMMAND_HPP_
+#define _HAILO_FW_LOGGER_COMMAND_COMMAND_HPP_
+
+#include "hailortcli.hpp"
+#include "command.hpp"
+
+#include "hailo/hailort.h"
+#include "hailo/device.hpp"
+#include "hailo/buffer.hpp"
+#include "CLI/CLI.hpp"
+
+
+class FwLoggerCommand : public DeviceCommand {
+public:
+    explicit FwLoggerCommand(CLI::App &parent_app);
+
+protected:
+    virtual hailo_status execute_on_device(Device &device) override;
+
+private:
+    std::string m_output_file;
+    bool m_should_overwrite;
+};
+
+#endif /* _HAILO_FW_LOGGER_COMMAND_COMMAND_HPP_ */
diff --git a/hailort/hailortcli/fw_update_command.cpp b/hailort/hailortcli/fw_update_command.cpp
new file mode 100644 (file)
index 0000000..b37883d
--- /dev/null
@@ -0,0 +1,44 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file fw_update_command.cpp
+ * @brief Update fw on hailo device with flash
+ **/
+
+#include "fw_update_command.hpp"
+#include "common/file_utils.hpp"
+
+FwUpdateCommand::FwUpdateCommand(CLI::App &parent_app) :
+    DeviceCommand(parent_app.add_subcommand("fw-update", "Firmware update tool (only for flash based devices)")),
+    m_firmware_path(),
+    m_skip_reset(false)
+{
+    m_app->add_option("firmware", m_firmware_path, "The path to the firmware binary")
+        ->required()
+        ->check(CLI::ExistingFile);
+    m_app->add_flag("--skip-reset", m_skip_reset, "Don't reset after update");
+}
+
+hailo_status FwUpdateCommand::execute_on_device(Device &device)
+{
+    auto firmware = read_binary_file(m_firmware_path);
+    if (!firmware) {
+        std::cerr << "Failed reading firmware file " << firmware.status() << std::endl;
+        return firmware.status();
+    }
+
+    std::cout << "Updating firmware" << std::endl;
+    const bool should_reset = !m_skip_reset;
+    auto status = device.firmware_update(MemoryView(firmware->data(), static_cast<uint32_t>(firmware->size())), should_reset);
+    if (HAILO_SUCCESS != status) {
+        std::cerr << "Update firmware failed, error code " << status << std::endl;
+        return status;
+    }
+
+    std::cout << "Firmware has been updated" << std::endl;
+
+    return HAILO_SUCCESS;
+}
+
diff --git a/hailort/hailortcli/fw_update_command.hpp b/hailort/hailortcli/fw_update_command.hpp
new file mode 100644 (file)
index 0000000..f099407
--- /dev/null
@@ -0,0 +1,34 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file fw_update_command.hpp
+ * @brief Update fw on hailo device with flash
+ **/
+
+#ifndef _HAILO_FW_UPDATE_COMMAND_COMMAND_HPP_
+#define _HAILO_FW_UPDATE_COMMAND_COMMAND_HPP_
+
+#include "hailortcli.hpp"
+#include "command.hpp"
+
+#include "hailo/hailort.h"
+#include "hailo/device.hpp"
+#include "hailo/buffer.hpp"
+#include "CLI/CLI.hpp"
+
+
+class FwUpdateCommand : public DeviceCommand {
+public:
+    explicit FwUpdateCommand(CLI::App &parent_app);
+
+protected:
+    virtual hailo_status execute_on_device(Device &device) override;
+
+private:
+    std::string m_firmware_path;
+    bool m_skip_reset;
+};
+
+#endif /* _HAILO_FW_UPDATE_COMMAND_COMMAND_HPP_ */
diff --git a/hailort/hailortcli/generate_definitions_json_str.in b/hailort/hailortcli/generate_definitions_json_str.in
new file mode 100644 (file)
index 0000000..32f94a2
--- /dev/null
@@ -0,0 +1,8 @@
+/* THIS FILE IS AUTOGENERATED! DO NOT MANUALLY EDIT. */
+
+#ifndef _DEFINITIONS_JSON_AUTO_HPP_
+#define _DEFINITIONS_JSON_AUTO_HPP_
+
+static const char* g_fw_config_str_definitions = R"(${config_definitions_json_file})";
+
+#endif /* _DEFINITIONS_JSON_AUTO_HPP_ */
\ No newline at end of file
diff --git a/hailort/hailortcli/graph_printer.cpp b/hailort/hailortcli/graph_printer.cpp
new file mode 100644 (file)
index 0000000..5aba8f9
--- /dev/null
@@ -0,0 +1,393 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file graph_printer.cpp
+ * @brief Implementation of graph_printer module
+ **/
+
+#include "graph_printer.hpp"
+#include "hailo/hailort.h"
+#include "common/filesystem.hpp"
+#include "common/utils.hpp"
+#include "infer_stats_printer.hpp"
+
+#include <set>
+#include <map>
+#include <sstream>
+#include <algorithm>
+
+namespace hailort
+{
+
+
+PipelineGraphNode::PipelineGraphNode(DotWriter::RootGraph &G, std::shared_ptr<PipelineElement> elem,
+                                     const std::vector<AccumulatorPtr> &runtime_stats_accumulators,
+                                     DotWriter::Color::e node_color, DotWriter::Color::e node_bg_color,
+                                     const std::string &node_style, DotWriter::LabelLoc::e node_label_loc,
+                                     const std::string &sink_source_style, DotWriter::Color::e sink_source_color,
+                                     DotWriter::NodeShape::e sink_source_shape, double sink_source_height,
+                                     double sink_source_width, DotWriter::LabelLoc::e sub_label_loc) :
+    m_graph_node(nullptr),
+    m_sinks(),
+    m_sources(),
+    m_visited(false)
+{
+    assert(nullptr != elem);
+
+    const auto label = create_multiline_label({ elem->name() /* TODO: other info will go here */ });
+    const auto sub_label = format_runtime_stats(runtime_stats_accumulators);
+    
+    if (static_cast<std::string>(sub_label).empty()) {
+        m_graph_node = G.AddCluster(label);
+        assert(nullptr != m_graph_node);
+    } else {
+        auto *enclosing_cluster = G.AddCluster(sub_label);
+        assert(nullptr != enclosing_cluster);
+
+        enclosing_cluster->GetAttributes().SetLabelLoc(sub_label_loc);
+        enclosing_cluster->GetAttributes().SetPeripheries(0);
+
+        m_graph_node = enclosing_cluster->AddCluster(label);
+        assert(nullptr != m_graph_node);
+        m_graph_node->GetAttributes().SetTooltip(static_cast<std::string>(sub_label));
+    }
+
+    m_graph_node->GetAttributes().SetColor(node_color);
+    m_graph_node->GetAttributes().SetBGColor(node_bg_color);
+    m_graph_node->GetAttributes().SetStyle(node_style);
+    m_graph_node->GetAttributes().SetLabelLoc(node_label_loc);
+    m_graph_node->GetDefaultNodeAttributes().SetStyle(sink_source_style);
+    m_graph_node->GetDefaultNodeAttributes().SetColor(sink_source_color);
+    m_graph_node->GetDefaultNodeAttributes().SetShape(sink_source_shape);
+    m_graph_node->GetDefaultNodeAttributes().SetHeight(sink_source_height);
+    m_graph_node->GetDefaultNodeAttributes().SetWidth(sink_source_width);
+    m_graph_node->GetDefaultNodeAttributes().SetFixedSize(true);
+
+    for (const auto &sink : elem->sinks()) {
+        add_sink(sink);
+    }
+    if (0 == elem->sinks().size()) {
+        add_dummy_sink();
+    }
+
+    for (const auto &source : elem->sources()) {
+        add_source(source);
+    }
+    if (0 == elem->sources().size()) {
+        add_dummy_source();
+    }
+}
+
+
+DotWriter::HtmlString PipelineGraphNode::create_multiline_label(const std::vector<std::string> &lines, Align align_to)
+{
+    if (lines.empty()) {
+        // Note: A html <table> without any "<tr></tr>" tags is invalid, so if lines is empty, we return an empty string
+        return DotWriter::HtmlString("");
+    }
+
+    std::stringstream result;
+    result << "<table border=\"0\">" << std::endl;
+    for (const auto& line : lines) {
+        result << "\t<tr><td align=\"text\">" << line;
+        if (align_to == Align::LEFT) {
+            result << "<br align=\"left\" />";
+        } else if (align_to == Align::RIGHT) {
+            result << "<br align=\"right\" />";
+        }
+        result << "</td></tr>" << std::endl;
+    }
+    result << "</table>";
+    return DotWriter::HtmlString(result.str());
+}
+
+DotWriter::HtmlString PipelineGraphNode::format_runtime_stats(const std::vector<AccumulatorPtr> &runtime_stats_accumulators)
+{
+    std::vector<std::string> lines;
+    for (const auto &accumulator : runtime_stats_accumulators) {
+        if (nullptr == accumulator) {
+            continue;
+        }
+
+        const auto &accumulator_result = accumulator->get();
+        if ((!accumulator_result.count()) || (accumulator_result.count().value() == 0)) {
+            continue;
+        }
+
+        // We split the statistics into two lines
+        std::stringstream string_stream;
+        string_stream << "<B>" << accumulator->get_data_type() << ": </B>";
+        string_stream << "mean=" << InferResultsFormatUtils::format_statistic(accumulator_result.mean()) << ", ";
+        string_stream << "min=" << InferResultsFormatUtils::format_statistic(accumulator_result.min()) << ", ";
+        string_stream << "max=" << InferResultsFormatUtils::format_statistic(accumulator_result.max()) << ", ";
+        lines.emplace_back(string_stream.str());
+
+        // Clear the stream and format the next line
+        string_stream.str("");
+        string_stream << "var=" << InferResultsFormatUtils::format_statistic(accumulator_result.var()) << ", ";
+        string_stream << "sd=" << InferResultsFormatUtils::format_statistic(accumulator_result.sd()) << ", ";
+        string_stream << "mean_sd=" << InferResultsFormatUtils::format_statistic(accumulator_result.mean_sd());
+        lines.emplace_back(string_stream.str());
+    }
+
+    return create_multiline_label(lines, Align::LEFT);
+}
+
+void PipelineGraphNode::add_sink(const hailort::PipelinePad &pad)
+{
+    auto *sink = m_graph_node->AddNode("sink");
+    assert(nullptr != sink);
+    sink->GetAttributes().SetTooltip(pad.name());
+
+    m_sinks.emplace(pad.name(), sink);
+}
+
+void PipelineGraphNode::add_dummy_sink()
+{
+    auto *sink = m_graph_node->AddNode();
+    assert(nullptr != sink);
+    sink->GetAttributes().SetStyle("invis");
+
+    m_sinks.emplace("dummy_sink", sink);
+}
+
+void PipelineGraphNode::add_source(const hailort::PipelinePad &pad)
+{
+    auto *source = m_graph_node->AddNode("source");
+    assert(nullptr != source);
+    source->GetAttributes().SetTooltip(pad.name());
+
+    m_sources.emplace(pad.name(), source);
+    
+    if (!m_sinks.empty()) {
+        const auto first_sink_name = m_sinks.cbegin()->first;
+        auto *edge = m_graph_node->AddEdge(m_sinks[first_sink_name], source);
+        assert(nullptr != edge);
+
+        edge->GetAttributes().SetStyle("invis");
+    }
+}
+
+void PipelineGraphNode::add_dummy_source()
+{
+    auto *source = m_graph_node->AddNode();
+    assert(nullptr != source);
+    source->GetAttributes().SetStyle("invis");
+
+    m_sources.emplace("dummy_source", source);
+    
+    if (!m_sinks.empty()) {
+        const auto first_sink_name = m_sinks.cbegin()->first;
+        auto *edge = m_graph_node->AddEdge(m_sinks[first_sink_name], source);
+        assert(nullptr != edge);
+
+        edge->GetAttributes().SetStyle("invis");
+    }
+}
+
+DotWriter::Node *PipelineGraphNode::get_pad(const std::string &pad_name) const
+{
+    if (m_sinks.count(pad_name) != 0) {
+        return m_sinks.at(pad_name);
+    }
+    assert(m_sources.count(pad_name) != 0);
+    return m_sources.at(pad_name);
+}
+
+bool PipelineGraphNode::has_been_visited() const
+{
+    return m_visited;
+}
+
+void PipelineGraphNode::set_visited()
+{
+    m_visited = true;
+}
+
+hailo_status GraphPrinter::write_dot_file(const std::map<std::string, std::vector<InputVStream>> &input_vstreams_per_network,
+    const std::map<std::string, std::vector<OutputVStream>> &output_vstreams_per_network, const std::string &graph_title,
+    const std::string &output_path, bool write_pipeline_stats)
+{
+    PipelineGraph graph(input_vstreams_per_network, output_vstreams_per_network, graph_title, write_pipeline_stats);
+    return graph.write_dot_file(output_path);
+}
+
+
+GraphPrinter::PipelineGraph::PipelineGraph(const std::map<std::string, std::vector<InputVStream>> &input_vstreams_per_network,
+                                           const std::map<std::string, std::vector<OutputVStream>> &output_vstreams_per_network,
+                                           const std::string &graph_title, bool write_pipeline_stats) :
+    m_graph(true, create_graph_title_label(graph_title, DefaultNodeAttrs::MAIN_LABEL_FONT_SIZE)),
+    m_elems_in_graph()
+{
+    size_t total_inputs_count = 0;
+    for (auto &input_vstreams_pair : input_vstreams_per_network) {
+        total_inputs_count += input_vstreams_pair.second.size();
+    }
+    size_t total_outputs_count = 0;
+    for (auto &output_vstreams_pair : output_vstreams_per_network) {
+        total_outputs_count += output_vstreams_pair.second.size();
+    }
+
+    // Set the graph "graph title" label to be on top
+    m_graph.GetAttributes().SetLabelLoc(DotWriter::LabelLoc::T);
+    // Set the graph direction from left to right
+    m_graph.GetAttributes().SetRankDir(DotWriter::RankDir::LR);
+    m_graph.GetAttributes().SetPackMode(format_pack_mode(total_outputs_count + total_inputs_count));
+
+    // Note: This order is important (input pipelines will be printed above output pipelines)
+    for (const auto &output_vstreams_pair : output_vstreams_per_network) {
+        for (const auto &vstream : output_vstreams_pair.second) {
+            update_graph_nodes(vstream.get_pipeline(), write_pipeline_stats);
+        }
+    }
+    for (const auto &input_vstreams_pair : input_vstreams_per_network) {
+        for (const auto &vstream : input_vstreams_pair.second) {
+            update_graph_nodes(vstream.get_pipeline(), write_pipeline_stats);
+        }
+    }
+    for (const auto &output_vstreams_pair : output_vstreams_per_network) {
+        for (const auto &vstream : output_vstreams_pair.second) {
+            update_edges_in_graph(vstream.get_pipeline(), "HW", "user_output");
+        }
+    }
+    for (const auto &input_vstreams_pair : input_vstreams_per_network) {
+        for (const auto &vstream : input_vstreams_pair.second) {
+            update_edges_in_graph(vstream.get_pipeline(), "user_input", "HW");
+        }
+    }
+}
+hailo_status GraphPrinter::PipelineGraph::write_dot_file(const std::string &output_path)
+{
+    CHECK(m_graph.WriteToFile(output_path), HAILO_FILE_OPERATION_FAILURE,
+        "Faild writing graph '.dot' file to output_path='{}'", output_path);
+    return HAILO_SUCCESS;
+}
+
+DotWriter::Node *GraphPrinter::PipelineGraph::add_external_node(const std::string &label,
+    DotWriter::NodeShape::e shape, double height, double width)
+{
+    auto *result = m_graph.AddNode(label);
+    assert(nullptr != result);
+
+    result->GetAttributes().SetShape(shape);
+    result->GetAttributes().SetHeight(height);
+    result->GetAttributes().SetWidth(width);
+    result->GetAttributes().SetFixedSize(true);
+    return result;
+}
+
+void GraphPrinter::PipelineGraph::update_graph_nodes(const std::vector<std::shared_ptr<PipelineElement>> &pipeline,
+    bool write_pipeline_stats)
+{
+    // We assume that PipelineElement names are unique (also across different vstreams)
+    for (const auto& elem : pipeline) {
+        const auto elem_name = elem->name();
+        if (m_elems_in_graph.count(elem_name) != 0) {
+            // This elem appears in the graph
+            continue;
+        }
+
+        std::vector<AccumulatorPtr> runtime_stats_accumulators;
+        if (write_pipeline_stats) {
+            if (nullptr != elem->get_fps_accumulator()) {
+                runtime_stats_accumulators.emplace_back(elem->get_fps_accumulator());
+            }
+            if (nullptr != elem->get_latency_accumulator()) {
+                runtime_stats_accumulators.emplace_back(elem->get_latency_accumulator());
+            }
+            for (const auto &queue_size_accumulator : elem->get_queue_size_accumulators()) {
+                if (nullptr != queue_size_accumulator) {
+                    runtime_stats_accumulators.emplace_back(queue_size_accumulator);
+                }
+            }
+        }
+        PipelineGraphNode curr_elem_graph_node(m_graph, elem, runtime_stats_accumulators);
+        m_elems_in_graph.emplace(elem_name, std::move(curr_elem_graph_node));
+    }
+}
+
+void GraphPrinter::PipelineGraph::update_edges_in_graph_recursive(const PipelineElement &root,
+    const std::string &output_elem_name)
+{
+    auto &curr_elem_graph_node = m_elems_in_graph.at(root.name());
+    if (curr_elem_graph_node.has_been_visited()) {
+        return;
+    }
+    curr_elem_graph_node.set_visited();
+
+    for (const auto &source : root.sources()) {
+        if (nullptr == source.next()) {
+            auto *left = curr_elem_graph_node.get_pad(source.name());
+            auto *right = add_external_node(output_elem_name);
+            m_graph.AddEdge(left, right);
+            continue;
+        }
+        const auto *sink = source.next();
+
+        const auto &next_elem = sink->element();
+        const auto next_elem_name = next_elem.name();
+
+        auto *left = curr_elem_graph_node.get_pad(source.name());
+        auto *right = m_elems_in_graph.at(next_elem_name).get_pad(sink->name());
+        m_graph.AddEdge(left, right);
+
+        update_edges_in_graph_recursive(next_elem, output_elem_name);
+    }
+}
+
+void GraphPrinter::PipelineGraph::update_edges_in_graph(const std::vector<std::shared_ptr<PipelineElement>> &pipeline,
+    const std::string &input_elem_name, const std::string &output_elem_name)  
+{
+    for (const auto &root_elem : get_pipeline_root_elements(pipeline)) {
+        auto &root_elem_graph_node = m_elems_in_graph.at(root_elem->name());
+        if (root_elem_graph_node.has_been_visited()) {
+            continue;
+        }
+
+        for (const auto &sink : root_elem->sinks()) {
+            auto *left = add_external_node(input_elem_name);
+            auto *right = root_elem_graph_node.get_pad(sink.name());
+            m_graph.AddEdge(left, right);
+        }
+
+        update_edges_in_graph_recursive(*root_elem, output_elem_name);
+    }
+}
+
+std::string GraphPrinter::PipelineGraph::format_pack_mode(size_t num_rows)
+{
+    // Align the graph as an array of num_rows rows (see https://graphviz.org/docs/attr-types/packMode/)
+    std::stringstream result;
+    result << "array_t" << std::to_string(num_rows);
+    return result.str();
+}
+
+DotWriter::HtmlString GraphPrinter::PipelineGraph::create_graph_title_label(const std::string &hef_path,
+    uint32_t font_size)
+{
+    std::stringstream result;
+    result << "<font point-size=\"" << std::to_string(font_size) << "\">";
+    result << "<b>Network:</b> " << hef_path;
+    result << "</font>";
+    return DotWriter::HtmlString(result.str());
+}
+
+std::vector<std::shared_ptr<PipelineElement>> GraphPrinter::PipelineGraph::get_pipeline_root_elements(
+    const std::vector<std::shared_ptr<PipelineElement>> &pipeline)
+{
+    std::vector<std::shared_ptr<PipelineElement>> root_elems;
+    for (auto& elem : pipeline) {
+        if ((0 == elem->sinks().size()) || std::all_of(elem->sinks().begin(), elem->sinks().end(),
+                                                       [](auto &sink){ return nullptr == sink.prev(); })) {
+            // A PipelineElement is a "root" if it has no sink pads or it's sink pads aren't connected to source pads
+            root_elems.emplace_back(elem);
+        }
+    }
+
+    return root_elems;
+}
+
+} /* namespace hailort */
diff --git a/hailort/hailortcli/graph_printer.hpp b/hailort/hailortcli/graph_printer.hpp
new file mode 100644 (file)
index 0000000..f0be525
--- /dev/null
@@ -0,0 +1,163 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file graph_printer.hpp
+ * @brief Print pipeline graphs to '.dot' files
+ **/
+
+#ifndef _HAILO_GRAPH_PRINTER_HPP_
+#define _HAILO_GRAPH_PRINTER_HPP_
+
+#include "hailo/hailort.h"
+#include "hailo/vstream.hpp"
+#include "pipeline.hpp"
+
+#include "DotWriter.h"
+
+#include <vector>
+#include <memory>
+#include <type_traits>
+
+namespace hailort
+{
+
+class DefaultNodeAttrs final
+{
+public:
+    DefaultNodeAttrs() = delete;
+
+    // Titles
+    static constexpr uint32_t MAIN_LABEL_FONT_SIZE = 28;
+    static constexpr uint32_t PIPELINE_LABEL_FONT_SIZE = 24;
+    
+    // Pipeline nodes
+    static constexpr DotWriter::Color::e PIPELINE_NODE_COLOR = DotWriter::Color::LIGHTGREY;
+    static constexpr DotWriter::Color::e PIPELINE_NODE_BG_COLOR = DotWriter::Color::LIGHTGREY;
+    static std::string PIPELINE_NODE_STYLE() { return "rounded"; };
+    static constexpr DotWriter::LabelLoc::e PIPELINE_NODE_LABEL_LOC = DotWriter::LabelLoc::T;
+
+    // Sink/source nodes
+    static std::string SINK_SOURCE_STYLE() { return "filled"; };
+    static constexpr DotWriter::Color::e SINK_SOURCE_COLOR = DotWriter::Color::WHITE;
+    static constexpr DotWriter::NodeShape::e SINK_SOURCE_SHAPE = DotWriter::NodeShape::RECTANGLE;
+    static constexpr double SINK_SOURCE_HEIGHT = 0.5;
+    static constexpr double SINK_SOURCE_WIDTH = 0.7;
+    
+    // External nodes (HW, user_input, user_output)
+    static constexpr DotWriter::NodeShape::e EXTERNAL_NODE_SHAPE = DotWriter::NodeShape::RECTANGLE;
+    static constexpr double EXTERNAL_NODE_HEIGHT = 0.5;
+    static constexpr double EXTERNAL_NODE_WIDTH = 1;
+
+    // Sublabel
+    static constexpr DotWriter::LabelLoc::e SUBLABEL_LOC = DotWriter::LabelLoc::B;
+};
+
+class PipelineGraphNode final
+{
+public:
+    PipelineGraphNode(DotWriter::RootGraph &G, std::shared_ptr<PipelineElement> elem,
+        const std::vector<AccumulatorPtr> &runtime_stats_accumulators,
+        DotWriter::Color::e node_color = DefaultNodeAttrs::PIPELINE_NODE_COLOR,
+        DotWriter::Color::e node_bg_color = DefaultNodeAttrs::PIPELINE_NODE_BG_COLOR,
+        const std::string &node_style = DefaultNodeAttrs::PIPELINE_NODE_STYLE(),
+        DotWriter::LabelLoc::e node_label_loc = DefaultNodeAttrs::PIPELINE_NODE_LABEL_LOC,
+        const std::string &sink_source_style = DefaultNodeAttrs::SINK_SOURCE_STYLE(),
+        DotWriter::Color::e sink_source_color = DefaultNodeAttrs::SINK_SOURCE_COLOR,
+        DotWriter::NodeShape::e sink_source_shape = DefaultNodeAttrs::SINK_SOURCE_SHAPE,
+        double sink_source_height = DefaultNodeAttrs::SINK_SOURCE_HEIGHT,
+        double sink_source_width = DefaultNodeAttrs::SINK_SOURCE_WIDTH,
+        DotWriter::LabelLoc::e sub_label_loc = DefaultNodeAttrs::SUBLABEL_LOC);
+
+    PipelineGraphNode(PipelineGraphNode &&) = default;
+    PipelineGraphNode(const PipelineGraphNode &) = delete;
+    PipelineGraphNode &operator=(PipelineGraphNode &&) = delete;
+    PipelineGraphNode &operator=(const PipelineGraphNode &) = delete;
+    ~PipelineGraphNode() = default;
+
+    DotWriter::Node *get_pad(const std::string &pad_name) const;
+    bool has_been_visited() const;
+    void set_visited();
+
+private:
+    enum class Align
+    {
+        LEFT,
+        RIGHT,
+        CENTER
+    };
+
+    // Creates a HtmlString with each line in lines on a new line, or an empty string if lines is empty
+    static DotWriter::HtmlString create_multiline_label(const std::vector<std::string> &lines, Align align_to = Align::CENTER);
+    static DotWriter::HtmlString format_runtime_stats(const std::vector<AccumulatorPtr> &runtime_stats_accumulators);
+
+    // Note:
+    // * Sink/source pads are nested under the pipeline element node.
+    // * Sinks always appear on the left side of the pipeline element node, and sources on the right.
+    // * If an element has no sinks/soruces, then an invisible "dummy" sink/source is added.
+    //   This is used to align the other pads in the elem to the left/right (a Graphviz oddity).
+    //   E.g:
+    //   ------------------------------------               ------------------------------------
+    //   |           Filter_Elem1           |               |            Sink_Elem1            |
+    //   |                                  |               |                                  |
+    //   |   --------           --------    |               |   --------                       |
+    // --|-->| sink |           |source|----|---------------|-->| sink |       (dummy source)  |
+    //   |   --------           --------    |               |   --------         (invisible)   |
+    //   |                                  |               |                                  |
+    //   ------------------------------------               ------------------------------------
+    void add_sink(const hailort::PipelinePad &pad);
+    void add_dummy_sink();
+    void add_source(const hailort::PipelinePad &pad);
+    void add_dummy_source();
+
+    DotWriter::Cluster *m_graph_node;
+    std::map<std::string, DotWriter::Node *> m_sinks;
+    std::map<std::string, DotWriter::Node *> m_sources;
+    bool m_visited;
+};
+
+class GraphPrinter
+{
+public:
+    GraphPrinter() = delete;
+    static hailo_status write_dot_file(const std::map<std::string, std::vector<InputVStream>> &input_vstreams_per_network,
+        const std::map<std::string, std::vector<OutputVStream>> &output_vstreams_per_network,
+        const std::string &graph_title, const std::string &output_path, bool write_pipeline_stats);
+
+private:
+    class PipelineGraph final
+    {
+    public:
+        PipelineGraph(const std::map<std::string, std::vector<InputVStream>> &input_vstreams_per_network,
+            const std::map<std::string, std::vector<OutputVStream>> &output_vstreams_per_network,
+            const std::string &graph_title, bool write_pipeline_stats);
+        hailo_status write_dot_file(const std::string &output_path);
+
+    private:
+        DotWriter::RootGraph m_graph;
+        std::map<std::string, PipelineGraphNode> m_elems_in_graph;
+
+        DotWriter::Node *add_external_node(const std::string &label,
+            DotWriter::NodeShape::e shape = DefaultNodeAttrs::EXTERNAL_NODE_SHAPE,
+            double height = DefaultNodeAttrs::EXTERNAL_NODE_HEIGHT,
+            double width = DefaultNodeAttrs::EXTERNAL_NODE_WIDTH);
+        // Add all the pipeline nodes in the graph to m_graph
+        void update_graph_nodes(const std::vector<std::shared_ptr<PipelineElement>> &pipeline, bool write_pipeline_stats);
+        // update_edges_* will be called after adding the graph nodes with 'update_graph_nodes'
+        // Update the edges in the pipeline graph recursively, starting at root (using DFS) 
+        void update_edges_in_graph_recursive(const PipelineElement &root, const std::string &output_elem_name);
+        // Update all of the edges in the pipeline graph
+        void update_edges_in_graph(const std::vector<std::shared_ptr<PipelineElement>> &pipeline,
+            const std::string &input_elem_name, const std::string &output_elem_name);
+
+        static std::string format_pack_mode(size_t num_rows);
+        static DotWriter::HtmlString create_graph_title_label(const std::string &hef_path, uint32_t font_size);
+        static std::vector<std::shared_ptr<PipelineElement>> get_pipeline_root_elements(
+            const std::vector<std::shared_ptr<PipelineElement>> &pipeline);
+    };
+};
+
+} /* namespace hailort */
+
+#endif /* _HAILO_GRAPH_PRINTER_HPP_ */
diff --git a/hailort/hailortcli/hailortcli.cpp b/hailort/hailortcli/hailortcli.cpp
new file mode 100644 (file)
index 0000000..ceabba7
--- /dev/null
@@ -0,0 +1,246 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file hailortcli.cpp
+ * @brief HailoRT CLI.
+ *
+ * HailoRT command line interface.
+ **/
+#include "hailortcli.hpp"
+#include "scan_command.hpp"
+#include "power_measurement_command.hpp"
+#include "run_command.hpp"
+#include "fw_update_command.hpp"
+#include "ssb_update_command.hpp"
+#include "sensor_config_command.hpp"
+#include "board_config_command.hpp"
+#include "fw_config_command.hpp"
+#include "fw_logger_command.hpp"
+#include "benchmark_command.hpp"
+#if defined(__GNUC__)
+#include "udp_rate_limiter_command.hpp"
+#endif
+#include "parse_hef_command.hpp"
+#include "fw_control_command.hpp"
+
+#include "firmware_header_utils.h"
+#include "hailo/hailort.h"
+#include "hailo/hailort_common.hpp"
+#include "hailo/device.hpp"
+#include "hailo/hef.hpp"
+#include "hailo/buffer.hpp"
+
+#include "CLI/CLI.hpp"
+
+#include <spdlog/sinks/stdout_color_sinks.h>
+
+#include <memory>
+#include <iostream>
+#include <vector>
+#include <thread>
+#include <map>
+
+static Expected<hailo_pcie_device_info_t> get_pcie_device_info(const hailo_pcie_params &pcie_params)
+{
+    if (pcie_params.pcie_bdf.empty()) {
+        auto scan_result = Device::scan_pcie();
+        if (!scan_result) {
+            std::cerr << "Hailo PCIe scan failed (maybe pcie device not exists). status=" << scan_result.status() << std::endl;
+            return make_unexpected(scan_result.status());
+        }
+
+        if (scan_result->size() == 0) {
+            std::cerr << "Hailo PCIe not found.." << std::endl;
+            return make_unexpected(HAILO_INTERNAL_FAILURE);
+        }
+        return std::move(scan_result->at(0));
+    } else {
+        auto device_info_expected = Device::parse_pcie_device_info(pcie_params.pcie_bdf);
+        if (!device_info_expected) {
+            std::cerr << "Invalid pcie bdf format" << std::endl;
+            return make_unexpected(device_info_expected.status());
+        }
+        return device_info_expected.release();
+    }
+}
+
+Expected<std::unique_ptr<Device>> create_pcie_device(const hailo_pcie_params &pcie_params)
+{
+    auto device_info = get_pcie_device_info(pcie_params);
+    if (!device_info) {
+        return make_unexpected(device_info.status());
+    }
+
+    auto device = Device::create_pcie(device_info.value());
+    if (!device) {
+        std::cerr << "Failed create pcie device. status=" << device.status() << std::endl;
+        return make_unexpected(device.status());
+    }
+
+    return Expected<std::unique_ptr<Device>>(device.release());
+}
+
+static Expected<std::unique_ptr<Device>> create_eth_device(const hailo_eth_params &eth_params)
+{
+    auto device = Device::create_eth(eth_params.ip_addr);
+    if (!device) {
+        std::cerr << "Failed create ethernet device. status=" << device.status() << std::endl;
+        return make_unexpected(device.status());
+    }
+
+    return Expected<std::unique_ptr<Device>>(device.release());
+}
+
+Expected<std::unique_ptr<Device>> create_device(const hailo_device_params &device_params)
+{
+    switch (device_params.device_type) {
+    case DeviceType::PCIE:
+        return create_pcie_device(device_params.pcie_params);
+    case DeviceType::ETH:
+        return create_eth_device(device_params.eth_params);
+    case DeviceType::DEFAULT:
+        // If core driver is loaded (we are running on Mercury) then the default is core device; else, pcie device
+        if (Device::is_core_driver_loaded()) {
+            return Device::create_core_device();
+        } else {
+            return create_pcie_device(device_params.pcie_params);
+        }
+    default:
+        std::cerr << "Invalid device type" << std::endl;
+        return make_unexpected(HAILO_INVALID_ARGUMENT);
+    }
+}
+
+void add_device_options(CLI::App *app, hailo_device_params &device_params)
+{
+    // Initialize the device type to default
+    device_params.device_type = DeviceType::DEFAULT;
+
+    auto group = app->add_option_group("Device Options");
+    
+    // TODO: `--target` and `udp` DeviceType::ETH are for backwards compatibility with the python implemention (`hailo`)
+    // TODO: Remove them (HRT-2676)
+    const HailoCheckedTransformer<DeviceType> device_type_transformer({
+            { "pcie", DeviceType::PCIE },
+            { "eth", DeviceType::ETH },
+            { "udp", DeviceType::ETH },
+        });
+    auto *device_type_option = group->add_option("-d,--device-type,--target", device_params.device_type,
+        "Device type to use\n"
+        "Default is pcie.\n"
+        "Note: 'udp' is an alias for 'eth'.")
+        ->transform(device_type_transformer);
+
+    std::vector<DeprecationActionPtr> actions{ std::make_shared<ValueDeprecation>(device_type_option, "udp", "eth") };
+    hailo_deprecate_options(app, actions, false);
+
+    // PCIe options
+    auto *pcie_bdf_option = group->add_option("-s,--bdf", device_params.pcie_params.pcie_bdf,
+        "Device id ([<domain>]:<bus>:<device>.<func>, same as in lspci command)")
+        ->default_val("");
+
+    // Ethernet options
+    auto *ip_option = group->add_option("--ip", device_params.eth_params.ip_addr, "IP address of the target")
+        ->default_val("")
+        ->check(CLI::ValidIPV4);
+
+    group->parse_complete_callback([&device_params, device_type_option, pcie_bdf_option, ip_option](){
+        // The user didn't put target, we can figure it ourself
+        if (device_type_option->empty()) {
+            if (!ip_option->empty()) {
+                // User gave IP, target is eth
+                device_params.device_type = DeviceType::ETH;
+            } else if (!pcie_bdf_option->empty()) {
+                // User gave bdf, target is pcie
+                device_params.device_type = DeviceType::PCIE;
+            }
+            else {
+                device_params.device_type = DeviceType::DEFAULT;
+            }
+        }
+
+        if (ip_option->empty() && device_params.device_type == DeviceType::ETH) {
+            throw CLI::ParseError("IP address is not set", CLI::ExitCodes::InvalidError);
+        }
+
+        if (!ip_option->empty() && device_params.device_type != DeviceType::ETH) {
+            throw CLI::ParseError("IP address is set on non eth device", CLI::ExitCodes::InvalidError);
+        }
+
+        if (!pcie_bdf_option->empty() && device_params.device_type != DeviceType::PCIE) {
+            throw CLI::ParseError("bdf (-s) is set on non pcie device", CLI::ExitCodes::InvalidError);
+        }
+    });
+}
+
+void add_vdevice_options(CLI::App *app, hailo_device_params &device_params) {
+    auto group = app->add_option_group("VDevice Options");
+
+    // VDevice options
+    auto *device_count_option = group->add_option("--device-count", device_params.vdevice_params.device_count, "VDevice device count")
+        ->default_val(HAILO_DEFAULT_DEVICE_COUNT)
+        ->check(CLI::PositiveNumber);
+
+    group->parse_complete_callback([&device_params, device_count_option](){
+        // The user gave device_count
+        if (!device_count_option->empty()) {
+            if ((device_params.vdevice_params.device_count > 1) &&
+                ((DeviceType::ETH == device_params.device_type) || (DeviceType::PCIE == device_params.device_type && !device_params.pcie_params.pcie_bdf.empty()))) {
+                    throw CLI::ParseError("Device type must not be specified when using multiple devices", CLI::ExitCodes::InvalidError);
+            }
+        }
+    });
+}
+
+class HailoRTCLI : public ContainerCommand {
+public:
+    HailoRTCLI(CLI::App *app) : ContainerCommand(app)
+    {
+
+        m_app->add_flag_callback("-v,--version",
+            [] () {
+                std::cout << "hailortcli version " <<
+                    HAILORT_MAJOR_VERSION << "." << HAILORT_MINOR_VERSION << "." << HAILORT_REVISION_VERSION << std::endl;
+                // throw CLI::Success to stop parsing and not failing require_subcommand(1) we set earlier
+                throw (CLI::Success{});
+            },
+            "Print program version and exit"
+        );
+
+        add_subcommand<RunCommand>();
+        add_subcommand<ScanSubcommand>();
+        add_subcommand<BenchmarkCommand>();
+        add_subcommand<PowerMeasurementSubcommand>();
+        add_subcommand<SensorConfigCommand>();
+        add_subcommand<BoardConfigCommand>();
+        add_subcommand<FwConfigCommand>();
+        add_subcommand<FwLoggerCommand>();
+        add_subcommand<FwUpdateCommand>();
+        add_subcommand<SSBUpdateCommand>();
+#if defined(__GNUC__)
+        add_subcommand<UdpRateLimiterCommand>();
+#endif
+        add_subcommand<ParseHefCommand>();
+        add_subcommand<FwControlCommand>();
+    }
+
+    int parse_and_execute(int argc, char **argv)
+    {
+        CLI11_PARSE(*m_app, argc, argv);
+        return execute();
+    }
+
+};
+
+int main(int argc, char** argv) {
+    auto console_sink = std::make_shared<spdlog::sinks::stderr_color_sink_mt>();
+    console_sink->set_level(spdlog::level::info);
+    console_sink->set_pattern("[%n] [%^%l%$] %v");
+    spdlog::set_default_logger(std::make_shared<spdlog::logger>("HailoRT CLI", console_sink));
+
+    CLI::App app{"HailoRT CLI"};
+    HailoRTCLI cli(&app);
+    return cli.parse_and_execute(argc, argv);
+}
diff --git a/hailort/hailortcli/hailortcli.hpp b/hailort/hailortcli/hailortcli.hpp
new file mode 100644 (file)
index 0000000..8edf17a
--- /dev/null
@@ -0,0 +1,202 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file hailortcli.hpp
+ * @brief HailoRT CLI.
+ **/
+
+#ifndef _HAILO_HAILORTCLI_HPP_
+#define _HAILO_HAILORTCLI_HPP_
+
+#include "hailo/hailort.h"
+#include "hailo/expected.hpp"
+#include "hailo/device.hpp"
+#include "common/logger_macros.hpp"
+#include "common/utils.hpp"
+#include "CLI/CLI.hpp"
+#include <string>
+
+using namespace hailort;
+
+#define PARSE_CHECK(cond, message) \
+    do {                                                                        \
+        if (!(cond)) {                                                          \
+            throw CLI::ParseError(message, CLI::ExitCodes::InvalidError);       \
+        }                                                                       \
+    } while (0)
+
+struct hailo_pcie_params {
+    std::string pcie_bdf;  // if empty use the first scanned
+};
+
+struct hailo_eth_params {
+    std::string ip_addr;
+};
+
+struct hailo_vdevice_params {
+    uint32_t device_count;
+};
+
+enum class DeviceType {
+    PCIE = 0,
+    ETH,
+    DEFAULT
+};
+
+struct hailo_device_params {
+    DeviceType device_type;
+    hailo_pcie_params pcie_params;
+    hailo_eth_params eth_params;
+    hailo_vdevice_params vdevice_params;
+};
+
+void add_device_options(CLI::App *app, hailo_device_params &device_params);
+void add_vdevice_options(CLI::App *app, hailo_device_params &device_params);
+Expected<std::unique_ptr<Device>> create_device(const hailo_device_params &device_params);
+Expected<std::unique_ptr<Device>> create_pcie_device(const hailo_pcie_params &pcie_params);
+
+/**
+ * CLI11 transformer object, converting enum argument from string.
+ * Use this object instead of CLI::CheckedTransformer in order
+ * to avoid ugly prints in the help message.
+ */
+template<typename EnumType>
+class HailoCheckedTransformer : public CLI::CheckedTransformer
+{
+public:
+    HailoCheckedTransformer(std::vector<std::pair<std::string, EnumType>> values) :
+        CLI::CheckedTransformer(values)
+    {
+        desc_function_ = [values]() {
+            return CLI::detail::generate_map(CLI::detail::smart_deref(values), true);
+        };
+    }
+};
+
+class DeprecationAction
+{
+public:
+    DeprecationAction() = default;
+    virtual ~DeprecationAction() = default;
+
+    virtual std::string deprecate(bool message_inline) = 0;
+    static std::string get_inline_description(CLI::Option *opt, const std::string &message)
+    {
+        const auto orig_desc = opt->get_description();
+        std::stringstream new_desc;
+        if (!orig_desc.empty()) {
+            new_desc << orig_desc;
+            if (orig_desc.back() != '\n') {
+                new_desc << std::endl;
+            }
+        }
+        new_desc << "Note: " << message;
+        return new_desc.str();
+    }
+};
+using DeprecationActionPtr = std::shared_ptr<DeprecationAction>;
+
+class OptionDeprecation : public DeprecationAction
+{
+public:
+    OptionDeprecation(CLI::Option *opt, const std::string &replacement) :
+        DeprecationAction(),
+        m_opt(opt),
+        m_replacement(replacement)
+    {
+        assert(nullptr != opt);
+    }
+
+    virtual ~OptionDeprecation() = default;
+
+    // Based off of CLI::deprecate_option, changed logic to suit our needs
+    virtual std::string deprecate(bool message_inline) override
+    {
+        std::stringstream message;
+        message << "'" << m_opt->get_name() << "' is deprecated, please use '" << m_replacement << "' instead." << std::endl;
+        CLI::Validator deprecate_warning(
+            [message = message.str()](std::string &) {
+                std::cout << message;
+                return std::string();
+            }, message_inline ? "" : "DEPRECATED");
+        deprecate_warning.application_index(0);
+        if (message_inline) {
+            m_opt->description(get_inline_description(m_opt, message.str()));
+        }
+        m_opt->check(deprecate_warning);
+        return message.str();
+    }
+
+private:
+    CLI::Option *const m_opt;
+    const std::string m_replacement;
+};
+
+class ValueDeprecation : public DeprecationAction
+{
+public:
+    ValueDeprecation(CLI::Option *opt, const std::string &value, const std::string &replacement) :
+        DeprecationAction(),
+        m_opt(opt),
+        m_value(value),
+        m_replacement(replacement)
+    {
+        assert(nullptr != opt);
+    }
+    
+    virtual ~ValueDeprecation() = default;
+
+    // Based off of CLI::deprecate_option, changed logic to suit our needs
+    virtual std::string deprecate(bool message_inline) override
+    {
+        std::stringstream message;
+        message << "'" << m_value << "' is deprecated, please use '" << m_replacement << "' instead." << std::endl;
+        // We capture the members by value (i.e. copy), since the Validator can outlive this object
+        CLI::Validator deprecate_warning(
+            [message = message.str(), opt = m_opt, value = m_value](std::string &) {
+                const auto results = opt->results();
+                if ((results.size() == 1) && (results[0] == value)) {
+                    std::cout << message;
+                }
+                return std::string();
+            }, "");
+        deprecate_warning.application_index(0);
+        if (message_inline) {
+            m_opt->description(get_inline_description(m_opt, message.str()));
+        }
+        // Hack: transform() and not check(), because we want the string values of opt->results() and not the enum values after HailoCheckedTransformer
+        // transform places the Validator at the head of the validators in opt, so we'll get this check before the transformation is done
+        m_opt->transform(deprecate_warning);
+        return message.str();
+    }
+
+private:
+    CLI::Option *const m_opt;
+    const std::string m_value;
+    const std::string m_replacement;
+};
+
+inline void hailo_deprecate_options(CLI::App *app, const std::vector<DeprecationActionPtr> &actions, bool set_footer = true)
+{
+    // std::set and not std::vector in case two actions have the smae deprection string
+    std::set<std::string> deprecation_messages;
+    for (const auto& deprecation_action : actions) {
+        deprecation_messages.insert(deprecation_action->deprecate(!set_footer));
+    }
+
+    if (set_footer) {
+        std::stringstream footer_message;
+        footer_message << "Deprecated flags/options:" << std::endl;
+        for (const auto &message : deprecation_messages) {
+            footer_message << " * " << message;
+            if (message.back() != '\n') {
+                footer_message << std::endl;
+            }
+        }
+        app->footer(footer_message.str());
+    }
+}
+
+#endif /* _HAILO_HAILORTCLI_HPP_ */
\ No newline at end of file
diff --git a/hailort/hailortcli/infer_stats_printer.cpp b/hailort/hailortcli/infer_stats_printer.cpp
new file mode 100644 (file)
index 0000000..3f91c1c
--- /dev/null
@@ -0,0 +1,459 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file infer_stats_printer.cpp
+ * @brief Show inference progress
+ **/
+
+#include "infer_stats_printer.hpp"
+#include "run_command.hpp"
+#include "common.hpp"
+
+#include <fstream>
+#include <iostream>
+#include <sstream>
+
+
+static std::string infer_mode_to_string(InferMode infer_mode)
+{
+    switch (infer_mode) {
+    case InferMode::STREAMING:
+        return "streaming";
+    case InferMode::HW_ONLY:
+        return "hw_only";
+    default:
+        return "???";
+    }
+}
+
+std::string InferResultsFormatUtils::format_statistic(const Expected<double> &statistic, uint32_t precision)
+{
+    if (!statistic.has_value()) {
+        return "-";
+    }
+
+    std::stringstream string_stream;
+    string_stream << std::fixed << std::setprecision(precision) << statistic.value();
+    return string_stream.str();
+}
+
+std::string InferResultsFormatUtils::format_statistic(const Expected<size_t> &statistic)
+{
+    if (!statistic.has_value()) {
+        return "-";
+    }
+
+    return std::to_string(statistic.value());
+}
+
+double InferResultsFormatUtils::latency_result_to_ms(std::chrono::nanoseconds latency)
+{
+    return std::chrono::duration_cast<std::chrono::duration<double, std::milli>>(latency).count();
+}
+
+Expected<InferStatsPrinter> InferStatsPrinter::create(const inference_runner_params &params, bool print_running_info)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    InferStatsPrinter printer(params, status, print_running_info);
+    CHECK_SUCCESS_AS_EXPECTED(status);
+    return printer;
+}
+
+
+InferStatsPrinter::InferStatsPrinter(const inference_runner_params &params, hailo_status &output_status, bool print_running_info) :
+    m_print_frame_count(0 != params.time_to_run)
+{
+    if (!params.csv_output.empty()) {
+        m_results_csv_path = params.csv_output;
+        m_results_csv_file.open(params.csv_output.c_str(), std::ios::out);
+        if (!m_results_csv_file.good()) {
+            LOGGER__ERROR("Failed creating csv output file {}", params.csv_output);
+            output_status = HAILO_OPEN_FILE_FAILURE;
+            return;
+        }
+    }
+
+    if (should_measure_pipeline_stats(params)) {
+        m_pipeline_stats_csv_path = params.pipeline_stats.pipeline_stats_output_path;
+        m_pipeline_stats_csv_file.open(params.pipeline_stats.pipeline_stats_output_path.c_str(),
+            std::ios::out);
+        if (!m_pipeline_stats_csv_file.good()) {
+            LOGGER__ERROR("Failed creating pipeline stats csv output file {}",
+                params.pipeline_stats.pipeline_stats_output_path);
+            output_status = HAILO_OPEN_FILE_FAILURE;
+            return;
+        }
+    }
+
+    if (print_running_info) {
+        std::cout << "Running " << infer_mode_to_string(params.mode) << " inference (" << params.hef_path << "):" << std::endl;
+        std::cout << "  Transform data: " << std::boolalpha << params.transform.transform << std::endl;
+        if (params.transform.transform) {
+            std::cout << "    Type:      " << format_type_to_string(params.transform.format_type) << std::endl;
+            std::cout << "    Quantized: " << std::boolalpha << params.transform.quantized << std::endl;
+        }
+    }
+
+    if (!params.dot_output.empty()) {
+        m_dot_output_path = params.dot_output;
+    }
+
+    output_status = HAILO_SUCCESS;
+}
+
+void InferStatsPrinter::print(const std::string &network_group_name, Expected<NetworkGroupInferResult>& inference_result)
+{
+    if (m_results_csv_file.is_open()) {
+        std::cout << "> Writing inference results to '" << m_results_csv_path << "'... ";
+        print_csv(network_group_name, inference_result);
+        std::cout << "done." << std::endl;
+    }
+    if (m_pipeline_stats_csv_file.is_open()) {
+        std::cout << "> Writing pipeline statistics to '" << m_pipeline_stats_csv_path << "'... ";
+        m_pipeline_stats_csv_file << "net_name,vstream_name,param_type,element,mean,min,max,var,sd,mean_sd,index" << std::endl;
+        print_pipeline_elem_stats_csv(network_group_name, inference_result->m_fps_accumulators);
+        print_pipeline_elem_stats_csv(network_group_name, inference_result->m_latency_accumulators);
+        print_pipeline_elem_stats_csv(network_group_name, inference_result->m_queue_size_accumulators);
+        print_entire_pipeline_stats_csv(network_group_name, inference_result->m_pipeline_latency_accumulators);
+        std::cout << "done." << std::endl;
+    }
+    print_stdout(inference_result);
+}
+
+void InferStatsPrinter::print_csv_header()
+{
+    m_results_csv_file << "net_name,status,status_description,fps,num_of_frames,send_rate,recv_rate,hw_latency,overall_latency,min_power,average_power,max_power,min_current,average_current,max_current,min_temp,average_temp,max_temp" << std::endl;
+}
+
+void InferStatsPrinter::print_benchmark_csv_header()
+{
+    m_results_csv_file << "net_name,fps,hw_only_fps,num_of_frames,num_of_frames_hw_only,hw_latency,overall_latency,min_power,average_power,max_power" << std::endl;
+}
+
+void InferStatsPrinter::print_csv(const std::string &network_group_name,  Expected<NetworkGroupInferResult>& inference_result)
+{
+    auto status_description = hailo_get_status_message(inference_result.status());
+    m_results_csv_file << network_group_name << "," << static_cast<uint32_t>(inference_result.status()) << "," << status_description;
+    if (!inference_result) {
+        m_results_csv_file << ",,,,,,,,,,,";
+    }
+    else {
+        m_results_csv_file << ",";
+
+        if (auto fps = inference_result->fps()) {
+            m_results_csv_file << fps.value();
+        }
+        m_results_csv_file << ",";
+
+        if (auto frames_count = inference_result->frames_count()) {
+            m_results_csv_file << frames_count.value();
+        }
+        m_results_csv_file << ",";
+
+        if (auto send_data_rate = inference_result->send_data_rate_mbit_sec()) {
+            m_results_csv_file << send_data_rate.value();
+        }
+        m_results_csv_file << ",";
+
+        if (auto recv_data_rate = inference_result->recv_data_rate_mbit_sec()) {
+            m_results_csv_file << recv_data_rate.value();
+        }
+        m_results_csv_file << ",";
+
+        if (auto hw_latency = inference_result->hw_latency()) {
+            m_results_csv_file << InferResultsFormatUtils::latency_result_to_ms(hw_latency.value());
+        }
+        m_results_csv_file << ",";
+
+        if (auto overall_latency = inference_result->overall_latency()) {
+            m_results_csv_file << InferResultsFormatUtils::latency_result_to_ms(overall_latency.value());
+        }
+
+        // TODO HRT-5363 support multiple devices (Currently assumes 1 device in the map)
+        if (1 == inference_result->m_power_measurements.size()) {
+            for (const auto &pair : inference_result->m_power_measurements) {
+                if (nullptr != pair.second) {
+                    m_results_csv_file << ",";
+                    m_results_csv_file << pair.second->data().min_value;
+                    m_results_csv_file << ",";
+                    m_results_csv_file << pair.second->data().average_value;
+                    m_results_csv_file << ",";
+                    m_results_csv_file << pair.second->data().max_value;
+                } else {
+                    m_results_csv_file << ",,,";
+                }
+            }
+        } else {
+            m_results_csv_file << ",,,";
+        }
+
+        // TODO HRT-5363 support multiple devices (Currently assumes 1 device in the map)
+        if (1 == inference_result->m_current_measurements.size()) {
+            for (const auto &pair : inference_result->m_current_measurements) {
+                if (nullptr != pair.second) {
+                    m_results_csv_file << ",";
+                    m_results_csv_file << pair.second->data().min_value;
+                    m_results_csv_file << ",";
+                    m_results_csv_file << pair.second->data().average_value;
+                    m_results_csv_file << ",";
+                    m_results_csv_file << pair.second->data().max_value;
+                } else {
+                    m_results_csv_file << ",,,";
+                }
+            }
+        } else {
+            m_results_csv_file << ",,,";
+        }
+
+        // TODO HRT-5363 support multiple devices (Currently assumes 1 device in the map)
+        if (1 == inference_result->m_temp_measurements.size()) {
+            for (const auto &pair : inference_result->m_temp_measurements) {
+                if (nullptr != pair.second) {
+                    m_results_csv_file << ",";
+                    m_results_csv_file << pair.second->min_value;
+                    m_results_csv_file << ",";
+                    m_results_csv_file << pair.second->average_value;
+                    m_results_csv_file << ",";
+                    m_results_csv_file << pair.second->max_value;
+                } else {
+                    m_results_csv_file << ",,,";
+                }
+            }
+        } else {
+            m_results_csv_file << ",,,";
+        }
+    }
+    m_results_csv_file << std::endl;
+}
+
+void InferStatsPrinter::print_pipeline_elem_stats_csv(const std::string &network_group_name,
+    const std::map<std::string, std::map<std::string, AccumulatorPtr>> &inference_result)
+{
+    if (inference_result.size() == 0) {
+        return;
+    }
+
+    for (const auto &vstream_name_results_pair : inference_result) {
+        for (const auto &elem_name_accumulator_pair : vstream_name_results_pair.second) {
+            write_accumulator_results(m_pipeline_stats_csv_file, elem_name_accumulator_pair.second, 
+                network_group_name, vstream_name_results_pair.first, elem_name_accumulator_pair.first);
+        }
+    }
+}
+
+void InferStatsPrinter::print_pipeline_elem_stats_csv(const std::string &network_group_name,
+    const std::map<std::string, std::map<std::string, std::vector<AccumulatorPtr>>> &inference_result)
+{
+    if (inference_result.size() == 0) {
+        return;
+    }
+
+    for (const auto &vstream_name_results_pair : inference_result) {
+        for (const auto &elem_name_accumulator_pair : vstream_name_results_pair.second) {
+            for (uint32_t i = 0; i < elem_name_accumulator_pair.second.size(); i++) {
+                write_accumulator_results(m_pipeline_stats_csv_file, elem_name_accumulator_pair.second[i], 
+                    network_group_name, vstream_name_results_pair.first, elem_name_accumulator_pair.first, i);
+            }
+        }
+    }
+}
+
+void InferStatsPrinter::print_entire_pipeline_stats_csv(const std::string &network_group_name,
+    const std::map<std::string, AccumulatorPtr> &inference_result)
+{
+    if (inference_result.size() == 0) {
+        return;
+    }
+
+    for (const auto &vstream_name_results_pair : inference_result) {
+        write_accumulator_results(m_pipeline_stats_csv_file, vstream_name_results_pair.second, 
+            network_group_name, vstream_name_results_pair.first, "entire_pipeline");
+    }
+}
+
+void InferStatsPrinter::print_benchmark_csv(const std::string &network_group_name, const NetworkGroupInferResult &hw_inference_result,
+    const NetworkGroupInferResult &streaming_inference_result, const NetworkGroupInferResult &hw_latency_result)
+{
+    m_results_csv_file << network_group_name << ",";
+
+    if (auto fps = streaming_inference_result.fps()) {
+        m_results_csv_file << fps.value();
+    }
+    m_results_csv_file << ",";
+
+    if (auto hw_only_fps = hw_inference_result.fps()) {
+        m_results_csv_file << hw_only_fps.value();
+    }
+    m_results_csv_file << ",";
+
+    if (auto frames_count = streaming_inference_result.frames_count()) {
+        m_results_csv_file << frames_count.value();
+    }
+    m_results_csv_file << ",";
+
+    if (auto frames_count = hw_inference_result.frames_count()) {
+        m_results_csv_file << frames_count.value();
+    }
+    m_results_csv_file << ",";
+
+    if (auto hw_latency = hw_latency_result.hw_latency()) {
+        m_results_csv_file << InferResultsFormatUtils::latency_result_to_ms(hw_latency.value());
+    }
+    m_results_csv_file << ",";
+
+    if (auto overall_latency = hw_latency_result.overall_latency()) {
+        m_results_csv_file << InferResultsFormatUtils::latency_result_to_ms(overall_latency.value());
+    }
+
+    // TODO HRT-5363 support multiple devices (Currently assumes 1 device in the map)
+    if (1 == streaming_inference_result.m_power_measurements.size()) {
+        for (const auto &pair : streaming_inference_result.m_power_measurements) {
+            if (nullptr != pair.second) {
+                m_results_csv_file << ",";
+                m_results_csv_file << pair.second->data().min_value;
+                m_results_csv_file << ",";
+                m_results_csv_file << pair.second->data().average_value;
+                m_results_csv_file << ",";
+                m_results_csv_file << pair.second->data().max_value;
+            } else {
+                m_results_csv_file << ",,,";
+            }
+        }
+    } else {
+        m_results_csv_file << ",,,";
+    }
+
+    m_results_csv_file << std::endl;
+}
+template< typename T>
+void InferStatsPrinter::print_stdout_single_element(const T &results, size_t frames_count)
+{
+    if (0 != frames_count) {
+        std::cout << "    Frames count: " << static_cast<uint32_t>(frames_count) << std::endl;
+    } else if (auto duration = results.infer_duration()) {
+        std::cout << "    Duration: " << CliCommon::duration_to_string(std::chrono::seconds(static_cast<uint32_t>(*duration))) << std::endl;
+    }
+
+    if (auto fps = results.fps()) {
+        std::cout << "    FPS: " << fps.value() << "" << std::endl;
+    }
+
+    if (auto send_data_rate = results.send_data_rate_mbit_sec()) {
+        std::cout << "    Send Rate: " << send_data_rate.value() << " Mbit/s" << std::endl;
+    }
+
+    if (auto recv_data_rate = results.recv_data_rate_mbit_sec()) {
+        std::cout << "    Recv Rate: " << recv_data_rate.value() << " Mbit/s" << std::endl;
+    }
+
+    if (auto hw_latency = results.hw_latency()) {
+        std::cout << "    HW Latency: " << InferResultsFormatUtils::latency_result_to_ms(hw_latency.value()) << " ms" << std::endl;
+    }
+    if (auto overall_latency = results.overall_latency()) {
+        std::cout << "    Overall Latency: " << InferResultsFormatUtils::latency_result_to_ms(overall_latency.value()) << " ms" << std::endl;
+    }
+
+}
+
+void InferStatsPrinter::print_stdout(Expected<NetworkGroupInferResult>& inference_result)
+{
+    if (!inference_result) {
+        return;
+    }
+
+    // Set precision and flags
+    auto original_precision = std::cout.precision();
+    auto original_flags(std::cout.flags());
+    std::cout << std::setprecision(2) << std::fixed;
+    std::cout  << FORMAT_CLEAR_LINE << "> Inference result:" << std::endl;
+
+    if (1 < inference_result->m_result_per_network.size()) {
+        // If there is more than 1 network, we print results per network, and than sum of bandwith
+        for (auto &network_result_pair : inference_result->m_result_per_network) {
+            std::cout << "  Network: " << network_result_pair.first <<  std::endl;
+            auto frames_count = (m_print_frame_count) ? network_result_pair.second.m_frames_count : 0;
+            print_stdout_single_element<NetworkInferResult>(network_result_pair.second, frames_count);
+        }
+        std::stringstream bandwidth_stream;
+        bandwidth_stream << std::setprecision(2) << std::fixed;
+        if (auto send_data_rate = inference_result->send_data_rate_mbit_sec()) {
+            bandwidth_stream << "    Send Rate: " << send_data_rate.value() << " Mbit/s" << std::endl;
+        }
+
+        if (auto recv_data_rate = inference_result->recv_data_rate_mbit_sec()) {
+            bandwidth_stream << "    Recv Rate: " << recv_data_rate.value() << " Mbit/s" << std::endl;
+        }
+
+        if (0 != bandwidth_stream.rdbuf()->in_avail()) {
+            std::cout << "  Total bandwidth: " <<  std::endl;
+            std::cout << bandwidth_stream.rdbuf();
+        }
+    } else {
+        auto frames_count_exp = inference_result->frames_count();
+        auto frames_count = ((frames_count_exp) && (m_print_frame_count)) ? frames_count_exp.value() : 0;
+        print_stdout_single_element<NetworkGroupInferResult>(inference_result.value(), frames_count);
+    }
+
+    if ((inference_result->m_power_measurements.size() != inference_result->m_current_measurements.size()) ||
+            (inference_result->m_power_measurements.size() != inference_result->m_temp_measurements.size())) {
+        LOGGER__ERROR("Error found different number of devices between different measurement types");
+    }
+    for (const auto &pair : inference_result->m_power_measurements) {
+        std::stringstream measurement_stream;
+        if (nullptr != pair.second) {
+            const auto &data = pair.second->data();
+            const auto &power_units = pair.second->power_units();
+            measurement_stream << "    Minimum power consumption: " << data.min_value << " " << power_units << std::endl;
+            measurement_stream << "    Average power consumption: " << data.average_value << " " << power_units << std::endl;
+            measurement_stream << "    Maximum power consumption: " << data.max_value << " " << power_units << std::endl;
+        }
+        auto current_measure_iter = inference_result->m_current_measurements.find(pair.first);
+        if ((current_measure_iter != inference_result->m_current_measurements.end()) && (nullptr != current_measure_iter->second)) {
+            const auto &data = current_measure_iter->second->data();
+            const auto &power_units = current_measure_iter->second->power_units();
+            measurement_stream << "    Minimum current consumption: " << data.min_value << " " << power_units << std::endl;
+            measurement_stream << "    Average current consumption: " << data.average_value << " " << power_units << std::endl;
+            measurement_stream << "    Maximum current consumption: " << data.max_value << " " << power_units << std::endl;
+        }
+        auto temp_measure_iter = inference_result->m_temp_measurements.find(pair.first);
+        if ((temp_measure_iter != inference_result->m_temp_measurements.end()) && (nullptr != temp_measure_iter->second)) {
+            measurement_stream << "    Minimum chip temperature: " << temp_measure_iter->second->min_value << "°C" << std::endl;
+            measurement_stream << "    Average chip temperature: " << temp_measure_iter->second->average_value << "°C" << std::endl;
+            measurement_stream << "    Maximum chip temperature: " << temp_measure_iter->second->max_value << "°C" << std::endl;
+        }
+        if (0 != measurement_stream.rdbuf()->in_avail()) {
+            std::cout << "  Device: " << pair.first << std::endl;
+            std::cout << measurement_stream.rdbuf();
+        }
+    }
+
+    // Restore precision and flags
+    std::cout.flags(original_flags);
+    std::cout.precision(original_precision);
+}
+
+void InferStatsPrinter::write_accumulator_results(std::ofstream &output_stream, AccumulatorPtr accumulator,
+    const std::string &network_group_name, const std::string &vstream_name, const std::string &elem_name, uint32_t index)
+{
+    const auto &accumulator_result = accumulator->get();
+    if ((!accumulator_result.count()) || (accumulator_result.count().value() == 0)) {
+        return;
+    }
+    
+    output_stream << network_group_name << ",";
+    output_stream << vstream_name << ",";
+    output_stream << accumulator->get_data_type() << ",";
+    output_stream << elem_name << ",";
+    output_stream << InferResultsFormatUtils::format_statistic(accumulator_result.mean()) << ",";
+    output_stream << InferResultsFormatUtils::format_statistic(accumulator_result.min()) << ",";
+    output_stream << InferResultsFormatUtils::format_statistic(accumulator_result.max()) << ",";
+    output_stream << InferResultsFormatUtils::format_statistic(accumulator_result.var()) << ",";
+    output_stream << InferResultsFormatUtils::format_statistic(accumulator_result.sd()) << ",";
+    output_stream << InferResultsFormatUtils::format_statistic(accumulator_result.mean_sd()) << ",";
+    if (NO_INDEX != index) {
+        output_stream << index;
+    }
+    output_stream << std::endl;
+}
diff --git a/hailort/hailortcli/infer_stats_printer.hpp b/hailort/hailortcli/infer_stats_printer.hpp
new file mode 100644 (file)
index 0000000..3400067
--- /dev/null
@@ -0,0 +1,66 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file infer_stats_printer.hpp
+ * @brief Prints the inference stats
+ **/
+
+#ifndef _HAILO_INFER_STATS_PRINTER_HPP_
+#define _HAILO_INFER_STATS_PRINTER_HPP_
+
+#include "inference_result.hpp"
+#include "run_command.hpp"
+
+#include <limits>
+
+class InferResultsFormatUtils final {
+public:
+    InferResultsFormatUtils() = delete;
+
+    static const uint32_t DEFAULT_FLOATING_POINT_PRECISION = 4;
+
+    static std::string format_statistic(const Expected<double> &statistic, uint32_t precision = DEFAULT_FLOATING_POINT_PRECISION);
+    static std::string format_statistic(const Expected<size_t> &statistic);
+    static double latency_result_to_ms(std::chrono::nanoseconds latency);
+};
+
+class InferStatsPrinter final {
+public:
+    static Expected<InferStatsPrinter> create(const inference_runner_params &params, bool print_running_info = true);
+    void print(const std::string &network_name, Expected<NetworkGroupInferResult>& inference_result);
+    void print_benchmark_csv(const std::string &network_name, const NetworkGroupInferResult &hw_inference_result,
+        const NetworkGroupInferResult &streaming_inference_result, const NetworkGroupInferResult &hw_latency_result);
+    void print_csv_header();
+    void print_benchmark_csv_header();
+
+private:
+    static constexpr uint32_t NO_INDEX = std::numeric_limits<uint32_t>::max();
+
+    InferStatsPrinter(const inference_runner_params &params, hailo_status &output_status, bool print_running_info = true);
+    void print_csv(const std::string &network_name, Expected<NetworkGroupInferResult>& inference_result);
+    void print_pipeline_elem_stats_csv(const std::string &network_name,
+        const std::map<std::string, std::map<std::string, AccumulatorPtr>> &inference_result);
+    void print_pipeline_elem_stats_csv(const std::string &network_name,
+        const std::map<std::string, std::map<std::string, std::vector<AccumulatorPtr>>> &inference_result);
+    void print_entire_pipeline_stats_csv(const std::string &network_name,
+        const std::map<std::string, AccumulatorPtr> &inference_result);
+    void print_stdout(Expected<NetworkGroupInferResult>& inference_result);
+    template <typename T>
+    void print_stdout_single_element(const T &results, size_t frames_count);
+
+    // 'index' is only printed if it's not equal to 'NO_INDEX'
+    static void write_accumulator_results(std::ofstream &output_stream, AccumulatorPtr accumulator,
+        const std::string &network_name, const std::string &vstream_name, const std::string &elem_name,
+        uint32_t index=NO_INDEX);
+
+    std::ofstream m_results_csv_file;
+    std::ofstream m_pipeline_stats_csv_file;
+    std::string m_results_csv_path;
+    std::string m_pipeline_stats_csv_path;
+    std::string m_dot_output_path;
+    bool m_print_frame_count;
+};
+
+#endif /* _HAILO_INFER_STATS_PRINTER_HPP_ */
\ No newline at end of file
diff --git a/hailort/hailortcli/inference_progress.cpp b/hailort/hailortcli/inference_progress.cpp
new file mode 100644 (file)
index 0000000..75161a9
--- /dev/null
@@ -0,0 +1,133 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file inference_progress.cpp
+ * @brief Show inference progress
+ **/
+#include "inference_progress.hpp"
+#include "infer_stats_printer.hpp"
+#include "common.hpp"
+
+#include <iostream>
+#include <iomanip>
+
+InferProgress::InferProgress(ConfiguredNetworkGroup &configured_network_group, const inference_runner_params &params,
+    std::chrono::duration<double> print_interval) :
+      m_configured_network_group(configured_network_group), m_params(params),
+      m_print_interval(print_interval), m_networks_progress(), m_stop(true) {}
+
+void InferProgress::start()
+{
+    m_stop = false;
+    m_print_thread = std::thread([this] () {
+        while (!m_stop.load()) {
+            print_progress(true);
+            std::this_thread::sleep_for(m_print_interval);
+        }
+    });
+}
+
+void InferProgress::finish(bool should_print_progress)
+{
+    m_stop = true;
+    m_print_thread.join();
+    if (should_print_progress) {
+        print_progress(false);
+    }
+}
+
+void InferProgress::print_progress(bool should_reset_cursor)
+{
+    for (auto &network_progress_bar : m_networks_progress) {
+        std::cout << network_progress_bar->get_progress_text() << std::endl;
+    }
+    if (should_reset_cursor) {
+        CliCommon::reset_cursor(m_networks_progress.size());
+    }
+}
+
+InferProgress::~InferProgress()
+{
+    if (!m_stop.load()) {
+        finish(false);
+    }
+}
+
+Expected<std::shared_ptr<NetworkProgressBar>> InferProgress::create_network_progress_bar(const std::string &network_name)
+{
+    std::shared_ptr<NetworkProgressBar> network_progress_ber =
+        make_shared_nothrow<NetworkProgressBar>(m_configured_network_group, m_params, network_name);
+    CHECK_NOT_NULL_AS_EXPECTED(network_progress_ber, HAILO_OUT_OF_HOST_MEMORY);
+
+    {
+        // We create NetworkProgressBar from different threads
+        std::unique_lock<std::mutex> lock(m_mutex);
+        m_networks_progress.push_back(network_progress_ber);
+    }
+
+    auto prog_bar_cpy = network_progress_ber;
+    return prog_bar_cpy;
+}
+
+NetworkProgressBar::NetworkProgressBar(ConfiguredNetworkGroup &configured_network_group,
+    const inference_runner_params &params, const std::string &network_name) :
+      m_network_name(network_name), m_configured_network_group(configured_network_group), m_params(params),
+      m_progress_count(0), m_start(std::chrono::steady_clock::now()) // NetworkProgressBar sets start time to its creation time
+      {}
+
+std::string NetworkProgressBar::get_progress_text()
+{
+    std::stringstream res;
+    auto elapsed_time = std::chrono::duration<double>(std::chrono::steady_clock::now() - m_start).count();
+    auto progress_count = m_progress_count.load();
+    auto fps = progress_count / elapsed_time;
+    auto eta = std::chrono::seconds(0);
+    if (0 == m_params.time_to_run) {
+        eta = std::chrono::seconds(static_cast<uint32_t>(static_cast<double>(m_params.frames_count - progress_count) / fps));
+    } else {
+        eta = std::chrono::seconds(std::max(static_cast<int32_t>(0),
+            static_cast<int32_t>(std::round(m_params.time_to_run - elapsed_time))));
+    }
+
+    // Set precision and flags
+    res << std::setprecision(2) << std::fixed;
+
+    uint32_t progress_percent = 0;
+    if (0 == m_params.time_to_run) {
+        progress_percent = 100 * progress_count / m_params.frames_count;
+    } else {
+        progress_percent = std::min(static_cast<uint32_t>(100 * elapsed_time / m_params.time_to_run), static_cast<uint32_t>(100));
+    }
+
+    res << "Network " << m_network_name << ": " << progress_percent << "% | " << progress_count;
+    if (0 == m_params.time_to_run) {
+        res << "/" << m_params.frames_count;
+    }
+
+    if (!m_params.measure_latency) {
+        res << " | FPS: " << fps;
+    } else {
+        double avg_hw_latency = 0;
+        auto latency_expected = m_configured_network_group.get_latency_measurement(m_network_name);
+        if (latency_expected) {
+            avg_hw_latency = InferResultsFormatUtils::latency_result_to_ms(latency_expected.release().avg_hw_latency);
+        }
+
+        if (avg_hw_latency > 0) {
+            res << " | HW Latency: " << avg_hw_latency << " ms";
+        }
+        else {
+            res << " | HW Latency: NaN";
+        }
+    }
+    res << " | ETA: " << CliCommon::duration_to_string(eta) << std::flush;
+
+    return res.str();
+}
+
+void NetworkProgressBar::make_progress()
+{
+    ++m_progress_count;
+}
\ No newline at end of file
diff --git a/hailort/hailortcli/inference_progress.hpp b/hailort/hailortcli/inference_progress.hpp
new file mode 100644 (file)
index 0000000..c0854f6
--- /dev/null
@@ -0,0 +1,58 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file inference_progress.hpp
+ * @brief Show inference progress
+ **/
+
+#ifndef _HAILO_INFERENCE_PROGRESS_HPP_
+#define _HAILO_INFERENCE_PROGRESS_HPP_
+
+#include "hailortcli.hpp"
+#include "run_command.hpp"
+
+#include "hailo/network_group.hpp"
+#include "CLI/CLI.hpp"
+
+
+class NetworkProgressBar final {
+public:
+    NetworkProgressBar(ConfiguredNetworkGroup &configured_network_group,
+        const inference_runner_params &params, const std::string &network_name);
+
+    void make_progress();
+    std::string get_progress_text();
+private:
+    const std::string m_network_name;
+    ConfiguredNetworkGroup &m_configured_network_group;
+    const inference_runner_params m_params;
+    std::atomic<uint32_t> m_progress_count;
+    std::chrono::time_point<std::chrono::steady_clock> m_start;
+};
+
+class InferProgress final {
+public:
+    InferProgress(ConfiguredNetworkGroup &configured_network_group,
+        const inference_runner_params &params, std::chrono::duration<double> print_interval);
+
+    ~InferProgress();
+
+    Expected<std::shared_ptr<NetworkProgressBar>> create_network_progress_bar(const std::string &network_name);
+    void start();
+    void finish(bool should_print_progress = true);
+
+private:
+    void print_progress(bool should_reset_cursor);
+
+    ConfiguredNetworkGroup &m_configured_network_group;
+    const inference_runner_params m_params;
+    std::chrono::duration<double> m_print_interval;
+    std::vector<std::shared_ptr<NetworkProgressBar>> m_networks_progress;
+    std::atomic_bool m_stop;
+    std::thread m_print_thread;
+    std::mutex m_mutex;
+};
+
+#endif /* _HAILO_INFERENCE_PROGRESS_HPP_ */
\ No newline at end of file
diff --git a/hailort/hailortcli/inference_result.hpp b/hailort/hailortcli/inference_result.hpp
new file mode 100644 (file)
index 0000000..9e5ce11
--- /dev/null
@@ -0,0 +1,326 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file inference_result.hpp
+ * @brief hold inference result
+ **/
+
+#ifndef _HAILO_INFER_RESULT_
+#define _HAILO_INFER_RESULT_
+
+#include "power_measurement_command.hpp"
+#include "temp_measurement.hpp"
+#include "hailo/runtime_statistics.hpp"
+#include "hailo/vstream.hpp"
+
+static constexpr double MBIT_PER_BYTE = 8.0f / 1000.0f / 1000.0f;
+
+struct NetworkInferResult
+{
+public:
+    NetworkInferResult(size_t frames_count = 0, size_t total_send_frame_size = 0, size_t total_recv_frame_size = 0) :
+        m_frames_count(frames_count),
+        m_total_send_frame_size(total_send_frame_size),
+        m_total_recv_frame_size(total_recv_frame_size),
+        m_infer_duration(nullptr),
+        m_hw_latency(nullptr),
+        m_overall_latency(nullptr)
+    {}
+
+    Expected<double> infer_duration() const{
+        if (!m_infer_duration) {
+            return make_unexpected(HAILO_NOT_AVAILABLE);
+        }
+        auto infer_duration_cpy = *m_infer_duration;
+        return infer_duration_cpy;
+    }
+
+    Expected<double> fps() const
+    {
+        if (!m_infer_duration) {
+            return make_unexpected(HAILO_NOT_AVAILABLE);
+        }
+        return static_cast<double>(m_frames_count) / *m_infer_duration;
+    }
+
+    Expected<double> send_data_rate_mbit_sec() const
+    {
+        if (!m_infer_duration) {
+            return make_unexpected(HAILO_NOT_AVAILABLE);
+        }
+       return (static_cast<double>(m_frames_count * m_total_send_frame_size) / *m_infer_duration) * MBIT_PER_BYTE;
+    }
+
+    Expected<double> recv_data_rate_mbit_sec() const
+    {
+        if (!m_infer_duration) {
+            return make_unexpected(HAILO_NOT_AVAILABLE);
+        }
+        return (static_cast<double>(m_frames_count * m_total_recv_frame_size) / *m_infer_duration) * MBIT_PER_BYTE;
+    }
+
+    Expected<std::chrono::nanoseconds> hw_latency() const
+    {
+        if (!m_hw_latency) {
+            return make_unexpected(HAILO_NOT_AVAILABLE);
+        }
+        std::chrono::nanoseconds latency_cpy = *m_hw_latency;
+        return latency_cpy;
+    }
+
+    Expected<std::chrono::nanoseconds> overall_latency() const
+    {
+        if (!m_overall_latency) {
+            return make_unexpected(HAILO_NOT_AVAILABLE);
+        }
+        std::chrono::nanoseconds latency_cpy = *m_overall_latency;
+        return latency_cpy;
+    }
+
+    size_t m_frames_count;
+    size_t m_total_send_frame_size;
+    size_t m_total_recv_frame_size;
+
+    // TODO: change to optional
+    std::unique_ptr<double> m_infer_duration;
+    std::unique_ptr<std::chrono::nanoseconds> m_hw_latency;
+    std::unique_ptr<std::chrono::nanoseconds> m_overall_latency;
+};
+
+struct NetworkGroupInferResult
+{
+public:
+    NetworkGroupInferResult(std::map<std::string, NetworkInferResult> &&result_per_network = {}) :
+        m_result_per_network(std::move(result_per_network)),
+        m_power_measurements(),
+        m_current_measurements(),
+        m_temp_measurements(),
+        m_fps_accumulators(),
+        m_latency_accumulators(),
+        m_queue_size_accumulators(),
+        m_pipeline_latency_accumulators()
+    {}
+
+    Expected<double> infer_duration(const std::string &network_name = "") const
+    {
+        if (network_name.empty()) {
+            // We set m_infer_duration to be the max of all durations
+            double max_duration = 0;
+            for (auto &network_result_pair : m_result_per_network) {
+                auto duration_per_network = network_result_pair.second.infer_duration();
+                if(!duration_per_network) {
+                    return make_unexpected(HAILO_NOT_AVAILABLE);
+                }
+                max_duration = std::max(max_duration, duration_per_network.value());
+            }
+            return max_duration;
+        }
+        CHECK_AS_EXPECTED(contains(m_result_per_network, network_name), HAILO_NOT_FOUND,
+            "There is no results for network {}", network_name);
+        return m_result_per_network.at(network_name).infer_duration();
+    }
+
+    Expected<double> fps(const std::string &network_name = "") const
+    {
+        if (network_name.empty()) {
+            double acc_fps = 0;
+            for (auto &network_result_pair : m_result_per_network) {
+                auto fps_per_network = network_result_pair.second.fps();
+                if (!fps_per_network) {
+                    return make_unexpected(HAILO_NOT_AVAILABLE);
+                }
+                acc_fps += fps_per_network.value();
+            }
+            return (acc_fps / static_cast<double>(m_result_per_network.size()));
+        }
+        CHECK_AS_EXPECTED(contains(m_result_per_network, network_name), HAILO_NOT_FOUND,
+            "There is no results for network {}", network_name);
+        return m_result_per_network.at(network_name).fps();
+    }
+
+    Expected<double> send_data_rate_mbit_sec(const std::string &network_name = "") const
+    {
+        if (network_name.empty()) {
+            double acc_send_data_rate_mbit_sec = 0;
+            for (auto &network_result_pair : m_result_per_network) {
+                auto send_data_rate_mbit_sec_per_network = network_result_pair.second.send_data_rate_mbit_sec();
+                if(!send_data_rate_mbit_sec_per_network) {
+                    return make_unexpected(HAILO_NOT_AVAILABLE);
+                }
+                acc_send_data_rate_mbit_sec += send_data_rate_mbit_sec_per_network.value();
+            }
+            return acc_send_data_rate_mbit_sec;
+        }
+        CHECK_AS_EXPECTED(contains(m_result_per_network, network_name), HAILO_NOT_FOUND,
+            "There is no results for network {}", network_name);
+        return m_result_per_network.at(network_name).send_data_rate_mbit_sec();
+    }
+
+    Expected<double> recv_data_rate_mbit_sec(const std::string &network_name = "") const
+    {
+        if (network_name.empty()) {
+            double acc_recv_data_rate_mbit_sec = 0;
+            for (auto &network_result_pair : m_result_per_network) {
+                auto recv_data_rate_mbit_sec_per_network = network_result_pair.second.recv_data_rate_mbit_sec();
+                if(!recv_data_rate_mbit_sec_per_network) {
+                    return make_unexpected(HAILO_NOT_AVAILABLE);
+                }
+                acc_recv_data_rate_mbit_sec += recv_data_rate_mbit_sec_per_network.value();
+            }
+            return acc_recv_data_rate_mbit_sec;
+        }
+        CHECK_AS_EXPECTED(contains(m_result_per_network, network_name), HAILO_NOT_FOUND,
+            "There is no results for network {}", network_name);
+        return m_result_per_network.at(network_name).send_data_rate_mbit_sec();
+    }
+
+    Expected<std::chrono::nanoseconds> hw_latency(const std::string &network_name = "") const
+    {
+        if (network_name.empty()) {
+            std::chrono::nanoseconds acc_hw_latency(0);
+            for (auto &network_result_pair : m_result_per_network) {
+                auto hw_latency_per_network = network_result_pair.second.hw_latency();
+                if(!hw_latency_per_network) {
+                    return make_unexpected(HAILO_NOT_AVAILABLE);
+                }
+                acc_hw_latency += hw_latency_per_network.value();
+            }
+            return static_cast<std::chrono::nanoseconds>(acc_hw_latency / m_result_per_network.size());
+        }
+        CHECK_AS_EXPECTED(contains(m_result_per_network, network_name), HAILO_NOT_FOUND,
+            "There is no results for network {}", network_name);
+        return m_result_per_network.at(network_name).hw_latency();
+    }
+
+    Expected<std::chrono::nanoseconds> overall_latency(const std::string &network_name = "") const
+    {
+        if (network_name.empty()) {
+            std::chrono::nanoseconds acc_overall_latency(0);
+            for (auto &network_result_pair : m_result_per_network) {
+                auto overall_latency_per_network = network_result_pair.second.overall_latency();
+                if(!overall_latency_per_network) {
+                    return make_unexpected(HAILO_NOT_AVAILABLE);
+                }
+                acc_overall_latency += overall_latency_per_network.value();
+            }
+            return static_cast<std::chrono::nanoseconds>(acc_overall_latency / m_result_per_network.size());
+        }
+        CHECK_AS_EXPECTED(contains(m_result_per_network, network_name), HAILO_NOT_FOUND,
+            "There is no results for network {}", network_name);
+        return m_result_per_network.at(network_name).overall_latency();
+    }
+
+    Expected<size_t> frames_count(const std::string &network_name = "") const
+    {
+        if (network_name.empty()) {
+            if (1 == m_result_per_network.size()) {
+                // If there is only one network, return its frames_count
+                auto frames_count_cpy = m_result_per_network.begin()->second.m_frames_count;
+                return frames_count_cpy;
+            }
+            return make_unexpected(HAILO_NOT_AVAILABLE);
+        }
+        CHECK_AS_EXPECTED(contains(m_result_per_network, network_name), HAILO_NOT_FOUND,
+            "There is no results for network {}", network_name);
+        auto frames_count_cpy =  m_result_per_network.at(network_name).m_frames_count;
+        return frames_count_cpy;
+    }
+
+    // <network_name, network_inference_results>
+    std::map<std::string, NetworkInferResult> m_result_per_network;
+
+    // <device_id, measurement>
+    // TODO: create a struct contianing all device measurements, and keep only one map
+    std::map<std::string, std::unique_ptr<LongPowerMeasurement>> m_power_measurements;
+    std::map<std::string, std::unique_ptr<LongPowerMeasurement>> m_current_measurements;
+    std::map<std::string, std::unique_ptr<TempMeasurementData>> m_temp_measurements;
+
+    // <vstream_name, accumulator>
+    std::map<std::string, std::map<std::string, AccumulatorPtr>> m_fps_accumulators;
+    std::map<std::string, std::map<std::string, AccumulatorPtr>> m_latency_accumulators;
+    std::map<std::string, std::map<std::string, std::vector<AccumulatorPtr>>> m_queue_size_accumulators;
+    std::map<std::string, AccumulatorPtr> m_pipeline_latency_accumulators;
+
+    void update_pipeline_stats(const std::map<std::string, std::vector<std::reference_wrapper<InputVStream>>> &inputs_per_network,
+        const std::map<std::string, std::vector<std::reference_wrapper<OutputVStream>>> &outputs_per_network)
+    {
+        for (const auto &inputs_pair : inputs_per_network) {
+            for (const auto &in_vstream : inputs_pair.second) {
+                update_accumulator_map(m_fps_accumulators, in_vstream.get().name(), in_vstream.get().get_fps_accumulators());
+                update_accumulator_map(m_latency_accumulators, in_vstream.get().name(), in_vstream.get().get_latency_accumulators());
+                update_accumulator_map(m_queue_size_accumulators, in_vstream.get().name(), in_vstream.get().get_queue_size_accumulators());
+                const auto pipeline_latency_accumulator = in_vstream.get().get_pipeline_latency_accumulator();
+                if (nullptr != pipeline_latency_accumulator) {
+                    m_pipeline_latency_accumulators.emplace(in_vstream.get().name(), pipeline_latency_accumulator);
+                }
+            }
+        }
+
+        for (const auto &outputs_pair : outputs_per_network) {
+            for (const auto &out_vstream : outputs_pair.second) {
+                update_accumulator_map(m_fps_accumulators, out_vstream.get().name(), out_vstream.get().get_fps_accumulators());
+                update_accumulator_map(m_latency_accumulators, out_vstream.get().name(), out_vstream.get().get_latency_accumulators());
+                update_accumulator_map(m_queue_size_accumulators, out_vstream.get().name(), out_vstream.get().get_queue_size_accumulators());
+                const auto pipeline_latency_accumulator = out_vstream.get().get_pipeline_latency_accumulator();
+                if (nullptr != pipeline_latency_accumulator) {
+                    m_pipeline_latency_accumulators.emplace(out_vstream.get().name(), pipeline_latency_accumulator);
+                }
+            }
+        }
+    }
+
+    void update_pipeline_stats(const std::map<std::string, std::vector<std::reference_wrapper<InputStream>>> &/*inputs_per_network*/,
+        const std::map<std::string, std::vector<std::reference_wrapper<OutputStream>>> &/*outputs_per_network*/)
+    {
+        // Overloading fow hw_object inference - not using any pipelines so nothing to update
+    }
+
+    void initialize_measurements(const std::vector<std::reference_wrapper<Device>> &devices)
+    {
+        for (const auto &device : devices) {
+            m_power_measurements.emplace(device.get().get_dev_id(), std::unique_ptr<LongPowerMeasurement>{});
+            m_current_measurements.emplace(device.get().get_dev_id(), std::unique_ptr<LongPowerMeasurement>{});
+            m_temp_measurements.emplace(device.get().get_dev_id(), std::unique_ptr<TempMeasurementData>{});
+        }
+    }
+
+    hailo_status set_power_measurement(const std::string &device_id, std::unique_ptr<LongPowerMeasurement> &&power_measure)
+    {
+        auto iter = m_power_measurements.find(device_id);
+        CHECK(m_power_measurements.end() != iter, HAILO_INVALID_ARGUMENT);
+        iter->second = std::move(power_measure);
+        return HAILO_SUCCESS;
+    }
+
+    hailo_status set_current_measurement(const std::string &device_id, std::unique_ptr<LongPowerMeasurement> &&current_measure)
+    {
+        auto iter = m_current_measurements.find(device_id);
+        CHECK(m_current_measurements.end() != iter, HAILO_INVALID_ARGUMENT);
+        iter->second = std::move(current_measure);
+        return HAILO_SUCCESS;
+    }
+
+    hailo_status set_temp_measurement(const std::string &device_id, std::unique_ptr<TempMeasurementData> &&temp_measure)
+    {
+        auto iter = m_temp_measurements.find(device_id);
+        CHECK(m_temp_measurements.end() != iter, HAILO_INVALID_ARGUMENT);
+        iter->second = std::move(temp_measure);
+        return HAILO_SUCCESS;
+    }
+
+private:
+    template <typename K, typename V>
+    static void update_accumulator_map(std::map<K, V> &map,
+        const K &key, const V &value)
+    {
+        if (value.size() == 0) {
+            return;
+        }
+
+        map.emplace(key, value);
+    }
+};
+
+#endif /* _HAILO_INFER_RESULT_ */
\ No newline at end of file
diff --git a/hailort/hailortcli/parse_hef_command.cpp b/hailort/hailortcli/parse_hef_command.cpp
new file mode 100644 (file)
index 0000000..f15ca9e
--- /dev/null
@@ -0,0 +1,161 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file parse_hef_command.cpp
+ * @brief Parses HEF and print info to stdout
+ **/
+
+#include "parse_hef_command.hpp"
+#include "common/filesystem.hpp"
+#include "hailo/hailort_common.hpp"
+
+#define TAB ("    ")
+
+static std::string add_tabs(uint8_t count)
+{
+    // Each TAB counts as 4 spaces
+    std::string res = "";
+    for (uint8_t i = 0; i < count; i++) {
+        res = res + TAB;
+    }
+    return res;
+}
+
+static std::string get_shape_str(const hailo_stream_info_t &stream_info)
+{
+    switch (stream_info.format.order)
+    {
+    case HAILO_FORMAT_ORDER_HAILO_NMS:
+        return HailoRTCommon::get_format_type_str(stream_info.format.type) + ", " + HailoRTCommon::get_format_order_str(stream_info.format.order) +
+            "(number of classes: " + std::to_string(stream_info.nms_info.number_of_classes) +
+            ", max_bboxes_per_class: "+ std::to_string(stream_info.nms_info.max_bboxes_per_class) + ")";
+    case HAILO_FORMAT_ORDER_NC:
+        return HailoRTCommon::get_format_type_str(stream_info.format.type) + ", " + HailoRTCommon::get_format_order_str(stream_info.format.order) +
+            "(" + std::to_string(stream_info.hw_shape.features) + ")";
+    case HAILO_FORMAT_ORDER_NHW:
+        return HailoRTCommon::get_format_type_str(stream_info.format.type) + ", " + HailoRTCommon::get_format_order_str(stream_info.format.order) +
+            "(" + std::to_string(stream_info.hw_shape.height) + "x" + std::to_string(stream_info.hw_shape.width) + ")";
+    default:
+        return HailoRTCommon::get_format_type_str(stream_info.format.type) + ", " + HailoRTCommon::get_format_order_str(stream_info.format.order) +
+            "(" + std::to_string(stream_info.hw_shape.height) + "x" + std::to_string(stream_info.hw_shape.width) +
+            "x" + std::to_string(stream_info.hw_shape.features) + ")";
+    }
+}
+
+static std::string get_shape_str(const hailo_vstream_info_t &vstream_info)
+{
+    switch (vstream_info.format.order)
+    {
+    case HAILO_FORMAT_ORDER_HAILO_NMS:
+        return HailoRTCommon::get_format_type_str(vstream_info.format.type) + ", " + HailoRTCommon::get_format_order_str(vstream_info.format.order) +
+            "(number of classes: " + std::to_string(vstream_info.nms_shape.number_of_classes) +
+            ", max_bboxes_per_class: " + std::to_string(vstream_info.nms_shape.max_bboxes_per_class) + ")";
+    case HAILO_FORMAT_ORDER_NC:
+        return HailoRTCommon::get_format_type_str(vstream_info.format.type) + ", " + HailoRTCommon::get_format_order_str(vstream_info.format.order) +
+            "(" + std::to_string(vstream_info.shape.features) + ")";
+    case HAILO_FORMAT_ORDER_NHW:
+        return HailoRTCommon::get_format_type_str(vstream_info.format.type) + ", " + HailoRTCommon::get_format_order_str(vstream_info.format.order) +
+            "(" +std::to_string(vstream_info.shape.height) + "x" + std::to_string(vstream_info.shape.width) + ")";
+    default:
+        return HailoRTCommon::get_format_type_str(vstream_info.format.type) + ", " + HailoRTCommon::get_format_order_str(vstream_info.format.order) +
+            "(" + std::to_string(vstream_info.shape.height) + "x" + std::to_string(vstream_info.shape.width) + "x" +
+            std::to_string(vstream_info.shape.features) + ")";
+    }
+}
+
+ParseHefCommand::ParseHefCommand(CLI::App &parent_app) :
+    Command(parent_app.add_subcommand("parse-hef", "Parse HEF to get information about its components"))
+{
+    m_app->add_option("hef", m_hef_path, "An existing HEF file/directory path")
+        ->check(CLI::ExistingFile | CLI::ExistingDirectory)
+        ->required();
+    m_app->add_flag("--parse-streams", m_parse_streams, "Parse stream infos")->default_val(false);
+    m_app->add_flag("--parse-vstreams", m_parse_vstreams, "Parse vstream infos")->default_val(true);
+}
+
+
+hailo_status ParseHefCommand::execute()
+{
+    auto is_dir = Filesystem::is_directory(m_hef_path.c_str());
+    CHECK_EXPECTED_AS_STATUS(is_dir, "Failed checking if path is directory");
+
+    if (is_dir.value()){
+        return ParseHefCommand::parse_hefs_infos_dir(m_hef_path, m_parse_streams, m_parse_vstreams);
+    } else {
+        return ParseHefCommand::parse_hefs_info(m_hef_path, m_parse_streams, m_parse_vstreams);
+    }
+}
+
+hailo_status ParseHefCommand::parse_hefs_info(const std::string &hef_path, bool stream_infos, bool vstream_infos)
+{
+    auto hef_exp = Hef::create(hef_path);
+    CHECK_EXPECTED_AS_STATUS(hef_exp, "Failed to parse HEF");
+    auto hef = hef_exp.release();
+
+    auto network_group_infos = hef.get_network_groups_infos();
+    CHECK_EXPECTED_AS_STATUS(network_group_infos);
+    for (auto &network_group_info : network_group_infos.release()) {
+        auto contexts_str = (network_group_info.is_multi_context ? "Multi Context" : "Single Context");
+        std::cout << "Network group name: " << network_group_info.name << " (" << contexts_str << ")" << std::endl;
+        auto network_infos = hef.get_network_infos(network_group_info.name);
+        CHECK_EXPECTED_AS_STATUS(network_infos, "Failed to parse networks infos");
+        for (auto &network_info : network_infos.value()) {
+            std::cout << add_tabs(1) << "Network name: " << network_info.name << std::endl;
+            if (stream_infos) {
+                std::cout << add_tabs(2) << "Stream infos:" << std::endl;
+                auto input_stream_infos = hef.get_input_stream_infos(network_info.name);
+                CHECK_EXPECTED_AS_STATUS(input_stream_infos, "Failed to parse input stream infos");
+                for (auto &stream_info : input_stream_infos.value()) {
+                    auto shape_str = get_shape_str(stream_info);
+                    std::cout << add_tabs(3) << "Input  " << stream_info.name << " " << shape_str << std::endl;
+                }
+                auto output_stream_infos = hef.get_output_stream_infos(network_info.name);
+                CHECK_EXPECTED_AS_STATUS(output_stream_infos, "Failed to parse output stream infos");
+                for (auto &stream_info : output_stream_infos.value()) {
+                    auto shape_str = get_shape_str(stream_info);
+                    std::cout << add_tabs(3) << "Output " << stream_info.name << " " << shape_str << std::endl;
+                }
+            }
+            if (vstream_infos) {
+                std::cout << add_tabs(2) << "VStream infos:" << std::endl;
+                auto input_vstream_infos = hef.get_input_vstream_infos(network_info.name);
+                CHECK_EXPECTED_AS_STATUS(input_vstream_infos, "Failed to parse input vstream infos");
+                for (auto &vstream_info : input_vstream_infos.value()) {
+                    auto shape_str = get_shape_str(vstream_info);
+                    std::cout << add_tabs(3) << "Input  " << vstream_info.name << " " << shape_str << std::endl;
+                }
+                auto output_vstream_infos = hef.get_output_vstream_infos(network_info.name);
+                CHECK_EXPECTED_AS_STATUS(output_vstream_infos, "Failed to parse output vstream infos");
+                for (auto &vstream_info : output_vstream_infos.value()) {
+                    auto shape_str = get_shape_str(vstream_info);
+                    std::cout << add_tabs(3) << "Output " << vstream_info.name << " " << shape_str << std::endl;
+                }
+            }
+        }
+    }
+    std::cout << std::endl;
+    return HAILO_SUCCESS;
+}
+
+hailo_status ParseHefCommand::parse_hefs_infos_dir(const std::string &hef_path, bool stream_infos, bool vstream_infos)
+{
+    bool contains_hef = false;
+    std::string hef_dir = hef_path;
+    const auto files = Filesystem::get_files_in_dir_flat(hef_dir);
+    CHECK_EXPECTED_AS_STATUS(files);
+
+    for (const auto &full_path : files.value()) {
+        if (Filesystem::has_suffix(full_path, ".hef")) {
+            contains_hef = true;
+            std::cout << std::string(80, '*') << std::endl << "Parsing " << full_path << ":"<< std::endl;
+            auto status = ParseHefCommand::parse_hefs_info(full_path, stream_infos, vstream_infos);
+            CHECK_SUCCESS(status, "Failed to parse HEF {}", full_path);
+        }
+    }
+
+    CHECK(contains_hef, HAILO_INVALID_ARGUMENT, "No HEF files were found in the directory: {}", hef_dir);
+
+    return HAILO_SUCCESS;
+}
diff --git a/hailort/hailortcli/parse_hef_command.hpp b/hailort/hailortcli/parse_hef_command.hpp
new file mode 100644 (file)
index 0000000..f85f968
--- /dev/null
@@ -0,0 +1,37 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file parse_hef_command.hpp
+ * @brief Parse HEF and print metadata to stdout
+ **/
+
+#ifndef _HAILO_PARSE_COMMAND_COMMAND_HPP_
+#define _HAILO_PARSE_COMMAND_COMMAND_HPP_
+
+#include "hailortcli.hpp"
+#include "command.hpp"
+
+#include "hailo/hailort.h"
+#include "hailo/device.hpp"
+#include "hailo/buffer.hpp"
+#include "CLI/CLI.hpp"
+
+
+class ParseHefCommand : public Command {
+public:
+    explicit ParseHefCommand(CLI::App &parent_app);
+
+    virtual hailo_status execute() override;
+
+private:
+    static hailo_status parse_hefs_infos_dir(const std::string &hef_path, bool stream_infos, bool vstream_infos);
+    static hailo_status parse_hefs_info(const std::string &hef_path, bool stream_infos, bool vstream_infos);
+
+    std::string m_hef_path;
+    bool m_parse_streams;
+    bool m_parse_vstreams;
+};
+
+#endif /* _HAILO_PARSE_COMMAND_COMMAND_HPP_ */
diff --git a/hailort/hailortcli/power_measurement_command.cpp b/hailort/hailortcli/power_measurement_command.cpp
new file mode 100644 (file)
index 0000000..493e475
--- /dev/null
@@ -0,0 +1,283 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file power_measurement_command.cpp
+ * @brief Measure power of Hailo chip
+ **/
+
+#include "power_measurement_command.hpp"
+#include <thread>
+
+#define POWER_MEASUREMENT_DELAY_MS(__sample_period, __average_factor) \
+    (static_cast<uint32_t>((__sample_period) / 1000.0 * (__average_factor) * 2 * 1.2))
+
+
+PowerMeasurementSubcommand::PowerMeasurementSubcommand(CLI::App &parent_app) :
+    DeviceCommand(parent_app.add_subcommand("measure-power", "Measures power consumption")), m_params(),
+    m_power_measurement_duration(0)
+{
+    m_app->add_option("--duration", m_power_measurement_duration, "The duration in seconds to measure power consumption")
+        ->check(CLI::Validator(CLI::PositiveNumber));
+
+    CLI::Option *sampling_period = m_app->add_option("--sampling-period", m_params.sampling_period, "Sampling Period")
+        ->needs("--duration");
+    CLI::Option *averaging_factor = m_app->add_option("--averaging-factor", m_params.averaging_factor, "Averaging Factor")
+        ->needs("--duration");
+    init_dvm_option(m_app->add_option("--dvm", m_params.dvm_option,
+                "DVM type. \n\
+Which DVM will be measured. Default (AUTO) will be different according to the board: \n\
+Default (AUTO) for EVB is an approximation to the total power consumption of the chip in PCIe setups. \n\
+It sums VDD_CORE, MIPI_AVDD and AVDD_H. Only POWER can measured with this option. \n\
+Default (AUTO) for platforms supporting current monitoring (such as M.2 and mPCIe): OVERCURRENT_PROTECTION"));
+    init_measurement_type_option(m_app->add_option("--type", m_params.measurement_type, "Power Measurement type"));
+    init_sampling_period_option(sampling_period);
+    init_averaging_factor_option(averaging_factor);
+}
+
+void PowerMeasurementSubcommand::init_dvm_option(CLI::Option *dvm_option)
+{
+    dvm_option->transform(HailoCheckedTransformer<hailo_dvm_options_t>({
+            { "AUTO", HAILO_DVM_OPTIONS_AUTO },
+            { "VDD_CORE", HAILO_DVM_OPTIONS_VDD_CORE },
+            { "VDD_IO", HAILO_DVM_OPTIONS_VDD_IO },
+            { "MIPI_AVDD", HAILO_DVM_OPTIONS_MIPI_AVDD },
+            { "MIPI_AVDD_H", HAILO_DVM_OPTIONS_MIPI_AVDD_H },
+            { "USB_AVDD_IO", HAILO_DVM_OPTIONS_USB_AVDD_IO },
+            { "VDD_TOP", HAILO_DVM_OPTIONS_VDD_TOP },
+            { "USB_AVDD_IO_HV", HAILO_DVM_OPTIONS_USB_AVDD_IO_HV },
+            { "AVDD_H", HAILO_DVM_OPTIONS_AVDD_H },
+            { "SDIO_VDD_IO", HAILO_DVM_OPTIONS_SDIO_VDD_IO },
+            { "OVERCURRENT_PROTECTION", HAILO_DVM_OPTIONS_OVERCURRENT_PROTECTION }
+        }))
+        ->default_val("AUTO");
+}
+
+void PowerMeasurementSubcommand::init_measurement_type_option(CLI::Option *measurement_type)
+{
+    measurement_type->transform(HailoCheckedTransformer<hailo_power_measurement_types_t>({
+            { "AUTO", HAILO_POWER_MEASUREMENT_TYPES__AUTO },
+            { "SHUNT_VOLTAGE", HAILO_POWER_MEASUREMENT_TYPES__SHUNT_VOLTAGE },
+            { "BUS_VOLTAGE", HAILO_POWER_MEASUREMENT_TYPES__BUS_VOLTAGE },
+            { "POWER", HAILO_POWER_MEASUREMENT_TYPES__POWER },
+            { "CURRENT", HAILO_POWER_MEASUREMENT_TYPES__CURRENT }
+        }))
+        ->default_val("AUTO");
+}
+
+void PowerMeasurementSubcommand::init_sampling_period_option(CLI::Option *sampling_period)
+{
+    sampling_period->default_val(1100)
+        ->transform(CLI::IsMember({140, 204, 332, 588, 1100, 2116, 4156, 8244}));
+}
+
+void PowerMeasurementSubcommand::init_averaging_factor_option(CLI::Option *averaging_factor)
+{
+    averaging_factor->default_val(256)
+        ->transform(CLI::IsMember({1, 4, 16, 64, 128, 256, 512, 1024}));
+}
+
+hailo_status PowerMeasurementSubcommand::execute_on_device(Device &device)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+
+    if (0 < m_power_measurement_duration) {
+        status = run_long_power_measurement(device);
+    } else {
+        status = run_single_power_measurement(device);
+    }
+
+    if (HAILO_SUCCESS != status) {
+        std::cerr << "Failed power measurement, status " << status << std::endl;
+        return status;
+    }
+
+    return HAILO_SUCCESS;
+}
+
+Expected<LongPowerMeasurement> PowerMeasurementSubcommand::start_power_measurement(Device &device,
+    hailo_dvm_options_t dvm, hailo_power_measurement_types_t measurement_type, uint32_t sampling_period,
+    uint32_t averaging_factor)
+{
+    hailo_sampling_period_t sampling_period_enum = get_sampling_period(
+        sampling_period);
+    if (HAILO_SAMPLING_PERIOD_MAX_ENUM == sampling_period_enum) {
+        std::cerr << "Failed to parse sampling period: " << sampling_period << std::endl;
+        return make_unexpected(HAILO_NOT_FOUND);
+    }
+
+    hailo_averaging_factor_t averaging_factor_enum = get_averaging_factor(
+        averaging_factor);
+    if (HAILO_AVERAGE_FACTOR_MAX_ENUM == averaging_factor_enum) {
+        std::cerr << "Failed to parse averaging factor: " << averaging_factor << std::endl;
+        return make_unexpected(HAILO_NOT_FOUND);
+    }
+
+    hailo_status status = hailo_stop_power_measurement(reinterpret_cast<hailo_device>(&device));
+    if (HAILO_SUCCESS != status) {
+        std::cerr << "Failed initial power measurement stop, status " << status << std::endl;
+        return make_unexpected(status);
+    }
+
+    status = hailo_set_power_measurement(reinterpret_cast<hailo_device>(&device), 0, dvm, measurement_type);
+    if (HAILO_SUCCESS != status) {
+        std::cerr << "Failed to set power measurement parameters, status " << status << std::endl;
+        return make_unexpected(status);
+    }
+
+    uint32_t measurement_delay = POWER_MEASUREMENT_DELAY_MS(sampling_period, averaging_factor);
+    // There is no logical way that measurement delay can be 0 - because sampling_period and averaging_factor cant be 0
+    // Hence if it is 0 - it means it was 0.xx and we want to round up to 1 in that case
+    if (0 == measurement_delay) {
+        measurement_delay = 1;
+    }
+    status = hailo_start_power_measurement(reinterpret_cast<hailo_device>(&device), measurement_delay,
+        averaging_factor_enum, sampling_period_enum);
+    if (HAILO_SUCCESS != status) {
+        std::cerr << "Failed to start power measurement, status " << status << std::endl;
+        return make_unexpected(status);
+    }
+
+    return LongPowerMeasurement(device, measurement_type);
+}
+
+LongPowerMeasurement::LongPowerMeasurement(Device &device,
+    hailo_power_measurement_types_t measurement_type) : m_device(device),
+    m_measurement_type(measurement_type), m_data(), m_power_units()
+{}
+
+hailo_status LongPowerMeasurement::stop()
+{
+    hailo_status status = hailo_stop_power_measurement(reinterpret_cast<hailo_device>(&m_device));
+    if (HAILO_SUCCESS != status) {
+        std::cerr << "Failed to stop power measurement, status " << status << std::endl;
+        return status;
+    }
+
+    status = hailo_get_power_measurement(reinterpret_cast<hailo_device>(&m_device), 0, true, &m_data);
+    if (HAILO_SUCCESS != status) {
+        std::cerr << "Failed to get power measurement results, status " << status << std::endl;
+        return status;
+    }
+
+    const char *power_units = PowerMeasurementSubcommand::get_power_units(m_measurement_type);
+    if (nullptr == power_units) {
+        std::cerr << "Failed to get power measurement units of type " << m_measurement_type << std::endl;
+        return HAILO_NOT_FOUND;
+    }
+    m_power_units = power_units;
+
+    return HAILO_SUCCESS;
+}
+
+hailo_sampling_period_t PowerMeasurementSubcommand::get_sampling_period(uint32_t sampling_period)
+{
+    switch (sampling_period) {
+    case 140:
+        return HAILO_SAMPLING_PERIOD_140US;
+    case 204:
+        return HAILO_SAMPLING_PERIOD_204US;
+    case 332:
+        return HAILO_SAMPLING_PERIOD_332US;
+    case 588:
+        return HAILO_SAMPLING_PERIOD_588US;
+    case 1100:
+        return HAILO_SAMPLING_PERIOD_1100US;
+    case 2116:
+        return HAILO_SAMPLING_PERIOD_2116US;
+    case 4156:
+        return HAILO_SAMPLING_PERIOD_4156US;
+    case 8244:
+        return HAILO_SAMPLING_PERIOD_8244US;
+    default:
+        return HAILO_SAMPLING_PERIOD_MAX_ENUM;
+    }
+}
+
+hailo_averaging_factor_t PowerMeasurementSubcommand::get_averaging_factor(uint32_t averaging_factor)
+{
+    switch (averaging_factor) {
+    case 1:
+        return HAILO_AVERAGE_FACTOR_1;
+    case 4:
+        return HAILO_AVERAGE_FACTOR_4;
+    case 16:
+        return HAILO_AVERAGE_FACTOR_16;
+    case 64:
+        return HAILO_AVERAGE_FACTOR_64;
+    case 128:
+        return HAILO_AVERAGE_FACTOR_128;
+    case 256:
+        return HAILO_AVERAGE_FACTOR_256;
+    case 512:
+        return HAILO_AVERAGE_FACTOR_512;
+    case 1024:
+        return HAILO_AVERAGE_FACTOR_1024;
+    default:
+        return HAILO_AVERAGE_FACTOR_MAX_ENUM;
+    }
+}
+
+hailo_status PowerMeasurementSubcommand::run_long_power_measurement(Device &device)
+{
+    auto long_power_measurement = start_power_measurement(device, m_params.dvm_option,
+        m_params.measurement_type, m_params.sampling_period, m_params.averaging_factor);
+    if (!long_power_measurement) {
+        return long_power_measurement.status();
+    }
+
+    std::this_thread::sleep_for(std::chrono::seconds(m_power_measurement_duration));
+
+    hailo_status status = long_power_measurement.value().stop();
+    if (HAILO_SUCCESS != status) {
+        return status;
+    }
+
+    const hailo_power_measurement_data_t &measurement_data = long_power_measurement.value().data();
+    const std::string &power_units = long_power_measurement.value().power_units();
+
+    std::cout << "Measuring power consumption over " << m_power_measurement_duration << " seconds:" << std::endl;
+    std::cout << "Total samples: " << measurement_data.total_number_of_samples << std::endl;
+    std::cout << "Max value (" << power_units << "): " << measurement_data.max_value << std::endl;
+    std::cout << "Min value (" << power_units << "): " << measurement_data.min_value << std::endl;
+    std::cout << "Average value (" << power_units << "): " << measurement_data.average_value << std::endl;
+    std::cout << "Average time per sample (ms): " << measurement_data.average_time_value_milliseconds << std::endl;
+    return HAILO_SUCCESS;
+}
+
+const char *PowerMeasurementSubcommand::get_power_units(hailo_power_measurement_types_t measurement_type)
+{
+    switch (measurement_type) {
+    case HAILO_POWER_MEASUREMENT_TYPES__SHUNT_VOLTAGE:
+    case HAILO_POWER_MEASUREMENT_TYPES__BUS_VOLTAGE:
+        return "mV";
+    case HAILO_POWER_MEASUREMENT_TYPES__AUTO:
+    case HAILO_POWER_MEASUREMENT_TYPES__POWER:
+        return "W";
+    case HAILO_POWER_MEASUREMENT_TYPES__CURRENT:
+        return "mA";
+    default:
+        return nullptr;
+    };
+}
+
+hailo_status PowerMeasurementSubcommand::run_single_power_measurement(Device &device)
+{
+    float32_t measurement = 0.0f;
+    hailo_status status = hailo_power_measurement(reinterpret_cast<hailo_device>(&device), m_params.dvm_option, m_params.measurement_type,
+        &measurement);
+    if (HAILO_SUCCESS != status) {
+        std::cerr << "Failed to get power measurement results, status " << status << std::endl;
+        return status;
+    }
+
+    const char *power_units = get_power_units(m_params.measurement_type);
+    if (nullptr == power_units) {
+        std::cerr << "Failed to get power measurement units of type " << m_params.measurement_type << std::endl;
+        return HAILO_NOT_FOUND;
+    }
+
+    std::cout << "Current power consumption (" << power_units << "): " << measurement << std::endl;
+    return HAILO_SUCCESS;
+}
diff --git a/hailort/hailortcli/power_measurement_command.hpp b/hailort/hailortcli/power_measurement_command.hpp
new file mode 100644 (file)
index 0000000..5d00fee
--- /dev/null
@@ -0,0 +1,75 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file power_measurement_command.hpp
+ * @brief Measure power of Hailo chip
+ **/
+
+#ifndef _HAILO_POWER_MEASUREMENT_COMMAND_HPP_
+#define _HAILO_POWER_MEASUREMENT_COMMAND_HPP_
+
+#include "hailortcli.hpp"
+#include "command.hpp"
+#include "hailo/hailort.h"
+#include "hailo/device.hpp"
+#include "CLI/CLI.hpp"
+
+class LongPowerMeasurement final {
+public:
+    LongPowerMeasurement(Device &device, hailo_power_measurement_types_t measurement_type);
+    ~LongPowerMeasurement() = default;
+    hailo_status stop();
+
+    const hailo_power_measurement_data_t &data() const
+    {
+        return m_data;
+    }
+
+    const std::string &power_units() const
+    {
+        return m_power_units;
+    }
+
+private:
+    Device &m_device;
+    hailo_power_measurement_types_t m_measurement_type;
+    hailo_power_measurement_data_t m_data;
+    std::string m_power_units;
+};
+
+class PowerMeasurementSubcommand final : public DeviceCommand {
+public:
+    explicit PowerMeasurementSubcommand(CLI::App &parent_app);
+
+    static Expected<LongPowerMeasurement> start_power_measurement(Device &device,
+        hailo_dvm_options_t dvm, hailo_power_measurement_types_t measurement_type,
+        uint32_t sampling_period, uint32_t averaging_factor);
+    static void init_sampling_period_option(CLI::Option *sampling_period);
+    static void init_averaging_factor_option(CLI::Option *averaging_factor);
+    static hailo_sampling_period_t get_sampling_period(uint32_t sampling_period);
+    static hailo_averaging_factor_t get_averaging_factor(uint32_t averaging_factor);
+    static const char *get_power_units(hailo_power_measurement_types_t measurement_type);
+
+protected:
+    hailo_status execute_on_device(Device &device) override;
+
+private:
+    struct power_measurement_params {
+        hailo_dvm_options_t dvm_option;
+        hailo_power_measurement_types_t measurement_type;
+        uint32_t sampling_period;
+        uint32_t averaging_factor;
+    };
+
+    power_measurement_params m_params;
+    uint32_t m_power_measurement_duration;
+
+    static void init_dvm_option(CLI::Option *dvm_option);
+    static void init_measurement_type_option(CLI::Option *measurement_type);
+    hailo_status run_long_power_measurement(Device &device);
+    hailo_status run_single_power_measurement(Device &device);
+};
+
+#endif /* _HAILO_POWER_MEASUREMENT_COMMAND_HPP_ */
diff --git a/hailort/hailortcli/run_command.cpp b/hailort/hailortcli/run_command.cpp
new file mode 100644 (file)
index 0000000..fc73096
--- /dev/null
@@ -0,0 +1,1229 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file run_command.cpp
+ * @brief Run inference on hailo device
+ **/
+
+#include "run_command.hpp"
+#include "hailortcli.hpp"
+#include "inference_progress.hpp"
+#include "infer_stats_printer.hpp"
+#include "temp_measurement.hpp"
+#include "graph_printer.hpp"
+#if defined(__GNUC__)
+// TODO: Support on windows (HRT-5919)
+#include "download_action_list_command.hpp"
+#endif
+#include "common.hpp"
+
+#include "common/file_utils.hpp"
+#include "common/async_thread.hpp"
+#include "common/barrier.hpp"
+#include "common/latency_meter.hpp"
+#include "common/filesystem.hpp"
+#include "hailo/network_group.hpp"
+#include "hailo/hef.hpp"
+#include "hailo/vstream.hpp"
+#include "hailo/vdevice.hpp"
+
+#include <vector>
+#include <algorithm>
+#include <signal.h>
+#include <condition_variable>
+std::condition_variable wait_for_exit_cv;
+
+/* The SIGUSR1 and SIGUSR2 signals are set aside for you to use any way you want.
+    They’re useful for simple interprocess communication. */
+#define USER_SIGNAL (SIGUSR1)
+
+constexpr size_t OVERALL_LATENCY_TIMESTAMPS_LIST_LENGTH (512);
+constexpr uint32_t DEFAULT_TIME_TO_RUN_SECONDS = 5;
+#ifndef HAILO_EMULATOR
+constexpr std::chrono::milliseconds TIME_TO_WAIT_FOR_CONFIG(300);
+#define HAILORTCLI_DEFAULT_VSTREAM_TIMEOUT_MS (HAILO_DEFAULT_VSTREAM_TIMEOUT_MS)
+#else /* ifndef HAILO_EMULATOR */
+constexpr std::chrono::milliseconds TIME_TO_WAIT_FOR_CONFIG(30000);
+#define HAILORTCLI_DEFAULT_VSTREAM_TIMEOUT_MS (HAILO_DEFAULT_VSTREAM_TIMEOUT_MS * 100)
+#endif /* ifndef HAILO_EMULATOR */
+
+#ifndef _MSC_VER
+void user_signal_handler_func(int signum)
+{
+    if (USER_SIGNAL == signum)
+    {
+        wait_for_exit_cv.notify_one();
+    }
+}
+#endif
+
+hailo_status wait_for_exit_with_timeout(std::chrono::seconds time_to_run)
+{
+#if defined(__unix__)
+    sighandler_t prev_handler = signal(USER_SIGNAL, user_signal_handler_func);
+    CHECK(prev_handler !=  SIG_ERR, HAILO_INVALID_OPERATION, "signal failed, errno = {}", errno);
+    std::mutex mutex;
+    std::unique_lock<std::mutex> condition_variable_lock(mutex);
+    wait_for_exit_cv.wait_for(condition_variable_lock, time_to_run);
+#else
+    std::this_thread::sleep_for(time_to_run);
+#endif
+    return HAILO_SUCCESS;
+}
+
+bool should_measure_pipeline_stats(const inference_runner_params& params)
+{
+    const std::vector<bool> measure_flags = { params.pipeline_stats.measure_elem_fps,
+        params.pipeline_stats.measure_elem_latency, params.pipeline_stats.measure_elem_queue_size,
+        params.pipeline_stats.measure_vstream_fps, params.pipeline_stats.measure_vstream_latency
+    };
+
+    return std::any_of(measure_flags.cbegin(), measure_flags.cend(), [](bool x){ return x; });
+}
+
+static void add_run_command_params(CLI::App *run_subcommand, inference_runner_params& params)
+{
+    // TODO: init values in RunCommand ctor
+    params.measure_latency = false;
+    params.measure_overall_latency = false;
+    params.power_measurement.measure_power = false;
+    params.power_measurement.measure_current = false;
+    params.show_progress = true;
+    params.time_to_run = 0;
+    params.frames_count = 0;
+    params.measure_temp = false;
+
+    add_device_options(run_subcommand, params.device_params);
+    add_vdevice_options(run_subcommand, params.device_params);
+
+    auto hef_new = run_subcommand->add_option("hef", params.hef_path, "An existing HEF file/directory path")
+        ->check(CLI::ExistingFile | CLI::ExistingDirectory);
+    auto hef_old = run_subcommand->add_option("--hef", params.hef_path, "An existing HEF file/directory path")
+        ->check(CLI::ExistingFile | CLI::ExistingDirectory);
+
+    // Allow multiple subcommands (see https://cliutils.github.io/CLI11/book/chapters/subcommands.html)
+    run_subcommand->require_subcommand(0, 0);
+
+    CLI::Option *frames_count = run_subcommand->add_option("-c,--frames-count", params.frames_count,
+        "Frames count to run")
+        ->check(CLI::PositiveNumber);
+    run_subcommand->add_option("-t,--time-to-run", params.time_to_run, "Time to run (seconds)")
+        ->check(CLI::PositiveNumber)
+        ->excludes(frames_count);
+    auto total_batch_size = run_subcommand->add_option("--batch-size", params.batch_size,
+        "Inference batch (should be a divisor of --frames-count if provided).\n"
+        "This batch applies to the whole network_group. for differential batch per network, see --net-batch-size")
+        ->check(CLI::PositiveNumber)
+        ->default_val(HAILO_DEFAULT_BATCH_SIZE);
+
+    run_subcommand->add_option("--net-batch-size", params.batch_per_network,
+        "Inference batch per network (network names can be found using parse-hef command).\n"
+        "In case of multiple networks, usage is as follows: --net-batch-size <network_name_1>=<batch_size_1> --net-batch-size <network_name_2>=<batch_size_2>")
+        ->check(NetworkBatchMap)
+        ->excludes(total_batch_size)
+        ->excludes(frames_count);
+
+    run_subcommand->add_option("--power-mode", params.power_mode,
+        "Core power mode (PCIE only; ignored otherwise)")
+        ->transform(HailoCheckedTransformer<hailo_power_mode_t>({
+            { "performance", hailo_power_mode_t::HAILO_POWER_MODE_PERFORMANCE },
+            { "ultra_performance", hailo_power_mode_t::HAILO_POWER_MODE_ULTRA_PERFORMANCE }
+        }))
+        ->default_val("performance");
+    run_subcommand->add_option("-m,--mode", params.mode, "Inference mode")
+        ->transform(HailoCheckedTransformer<InferMode>({
+            { "streaming", InferMode::STREAMING },
+            { "hw_only", InferMode::HW_ONLY }
+        }))
+        ->default_val("streaming");
+    run_subcommand->add_option("--csv", params.csv_output, "If set print the output as csv to the specified path");
+    run_subcommand->add_option("--input-files", params.inputs_name_and_file_path)
+        ->check(InputNameToFileMap);
+    run_subcommand->add_flag("--measure-latency", params.measure_latency,
+        "Measure network latency");
+    run_subcommand->add_flag("--measure-overall-latency", params.measure_overall_latency,
+        "Include overall latency measurement")
+        ->needs("--measure-latency");
+    
+    static const char *DOT_SUFFIX = ".dot";
+    run_subcommand->add_option("--dot", params.dot_output,
+        "If set print the pipeline graph as a .dot file at the specified path")
+        ->check(FileSuffixValidator(DOT_SUFFIX));
+    CLI::Option *measure_power_opt = run_subcommand->add_flag("--measure-power",
+        params.power_measurement.measure_power, "Measure power consumption");
+    CLI::Option *measure_current_opt = run_subcommand->add_flag("--measure-current",
+        params.power_measurement.measure_current, "Measure current")->excludes(measure_power_opt);
+    measure_power_opt->excludes(measure_current_opt);
+
+    run_subcommand->add_flag("--show-progress,!--dont-show-progress", params.show_progress,
+        "Show inference progress")
+        ->default_val("true");
+
+    auto transformation_group = run_subcommand->add_option_group("Transformations");
+    transformation_group->add_option("--quantized", params.transform.quantized,
+        "true means the tool assumes that the data is already quantized,\n"
+        "false means it is the tool's responsability to quantize (scale) the data.")
+        ->default_val("true");
+    transformation_group->add_option("--user-format-type", params.transform.format_type,
+        "The host data type")
+        ->transform(HailoCheckedTransformer<hailo_format_type_t>({
+            { "auto", HAILO_FORMAT_TYPE_AUTO },
+            { "uint8", HAILO_FORMAT_TYPE_UINT8 },
+            { "uint16", HAILO_FORMAT_TYPE_UINT16 },
+            { "float32", HAILO_FORMAT_TYPE_FLOAT32 }
+        }))
+        ->default_val("auto");
+
+    auto *measure_stats_subcommand = run_subcommand->add_subcommand("measure-stats", "Pipeline Statistics Measurements");
+    measure_stats_subcommand->add_flag("--elem-fps", params.pipeline_stats.measure_elem_fps,
+        "Measure the fps of each pipeline element separately");
+    measure_stats_subcommand->add_flag("--elem-latency", params.pipeline_stats.measure_elem_latency,
+        "Measure the latency of each pipeline element separately")
+        ->group(""); // --elem-latency will be hidden in the --help print.
+    measure_stats_subcommand->add_flag("--elem-queue-size", params.pipeline_stats.measure_elem_queue_size,
+        "Measure the queue size of each pipeline element separately");
+
+    // TODO (HRT-4522): Remove comment-out
+    // measure_stats_subcommand->add_flag("--vstream-fps", params.pipeline_stats.measure_vstream_fps,
+    //     "Measure the fps of the entire vstream pipeline");
+
+    measure_stats_subcommand->add_flag("--vstream-latency", params.pipeline_stats.measure_vstream_latency,
+        "Measure the latency of the entire vstream pipeline")
+        ->group(""); // --vstream-latency will be hidden in the --help print.
+    measure_stats_subcommand->add_option("--output-path", params.pipeline_stats.pipeline_stats_output_path,
+        "Path to a '.csv' file that will contain the measured pipeline statistics")
+        ->default_val("pipeline_stats.csv");
+    measure_stats_subcommand->parse_complete_callback([&params]() {
+        PARSE_CHECK(should_measure_pipeline_stats(params),
+            "No measurement flags provided; Run 'hailortcli run measure-stats --help' for options");
+    });
+
+    // TODO: Support on windows (HRT-5919)
+    #if defined(__GNUC__)
+    // TODO: Unhide (HRT-6035)
+    // Unnamed "option groups" hide subcommands/options from the help message
+    // (see https://github.com/CLIUtils/CLI11/blob/main/README.md)
+    auto *hidden_group = run_subcommand->add_option_group("");
+    auto *download_runtime_data_subcommand = hidden_group->add_subcommand("download-runtime-data",
+        "Download runtime data to be used by the Profiler");
+    static const char *JSON_SUFFIX = ".json";
+    download_runtime_data_subcommand->add_option("--output-path",
+        params.runtime_data.runtime_data_output_path, "Runtime data output file path")
+        ->default_val("context_action_list.json")
+        ->check(FileSuffixValidator(JSON_SUFFIX));
+    download_runtime_data_subcommand->parse_complete_callback([&params]() {
+        // If this subcommand was parsed, then we need to download runtime_data
+        params.runtime_data.download_runtime_data = true;
+    });
+    #endif
+
+    auto measure_power_group = run_subcommand->add_option_group("Measure Power/Current");
+    CLI::Option *power_sampling_period = measure_power_group->add_option("--sampling-period",
+        params.power_measurement.sampling_period, "Sampling Period");
+    CLI::Option *power_averaging_factor = measure_power_group->add_option("--averaging-factor",
+        params.power_measurement.averaging_factor, "Averaging Factor");
+    PowerMeasurementSubcommand::init_sampling_period_option(power_sampling_period);
+    PowerMeasurementSubcommand::init_averaging_factor_option(power_averaging_factor);
+
+    run_subcommand->add_flag("--measure-temp", params.measure_temp, "Measure chip temperature");
+
+    run_subcommand->parse_complete_callback([&params, hef_new, hef_old, power_sampling_period,
+            power_averaging_factor, measure_power_opt, measure_current_opt]() {
+        PARSE_CHECK(hef_new->empty() ^ hef_old->empty(), "Single HEF file/directory is required");
+        bool is_hw_only = InferMode::HW_ONLY == params.mode;
+        params.transform.transform = (!is_hw_only || (params.inputs_name_and_file_path.size() > 0));
+        PARSE_CHECK((!params.transform.quantized || (HAILO_FORMAT_TYPE_AUTO == params.transform.format_type)),
+            "User data type must be auto when quantized is set");
+        bool has_oneof_measure_flags = (!measure_power_opt->empty() || !measure_current_opt->empty());
+        PARSE_CHECK(power_sampling_period->empty() || has_oneof_measure_flags,
+            "--sampling-period requires --measure-power or --measure-current");
+        PARSE_CHECK(power_averaging_factor->empty() || has_oneof_measure_flags,
+            "--averaging-period factor --measure-power or --measure-current");
+        PARSE_CHECK(((0 != params.time_to_run) || (0 == (params.frames_count % params.batch_size))),
+            "--batch-size should be a divisor of --frames-count if provided");
+        // TODO HRT-5363 support multiple devices
+        PARSE_CHECK((params.device_params.vdevice_params.device_count == 1) || params.csv_output.empty() ||
+            !(params.power_measurement.measure_power || params.power_measurement.measure_current || params.measure_temp),
+            "Writing measurements in csv format is not supported for multiple devices");
+
+        if ((0 == params.time_to_run) && (0 == params.frames_count)) {
+            // Use default
+            params.time_to_run = DEFAULT_TIME_TO_RUN_SECONDS;
+        }
+    });
+
+    std::vector<DeprecationActionPtr> actions{
+        std::make_shared<OptionDeprecation>(hef_old, "hef (positional)"),
+    };
+    hailo_deprecate_options(run_subcommand, actions, false);
+}
+
+std::map<std::string, std::string> format_strings_to_key_value_pairs(const std::vector<std::string> &key_value_pairs_str) {
+    std::map<std::string, std::string> pairs = {};
+    for (const auto &key_value_pair_str : key_value_pairs_str) {
+        size_t first_delimiter = key_value_pair_str.find("=");
+        auto key = key_value_pair_str.substr(0, first_delimiter);
+        auto file_path = key_value_pair_str.substr(first_delimiter + 1);
+        pairs.emplace(key, file_path);
+    }
+    return pairs;
+}
+
+std::string format_type_to_string(hailo_format_type_t format_type) {
+    switch (format_type) {
+    case HAILO_FORMAT_TYPE_AUTO:
+        return "auto";
+    case HAILO_FORMAT_TYPE_UINT8:
+        return "uint8";
+    case HAILO_FORMAT_TYPE_UINT16:
+        return "uint16";
+    case HAILO_FORMAT_TYPE_FLOAT32:
+        return "float32";
+    default:
+        return "<INVALID_TYPE>";
+    }
+}
+
+static hailo_vstream_stats_flags_t inference_runner_params_to_vstream_stats_flags(
+    const pipeline_stats_measurement_params &params)
+{
+    hailo_vstream_stats_flags_t result = HAILO_VSTREAM_STATS_NONE;
+    if (params.measure_vstream_fps) {
+        result |= HAILO_VSTREAM_STATS_MEASURE_FPS;
+    }
+    if (params.measure_vstream_latency) {
+        result |= HAILO_VSTREAM_STATS_MEASURE_LATENCY;
+    }
+
+    return result;
+}
+
+static hailo_pipeline_elem_stats_flags_t inference_runner_params_to_pipeline_elem_stats_flags(
+    const pipeline_stats_measurement_params &params)
+{
+    hailo_pipeline_elem_stats_flags_t result = HAILO_PIPELINE_ELEM_STATS_NONE;
+    if (params.measure_elem_fps) {
+        result |= HAILO_PIPELINE_ELEM_STATS_MEASURE_FPS;
+    }
+    if (params.measure_elem_latency) {
+        result |= HAILO_PIPELINE_ELEM_STATS_MEASURE_LATENCY;
+    }
+    if (params.measure_elem_queue_size) {
+        result |= HAILO_PIPELINE_ELEM_STATS_MEASURE_QUEUE_SIZE;
+    }
+
+    return result;
+}
+
+static size_t total_send_frame_size(const std::vector<std::reference_wrapper<InputStream>> &input_streams)
+{
+    size_t total_send_frame_size = 0;
+    for (const auto &input_stream : input_streams) {
+        total_send_frame_size += input_stream.get().get_frame_size();
+    }
+    return total_send_frame_size;
+}
+
+static size_t total_recv_frame_size(const std::vector<std::reference_wrapper<OutputStream>> &output_streams)
+{
+    size_t total_recv_frame_size = 0;
+    for (const auto &output_stream : output_streams) {
+        total_recv_frame_size += output_stream.get().get_frame_size();
+    }
+    return total_recv_frame_size;
+}
+
+template<typename SendObject>
+hailo_status send_loop(const inference_runner_params &params, SendObject &send_object,
+    std::map<std::string, Buffer> &input_dataset, Barrier &barrier, LatencyMeter &overall_latency_meter, uint16_t batch_size)
+{
+    assert(input_dataset.find(send_object.name()) != input_dataset.end());
+    const Buffer &input_buffer = input_dataset.at(send_object.name());
+    assert((input_buffer.size() % send_object.get_frame_size()) == 0);
+    const size_t frames_in_buffer = input_buffer.size() / send_object.get_frame_size();
+    // TODO: pass the correct batch (may be different between networks)
+    uint32_t num_of_batches = (0 == params.time_to_run ? (params.frames_count / batch_size) : UINT32_MAX);
+    for (uint32_t i = 0; i < num_of_batches; i++) {
+        if (params.measure_latency) {
+            barrier.arrive_and_wait();
+        }
+        for (int j = 0; j < batch_size; j++) {
+            if (params.measure_overall_latency) {
+                overall_latency_meter.add_start_sample(std::chrono::steady_clock::now().time_since_epoch());
+            }
+
+            const size_t offset = (i % frames_in_buffer) * send_object.get_frame_size();
+            auto status = send_object.write(MemoryView(
+                const_cast<uint8_t*>(input_buffer.data()) + offset,
+                send_object.get_frame_size()));
+            if (HAILO_STREAM_INTERNAL_ABORT == status) {
+                LOGGER__DEBUG("Input stream was aborted!");
+                return status;
+            }
+            CHECK_SUCCESS(status);
+        }
+    }
+    // Flushing the send object in order to make sure all data is sent. Needed for latency measurement as well.
+    auto status = send_object.flush();
+    CHECK_SUCCESS(status, "Failed flushing stream");
+    return HAILO_SUCCESS;
+}
+
+template<typename RecvObject>
+hailo_status recv_loop(const inference_runner_params &params, RecvObject &recv_object,
+    std::shared_ptr<NetworkProgressBar> progress_bar, Barrier &barrier, LatencyMeter &overall_latency_meter,
+    std::map<std::string, Buffer> &dst_data, std::atomic_size_t &received_frames_count, uint32_t output_idx, bool show_progress,
+    uint16_t batch_size)
+{
+    uint32_t num_of_batches = ((0 == params.time_to_run) ? (params.frames_count / batch_size) : UINT32_MAX);
+    for (size_t i = 0; i < num_of_batches; i++) {
+        if (params.measure_latency) {
+            barrier.arrive_and_wait();
+        }
+        for (int j = 0; j < batch_size; j++) {
+            auto status = recv_object.read(MemoryView(dst_data[recv_object.name()]));
+            if (HAILO_SUCCESS != status) {
+                return status;
+            }
+
+            if (params.measure_overall_latency) {
+                overall_latency_meter.add_end_sample(output_idx, std::chrono::steady_clock::now().time_since_epoch());
+            }
+
+            if (show_progress && params.show_progress) {
+                progress_bar->make_progress();
+            }
+            received_frames_count++;
+        }
+    }
+    return HAILO_SUCCESS;
+}
+
+hailo_status abort_low_level_streams(ConfiguredNetworkGroup &configured_net_group, const std::string &network_name = "")
+{
+    // If network_name is not given, all networks are addressed
+    auto status = HAILO_SUCCESS; // Best effort
+    auto input_streams = configured_net_group.get_input_streams_by_network(network_name);
+    CHECK_EXPECTED_AS_STATUS(input_streams);
+    for (auto &input_stream : input_streams.release()) {
+        auto abort_status = input_stream.get().abort();
+        if (HAILO_SUCCESS != abort_status) {
+            LOGGER__ERROR("Failed to abort input stream {}", input_stream.get().name());
+            status = abort_status;
+        }
+    }
+    auto output_streams = configured_net_group.get_output_streams_by_network(network_name);
+    CHECK_EXPECTED_AS_STATUS(output_streams);
+    for (auto &output_stream : output_streams.release()) {
+        auto abort_status = output_stream.get().abort();
+        if (HAILO_SUCCESS != abort_status) {
+            LOGGER__ERROR("Failed to abort output stream {}", output_stream.get().name());
+            status = abort_status;
+        }
+    }
+    return status;
+}
+
+Expected<std::map<std::string, std::vector<InputVStream>>> create_input_vstreams(ConfiguredNetworkGroup &configured_net_group,
+    const inference_runner_params &params)
+{
+    std::map<std::string, std::vector<InputVStream>> res;
+    auto network_infos = configured_net_group.get_network_infos();
+    CHECK_EXPECTED(network_infos);
+    for (auto &network_info : network_infos.value()) {
+        auto input_vstreams_params = configured_net_group.make_input_vstream_params(params.transform.quantized,
+            params.transform.format_type, HAILORTCLI_DEFAULT_VSTREAM_TIMEOUT_MS, HAILO_DEFAULT_VSTREAM_QUEUE_SIZE, network_info.name);
+        CHECK_EXPECTED(input_vstreams_params);
+
+        for (auto &vstream_params : input_vstreams_params.value()) {
+            vstream_params.second.pipeline_elements_stats_flags = inference_runner_params_to_pipeline_elem_stats_flags(params.pipeline_stats);
+            vstream_params.second.vstream_stats_flags = inference_runner_params_to_vstream_stats_flags(params.pipeline_stats);
+        }
+        auto input_vstreams = VStreamsBuilder::create_input_vstreams(configured_net_group, input_vstreams_params.value());
+        CHECK_EXPECTED(input_vstreams);
+        res.emplace(network_info.name, input_vstreams.release());
+    }
+    return res;
+}
+
+Expected<std::map<std::string, std::vector<OutputVStream>>> create_output_vstreams(ConfiguredNetworkGroup &configured_net_group,
+    const inference_runner_params &params)
+{
+    std::map<std::string, std::vector<OutputVStream>> res;
+    auto network_infos = configured_net_group.get_network_infos();
+    CHECK_EXPECTED(network_infos);
+    for (auto &network_info : network_infos.value()) {
+        auto output_vstreams_params = configured_net_group.make_output_vstream_params(params.transform.quantized,
+            params.transform.format_type, HAILORTCLI_DEFAULT_VSTREAM_TIMEOUT_MS, HAILO_DEFAULT_VSTREAM_QUEUE_SIZE, network_info.name);
+        CHECK_EXPECTED(output_vstreams_params);
+
+        for (auto &vstream_params : output_vstreams_params.value()) {
+            vstream_params.second.pipeline_elements_stats_flags = inference_runner_params_to_pipeline_elem_stats_flags(params.pipeline_stats);
+            vstream_params.second.vstream_stats_flags = inference_runner_params_to_vstream_stats_flags(params.pipeline_stats);
+        }
+        auto output_vstreams = VStreamsBuilder::create_output_vstreams(configured_net_group, output_vstreams_params.value());
+        CHECK_EXPECTED(output_vstreams);
+        res.emplace(network_info.name, output_vstreams.release());
+    }
+    return res;
+}
+
+Expected<std::map<std::string, std::vector<std::reference_wrapper<InputStream>>>> create_input_streams(ConfiguredNetworkGroup &configured_net_group)
+{
+    std::map<std::string, std::vector<std::reference_wrapper<InputStream>>> res;
+    auto network_infos = configured_net_group.get_network_infos();
+    CHECK_EXPECTED(network_infos);
+    for (auto &network_info : network_infos.value()) {
+        auto input_streams = configured_net_group.get_input_streams_by_network(network_info.name);
+        CHECK_EXPECTED(input_streams);
+        res.emplace(network_info.name, input_streams.release());
+    }
+    return res;
+}
+
+Expected<std::map<std::string, std::vector<std::reference_wrapper<OutputStream>>>> create_output_streams(ConfiguredNetworkGroup &configured_net_group)
+{
+    std::map<std::string, std::vector<std::reference_wrapper<OutputStream>>> res;
+    auto network_infos = configured_net_group.get_network_infos();
+    CHECK_EXPECTED(network_infos);
+    for (auto &network_info : network_infos.value()) {
+        auto output_streams = configured_net_group.get_output_streams_by_network(network_info.name);
+        CHECK_EXPECTED(output_streams);
+        res.emplace(network_info.name, output_streams.release());
+    }
+    return res;
+}
+
+// TODO: HRT-5177 create output buffers inside run_streaming
+template< typename RecvObject>
+Expected<std::map<std::string, Buffer>> create_output_buffers(
+    std::map<std::string, std::vector<std::reference_wrapper<RecvObject>>> &recv_objects_per_network)
+{
+    std::map<std::string, Buffer> dst_data;
+    for (auto &recv_objects : recv_objects_per_network) {
+        for (auto &recv_object : recv_objects.second) {
+            auto buffer = Buffer::create(recv_object.get().get_frame_size());
+            CHECK_EXPECTED(buffer);
+            dst_data[recv_object.get().name()] = buffer.release();
+        }
+    }
+
+    return dst_data;
+}
+
+std::pair<std::string, uint16_t> get_network_to_batch(const std::string &name_to_batch)
+{
+    /* name_to_batch is formed like <network_name>=<batch_size>
+       We know the string is valid - we check it in NetworkBatchValidator on inference params */
+    size_t first_delimiter = name_to_batch.find("=");
+    auto batch_size_str = name_to_batch.substr(first_delimiter + 1);
+    auto network_name = name_to_batch.substr(0, first_delimiter);
+    return std::make_pair(network_name, static_cast<uint16_t>(std::stoi(batch_size_str)));
+}
+
+uint16_t get_batch_size(const inference_runner_params &params, const std::string &network_name)
+{
+    /* params.batch_per_network is a partial list of networks.
+       If a network is not in it, it gets the network_group_batch (params.batch_size) */
+    for (auto &name_to_batch_str : params.batch_per_network) {
+        auto name_to_batch = get_network_to_batch(name_to_batch_str);
+        if (network_name == name_to_batch.first) {
+            return name_to_batch.second;
+        }
+    }
+    return params.batch_size;
+}
+
+Expected<std::map<std::string, ConfigureNetworkParams>> get_configure_params(const inference_runner_params &params, hailort::Hef &hef, hailo_stream_interface_t interface)
+{
+    std::map<std::string, ConfigureNetworkParams> configure_params = {};
+
+    hailo_configure_params_t config_params = {};
+    hailo_status status = hailo_init_configure_params(reinterpret_cast<hailo_hef>(&hef), interface, &config_params);
+    CHECK_SUCCESS_AS_EXPECTED(status);
+
+    // TODO: SDK-14842, for now this function supports only one network_group
+    /* params.batch_per_network is a partial list of networks.
+       If a network is not in it, it gets the network_group_batch (params.batch_size) */
+    if (params.batch_per_network.empty()) {
+        config_params.network_group_params[0].batch_size = params.batch_size;
+    } else {
+        for (auto &name_to_batch_str : params.batch_per_network) {
+            auto name_to_batch = get_network_to_batch(name_to_batch_str);
+            auto network_name = name_to_batch.first;
+            auto batch_size = name_to_batch.second;
+            bool found = false;
+            for (uint8_t network_idx = 0; network_idx < config_params.network_group_params[0].network_params_by_name_count; network_idx++) {
+                if (0 == strcmp(network_name.c_str(), config_params.network_group_params[0].network_params_by_name[network_idx].name)) {
+                    config_params.network_group_params[0].network_params_by_name[network_idx].network_params.batch_size = batch_size;
+                    found = true;
+                }
+            }
+            CHECK_AS_EXPECTED(found, HAILO_INVALID_ARGUMENT, "Did not find any network named {}. Use 'parse-hef' option to see network names.",
+                network_name);
+        }
+    }
+    config_params.network_group_params[0].power_mode = params.power_mode;
+    configure_params.emplace(std::string(config_params.network_group_params[0].name),
+        ConfigureNetworkParams(config_params.network_group_params[0]));
+
+    if (params.measure_latency) {
+        configure_params[std::string(config_params.network_group_params[0].name)].latency |= HAILO_LATENCY_MEASURE;
+    }
+
+    return configure_params;
+}
+
+template<typename SendObject, typename RecvObject>
+static hailo_status run_streaming_impl(ConfiguredNetworkGroup &configured_net_group,
+    std::map<std::string, Buffer> &input_dataset,
+    std::map<std::string, Buffer> &output_buffers,
+    const inference_runner_params &params,
+    std::vector<std::reference_wrapper<SendObject>> &send_objects,
+    std::vector<std::reference_wrapper<RecvObject>> &recv_objects,
+    const std::string network_name,
+    InferProgress &network_group_progress_bar,
+    NetworkInferResult &inference_result)
+{
+    // latency resources init
+    if (params.measure_overall_latency) {
+        CHECK((send_objects.size() == 1), HAILO_INVALID_OPERATION, "Overall latency measurement not support multiple inputs network");
+    }
+    std::set<uint32_t> output_channels;
+    for (uint32_t output_channel_index = 0; output_channel_index < recv_objects.size(); output_channel_index++) {
+        output_channels.insert(output_channel_index);
+    }
+
+    LatencyMeter overall_latency_meter(output_channels, OVERALL_LATENCY_TIMESTAMPS_LIST_LENGTH);
+    Barrier barrier(send_objects.size() + recv_objects.size());
+
+    std::vector<std::atomic_size_t> frames_recieved_per_output(recv_objects.size());
+    for (auto &count : frames_recieved_per_output) {
+        count = 0;
+    }
+    auto batch_size = get_batch_size(params, network_name);
+
+    std::vector<AsyncThreadPtr<hailo_status>> results;
+
+    auto progress_bar_exp = network_group_progress_bar.create_network_progress_bar(network_name);
+    CHECK_EXPECTED_AS_STATUS(progress_bar_exp);
+    auto progress_bar = progress_bar_exp.release();
+    const auto start = std::chrono::high_resolution_clock::now();
+
+    // Launch async read/writes
+    uint32_t output_index = 0;
+    auto first = true;
+    for (auto& recv_object : recv_objects) {
+        auto &frames_recieved = frames_recieved_per_output[output_index];
+        results.emplace_back(std::make_unique<AsyncThread<hailo_status>>(
+            [progress_bar, params, &recv_object, &output_buffers, first, &barrier, &overall_latency_meter,
+            &frames_recieved, output_index, batch_size]() {
+                auto res = recv_loop(params, recv_object.get(), progress_bar, barrier, overall_latency_meter,
+                    output_buffers, frames_recieved, output_index, first, batch_size);
+                if (HAILO_SUCCESS != res) {
+                    barrier.terminate();
+                }
+                return res;
+            }
+        ));
+        first = false;
+        ++output_index;
+    }
+    for (auto &send_object : send_objects) {
+        results.emplace_back(std::make_unique<AsyncThread<hailo_status>>(
+            [params, &send_object, &input_dataset, &barrier, &overall_latency_meter, batch_size]() -> hailo_status {
+                auto res = send_loop(params, send_object.get(), input_dataset, barrier, overall_latency_meter, batch_size);
+                if (HAILO_SUCCESS != res) {
+                    barrier.terminate();
+                }
+                return res;
+            }
+        ));
+    }
+
+    if (0 < params.time_to_run) {
+        auto status = wait_for_exit_with_timeout(std::chrono::seconds(params.time_to_run));
+        CHECK_SUCCESS(status);
+
+        status = abort_low_level_streams(configured_net_group, network_name);
+        barrier.terminate();
+        CHECK_SUCCESS(status);
+    }
+
+    // Wait for all results
+    auto error_status = HAILO_SUCCESS;
+    for (auto& result : results) {
+        auto status = result->get();
+        if (HAILO_STREAM_INTERNAL_ABORT == status) {
+            continue;
+        }
+        if (HAILO_SUCCESS != status) {
+            error_status = status;
+            LOGGER__ERROR("Failed waiting for threads with status {}", error_status);
+        }
+    }
+    CHECK_SUCCESS(error_status);
+
+    auto end = std::chrono::high_resolution_clock::now();
+
+    // Update inference_result struct
+    size_t min_frame_count_recieved = *std::min_element(frames_recieved_per_output.begin(),
+        frames_recieved_per_output.end());
+
+    inference_result.m_frames_count = min_frame_count_recieved;
+
+    auto network_input_streams = configured_net_group.get_input_streams_by_network(network_name);
+    CHECK_EXPECTED_AS_STATUS(network_input_streams);
+    inference_result.m_total_send_frame_size = total_send_frame_size(network_input_streams.value());
+    auto network_output_streams = configured_net_group.get_output_streams_by_network(network_name);
+    CHECK_EXPECTED_AS_STATUS(network_output_streams);
+    inference_result.m_total_recv_frame_size = total_recv_frame_size(network_output_streams.value());
+
+    if (params.measure_latency) {
+        if (auto hw_latency = configured_net_group.get_latency_measurement(network_name)) {
+            auto hw_latency_p = make_unique_nothrow<std::chrono::nanoseconds>(hw_latency->avg_hw_latency);
+            CHECK_NOT_NULL(hw_latency_p, HAILO_OUT_OF_HOST_MEMORY);
+            inference_result.m_hw_latency = std::move(hw_latency_p);
+        }
+    } else {
+        inference_result.m_infer_duration = std::make_unique<double>(std::chrono::duration<double>(end - start).count());
+    }
+
+    if (params.measure_overall_latency) {
+        auto overall_latency = overall_latency_meter.get_latency(true);
+        CHECK_EXPECTED_AS_STATUS(overall_latency);
+        inference_result.m_overall_latency = std::make_unique<std::chrono::nanoseconds>(*overall_latency);
+    }
+
+    return HAILO_SUCCESS;
+}
+
+template<typename SendObject, typename RecvObject>
+static Expected<NetworkGroupInferResult> run_streaming(ConfiguredNetworkGroup &configured_net_group,
+    std::map<std::string, Buffer> &input_dataset,
+    std::map<std::string, Buffer> &output_buffers,
+    const inference_runner_params &params,
+    std::map<std::string, std::vector<std::reference_wrapper<SendObject>>> &send_objects_per_network,
+    std::map<std::string, std::vector<std::reference_wrapper<RecvObject>>> &recv_objects_per_network)
+{
+    CHECK_AS_EXPECTED(send_objects_per_network.size() == recv_objects_per_network.size(), HAILO_INTERNAL_FAILURE,
+        "Not all networks was parsed correctly.");
+
+    // TODO: support AsyncThreadPtr for Expected, and use it instead of status
+    std::vector<AsyncThreadPtr<hailo_status>> networks_threads_status;
+    networks_threads_status.reserve(send_objects_per_network.size());
+    std::map<std::string, NetworkInferResult> networks_results;
+
+    // TODO (HRT-5789): instead of init this map and giving it to run_streaming_impl, change AsyncThread to return Expected
+    for (auto &network_name_pair : send_objects_per_network) {
+        networks_results.emplace(network_name_pair.first, NetworkInferResult());
+    }
+
+    InferProgress network_group_progress_bar(configured_net_group, params, std::chrono::seconds(1));
+
+    if (params.show_progress) {
+        network_group_progress_bar.start();
+    }
+
+    for (auto &network_name_pair : send_objects_per_network) {
+        CHECK_AS_EXPECTED(contains(recv_objects_per_network, network_name_pair.first), HAILO_INTERNAL_FAILURE,
+            "Not all networks was parsed correctly.");
+        auto network_name = network_name_pair.first;
+        networks_threads_status.emplace_back(std::make_unique<AsyncThread<hailo_status>>(
+            [&configured_net_group, &input_dataset, &output_buffers, &params, &send_objects_per_network, &recv_objects_per_network, network_name,
+            &network_group_progress_bar, &networks_results]() {
+                return run_streaming_impl(configured_net_group, input_dataset, output_buffers, params,
+                send_objects_per_network.at(network_name),
+                recv_objects_per_network.at(network_name),
+                network_name, network_group_progress_bar, networks_results.at(network_name));
+            }
+        ));
+    }
+
+    // Wait for all results
+    for (auto& status : networks_threads_status) {
+        auto network_status = status->get();
+        CHECK_SUCCESS_AS_EXPECTED(network_status);
+    }
+
+    if (params.show_progress) {
+        network_group_progress_bar.finish();
+    }
+
+    // Update final_result struct - with all inferences results
+    NetworkGroupInferResult final_result(std::move(networks_results));
+
+    if (should_measure_pipeline_stats(params)) {
+        final_result.update_pipeline_stats(send_objects_per_network, recv_objects_per_network);
+    }
+
+    return final_result;
+}
+
+static Expected<NetworkGroupInferResult> run_inference(ConfiguredNetworkGroup &configured_net_group,
+    std::map<std::string, Buffer> &input_dataset,
+    const inference_runner_params &params)
+{
+    switch (params.mode) {
+    case InferMode::STREAMING:
+    {
+        auto in_vstreams = create_input_vstreams(configured_net_group, params);
+        CHECK_EXPECTED(in_vstreams);
+
+        auto out_vstreams = create_output_vstreams(configured_net_group, params);
+        CHECK_EXPECTED(out_vstreams);
+
+        // run_streaming function should get reference_wrappers to vstreams instead of the instances themselves
+        std::map<std::string, std::vector<std::reference_wrapper<InputVStream>>> input_refs_map;
+        for (auto &input_vstreams_per_network : in_vstreams.value()) {
+            std::vector<std::reference_wrapper<InputVStream>> input_refs;
+            for (auto &input_vstream : input_vstreams_per_network.second) {
+                input_refs.emplace_back(input_vstream);
+            }
+            input_refs_map.emplace(input_vstreams_per_network.first, input_refs);
+        }
+        std::map<std::string, std::vector<std::reference_wrapper<OutputVStream>>> output_refs_map;
+        for (auto &output_vstreams_per_network : out_vstreams.value()) {
+            std::vector<std::reference_wrapper<OutputVStream>> output_refs;
+            for (auto &output_vstream : output_vstreams_per_network.second) {
+                output_refs.emplace_back(output_vstream);
+            }
+            output_refs_map.emplace(output_vstreams_per_network.first, output_refs);
+        }
+
+        auto output_buffers = create_output_buffers(output_refs_map);
+        CHECK_EXPECTED(output_buffers);
+
+        auto res = run_streaming<InputVStream, OutputVStream>(configured_net_group, input_dataset,
+            output_buffers.value(), params, input_refs_map, output_refs_map);
+
+        if (!params.dot_output.empty()) {
+            const auto status = GraphPrinter::write_dot_file(in_vstreams.value(), out_vstreams.value(), params.hef_path,
+                params.dot_output, should_measure_pipeline_stats(params));
+            CHECK_SUCCESS_AS_EXPECTED(status);
+        }
+
+        // Note: In VStreams d'tor, low-level-streams clears their abort flag. In low-level-streams d'tor, 'flush()' is called.
+        //       In order to avoid error logs on 'flush()', we re-set the abort flag in the low-level streams after vstreams d'tor.
+        // TODO: HRT-5177 fix that note
+        in_vstreams->clear();
+        out_vstreams->clear();
+
+        if (0 < params.time_to_run) {
+            auto status = abort_low_level_streams(configured_net_group);
+            CHECK_SUCCESS_AS_EXPECTED(status);
+        }
+        CHECK_EXPECTED(res);
+        return res;
+
+    }
+    case InferMode::HW_ONLY:
+    {
+        auto input_streams = create_input_streams(configured_net_group);
+        CHECK_EXPECTED(input_streams);
+        auto output_streams = create_output_streams(configured_net_group);
+        CHECK_EXPECTED(output_streams);
+
+        auto output_buffers = create_output_buffers(output_streams.value());
+        CHECK_EXPECTED(output_buffers);
+
+        return run_streaming<InputStream, OutputStream>(configured_net_group, input_dataset, output_buffers.value(),
+            params, input_streams.value(), output_streams.value());
+    }
+    default:
+        return make_unexpected(HAILO_INVALID_OPERATION);
+    }
+}
+
+static Expected<std::unique_ptr<ActivatedNetworkGroup>> activate_network_group(ConfiguredNetworkGroup &network_group)
+{
+    hailo_activate_network_group_params_t network_group_params = {};
+    auto activated_network_group = network_group.activate(network_group_params);
+    CHECK_EXPECTED(activated_network_group, "Failed activating network group");
+
+    // Wait for configuration
+    // TODO: HRT-2492 wait for config in a normal way
+    std::this_thread::sleep_for(TIME_TO_WAIT_FOR_CONFIG);
+
+    return activated_network_group;
+}
+
+static Expected<std::map<std::string, Buffer>> create_constant_dataset(
+    const std::vector<std::reference_wrapper<InputStream>> &input_streams, const hailo_transform_params_t &trans_params)
+{
+    const uint8_t const_byte = 0xAB;
+    std::map<std::string, Buffer> dataset;
+    for (const auto &input_stream : input_streams) {
+        const auto frame_size = hailo_get_host_frame_size(&(input_stream.get().get_info()), &trans_params);
+        auto constant_buffer = Buffer::create(frame_size, const_byte);
+        if (!constant_buffer) {
+            std::cerr << "Out of memory, tried to allocate " << frame_size << std::endl;
+            return make_unexpected(constant_buffer.status());
+        }
+
+        dataset.emplace(input_stream.get().name(), constant_buffer.release());
+    }
+
+    return dataset;
+}
+
+static Expected<std::map<std::string, Buffer>> create_dataset_from_files(
+    const std::vector<std::reference_wrapper<InputStream>> &input_streams, const std::vector<std::string> &input_files,
+    const hailo_transform_params_t &trans_params, InferMode mode)
+{
+    CHECK_AS_EXPECTED(input_streams.size() == input_files.size(), HAILO_INVALID_ARGUMENT, "Number of input files ({}) must be equal to the number of inputs ({})", input_files.size(), input_streams.size());
+
+    std::map<std::string, std::string> file_paths;
+    if ((input_streams.size() == 1) && (input_files[0].find("=") == std::string::npos)) { // Legacy single input format
+        file_paths.emplace(input_streams[0].get().name(), input_files[0]);
+    }
+    else {
+        file_paths = format_strings_to_key_value_pairs(input_files);
+    }
+
+    std::map<std::string, Buffer> dataset;
+    for (const auto &input_stream : input_streams) {
+        const auto host_frame_size = hailo_get_host_frame_size(&(input_stream.get().get_info()), &trans_params);
+        const auto stream_name = std::string(input_stream.get().name());
+        CHECK_AS_EXPECTED(stream_name.find("=") == std::string::npos, HAILO_INVALID_ARGUMENT, "stream inputs must not contain '=' characters: {}", stream_name);
+
+        const auto file_path_it = file_paths.find(stream_name);
+        CHECK_AS_EXPECTED(file_paths.end() != file_path_it, HAILO_INVALID_ARGUMENT, "Missing input file for input: {}", stream_name);
+        
+        auto host_buffer = read_binary_file(file_path_it->second);
+        CHECK_EXPECTED(host_buffer, "Failed reading file {}", file_path_it->second);
+        CHECK_AS_EXPECTED((host_buffer->size() % host_frame_size) == 0, HAILO_INVALID_ARGUMENT,
+            "Input file ({}) size {} must be a multiple of the frame size {} ({})", file_path_it->second, host_buffer->size(), host_frame_size, stream_name);
+
+        if (InferMode::HW_ONLY == mode) {
+            const size_t frames_count = (host_buffer->size() / host_frame_size);
+            const size_t hw_frame_size = input_stream.get().get_frame_size();
+            const size_t hw_buffer_size = frames_count * hw_frame_size;
+            auto hw_buffer = Buffer::create(hw_buffer_size);
+            CHECK_EXPECTED(hw_buffer);
+
+            auto transform_context = InputTransformContext::create(input_stream.get().get_info(), trans_params);
+            CHECK_EXPECTED(transform_context);
+            
+            for (size_t i = 0; i < frames_count; i++) {
+                MemoryView host_data(static_cast<uint8_t*>(host_buffer->data() + (i*host_frame_size)), host_frame_size);
+                MemoryView hw_data(static_cast<uint8_t*>(hw_buffer->data() + (i*hw_frame_size)), hw_frame_size);
+
+                auto status = transform_context.value()->transform(host_data, hw_data);
+                CHECK_SUCCESS_AS_EXPECTED(status);
+            }
+            dataset[stream_name] = hw_buffer.release();
+        }
+        else {
+            dataset[stream_name] = host_buffer.release();
+        }
+    }
+
+    return dataset;
+}
+
+static Expected<std::map<std::string, Buffer>> create_dataset(
+    const std::vector<std::reference_wrapper<InputStream>> &input_streams,
+    const inference_runner_params &params)
+{
+    hailo_transform_params_t trans_params = {};
+    trans_params.transform_mode = (params.transform.transform ? HAILO_STREAM_TRANSFORM_COPY : HAILO_STREAM_NO_TRANSFORM);
+    trans_params.user_buffer_format.order = HAILO_FORMAT_ORDER_AUTO;
+    trans_params.user_buffer_format.flags = (params.transform.quantized ? HAILO_FORMAT_FLAGS_QUANTIZED : HAILO_FORMAT_FLAGS_NONE);
+    trans_params.user_buffer_format.type = params.transform.format_type;
+
+    if (!params.inputs_name_and_file_path.empty()) {
+        return create_dataset_from_files(input_streams, params.inputs_name_and_file_path, trans_params, params.mode);
+    }
+    else {
+        return create_constant_dataset(input_streams, trans_params);
+    }
+}
+
+Expected<NetworkGroupInferResult> activate_network_group_and_run(
+    Device &device,
+    std::shared_ptr<ConfiguredNetworkGroup> network_group,
+    const inference_runner_params &params)
+{
+    auto activated_net_group = activate_network_group(*network_group);
+    CHECK_EXPECTED(activated_net_group, "Failed activate network_group");
+
+    auto input_streams = network_group->get_input_streams();
+    auto input_dataset = create_dataset(input_streams, params);
+    CHECK_EXPECTED(input_dataset, "Failed creating input dataset");
+
+    hailo_power_measurement_types_t measurement_type = HAILO_POWER_MEASUREMENT_TYPES__MAX_ENUM;
+    bool should_measure_power = false;
+    if (params.power_measurement.measure_power) {
+        measurement_type = HAILO_POWER_MEASUREMENT_TYPES__POWER;
+        should_measure_power = true;
+    } else if (params.power_measurement.measure_current) {
+        measurement_type = HAILO_POWER_MEASUREMENT_TYPES__CURRENT;
+        should_measure_power = true;
+    }
+
+    std::unique_ptr<LongPowerMeasurement> long_power_measurement = nullptr;
+    if (should_measure_power) {
+        auto long_power_measurement_exp = PowerMeasurementSubcommand::start_power_measurement(device,
+            HAILO_DVM_OPTIONS_AUTO,
+            measurement_type, params.power_measurement.sampling_period, params.power_measurement.averaging_factor);
+        CHECK_EXPECTED(long_power_measurement_exp);
+        long_power_measurement = make_unique_nothrow<LongPowerMeasurement>(long_power_measurement_exp.release());
+        CHECK_NOT_NULL_AS_EXPECTED(long_power_measurement, HAILO_OUT_OF_HOST_MEMORY);
+    }
+
+    bool should_measure_temp = params.measure_temp;
+    TemperatureMeasurement temp_measure(device);
+    if (should_measure_temp) {
+        auto status = temp_measure.start_measurement();
+        CHECK_SUCCESS_AS_EXPECTED(status, "Failed to get chip's temperature");
+    }
+
+    auto infer_result = run_inference(*network_group, input_dataset.value(), params);
+    CHECK_EXPECTED(infer_result, "Error failed running inference");
+
+    NetworkGroupInferResult inference_result(infer_result.release());
+    std::vector<std::reference_wrapper<Device>> device_refs;
+    device_refs.push_back(device);
+    inference_result.initialize_measurements(device_refs);
+
+    if (should_measure_power) {
+        auto status = long_power_measurement->stop();
+        CHECK_SUCCESS_AS_EXPECTED(status);
+
+        if (params.power_measurement.measure_current) {
+            status = inference_result.set_current_measurement(device.get_dev_id(), std::move(long_power_measurement));
+            CHECK_SUCCESS_AS_EXPECTED(status);
+        } else {
+            status = inference_result.set_power_measurement(device.get_dev_id(), std::move(long_power_measurement));
+            CHECK_SUCCESS_AS_EXPECTED(status);
+        }
+    }
+
+    if (should_measure_temp) {
+        temp_measure.stop_measurement();
+        auto temp_measure_p = make_unique_nothrow<TempMeasurementData>(temp_measure.get_data());
+        CHECK_NOT_NULL_AS_EXPECTED(temp_measure_p, HAILO_OUT_OF_HOST_MEMORY);
+        auto status = inference_result.set_temp_measurement(device.get_dev_id(), std::move(temp_measure_p));
+        CHECK_SUCCESS_AS_EXPECTED(status);
+    }
+
+    return inference_result;
+}
+
+Expected<NetworkGroupInferResult> run_command_hef_single_device(const inference_runner_params &params)
+{
+    auto device = create_device(params.device_params);
+    CHECK_EXPECTED(device, "Failed creating device");
+
+    auto hef = Hef::create(params.hef_path.c_str());
+    CHECK_EXPECTED(hef, "Failed reading hef file {}", params.hef_path);
+
+    auto interface = device.value()->get_default_streams_interface();
+    CHECK_EXPECTED(interface, "Failed to get default streams interface");
+
+    auto configure_params = get_configure_params(params, hef.value(), interface.value());
+    CHECK_EXPECTED(configure_params);
+
+    auto network_group_list = device.value()->configure(hef.value(), configure_params.value());
+    CHECK_EXPECTED(network_group_list, "Failed configure device from hef");
+
+    // TODO: SDK-14842, for now this function supports only one network_group
+    auto network_group = network_group_list.value()[0];
+    auto inference_result = activate_network_group_and_run(*device.value().get(), network_group, params);
+
+    #if defined(__GNUC__)
+    // TODO: Support on windows (HRT-5919)
+    if (params.runtime_data.download_runtime_data) {
+        DownloadActionListCommand::execute(*device.value(), params.runtime_data.runtime_data_output_path,
+            network_group_list.value(), params.hef_path);
+    }
+    #endif
+
+    return inference_result;
+}
+
+Expected<NetworkGroupInferResult> run_command_hef_vdevice(const inference_runner_params &params)
+{
+    auto hef = Hef::create(params.hef_path.c_str());
+    CHECK_EXPECTED(hef, "Failed reading hef file {}", params.hef_path);
+
+    hailo_vdevice_params_t vdevice_params = {};
+    vdevice_params.device_count = params.device_params.vdevice_params.device_count;
+    auto vdevice = VDevice::create(vdevice_params);
+    CHECK_EXPECTED(vdevice, "Failed creating vdevice");
+
+    // VDevice always has Pcie devices
+    auto configure_params = get_configure_params(params, hef.value(), hailo_stream_interface_t::HAILO_STREAM_INTERFACE_PCIE);
+    CHECK_EXPECTED(configure_params);
+
+    auto network_group_list = vdevice.value()->configure(hef.value(), configure_params.value());
+    CHECK_EXPECTED(network_group_list, "Failed configure vdevice from hef");
+
+    // TODO: SDK-14842, for now this function supports only one network_group
+    auto network_group = network_group_list.value()[0];
+    auto activated_net_group = activate_network_group(*network_group);
+    CHECK_EXPECTED(activated_net_group, "Failed activate network_group");
+
+    auto input_streams = network_group->get_input_streams();
+    auto input_dataset = create_dataset(input_streams, params);
+    CHECK_EXPECTED(input_dataset, "Failed creating input dataset");
+
+    hailo_power_measurement_types_t measurement_type = HAILO_POWER_MEASUREMENT_TYPES__MAX_ENUM;
+    bool should_measure_power = false;
+    if (params.power_measurement.measure_power) {
+        measurement_type = HAILO_POWER_MEASUREMENT_TYPES__POWER;
+        should_measure_power = true;
+    } else if (params.power_measurement.measure_current) {
+        measurement_type = HAILO_POWER_MEASUREMENT_TYPES__CURRENT;
+        should_measure_power = true;
+    }
+
+    auto physical_devices = vdevice.value()->get_physical_devices();
+    CHECK_EXPECTED(physical_devices);
+
+    std::map<std::string, std::unique_ptr<LongPowerMeasurement>> power_measurements;
+    if (should_measure_power) {
+        for (auto &device : physical_devices.value()) {
+            auto long_power_measurement_exp = PowerMeasurementSubcommand::start_power_measurement(device,
+                HAILO_DVM_OPTIONS_AUTO,
+                measurement_type, params.power_measurement.sampling_period, params.power_measurement.averaging_factor);
+            CHECK_EXPECTED(long_power_measurement_exp, "Failed starting power measurement on device {}", device.get().get_dev_id());
+            auto long_power_measurement_p = make_unique_nothrow<LongPowerMeasurement>(long_power_measurement_exp.release());
+            CHECK_NOT_NULL_AS_EXPECTED(long_power_measurement_p, HAILO_OUT_OF_HOST_MEMORY);
+            power_measurements.emplace(device.get().get_dev_id(), std::move(long_power_measurement_p));
+        }
+    }
+
+    std::map<std::string, std::unique_ptr<TemperatureMeasurement>> temp_measurements;
+    if (params.measure_temp) {
+        for (auto &device : physical_devices.value()) {
+            auto temp_measure = make_unique_nothrow<TemperatureMeasurement>(device);
+            CHECK_NOT_NULL_AS_EXPECTED(temp_measure, HAILO_OUT_OF_HOST_MEMORY);
+            auto status = temp_measure->start_measurement();
+            CHECK_SUCCESS_AS_EXPECTED(status, "Failed starting temperature measurement on device {}", device.get().get_dev_id());
+            temp_measurements.emplace(device.get().get_dev_id(), std::move(temp_measure));
+        }
+    }
+
+    auto infer_result = run_inference(*network_group, input_dataset.value(), params);
+    CHECK_EXPECTED(infer_result, "Error failed running inference");
+
+    NetworkGroupInferResult inference_result(infer_result.release());
+    inference_result.initialize_measurements(physical_devices.value());
+
+    if (should_measure_power) {
+        auto status = HAILO_SUCCESS;
+        for (auto &power_measure_pair : power_measurements) {
+            auto measurement_status = power_measure_pair.second->stop();
+            if (HAILO_SUCCESS != measurement_status) {
+                // The returned status will be the last non-success status.
+                status = measurement_status;
+                LOGGER__ERROR("Failed stopping power measurement on device {} with status {}", power_measure_pair.first, measurement_status);
+            } else {
+                auto set_measurement_status = HAILO_UNINITIALIZED;
+                if (params.power_measurement.measure_current) {
+                    set_measurement_status = inference_result.set_current_measurement(power_measure_pair.first, std::move(power_measure_pair.second));
+                } else {
+                    set_measurement_status = inference_result.set_power_measurement(power_measure_pair.first, std::move(power_measure_pair.second));
+                }
+                if (HAILO_SUCCESS != set_measurement_status) {
+                    status = set_measurement_status;
+                    LOGGER__ERROR("Failed setting power measurement to inference result with status {}", set_measurement_status);
+                }
+            }
+        }
+        CHECK_SUCCESS_AS_EXPECTED(status);
+    }
+
+    if (params.measure_temp) {
+        for(const auto &temp_measure_pair : temp_measurements) {
+            temp_measure_pair.second->stop_measurement();
+            auto temp_measure_p = make_unique_nothrow<TempMeasurementData>(temp_measure_pair.second->get_data());
+            CHECK_NOT_NULL_AS_EXPECTED(temp_measure_p, HAILO_OUT_OF_HOST_MEMORY);
+            auto status = inference_result.set_temp_measurement(temp_measure_pair.first, std::move(temp_measure_p));
+            CHECK_SUCCESS_AS_EXPECTED(status);
+        }
+    }
+
+    return inference_result;
+}
+
+Expected<NetworkGroupInferResult> run_command_hef(const inference_runner_params &params)
+{
+    if (params.device_params.vdevice_params.device_count > 1) {
+        return run_command_hef_vdevice(params);
+    }
+    else {
+        return run_command_hef_single_device(params);
+    }
+}
+
+static hailo_status run_command_hefs_dir(const inference_runner_params &params, InferStatsPrinter &printer)
+{
+    hailo_status overall_status = HAILO_SUCCESS;
+    bool contains_hef = false; 
+    std::string hef_dir = params.hef_path;
+    inference_runner_params curr_params = params;
+
+    const auto files = Filesystem::get_files_in_dir_flat(hef_dir);
+    CHECK_EXPECTED_AS_STATUS(files);
+
+    for (const auto &full_path : files.value()) {
+        if (Filesystem::has_suffix(full_path, ".hef")) {
+            contains_hef = true;
+            curr_params.hef_path = full_path;
+            std::cout << std::string(80, '*') << std::endl << "Inferring " << full_path << ":"<< std::endl;
+            auto infer_stats = run_command_hef(curr_params);
+            printer.print(full_path, infer_stats);
+
+            if (!infer_stats) {
+                overall_status = infer_stats.status();
+            }
+        }
+    }
+
+    if (!contains_hef){
+        std::cerr << "No HEF files were found in the directory: " << hef_dir << std::endl;
+        return HAILO_INVALID_ARGUMENT;
+    }
+
+    return overall_status;
+}
+
+hailo_status run_command(const inference_runner_params &params)
+{
+    auto printer = InferStatsPrinter::create(params);
+    CHECK_EXPECTED_AS_STATUS(printer, "Failed to initialize infer stats printer");
+    if (!params.csv_output.empty()) {
+        printer->print_csv_header();
+    }
+
+    auto is_dir = Filesystem::is_directory(params.hef_path.c_str());
+    CHECK_EXPECTED_AS_STATUS(is_dir, "Failed checking if path is directory");
+
+    if (is_dir.value()){
+        return run_command_hefs_dir(params, printer.value());
+    } else {
+        auto infer_stats = run_command_hef(params);
+        // TODO: pass here network name without .hef
+        printer->print(params.hef_path, infer_stats);
+        return infer_stats.status();
+    }
+}
+
+RunCommand::RunCommand(CLI::App &parent_app) :
+    Command(parent_app.add_subcommand("run", "Run a compiled network")),
+    m_params({})
+{
+    // TODO: move add_run_command_params to the ctor
+    add_run_command_params(m_app, m_params);
+}
+
+hailo_status RunCommand::execute()
+{
+    // TOOD: move implement here
+    return run_command(m_params);
+}
diff --git a/hailort/hailortcli/run_command.hpp b/hailort/hailortcli/run_command.hpp
new file mode 100644 (file)
index 0000000..7cfcae6
--- /dev/null
@@ -0,0 +1,162 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file run_command.hpp
+ * @brief Run inference on hailo device
+ **/
+
+#ifndef _HAILO_RUN_COMMAND_HPP_
+#define _HAILO_RUN_COMMAND_HPP_
+
+#include "hailortcli.hpp"
+#include "power_measurement_command.hpp"
+#include "temp_measurement.hpp"
+#include "CLI/CLI.hpp"
+#include "inference_result.hpp"
+
+
+enum class InferMode {
+    STREAMING,
+    HW_ONLY,
+};
+
+struct transformation_params {
+    bool transform;
+    bool quantized;
+    hailo_format_type_t format_type;
+};
+
+struct measure_power_params {
+    bool measure_power;
+    bool measure_current;
+    uint32_t sampling_period;
+    uint32_t averaging_factor;
+};
+
+struct pipeline_stats_measurement_params {
+    bool measure_elem_fps;
+    bool measure_elem_latency;
+    bool measure_elem_queue_size;
+    bool measure_vstream_fps;
+    bool measure_vstream_latency;
+    std::string pipeline_stats_output_path;
+};
+
+struct runtime_data_params {
+    bool download_runtime_data;
+    std::string runtime_data_output_path;
+};
+
+struct inference_runner_params {
+    hailo_device_params device_params;
+    std::string hef_path;
+    uint32_t frames_count;
+    uint32_t time_to_run;
+    InferMode mode;
+    std::string csv_output;
+    std::vector<std::string> inputs_name_and_file_path;
+    bool measure_latency;
+    bool measure_overall_latency;
+    bool show_progress;
+    transformation_params transform;
+    measure_power_params power_measurement;
+    uint16_t batch_size;
+    hailo_power_mode_t power_mode;
+    pipeline_stats_measurement_params pipeline_stats;
+    runtime_data_params runtime_data;
+    std::string dot_output;
+    bool measure_temp;
+    std::vector<std::string> batch_per_network;
+};
+
+bool should_measure_pipeline_stats(const inference_runner_params& params);
+CLI::App* create_run_command(CLI::App& parent, inference_runner_params& params);
+hailo_status run_command(const inference_runner_params &params);
+Expected<NetworkGroupInferResult> run_command_hef(const inference_runner_params &params);
+
+std::string format_type_to_string(hailo_format_type_t format_type);
+
+class RunCommand : public Command {
+public:
+    explicit RunCommand(CLI::App &parent_app);
+    hailo_status execute() override;
+
+private:
+    inference_runner_params m_params;
+};
+
+class InputNameToFilePairValidator : public CLI::Validator {
+public:
+    InputNameToFilePairValidator() : CLI::Validator("InputNameToFilePair"), m_first_run(true), m_must_fail(false) {
+        func_ = [this](std::string &key_value_pair_str) {
+            bool old_first_run = m_first_run;
+            m_first_run = false;
+            if (m_must_fail) { // If a previous argument was not in key-value pair form, there shouldn't be more parameters
+                return std::string("Parse failed at (" + key_value_pair_str + ")");
+            }
+
+            size_t first_delimiter = key_value_pair_str.find("=");
+            if((std::string::npos == first_delimiter) || (key_value_pair_str.size() == first_delimiter + 1)) {
+                if (old_first_run) { // We only accept non-key-value pair form if it's the very first parameter
+                    m_must_fail = true;
+                    return CLI::ExistingFile(key_value_pair_str);
+                }
+                return std::string("Failed parsing key-value pair: (" + key_value_pair_str + ")");
+            }
+            auto file_path = key_value_pair_str.substr(first_delimiter + 1);
+            return CLI::ExistingFile(file_path);
+        };
+
+        desc_function_ = []() {
+            return "\t\tInput file path/paths. On single input network, give the full path of the data file.\n\
+                    \t\tOn multiple inputs network, the format is input_name1=path1 input_name2=path2, where\n\
+                    \t\tinput_name1 is the name of the input stream. If not given, random data will be used";
+        };
+    }
+private:
+    bool m_first_run;
+    bool m_must_fail;
+};
+
+const static InputNameToFilePairValidator InputNameToFileMap;
+
+class NetworkBatchValidator : public CLI::Validator {
+public:
+    NetworkBatchValidator() : CLI::Validator(), m_must_fail(false) {
+        func_ = [this](std::string &key_value_pair_str) {
+            if (m_must_fail) { // If a previous argument was not in a valid key-value pair form, there shouldn't be more parameters
+                return std::string("Parse failed at (" + key_value_pair_str + ")");
+            }
+
+            size_t first_delimiter = key_value_pair_str.find("=");
+            if((std::string::npos == first_delimiter) || (key_value_pair_str.size() == first_delimiter + 1)) {
+                // We do not accept non-key-value pair form
+                m_must_fail = true;
+                return std::string("Failed parsing key-value pair: (" + key_value_pair_str + ")");
+            }
+            auto batch_size = key_value_pair_str.substr(first_delimiter + 1);
+            auto network_name = key_value_pair_str.substr(0, first_delimiter);
+            // Batch size must be a positive number
+            if (!is_positive_number(batch_size)) {
+                m_must_fail = true;
+                return std::string("Failed parsing batch size (" + batch_size + ") for network (" + network_name + "). batch should be a positive number.");
+            }
+            return std::string("");
+        };
+
+    }
+private:
+    bool is_positive_number(const std::string &s)
+    {
+        bool is_number = (!s.empty()) && (std::all_of(s.begin(), s.end(), ::isdigit));
+        return is_number && (0 < std::stoi(s));
+    }
+
+    bool m_must_fail;
+};
+
+const static NetworkBatchValidator NetworkBatchMap;
+
+#endif /* _HAILO_RUN_COMMAND_HPP_ */
\ No newline at end of file
diff --git a/hailort/hailortcli/scan_command.cpp b/hailort/hailortcli/scan_command.cpp
new file mode 100644 (file)
index 0000000..073d3ae
--- /dev/null
@@ -0,0 +1,154 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file scan_command.cpp
+ * @brief Scan hailo devices
+ **/
+#include "scan_command.hpp"
+#include "hailortcli.hpp"
+#include "common/socket.hpp"
+
+#include <iostream>
+
+
+ScanSubcommand::ScanSubcommand(CLI::App &parent_app) :
+    Command(parent_app.add_subcommand("scan", "Shows all available devices")),
+    m_device_type(Device::Type::PCIE)
+{
+    // TODO: `--target` and `udp` Device::TYPE::ETH are for backwards compatibility with the python implemention (`hailo`)
+    // TODO: Remove them (HRT-2676)
+    const HailoCheckedTransformer<Device::Type> device_type_transformer({
+            { "pcie", Device::Type::PCIE },
+            { "eth", Device::Type::ETH },
+            { "udp", Device::Type::ETH },
+        });
+    auto *device_type_option = m_app->add_option("-d,--device-type,--target", m_device_type,
+        "Device type to use\n"
+        "Note: 'udp' is an alias for 'eth'.")
+        ->transform(device_type_transformer)
+        ->default_val("pcie");
+
+    // Ethernet options
+    auto *eth_options_group = m_app->add_option_group("Ethernet Device Options");
+    // TODO: `--ip` is for backwards compatibility with the python implemention (`hailo`)
+    // TODO: Remove it (HRT-2676)
+    auto *interface_ip_option = eth_options_group->add_option("--interface-ip", m_interface_ip_addr,
+        "Interface IP address to scan")
+        ->default_val("")
+        ->check(CLI::ValidIPV4);
+    auto *ip_option = eth_options_group->add_option("--ip", m_interface_ip_addr)
+        ->default_val("")
+        ->excludes(interface_ip_option)
+        ->check(CLI::ValidIPV4);
+
+    auto *interface_name_option = eth_options_group->add_option("--interface", m_interface_name, "Interface name to scan")
+        ->default_val("")
+        ->excludes(interface_ip_option)
+        ->excludes(ip_option);
+
+    m_app->parse_complete_callback([this, device_type_option, interface_ip_option, ip_option, interface_name_option]() {
+        bool eth_options_given = !interface_ip_option->empty() || !ip_option->empty() || !interface_name_option->empty();
+
+        // The user didn't put target, we can figure it ourself
+        if (device_type_option->empty()) {
+            if (eth_options_given) {
+                // User gave IP, target is eth
+                m_device_type = Device::Type::ETH;
+            }
+            else {
+                // Default is pcie
+                m_device_type = Device::Type::PCIE;
+            }
+        }
+
+        if (!eth_options_given && (m_device_type == Device::Type::ETH)) {
+            throw CLI::ParseError("Ethernet options not set", CLI::ExitCodes::InvalidError);
+        }
+
+        if (eth_options_given && (m_device_type != Device::Type::ETH)) {
+            throw CLI::ParseError("Ethernet options set on non eth device", CLI::ExitCodes::InvalidError);
+        }
+    });
+
+    std::vector<DeprecationActionPtr> actions{
+        std::make_shared<ValueDeprecation>(device_type_option, "udp", "eth"),
+        std::make_shared<OptionDeprecation>(ip_option, "--interface-ip")
+    };
+    hailo_deprecate_options(m_app, actions, false);
+}
+
+hailo_status ScanSubcommand::execute()
+{
+    switch (m_device_type)
+    {
+    case Device::Type::PCIE:
+        return scan_pcie();
+    case Device::Type::ETH:
+        return scan_ethernet(m_interface_ip_addr, m_interface_name).status();
+    default:
+        std::cerr << "Unkown target" << std::endl;
+        return HAILO_INVALID_ARGUMENT;
+    }
+}
+
+hailo_status ScanSubcommand::scan_pcie()
+{
+    auto scan_result = Device::scan_pcie();
+    CHECK_SUCCESS(scan_result.status(), "Error scan failed status = {}", scan_result.status());
+
+    if (scan_result->size() == 0) {
+        std::cout << "Hailo PCIe devices not found" << std::endl;
+    }
+    else {
+        std::cout << "Hailo PCIe Devices:" << std::endl;
+        for (const auto& device_info : scan_result.value()) {
+            auto device_info_str = Device::pcie_device_info_to_string(device_info);
+            CHECK_EXPECTED_AS_STATUS(device_info_str);
+            std::cout << "[-] Device BDF: " << device_info_str.value() << std::endl;
+        }
+    }
+
+    return HAILO_SUCCESS;
+}
+
+Expected<std::vector<std::string>> ScanSubcommand::scan_ethernet(const std::string &interface_ip_addr,
+    const std::string &interface_name)
+{
+    const std::chrono::seconds timeout(3);
+    std::vector<hailo_eth_device_info_t> device_infos;
+
+    if (!interface_ip_addr.empty()) {
+        auto result = Device::scan_eth_by_host_address(interface_ip_addr, timeout);
+        if (!result) {
+            std::cerr << "Failed scanning ethernet device from host address (" << result.status() << ")" << std::endl;
+            return make_unexpected(result.status());
+        }
+        device_infos = result.release();
+    } else {
+        auto result = Device::scan_eth(interface_name, timeout);
+        if (!result) {
+            std::cerr << "Failed scanning ethernet device from interface name (" << result.status() << ")" << std::endl;
+            return make_unexpected(result.status());
+        }
+        device_infos = result.release();
+    }
+
+    std::cout << "Hailo Ethernet Devices:" << std::endl;
+    std::vector<std::string> ip_addresses;
+    ip_addresses.reserve(device_infos.size());
+    char textual_ip_address[INET_ADDRSTRLEN] = {};
+    for (size_t i = 0; i < device_infos.size(); i++) {
+        auto status = Socket::ntop(AF_INET, &(device_infos[i].device_address.sin_addr), textual_ip_address,
+            INET_ADDRSTRLEN);
+        if (status != HAILO_SUCCESS) {
+            std::cerr << "Could not convert ip address to textual format (inet_ntop has failed)" << std::endl;
+            continue;
+        }
+
+        std::cout << "[-] Board IP: " << textual_ip_address << std::endl;
+        ip_addresses.emplace_back(textual_ip_address);
+    }
+    return ip_addresses;
+}
diff --git a/hailort/hailortcli/scan_command.hpp b/hailort/hailortcli/scan_command.hpp
new file mode 100644 (file)
index 0000000..be0c548
--- /dev/null
@@ -0,0 +1,38 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file scan_command.hpp
+ * @brief Scan hailo devices
+ **/
+
+#ifndef _HAILO_SCAN_COMMAND_HPP_
+#define _HAILO_SCAN_COMMAND_HPP_
+
+#include "hailortcli.hpp"
+#include "command.hpp"
+#include "hailo/hailort.h"
+#include "hailo/device.hpp"
+#include "CLI/CLI.hpp"
+
+
+class ScanSubcommand final : public Command {
+public:
+    explicit ScanSubcommand(CLI::App &parent_app);
+    hailo_status execute() override;
+
+    static Expected<std::vector<std::string>> scan_ethernet(const std::string &interface_ip_addr,
+        const std::string &interface_name);
+
+private:
+    hailo_status scan_pcie();
+
+    Device::Type m_device_type;
+
+    // Ethernet scan options
+    std::string m_interface_ip_addr;
+    std::string m_interface_name;
+};
+
+#endif /* _HAILO_SCAN_COMMAND_HPP_ */
\ No newline at end of file
diff --git a/hailort/hailortcli/sensor_config_command.cpp b/hailort/hailortcli/sensor_config_command.cpp
new file mode 100644 (file)
index 0000000..0342edf
--- /dev/null
@@ -0,0 +1,209 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file sensor_config_command.cpp
+ * @brief Config sensor attached to the Hailo chip
+ **/
+
+#include "sensor_config_command.hpp"
+
+SensorConfigCommand::SensorConfigCommand(CLI::App &parent_app) :
+    ContainerCommand(parent_app.add_subcommand("sensor-config", "Config sensor attached to the Hailo chip"))
+{
+    add_subcommand<SensorStoreConfigSubcommand>();
+    add_subcommand<SensorLoadConfigSubcommand>();
+    add_subcommand<SensorResetSubcommand>();
+    add_subcommand<SensorSectionsInfoSubcommand>();
+    add_subcommand<SensorDumpConfigSubcommand>();
+    add_subcommand<SensorStoreISPConfigSubcommand>();
+    add_subcommand<SensorSetGenericSlaveSubcommand>();
+}
+
+Expected<std::string> convert_sensor_type_to_string(uint32_t sensor_type)
+{
+    switch (sensor_type) {
+    case HAILO_SENSOR_TYPES_GENERIC:
+        return std::string("SENSOR_GENERIC");
+
+    case HAILO_SENSOR_TYPES_ONSEMI_AR0220AT:
+        return std::string("ONSEMI_AR0220AT");
+
+    case HAILO_SENSOR_TYPES_RASPICAM:
+        return std::string("SENSOR_RASPICAM");
+
+    case HAILO_SENSOR_TYPES_ONSEMI_AS0149AT:
+        return std::string("ONSEMI_AS0149AT");
+
+    case HAILO_SENSOR_TYPES_HAILO8_ISP:
+        return std::string("HAILO8_ISP");
+
+    default:
+        LOGGER__ERROR("Failed converting sensor type to string");
+        return make_unexpected(HAILO_NOT_FOUND);
+    }
+}
+
+SensorStoreConfigSubcommand::SensorStoreConfigSubcommand(CLI::App &parent_app) :
+    DeviceCommand(parent_app.add_subcommand("store-sensor-config", "Store a sensor configuration to a Hailo device")), m_store_config_params()
+{
+    m_app->add_option("section_index", m_store_config_params.section_index, "Sensor index")
+        ->required();
+    m_app->add_option("config_file_path", m_config_file_path, "Config file path (csv)")
+        ->check(CLI::ExistingFile)
+        ->required();
+    m_app->add_option("sensor_type", m_store_config_params.sensor_type, "Type of sensor")
+        ->transform(HailoCheckedTransformer<hailo_sensor_types_t>({
+            { "SENSOR_GENERIC", HAILO_SENSOR_TYPES_GENERIC },
+            { "ONSEMI_AR0220AT", HAILO_SENSOR_TYPES_ONSEMI_AR0220AT },
+            { "SENSOR_RASPICAM", HAILO_SENSOR_TYPES_RASPICAM },
+            { "ONSEMI_AS0149AT", HAILO_SENSOR_TYPES_ONSEMI_AS0149AT },
+            { "HAILO8_ISP", HAILO_SENSOR_TYPES_HAILO8_ISP }
+        }))
+        ->required();
+    m_app->add_option("--reset-config-size", m_store_config_params.reset_config_size, "The size of the reset configuration data");
+    m_app->add_option("--config-height", m_store_config_params.config_height, "Configuration resolution height");
+    m_app->add_option("--config-width", m_store_config_params.config_width, "Configuration resolution width");
+    m_app->add_option("--config-fps", m_store_config_params.config_fps, "Configuration resolution fps");
+    m_app->add_option("--config-name", m_store_config_params.config_name, "Configuration name")
+        ->default_val("UNINITIALIZED");
+}
+
+hailo_status SensorStoreConfigSubcommand::execute_on_device(Device &device)
+{
+    return device.store_sensor_config(m_store_config_params.section_index, m_store_config_params.sensor_type,
+        m_store_config_params.reset_config_size, m_store_config_params.config_height,
+        m_store_config_params.config_width, m_store_config_params.config_fps, m_config_file_path,
+        m_store_config_params.config_name);
+}
+
+SensorLoadConfigSubcommand::SensorLoadConfigSubcommand(CLI::App &parent_app) :
+    DeviceCommand(parent_app.add_subcommand("load-config", "Load the sensor configuration stored in the given section"))
+{
+    m_app->add_option("section_index", m_section_index, "Sensor index")
+        ->required();
+}
+
+hailo_status SensorLoadConfigSubcommand::execute_on_device(Device &device)
+{
+    return device.sensor_load_and_start_config(m_section_index);
+}
+
+SensorResetSubcommand::SensorResetSubcommand(CLI::App &parent_app) :
+    DeviceCommand(parent_app.add_subcommand("reset-sensor", "Load reset configuration stored in the given section"))
+{
+    m_app->add_option("section_index", m_section_index, "Sensor index")
+        ->required();
+}
+
+hailo_status SensorResetSubcommand::execute_on_device(Device &device)
+{
+    return device.sensor_reset(m_section_index);
+}
+
+SensorSectionsInfoSubcommand::SensorSectionsInfoSubcommand(CLI::App &parent_app) : 
+    DeviceCommand(parent_app.add_subcommand("get-sections-info", "Get the flash sections information"))
+{}
+
+hailo_status SensorSectionsInfoSubcommand::execute_on_device(Device &device)
+{
+    auto sections_info = device.sensor_get_sections_info();
+    CHECK_EXPECTED_AS_STATUS(sections_info);
+    return print_sections_info((SENSOR_CONFIG__section_info_t*)sections_info->data());
+}
+
+hailo_status SensorSectionsInfoSubcommand::print_sections_info(SENSOR_CONFIG__section_info_t *operation_cfg)
+{
+    for (uint32_t section_index = 0; section_index < SENSOR_CONFIG__TOTAL_SECTIONS_BLOCK_COUNT; section_index++) {
+        SENSOR_CONFIG__section_info_t *section_info = &operation_cfg[section_index];
+
+        std::cout << "======== section_index: " << section_index << " =========" << std::endl;
+
+        if (section_info->is_free) {
+            std::cout << "Section is not active\n" << std::endl;
+        }
+        else {
+            std::string reset_config = section_info->no_reset_offset ? "not valid" : "valid";
+            auto sensor_type_expected = convert_sensor_type_to_string(section_info->sensor_type);
+            CHECK_EXPECTED_AS_STATUS(sensor_type_expected, "Failed convert sensor type to string");
+
+            std::cout << "Configuration Name:               " << section_info->config_name << "\n";
+            std::cout << "Sensor Type:                      " << sensor_type_expected.value() << "\n";
+            std::cout << "Configuration lines number:       " << (section_info->config_size / sizeof(SENSOR_CONFIG__operation_cfg_t)) << "\n";
+            std::cout << "Configuration size in bytes:      " << section_info->config_size << "\n";
+            std::cout << "Reset configuration:              " << reset_config << "\n";
+            std::cout << "Reset configuration lines number: " << section_info->reset_config_size << "\n";
+            std::cout << "Configuration resolution:         [height " << section_info->config_height << " : width " << section_info->config_width << "]" << "\n";
+            std::cout << "Configuration fps:                " << section_info->config_fps << "\n";
+            std::cout << "Section configuration version:    " << section_info->section_version << "\n";
+            std::cout << "Section is active\n" << std::endl;
+        }
+    }
+
+    return HAILO_SUCCESS;
+}
+
+// TODO: change "get-config" to "dump-config" after solving backward compatibility issues 
+SensorDumpConfigSubcommand::SensorDumpConfigSubcommand(CLI::App &parent_app) : 
+    DeviceCommand(parent_app.add_subcommand("get-config", "Dumps the configuration stored in the given section into a csv file"))
+{
+    m_app->add_option("section_index", m_section_index, "Sensor index")
+        ->check(CLI::Range(0, (SENSOR_CONFIG__TOTAL_SECTIONS_BLOCK_COUNT - 1)))
+        ->required();
+    m_app->add_option("config_file_path", m_output_file_path, "File path to write section configuration")
+        ->required();
+}
+
+hailo_status SensorDumpConfigSubcommand::execute_on_device(Device &device)
+{
+    return device.sensor_dump_config(m_section_index, m_output_file_path);
+}
+
+SensorStoreISPConfigSubcommand::SensorStoreISPConfigSubcommand(CLI::App &parent_app) :
+    DeviceCommand(parent_app.add_subcommand("store_isp_config", "Store an ISP configuration to Hailo device, in section index " + std::to_string(SENSOR_CONFIG__ISP_SECTION_INDEX))),
+    m_store_config_params()
+{
+    m_app->add_option("isp_static_config_file_path", m_isp_static_config_file_path, "ISP static config file path")
+        ->required();
+    m_app->add_option("isp_runtime_config_file_path", m_isp_runtime_config_file_path, "ISP runtime config file path")
+        ->required();
+    m_app->add_option("--reset-config-size", m_store_config_params.reset_config_size, "The size of the reset configuration data");
+    m_app->add_option("--config-height", m_store_config_params.config_height, "Configuration resolution height");
+    m_app->add_option("--config-width", m_store_config_params.config_width, "Configuration resolution width");
+    m_app->add_option("--config-fps", m_store_config_params.config_fps, "Configuration resolution fps");
+    m_app->add_option("--config-name", m_store_config_params.config_name, "Configuration name")
+        ->default_val("UNINITIALIZED");
+}
+
+hailo_status SensorStoreISPConfigSubcommand::execute_on_device(Device &device)
+{
+    return device.store_isp_config(m_store_config_params.reset_config_size, m_store_config_params.config_height,
+        m_store_config_params.config_width, m_store_config_params.config_fps, m_isp_static_config_file_path,
+        m_isp_runtime_config_file_path, m_store_config_params.config_name);
+}
+
+SensorSetGenericSlaveSubcommand::SensorSetGenericSlaveSubcommand(CLI::App &parent_app) :
+    DeviceCommand(parent_app.add_subcommand("set_generic_slave", "Set a custom i2c slave that can be used"))
+{
+    m_app->add_option("slave_address", m_sensor_i2c_slave_info.slave_address, "The address of the i2c slave")
+        ->required();
+    m_app->add_option("register_address_size", m_sensor_i2c_slave_info.register_address_size, "Slave offset length in bytes")
+        ->required();
+    m_app->add_option("bus_index", m_sensor_i2c_slave_info.bus_index, "The bus number the i2c slave is connected to")
+        ->required();
+    m_app->add_option("--should-hold-bus", m_sensor_i2c_slave_info.should_hold_bus, "Should hold the bus during the read")
+        ->default_val("false");
+    m_app->add_option("--slave-endianness", m_sensor_i2c_slave_info.endianness)
+        ->transform(HailoCheckedTransformer<i2c_slave_endianness_t>({
+            { "BIG_ENDIAN", I2C_SLAVE_ENDIANNESS_BIG_ENDIAN },
+            { "LITTLE_ENDIAN", I2C_SLAVE_ENDIANNESS_LITTLE_ENDIAN }
+            }))
+        ->default_val(I2C_SLAVE_ENDIANNESS_BIG_ENDIAN);
+}
+
+hailo_status SensorSetGenericSlaveSubcommand::execute_on_device(Device &device)
+{
+    return device.sensor_set_generic_i2c_slave(m_sensor_i2c_slave_info.slave_address, m_sensor_i2c_slave_info.register_address_size,
+        m_sensor_i2c_slave_info.bus_index, m_sensor_i2c_slave_info.should_hold_bus, m_sensor_i2c_slave_info.endianness);
+}
diff --git a/hailort/hailortcli/sensor_config_command.hpp b/hailort/hailortcli/sensor_config_command.hpp
new file mode 100644 (file)
index 0000000..480563b
--- /dev/null
@@ -0,0 +1,116 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file sensor_config_command.hpp
+ * @brief Config sensor attached to the Hailo chip
+ **/
+
+#ifndef _HAILO_SENSOR_CONFIG_COMMAND_HPP_
+#define _HAILO_SENSOR_CONFIG_COMMAND_HPP_
+
+#include "hailortcli.hpp"
+#include "command.hpp"
+#include "hailo/hailort.h"
+#include "sensor_config_exports.h"
+#include "CLI/CLI.hpp"
+
+
+struct store_config_params_t {
+    uint32_t section_index;
+    hailo_sensor_types_t sensor_type;
+    uint32_t reset_config_size;
+    uint16_t config_height;
+    uint16_t config_width;
+    uint16_t config_fps;
+    std::string config_name;
+};
+
+class SensorStoreConfigSubcommand final : public DeviceCommand {
+public:
+    explicit SensorStoreConfigSubcommand(CLI::App &parent_app);
+
+protected:
+    virtual hailo_status execute_on_device(Device &device) override;
+
+private:
+    store_config_params_t m_store_config_params;
+    std::string m_config_file_path;
+};
+
+class SensorLoadConfigSubcommand final : public DeviceCommand {
+public:
+    explicit SensorLoadConfigSubcommand(CLI::App &parent_app);
+
+protected:
+    virtual hailo_status execute_on_device(Device &device) override;
+
+private:
+    uint8_t m_section_index;
+};
+
+class SensorResetSubcommand final : public DeviceCommand {
+public:
+    explicit SensorResetSubcommand(CLI::App &parent_app);
+
+protected:
+    virtual hailo_status execute_on_device(Device &device) override;
+
+private:
+    uint8_t m_section_index;
+};
+
+class SensorSectionsInfoSubcommand final : public DeviceCommand {
+public:
+    explicit SensorSectionsInfoSubcommand(CLI::App &parent_app);
+
+protected:
+    virtual hailo_status execute_on_device(Device &device) override;
+
+private:
+    static hailo_status print_sections_info(SENSOR_CONFIG__section_info_t *operation_cfg);
+};
+
+class SensorDumpConfigSubcommand final : public DeviceCommand {
+public:
+    explicit SensorDumpConfigSubcommand(CLI::App &parent_app);
+
+protected:
+    virtual hailo_status execute_on_device(Device &device) override;
+
+private:
+    uint8_t m_section_index;
+    std::string m_output_file_path;
+};
+
+class SensorStoreISPConfigSubcommand final : public DeviceCommand {
+public:
+    explicit SensorStoreISPConfigSubcommand(CLI::App &parent_app);
+
+protected:
+    virtual hailo_status execute_on_device(Device &device) override;
+
+private:
+    store_config_params_t m_store_config_params;
+    std::string m_isp_static_config_file_path;
+    std::string m_isp_runtime_config_file_path;
+};
+
+class SensorSetGenericSlaveSubcommand final : public DeviceCommand {
+public:
+    explicit SensorSetGenericSlaveSubcommand(CLI::App &parent_app);
+
+protected:
+    virtual hailo_status execute_on_device(Device &device) override;
+
+private:
+    SENSOR_I2C_SLAVE_INFO_t m_sensor_i2c_slave_info;
+};
+
+class SensorConfigCommand final : public ContainerCommand {
+public:
+    explicit SensorConfigCommand(CLI::App &parent_app);
+};
+
+#endif /* _HAILO_SENSOR_CONFIG_COMMAND_HPP_ */
\ No newline at end of file
diff --git a/hailort/hailortcli/ssb_update_command.cpp b/hailort/hailortcli/ssb_update_command.cpp
new file mode 100644 (file)
index 0000000..974f402
--- /dev/null
@@ -0,0 +1,41 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file ssb_update_command.cpp
+ * @brief Update second stage boot on hailo device with flash
+ **/
+
+#include "ssb_update_command.hpp"
+#include "common/file_utils.hpp"
+
+
+SSBUpdateCommand::SSBUpdateCommand(CLI::App &parent_app) :
+    DeviceCommand(parent_app.add_subcommand("ssb-update", "Second stage boot update command (only for flash based devices)")),
+    m_second_stage_path()
+{
+    m_app->add_option("file_path", m_second_stage_path, "The path to the second stage boot binary")
+        ->required()
+        ->check(CLI::ExistingFile);
+}
+
+hailo_status SSBUpdateCommand::execute_on_device(Device &device)
+{
+    auto second_stage = read_binary_file(m_second_stage_path);
+    if (!second_stage) {
+        std::cerr << "Failed reading second stage boot file " << second_stage.status() << std::endl;
+        return second_stage.status();
+    }
+
+    std::cout << "Updating second stage boot..." << std::endl;
+    auto status = device.second_stage_update(second_stage->data(), static_cast<uint32_t>(second_stage->size()));
+    if (HAILO_SUCCESS != status) {
+        std::cerr << "Update second stage boot failed with error: " << status << std::endl;
+        return status;
+    }
+
+    std::cout << "second stage boot has been updated successfully" << std::endl;
+
+    return HAILO_SUCCESS;
+}
\ No newline at end of file
diff --git a/hailort/hailortcli/ssb_update_command.hpp b/hailort/hailortcli/ssb_update_command.hpp
new file mode 100644 (file)
index 0000000..08ed1cb
--- /dev/null
@@ -0,0 +1,33 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file ssb_update_command.hpp
+ * @brief Update second stage boot on hailo device with flash
+ **/
+
+#ifndef _HAILORTCLI_SSB_UPDATE_COMMAND_HPP_
+#define _HAILORTCLI_SSB_UPDATE_COMMAND_HPP_
+
+#include "hailortcli.hpp"
+#include "command.hpp"
+
+#include "hailo/hailort.h"
+#include "hailo/device.hpp"
+#include "hailo/buffer.hpp"
+#include "CLI/CLI.hpp"
+
+
+class SSBUpdateCommand : public DeviceCommand {
+public:
+    explicit SSBUpdateCommand(CLI::App &parent_app);
+
+protected:
+    virtual hailo_status execute_on_device(Device &device) override;
+
+private:
+    std::string m_second_stage_path;
+};
+
+#endif /* _HAILORTCLI_SSB_UPDATE_COMMAND_HPP_ */
diff --git a/hailort/hailortcli/temp_measurement.cpp b/hailort/hailortcli/temp_measurement.cpp
new file mode 100644 (file)
index 0000000..fbba350
--- /dev/null
@@ -0,0 +1,83 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file temp_measurement.cpp
+ * @brief Measure temperature of Hailo chip
+ **/
+
+#include "temp_measurement.hpp"
+
+constexpr std::chrono::milliseconds DEFAULT_TEMPERATURE_MEASUREMENTS_INTERVAL(1000);
+
+static float32_t calc_avg(uint32_t old_samples_count, float32_t old_avg, uint32_t new_samples_count, float32_t new_value)
+{
+    float32_t old_samples = static_cast<float32_t>(old_samples_count);
+    float32_t new_samples = static_cast<float32_t>(new_samples_count);
+    float32_t total_samples_count = old_samples + new_samples;
+    return (((old_avg * old_samples) + (new_value * new_samples)) / total_samples_count);
+}
+
+TemperatureMeasurement::TemperatureMeasurement(Device &device) :
+    m_device(device),
+    m_is_thread_running(false),
+    m_data()
+{}
+
+TemperatureMeasurement::~TemperatureMeasurement()
+{
+    stop_measurement();
+}
+
+hailo_status TemperatureMeasurement::start_measurement()
+{
+    // Checking temperature sensor before starting thread
+    auto temp_info = m_device.get_chip_temperature();
+    CHECK_EXPECTED_AS_STATUS(temp_info);
+
+    m_is_thread_running = true;
+    m_thread = std::thread([this] () {
+        while (m_is_thread_running.load()) {
+            auto temp_info = m_device.get_chip_temperature();
+            if (temp_info.status() != HAILO_SUCCESS) {
+                LOGGER__ERROR("Failed to get chip's temperature, status = {}", temp_info.status());
+                m_is_thread_running = false;
+                break;
+            }
+
+            TempMeasurementData new_data = {};
+            auto old_data = m_data;
+
+            float32_t ts_avg = ((temp_info->ts0_temperature + temp_info->ts1_temperature) / 2);
+            new_data.max_value  = std::max(old_data.max_value, ts_avg);
+            new_data.min_value  = (old_data.min_value == 0) ? ts_avg : std::min(old_data.min_value, ts_avg);
+            new_data.average_value = calc_avg(old_data.sample_count, old_data.average_value, temp_info->sample_count, ts_avg);
+            new_data.sample_count = old_data.sample_count + temp_info->sample_count;
+
+            {
+                std::unique_lock<std::mutex> lock(m_mutex);
+                m_data = new_data;
+            }
+            
+            std::this_thread::sleep_for(DEFAULT_TEMPERATURE_MEASUREMENTS_INTERVAL); 
+        }
+    });
+
+    return HAILO_SUCCESS;
+}
+
+void TemperatureMeasurement::stop_measurement()
+{
+    m_is_thread_running = false;
+
+    if (m_thread.joinable()) {
+        m_thread.join();
+    }
+}
+
+const TempMeasurementData TemperatureMeasurement::get_data()
+{
+    std::unique_lock<std::mutex> lock(m_mutex);
+    return m_data;
+}
\ No newline at end of file
diff --git a/hailort/hailortcli/temp_measurement.hpp b/hailort/hailortcli/temp_measurement.hpp
new file mode 100644 (file)
index 0000000..b991645
--- /dev/null
@@ -0,0 +1,48 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file temp_measurement.hpp
+ * @brief Measure temperature of Hailo chip
+ **/
+
+#ifndef _HAILO_TEMP_MEASUREMENT_HPP_
+#define _HAILO_TEMP_MEASUREMENT_HPP_
+
+#include "hailortcli.hpp"
+#include "command.hpp"
+#include "hailo/hailort.h"
+#include "hailo/device.hpp"
+#include "CLI/CLI.hpp"
+
+#include <thread>
+
+struct TempMeasurementData {
+    float32_t average_value;
+    float32_t min_value;
+    float32_t max_value;
+    uint32_t sample_count;
+};
+
+
+class TemperatureMeasurement final {
+public:
+    TemperatureMeasurement(Device &device);
+    virtual ~TemperatureMeasurement();
+
+    hailo_status start_measurement();
+    void stop_measurement();
+    const TempMeasurementData get_data();
+
+private:
+    void measure_temp();
+
+    Device &m_device;
+    std::thread m_thread;
+    std::atomic_bool m_is_thread_running;
+    std::mutex m_mutex;
+    TempMeasurementData m_data;
+};
+
+#endif /* _HAILO_TEMP_MEASUREMENT_HPP_ */
diff --git a/hailort/hailortcli/udp_rate_limiter_command.cpp b/hailort/hailortcli/udp_rate_limiter_command.cpp
new file mode 100644 (file)
index 0000000..b1978af
--- /dev/null
@@ -0,0 +1,218 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file udp_rate_limiter_command.cpp
+ * @brief Rate limiter command impl
+ **/
+
+#include "hailo/network_rate_calculator.hpp"
+#include "hailo/hailort_common.hpp"
+#include "udp_rate_limiter_command.hpp"
+#include "hailortcli.hpp"
+
+#include <numeric>
+#include <algorithm>
+
+#define PORTS_COUNT (16)     // Should be same as HW_PACKAGE__CORE_PKG__N_AXIS_IN
+
+UdpRateLimiterCommand::UdpRateLimiterCommand (CLI::App &parent_app) :
+    Command(parent_app.add_subcommand("udp-rate-limiter", "Limit UDP rate"))
+{
+    m_set_command = m_app->add_subcommand("set", "Sets the udp rate limit");
+    m_set_command->add_option("--kbit-rate", m_rate_kbit_sec, "rate in Kbit/s")
+        ->required();
+    m_set_command->add_flag("-f,--force", m_force_reset, "When forced the tool will reset the udp limit before setting it");
+    add_device_options(m_set_command, m_board_ip, m_interface_name, m_dport);
+    
+    m_reset_command = m_app->add_subcommand("reset", "Resets the udp rate limit");
+    add_device_options(m_reset_command, m_board_ip, m_interface_name, m_dport);
+
+    m_autoset_command = m_app->add_subcommand("autoset", "Sets the udp limiter based on an existing HEF");
+    m_autoset_command->add_option("--hef", m_hef_path, "HEF path")
+        ->required()
+        ->check(CLI::ExistingFile);
+    m_autoset_command->add_option("--fps", m_fps, "Required FPS")
+        ->required();
+    m_autoset_command->add_option("--network-group-name", m_network_group_name, "The name of the network_group to configure rates for");
+    m_autoset_command->add_flag("-f,--force", m_force_reset, "When forced the tool will reset the udp limit before setting it");
+    add_device_options(m_autoset_command, m_board_ip, m_interface_name, m_dport);
+
+    m_app->require_subcommand(1);
+}
+
+hailo_status UdpRateLimiterCommand::execute()
+{
+    std::vector<uint16_t> board_ports;
+    if (0 == m_dport) {
+        board_ports = get_dports();
+    } else {
+        board_ports.emplace_back(m_dport);
+    }
+
+    if (m_board_ip.empty()) {
+        const auto result = ScanSubcommand::scan_ethernet("", m_interface_name);
+        if (!result) {
+            return result.status();
+        }
+        if (0 == result->size()) {
+            // scan_ethernet didn't fail but didn't return any ips
+            return HAILO_NOT_FOUND;
+        }
+        m_board_ip = result.value()[0];
+    }
+
+    if (m_set_command->parsed()) {
+        const auto rate_bytes_sec = bit_rate_kbit_sec_to_bytes_sec(m_rate_kbit_sec);
+        return set_command(board_ports, rate_bytes_sec);
+    }
+    else if (m_reset_command->parsed()) {
+        const auto results = reset_commnad(board_ports);
+        if (std::any_of(results.cbegin(), results.cend(),
+                [](const auto& key_value){ return key_value.second != HAILO_SUCCESS; })) {
+            return HAILO_INTERNAL_FAILURE;
+        }
+        return HAILO_SUCCESS;
+    }
+    else if (m_autoset_command->parsed()) {
+        return autoset_commnad(board_ports);
+    }
+    // Shouldn't get here
+    return HAILO_INTERNAL_FAILURE;
+}
+
+hailo_status UdpRateLimiterCommand::set_command(const std::vector<uint16_t> &board_ports, uint32_t rate_bytes_sec)
+{
+    std::map<uint16_t, hailo_status> reset_results;
+    if (m_force_reset) {
+        reset_results = reset_commnad(board_ports);
+    }
+
+    hailo_status status = HAILO_SUCCESS;
+    for (const auto& board_port : board_ports) {
+        if ((reset_results.end() != reset_results.find(board_port)) && (HAILO_SUCCESS != reset_results[board_port])) {
+            std::cout << "Failed resetting " << m_board_ip << ":" << board_port << "; won't set rate limit." << std::endl;
+            continue;
+        }
+        auto tc = TrafficControlUtil::create(m_board_ip, board_port, rate_bytes_sec);
+        if (!tc) {
+            // Best effort
+            std::cout << "Failed creating TrafficControlUtil for " << m_board_ip << ":" << board_port
+                      << " with status " << tc.status() << "; Continuing"<< std::endl;
+            status = tc.status();
+            continue;
+        }
+
+        std::cout << "Setting rate limit to " << rate_bytes_sec << " Bytes/sec for " << m_board_ip << ":" << board_port << "...";
+        const auto set_status = tc->set_rate_limit();
+        if (HAILO_SUCCESS != set_status) {
+            std::cout << std::endl << "Setting rate limit failed with status " << set_status << std::endl;
+            status = set_status;
+            // Best effort
+            continue;
+        }
+        std::cout << " done." << std::endl;
+    }
+
+    return status;
+}
+
+std::map<uint16_t, hailo_status> UdpRateLimiterCommand::reset_commnad(const std::vector<uint16_t> &board_ports)
+{
+    std::map<uint16_t, hailo_status> reset_results;
+    for (const auto& board_port : board_ports) {
+        auto tc = TrafficControlUtil::create(m_board_ip, board_port, bit_rate_kbit_sec_to_bytes_sec(m_rate_kbit_sec));
+        if (!tc) {
+            // Best effort
+            std::cout << "Failed creating TrafficControlUtil for " << m_board_ip << ":" << board_port
+                      << " with status " << tc.status() << "; Continuing"<< std::endl;
+            reset_results.emplace(board_port, tc.status());
+            continue;
+        }
+
+        const auto reset_status = do_reset(tc.value(), m_board_ip, board_port);
+        reset_results.emplace(board_port, reset_status);
+    }
+
+    return reset_results;
+}
+
+hailo_status UdpRateLimiterCommand::autoset_commnad(const std::vector<uint16_t> &board_ports)
+{
+    const auto rates_from_hef = calc_rate_from_hef(m_hef_path, m_network_group_name, m_fps);
+    CHECK_EXPECTED_AS_STATUS(rates_from_hef);
+
+    // On auto set, we use min rate for all input ports
+    auto min_rate_pair = *std::min_element(rates_from_hef.value().begin(), rates_from_hef.value().end(),
+        [](const auto& lhs, const auto& rhs) { return lhs.second < rhs.second; });
+
+    return set_command(board_ports, static_cast<uint32_t>(min_rate_pair.second));
+}
+
+void UdpRateLimiterCommand::add_device_options(CLI::App *app, std::string &board_ip, std::string &interface_name,
+    uint16_t &dport)
+{
+    assert(nullptr != app);
+
+    auto *board_ip_option = app->add_option("--board-ip", board_ip,
+        "board ip.\nIf not suplied the board ip will be found by scanning for connected boards.")
+        ->check(CLI::ValidIPV4);
+    
+    auto *interface_name_option = app->add_option("--interface-name", interface_name,
+        "Name of the interface on which to scan, if no board ip is provided")
+        ->excludes(board_ip_option);
+
+    (void) app->add_option("--dport", dport, "destination port")
+        ->default_val(0);
+
+    app->parse_complete_callback([board_ip_option, interface_name_option]() {
+        if (board_ip_option->empty() && interface_name_option->empty()) {
+            throw CLI::ParseError("Either --board-ip or --interface-name must be set", CLI::ExitCodes::InvalidError);
+        }
+    });
+}
+
+
+hailo_status UdpRateLimiterCommand::do_reset(TrafficControlUtil &tc, const std::string &board_ip, uint16_t board_port)
+{
+    std::cout << "Resetting rate limit for " << board_ip << ":" << board_port << "...";
+    const auto status = tc.reset_rate_limit();
+    if (HAILO_SUCCESS != status) {
+        std::cout << std::endl << "Reset rate limit failed with status " << status << std::endl;
+        return status;
+    }
+    std::cout << " done." << std::endl;
+
+    return HAILO_SUCCESS;
+}
+
+uint32_t UdpRateLimiterCommand::bit_rate_kbit_sec_to_bytes_sec(uint32_t rate_kbit_sec)
+{
+    //   rate      * 1000      / 8    = rate * 125
+    // (kbit/sec) (bits/kbit) (bits/byte)
+    return rate_kbit_sec * 125;
+}
+
+Expected<std::map<std::string, uint32_t>> UdpRateLimiterCommand::calc_rate_from_hef(const std::string &hef_path,
+    const std::string &network_group_name, uint32_t fps)
+{
+    auto hef = Hef::create(hef_path.c_str());
+    CHECK_EXPECTED(hef, "Failed reading hef file {}", hef_path.c_str());
+
+    auto rate_calc = NetworkUdpRateCalculator::create(&(hef.value()), network_group_name);
+    CHECK_EXPECTED(rate_calc);
+
+    auto calculated_rates = rate_calc->calculate_inputs_bandwith(fps);
+    CHECK_EXPECTED(calculated_rates);
+
+    return calculated_rates.release();
+}
+
+std::vector<uint16_t> UdpRateLimiterCommand::get_dports()
+{
+    std::vector<uint16_t> ports(PORTS_COUNT);
+    std::iota(ports.begin(), ports.end(), HailoRTCommon::ETH_INPUT_BASE_PORT);
+
+    return ports;
+}
\ No newline at end of file
diff --git a/hailort/hailortcli/udp_rate_limiter_command.hpp b/hailort/hailortcli/udp_rate_limiter_command.hpp
new file mode 100644 (file)
index 0000000..6f5dd60
--- /dev/null
@@ -0,0 +1,56 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file udp_rate_limiter_command.hpp
+ * @brief Tool for limiting the packet sending rate via UDP. Needed to ensure the board will not get more
+ * traffic than it can handle, which would cause packet loss.
+ **/
+
+#ifndef _HAILO_UDP_RATE_LIMITER_COMMAND_HPP_
+#define _HAILO_UDP_RATE_LIMITER_COMMAND_HPP_
+
+#include "hailortcli.hpp"
+#include "command.hpp"
+#include "scan_command.hpp"
+#if defined(__GNUC__)
+#include "common/os/posix/traffic_control.hpp"
+#endif
+
+#include "CLI/CLI.hpp"
+
+#include <map>
+
+
+class UdpRateLimiterCommand : public Command {
+public:
+    explicit UdpRateLimiterCommand(CLI::App &parent_app);
+    hailo_status execute() override;
+
+private:
+    hailo_status set_command(const std::vector<uint16_t> &board_ports, uint32_t rate_bytes_sec);
+    std::map<uint16_t, hailo_status> reset_commnad(const std::vector<uint16_t> &board_ports);
+    hailo_status autoset_commnad(const std::vector<uint16_t> &board_ports);
+
+    static void add_device_options(CLI::App *app, std::string &board_ip, std::string &interface_name, uint16_t &dport);
+    static hailo_status do_reset(TrafficControlUtil &tc, const std::string &board_ip, uint16_t board_port);
+    static uint32_t bit_rate_kbit_sec_to_bytes_sec(uint32_t rate_kbit_sec);
+    static Expected<std::map<std::string, uint32_t>> calc_rate_from_hef(const std::string &hef_path, const std::string &network_group_name,
+        uint32_t fps);
+    static std::vector<uint16_t> get_dports();
+
+    CLI::App *m_set_command;
+    CLI::App *m_reset_command;
+    CLI::App *m_autoset_command;
+    std::string m_board_ip;
+    std::string m_interface_name;
+    uint16_t m_dport;
+    uint32_t m_rate_kbit_sec;
+    bool m_force_reset;
+    std::string m_hef_path;
+    std::string m_network_group_name;
+    uint32_t m_fps;
+};
+
+#endif /* _HAILO_UDP_RATE_LIMITER_COMMAND_HPP_ */
diff --git a/hailort/libhailort/CMakeLists.txt b/hailort/libhailort/CMakeLists.txt
new file mode 100644 (file)
index 0000000..393ad62
--- /dev/null
@@ -0,0 +1,40 @@
+cmake_minimum_required(VERSION 3.0.0)
+# set(CMAKE_C_CLANG_TIDY "clang-tidy;-checks=*")
+
+set(HAILORT_MAJOR_VERSION    4)
+set(HAILORT_MINOR_VERSION    6)
+set(HAILORT_REVISION_VERSION 0)
+
+# Add the cmake folder so the modules there are found
+set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake" ${CMAKE_MODULE_PATH})
+
+# Generate hef-proto files using host protobuf
+protobuf_generate_cpp(PROTO_HEF_SRC PROTO_HEF_HEADR hef.proto)
+
+add_library(hef_proto ${PROTO_HEF_SRC} ${PROTO_HEF_HEADR})
+target_link_libraries(hef_proto libprotobuf)
+set_target_properties(hef_proto PROPERTIES CXX_STANDARD 14 GENERATED TRUE POSITION_INDEPENDENT_CODE ON)
+if(CMAKE_HOST_WIN32)
+    # https://github.com/protocolbuffers/protobuf/tree/master/cmake#notes-on-compiler-warnings
+    target_compile_options(hef_proto PRIVATE /wd4244)
+endif()
+get_filename_component(PROTO_HEADER_DIRECTORY ${PROTO_HEF_HEADR} DIRECTORY)
+target_include_directories(hef_proto
+    PUBLIC
+    $<BUILD_INTERFACE: ${PROTO_HEADER_DIRECTORY}>
+    $<BUILD_INTERFACE: ${Protobuf_INCLUDE_DIRS}>
+)
+
+# Add readerwriterqueue as a header-only library
+add_library(readerwriterqueue INTERFACE)
+target_include_directories(readerwriterqueue INTERFACE ${HAILO_EXTERNAL_DIR}/readerwriterqueue)
+
+add_subdirectory(src)
+if(HAILO_BUILD_EXAMPLES)
+    add_subdirectory(examples)
+endif()
+if(HAILO_BUILD_UT)
+    add_subdirectory(tests)
+endif()
+add_subdirectory(bindings)
+add_subdirectory(doc)
diff --git a/hailort/libhailort/bindings/CMakeLists.txt b/hailort/libhailort/bindings/CMakeLists.txt
new file mode 100644 (file)
index 0000000..3388d7f
--- /dev/null
@@ -0,0 +1,8 @@
+cmake_minimum_required(VERSION 3.0.0)
+if(HAILO_BUILD_PYBIND)
+    add_subdirectory(python)
+endif()
+# QNX currently doesnt support GStreamer
+if(HAILO_BUILD_GSTREAMER AND CMAKE_HOST_UNIX AND NOT CMAKE_SYSTEM_NAME STREQUAL QNX)
+    add_subdirectory(gstreamer)
+endif()
diff --git a/hailort/libhailort/bindings/gstreamer/AUTHORS b/hailort/libhailort/bindings/gstreamer/AUTHORS
new file mode 100644 (file)
index 0000000..0eb0cf9
--- /dev/null
@@ -0,0 +1,2 @@
+Yuval Belzer <yuvalb@hailo.ai>
+Omer Salem <omers@hailo.ai>
diff --git a/hailort/libhailort/bindings/gstreamer/CMakeLists.txt b/hailort/libhailort/bindings/gstreamer/CMakeLists.txt
new file mode 100644 (file)
index 0000000..7094c83
--- /dev/null
@@ -0,0 +1,50 @@
+cmake_minimum_required(VERSION 3.0.0)
+
+project(gsthailo)
+
+if(NOT CMAKE_HOST_UNIX)
+    message(FATAL_ERROR "Only unix hosts are supported, stopping build")
+endif()
+
+set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_LIST_DIR}/cmake/")
+find_package(HailoRT)
+
+# GST_PLUGIN_DEFINE needs PACKAGE to be defined
+set(GST_HAILO_PACKAGE_NAME "hailo")
+set(GST_HAILO_VERSION "1.0")
+
+find_package(PkgConfig REQUIRED)
+pkg_search_module(GLIB REQUIRED glib-2.0)
+pkg_search_module(GSTREAMER REQUIRED gstreamer-1.0)
+pkg_search_module(GSTREAMER_BASE REQUIRED gstreamer-base-1.0)
+pkg_search_module(GSTREAMER_VIDEO REQUIRED gstreamer-video-1.0)
+
+add_library(gsthailo SHARED
+    gst-hailo/gsthailoplugin.cpp
+    gst-hailo/gsthailonet.cpp
+    gst-hailo/gsthailosend.cpp
+    gst-hailo/gsthailorecv.cpp
+    gst-hailo/gsthailodevicestats.cpp
+    gst-hailo/common.cpp
+    gst-hailo/network_group_handle.cpp
+    gst-hailo/metadata/hailo_buffer_flag_meta.cpp
+    gst-hailo/metadata/tensor_meta.cpp
+    gst-hailo/hailo_events/hailo_events.cpp)
+
+set_target_properties(gsthailo PROPERTIES
+    PUBLIC_HEADER "gst-hailo/metadata/tensor_meta.hpp"
+)
+
+target_compile_options(gsthailo PRIVATE
+    -Werror -Wall -Wextra -Wconversion -O3 -DNDEBUG # TODO support debug/release builds
+    -DVERSION="${GST_HAILO_VERSION}"
+    -DPACKAGE="${GST_HAILO_PACKAGE_NAME}")
+
+target_include_directories(gsthailo PRIVATE ${GSTREAMER_VIDEO_INCLUDE_DIRS})
+target_link_libraries(gsthailo HailoRT::libhailort ${GSTREAMER_VIDEO_LDFLAGS})
+
+install(TARGETS gsthailo
+    LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+    # TODO: get gstreamer-1.0 in an automate way
+    PUBLIC_HEADER DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/gstreamer-1.0/gst/hailo/"
+    CONFIGURATIONS Release)
diff --git a/hailort/libhailort/bindings/gstreamer/COPYING b/hailort/libhailort/bindings/gstreamer/COPYING
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/hailort/libhailort/bindings/gstreamer/LICENSE b/hailort/libhailort/bindings/gstreamer/LICENSE
new file mode 100644 (file)
index 0000000..e5ab03e
--- /dev/null
@@ -0,0 +1,502 @@
+                  GNU LESSER GENERAL PUBLIC LICENSE
+                       Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+ 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL.  It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+                            Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+  This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it.  You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations below.
+
+  When we speak of free software, we are referring to freedom of use,
+not price.  Our General Public Licenses are designed to make sure that
+you have the freedom to distribute copies of free software (and charge
+for this service if you wish); that you receive source code or can get
+it if you want it; that you can change the software and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+  To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights.  These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+  For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you.  You must make sure that they, too, receive or can get the source
+code.  If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it.  And you must show them these terms so they know their rights.
+
+  We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+  To protect each distributor, we want to make it very clear that
+there is no warranty for the free library.  Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+
+  Finally, software patents pose a constant threat to the existence of
+any free program.  We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder.  Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+  Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License.  This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License.  We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+  When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library.  The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom.  The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+  We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License.  It also provides other free software developers Less
+of an advantage over competing non-free programs.  These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries.  However, the Lesser license provides advantages in certain
+special circumstances.
+
+  For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it becomes
+a de-facto standard.  To achieve this, non-free programs must be
+allowed to use the library.  A more frequent case is that a free
+library does the same job as widely used non-free libraries.  In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+  In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software.  For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+  Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.  Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library".  The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+
+                  GNU LESSER GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+  A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+  The "Library", below, refers to any such software library or work
+which has been distributed under these terms.  A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language.  (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+  "Source code" for a work means the preferred form of the work for
+making modifications to it.  For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+  Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it).  Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+
+  1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+  You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+
+  2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) The modified work must itself be a software library.
+
+    b) You must cause the files modified to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    c) You must cause the whole of the work to be licensed at no
+    charge to all third parties under the terms of this License.
+
+    d) If a facility in the modified Library refers to a function or a
+    table of data to be supplied by an application program that uses
+    the facility, other than as an argument passed when the facility
+    is invoked, then you must make a good faith effort to ensure that,
+    in the event an application does not supply such function or
+    table, the facility still operates, and performs whatever part of
+    its purpose remains meaningful.
+
+    (For example, a function in a library to compute square roots has
+    a purpose that is entirely well-defined independent of the
+    application.  Therefore, Subsection 2d requires that any
+    application-supplied function or table used by this function must
+    be optional: if the application does not supply it, the square
+    root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library.  To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License.  (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.)  Do not make any other change in
+these notices.
+
+  Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+  This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+  4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+  If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library".  Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+  However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library".  The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+  When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library.  The
+threshold for this to be true is not precisely defined by law.
+
+  If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work.  (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+  Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+  6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+  You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License.  You must supply a copy of this License.  If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License.  Also, you must do one
+of these things:
+
+    a) Accompany the work with the complete corresponding
+    machine-readable source code for the Library including whatever
+    changes were used in the work (which must be distributed under
+    Sections 1 and 2 above); and, if the work is an executable linked
+    with the Library, with the complete machine-readable "work that
+    uses the Library", as object code and/or source code, so that the
+    user can modify the Library and then relink to produce a modified
+    executable containing the modified Library.  (It is understood
+    that the user who changes the contents of definitions files in the
+    Library will not necessarily be able to recompile the application
+    to use the modified definitions.)
+
+    b) Use a suitable shared library mechanism for linking with the
+    Library.  A suitable mechanism is one that (1) uses at run time a
+    copy of the library already present on the user's computer system,
+    rather than copying library functions into the executable, and (2)
+    will operate properly with a modified version of the library, if
+    the user installs one, as long as the modified version is
+    interface-compatible with the version that the work was made with.
+
+    c) Accompany the work with a written offer, valid for at
+    least three years, to give the same user the materials
+    specified in Subsection 6a, above, for a charge no more
+    than the cost of performing this distribution.
+
+    d) If distribution of the work is made by offering access to copy
+    from a designated place, offer equivalent access to copy the above
+    specified materials from the same place.
+
+    e) Verify that the user has already received a copy of these
+    materials or that you have already sent this user a copy.
+
+  For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it.  However, as a special exception,
+the materials to be distributed need not include anything that is
+normally distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+  It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system.  Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+  7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+    a) Accompany the combined library with a copy of the same work
+    based on the Library, uncombined with any other library
+    facilities.  This must be distributed under the terms of the
+    Sections above.
+
+    b) Give prominent notice with the combined library of the fact
+    that part of it is a work based on the Library, and explaining
+    where to find the accompanying uncombined form of the same work.
+
+  8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License.  Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License.  However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+  9. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Library or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+  10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties with
+this License.
+
+  11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded.  In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+  13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation.  If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+  14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission.  For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this.  Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+                            NO WARRANTY
+
+  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+                     END OF TERMS AND CONDITIONS
+
+           How to Apply These Terms to Your New Libraries
+
+  If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change.  You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+  To apply these terms, attach the following notices to the library.  It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the library's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the
+  library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+  <signature of Ty Coon>, 1 April 1990
+  Ty Coon, President of Vice
+
+That's all there is to it!
diff --git a/hailort/libhailort/bindings/gstreamer/README b/hailort/libhailort/bindings/gstreamer/README
new file mode 100644 (file)
index 0000000..621cf1e
--- /dev/null
@@ -0,0 +1,7 @@
+Compiling the plugin:
+  cmake -H. -Bbuild
+  cmake --build build
+
+Using the plugin:
+  The plugin is dependent on libhailort which is included in the `hailort/lib/` directory.
+  Use LD_LIBRARY_PATH to specify the location of the libhailort library.
\ No newline at end of file
diff --git a/hailort/libhailort/bindings/gstreamer/cmake/FindHailoRT.cmake b/hailort/libhailort/bindings/gstreamer/cmake/FindHailoRT.cmake
new file mode 100644 (file)
index 0000000..85906ac
--- /dev/null
@@ -0,0 +1,28 @@
+# - Try to find HailoRT
+#   - If libhailort is defined (building as part of the build tree), use it
+#   - Otherwise, find HAILORT_LIB and HAILORT_INCLUDE_DIR, and import libhailort
+
+if (NOT TARGET libhailort)
+    # find_path finds a directory containing the named file
+    find_path(HAILORT_INCLUDE_DIR "hailo/" PATH_SUFFIXES "include/")
+
+    find_library(HAILORT_LIB "libhailort.so.4.6.0" PATH_SUFFIXES "lib/")
+
+    include(FindPackageHandleStandardArgs)
+    # Handle the QUIETLY and REQUIRED arguments and set HAILORT_FOUND to TRUE
+    # if all listed variables are TRUE
+    find_package_handle_standard_args(
+        HailoRT
+        DEFAULT_MSG
+        HAILORT_LIB
+        HAILORT_INCLUDE_DIR
+    )
+
+    add_library(HailoRT::libhailort SHARED IMPORTED)
+    set_target_properties(HailoRT::libhailort PROPERTIES
+        IMPORTED_LOCATION "${HAILORT_LIB}"
+        INTERFACE_INCLUDE_DIRECTORIES "${HAILORT_INCLUDE_DIR}"
+    )
+else()
+    add_library(HailoRT::libhailort ALIAS libhailort)
+endif()
diff --git a/hailort/libhailort/bindings/gstreamer/gst-hailo/common.cpp b/hailort/libhailort/bindings/gstreamer/gst-hailo/common.cpp
new file mode 100644 (file)
index 0000000..87ee585
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2021-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the LGPL 2.1 license (https://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+#include "common.hpp"
+
+template<>
+HailoElemProperty<gchar*>::~HailoElemProperty()
+{
+    if (nullptr != m_value) {
+        g_free(m_value);
+    }
+}
\ No newline at end of file
diff --git a/hailort/libhailort/bindings/gstreamer/gst-hailo/common.hpp b/hailort/libhailort/bindings/gstreamer/gst-hailo/common.hpp
new file mode 100644 (file)
index 0000000..1ee88a3
--- /dev/null
@@ -0,0 +1,134 @@
+/*
+ * Copyright (c) 2021-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the LGPL 2.1 license (https://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+#ifndef _GST_HAILO_COMMON_HPP_
+#define _GST_HAILO_COMMON_HPP_
+
+#include "hailo/device.hpp"
+#include "hailo/network_group.hpp"
+#include "hailo/vstream.hpp"
+
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wconversion"
+#include <gst/gst.h>
+#pragma GCC diagnostic pop
+
+#include <vector>
+
+using namespace hailort;
+
+#define PLUGIN_AUTHOR "Hailo Technologies Ltd. (\"Hailo\")"
+
+#define MAX_QUEUED_BUFFERS_IN_INPUT (16)
+#define MAX_QUEUED_BUFFERS_IN_OUTPUT (16)
+#define MAX_QUEUED_BUFFERS_IN_CORE (16)
+#define MAX_BUFFER_COUNT(_batch_size) (MAX_QUEUED_BUFFERS_IN_INPUT + MAX_QUEUED_BUFFERS_IN_OUTPUT + (1 < (_batch_size) ? (_batch_size) : MAX_QUEUED_BUFFERS_IN_CORE))
+
+#define MAX_GSTREAMER_BATCH_SIZE (16)
+#define MIN_GSTREAMER_BATCH_SIZE (HAILO_DEFAULT_BATCH_SIZE)
+#define DEFAULT_OUTPUTS_MIN_POOL_SIZE (MAX_GSTREAMER_BATCH_SIZE)
+#define DEFAULT_OUTPUTS_MAX_POOL_SIZE (0) // 0 means unlimited upper limit
+
+#define DEFAULT_VDEVICE_KEY (0)
+#define MIN_VALID_VDEVICE_KEY (1)
+
+#define HAILO_SUPPORTED_FORMATS "{ RGB, YUY2 }"
+#define HAILO_VIDEO_CAPS GST_VIDEO_CAPS_MAKE(HAILO_SUPPORTED_FORMATS)
+
+#define GST_CHECK(cond, ret_val, element, domain, ...)      \
+    do {                                \
+        if (!(cond)) {                  \
+            GST_ELEMENT_ERROR((element), domain, FAILED, (__VA_ARGS__), (NULL)); \
+            return (ret_val);             \
+        }                               \
+    } while(0)
+
+#define GST_CHECK_SUCCESS(status, element, domain, ...)      \
+    do {                                \
+        if (HAILO_SUCCESS != (status)) {                  \
+            GST_ELEMENT_ERROR((element), domain, FAILED, (__VA_ARGS__), (NULL)); \
+            return (status);           \
+        }                               \
+    } while(0)
+
+#define GST_CHECK_EXPECTED(obj, element, domain, ...)      \
+    do {                                \
+        if (!(obj)) {                  \
+            GST_ELEMENT_ERROR((element), domain, FAILED, (__VA_ARGS__), (NULL)); \
+            return make_unexpected(obj.status());           \
+        }                               \
+    } while(0)
+
+#define GST_CHECK_EXPECTED_AS_STATUS(obj, element, domain, ...)      \
+    do {                                \
+        if (!(obj)) {                  \
+            GST_ELEMENT_ERROR((element), domain, FAILED, (__VA_ARGS__), (NULL)); \
+            return obj.status();           \
+        }                               \
+    } while(0)
+
+// From https://stackoverflow.com/questions/57092289/do-stdmake-shared-and-stdmake-unique-have-a-nothrow-version
+template <class T, class... Args>
+static inline std::unique_ptr<T> make_unique_nothrow(Args&&... args)
+    noexcept(noexcept(T(std::forward<Args>(args)...)))
+{
+    return std::unique_ptr<T>(new (std::nothrow) T(std::forward<Args>(args)...));
+}
+
+template <class T, class... Args>
+static inline std::shared_ptr<T> make_shared_nothrow(Args&&... args)
+    noexcept(noexcept(T(std::forward<Args>(args)...)))
+{
+    return std::shared_ptr<T>(new (std::nothrow) T(std::forward<Args>(args)...));
+}
+
+template<typename T>
+class HailoElemProperty final
+{
+public:
+    HailoElemProperty(T default_val) : m_value(default_val), m_was_changed(false) {}
+
+    ~HailoElemProperty() {}
+    
+    HailoElemProperty<T> &operator=(const T &value)
+    {
+        m_was_changed = true;
+        m_value = value;
+        return *this;
+    }
+
+    const T &get()
+    {
+        return m_value;
+    }
+
+    bool was_changed()
+    {
+        return m_was_changed;
+    }
+
+private:
+    T m_value;
+    bool m_was_changed;
+};
+
+template<>
+HailoElemProperty<gchar*>::~HailoElemProperty();
+
+#endif /* _GST_HAILO_COMMON_HPP_ */
\ No newline at end of file
diff --git a/hailort/libhailort/bindings/gstreamer/gst-hailo/gsthailodevicestats.cpp b/hailort/libhailort/bindings/gstreamer/gst-hailo/gsthailodevicestats.cpp
new file mode 100644 (file)
index 0000000..1049a16
--- /dev/null
@@ -0,0 +1,333 @@
+/*
+ * Copyright (c) 2021-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the LGPL 2.1 license (https://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+#include "gsthailodevicestats.hpp"
+#include "hailo_events/hailo_events.hpp"
+
+#include <limits>
+
+GST_DEBUG_CATEGORY_STATIC(gst_hailodevicestats_debug_category);
+#define GST_CAT_DEFAULT gst_hailodevicestats_debug_category
+#define DEFAULT_SAMPLING_INTERVAL_SECONDS (1)
+
+/* Amount of time between each power measurement interval.
+    The default values for provides by the sensor a new value every:
+    2 * sampling_period (1.1) * averaging_factor (256) [ms].
+    Therefore we want it to be the period of time that the core will sleep between samples,
+    plus a factor of 20 percent */
+#define POWER_MEASUREMENT_DELAY_MS (static_cast<uint32_t>(1100 / 1000.0 * 256 * 2 * 1.2))
+
+static void gst_hailodevicestats_set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec);
+static void gst_hailodevicestats_get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec);
+static GstStateChangeReturn gst_hailodevicestats_change_state(GstElement *element, GstStateChange transition);
+static void gst_hailodevicestats_finalize(GObject *object);
+
+enum
+{
+    PROP_0,
+    PROP_SAMPLING_INTERVAL,
+    PROP_DEVICE_ID,
+    PROP_SILENT,
+    PROP_POWER_MEASUREMENT,
+    PROP_TEMPERATURE
+};
+
+G_DEFINE_TYPE(GstHailoDeviceStats, gst_hailodevicestats, GST_TYPE_ELEMENT);
+
+static void gst_hailodevicestats_class_init(GstHailoDeviceStatsClass *klass)
+{
+    GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
+    GstElementClass *element_class = GST_ELEMENT_CLASS(klass);
+
+    gobject_class->set_property = gst_hailodevicestats_set_property;
+    gobject_class->get_property = gst_hailodevicestats_get_property;
+    g_object_class_install_property(gobject_class, PROP_SAMPLING_INTERVAL,
+        g_param_spec_uint("interval", "Sampling Interval", "Time period between samples, in seconds",
+            0, std::numeric_limits<uint32_t>::max(), DEFAULT_SAMPLING_INTERVAL_SECONDS, (GParamFlags)(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
+    g_object_class_install_property(gobject_class, PROP_DEVICE_ID,
+        g_param_spec_string("device-id", "Device ID", "Device ID ([<domain>]:<bus>:<device>.<func>, same as in lspci command)", nullptr,
+            (GParamFlags)(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
+    g_object_class_install_property(gobject_class, PROP_SILENT,
+        g_param_spec_boolean("silent", "Silent flag", "Should print statistics", false,
+            (GParamFlags)(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
+    g_object_class_install_property(gobject_class, PROP_POWER_MEASUREMENT,
+        g_param_spec_float("power-measurement", "Power Measurement", "Current power measurement of device",
+            0.0f, std::numeric_limits<float>::max(), 0.0f, (GParamFlags)(G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)));
+    g_object_class_install_property(gobject_class, PROP_TEMPERATURE,
+        g_param_spec_float("temperature", "Temperature", "Current temperature of device",
+            0.0f, std::numeric_limits<float>::max(), 0.0f, (GParamFlags)(G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)));
+    gobject_class->finalize = gst_hailodevicestats_finalize;
+
+    gst_element_class_set_static_metadata(element_class, "hailodevicestats element", "Hailo/Device", "Log Hailo8 device statistics", PLUGIN_AUTHOR);
+    element_class->change_state = GST_DEBUG_FUNCPTR(gst_hailodevicestats_change_state);
+}
+
+Expected<std::unique_ptr<HailoDeviceStatsImpl>> HailoDeviceStatsImpl::create(GstHailoDeviceStats *element)
+{
+    if (nullptr == element) {
+        return make_unexpected(HAILO_INVALID_ARGUMENT);
+    }
+
+    auto ptr = make_unique_nothrow<HailoDeviceStatsImpl>(element);
+    GST_CHECK(nullptr != ptr, make_unexpected(HAILO_OUT_OF_HOST_MEMORY), element, RESOURCE, "Could not create HailoDeviceStats implementation!");
+
+    return ptr;
+}
+
+HailoDeviceStatsImpl::HailoDeviceStatsImpl(GstHailoDeviceStats *element) : m_element(element), m_sampling_interval(DEFAULT_SAMPLING_INTERVAL_SECONDS),
+    m_device_id(nullptr), m_device_info(), m_is_silent(false), m_was_configured(false), m_power_measure(0.0f), m_avg_temp(0.0f)
+{
+    GST_DEBUG_CATEGORY_INIT(gst_hailodevicestats_debug_category, "hailodevicestats", 0, "debug category for hailodevicestats element");
+}
+
+HailoDeviceStatsImpl::~HailoDeviceStatsImpl()
+{
+    if (nullptr != m_device_id) {
+        g_free(m_device_id);
+    }
+
+    m_is_thread_running = false;
+    if (m_thread.joinable()) {
+        m_thread.join();
+    }
+}
+
+void HailoDeviceStatsImpl::set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec)
+{
+    GST_DEBUG_OBJECT(m_element, "set_property");
+
+    if ((object == nullptr) || (value == nullptr) || (pspec == nullptr)) {
+        g_error("set_property got null parameter!");
+        return;
+    }
+
+    switch (property_id) {
+    case PROP_SAMPLING_INTERVAL:
+        m_sampling_interval = g_value_get_uint(value);
+        break;
+    case PROP_DEVICE_ID:
+        if (m_was_configured) {
+            g_warning("The device was already configured so changing the device ID will not take place!");
+            break;
+        }
+        if (nullptr != m_device_id) {
+            g_free(m_device_id);
+        }
+        m_device_id = g_strdup(g_value_get_string(value));
+        break;
+    case PROP_SILENT:
+        m_is_silent = g_value_get_boolean(value);
+        break;
+    default:
+        G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
+        break;
+    }
+}
+
+void HailoDeviceStatsImpl::get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec)
+{
+    GST_DEBUG_OBJECT(m_element, "get_property");
+
+    if ((object == nullptr) || (value == nullptr) || (pspec == nullptr)) {
+        g_error("get_property got null parameter!");
+        return;
+    }
+
+    switch (property_id) {
+    case PROP_SAMPLING_INTERVAL:
+        g_value_set_uint(value, m_sampling_interval);
+        break;
+    case PROP_DEVICE_ID:
+        g_value_set_string(value, m_device_id);
+        break;
+    case PROP_SILENT:
+        g_value_set_boolean(value, m_is_silent);
+        break;
+    case PROP_POWER_MEASUREMENT:
+        {
+            std::unique_lock<std::mutex> lock(m_mutex);
+            g_value_set_float(value, m_power_measure);
+        }
+        break;
+    case PROP_TEMPERATURE:
+        {
+            std::unique_lock<std::mutex> lock(m_mutex);
+            g_value_set_float(value, m_avg_temp);
+        }
+        break;
+    default:
+        G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
+        break;
+    }
+}
+
+Expected<std::unique_ptr<Device>> HailoDeviceStatsImpl::create_device(const char *device_id, hailo_pcie_device_info_t &device_info)
+{
+    if (nullptr == device_id) {
+        auto scan_result = Device::scan_pcie();
+        GST_CHECK_EXPECTED(scan_result, m_element, RESOURCE, "Failed scanning pcie devices, status = %d", scan_result.status());
+        GST_CHECK(scan_result->size() == 1, make_unexpected(HAILO_INVALID_OPERATION), m_element, RESOURCE, "Expected only 1 PCIe device");
+        device_info = scan_result->at(0);
+    } else {
+        std::string device_bdf = device_id;
+        auto device_info_expected = Device::parse_pcie_device_info(device_bdf);
+        GST_CHECK_EXPECTED(device_info_expected, m_element, RESOURCE, "Failed parsing pcie device info, status = %d", device_info_expected.status());
+        device_info = device_info_expected.value();
+    }
+
+    auto device = Device::create_pcie(device_info);
+    GST_CHECK_EXPECTED(device, m_element, RESOURCE, "Failed creating device, status = %d", device.status());
+
+    return device.release();
+}
+
+hailo_status HailoDeviceStatsImpl::start_thread()
+{
+    auto device = create_device(m_device_id, m_device_info);
+    GST_CHECK_EXPECTED_AS_STATUS(device, m_element, RESOURCE, "Creating device failed, status = %d", device.status());
+
+    m_device = device.release();
+    m_is_thread_running = true;
+    m_thread = std::thread([this] () {
+        (void)run_measure_loop();
+    });
+
+    return HAILO_SUCCESS;
+}
+
+void HailoDeviceStatsImpl::join_thread()
+{
+    m_is_thread_running = false;
+    m_thread.join();
+}
+
+hailo_status HailoDeviceStatsImpl::run_measure_loop()
+{
+    // Checking temperature sensor before starting thread
+    auto temp_info = m_device->get_chip_temperature();
+    GST_CHECK_EXPECTED_AS_STATUS(temp_info, m_element, RESOURCE, "Getting chip temperature failed, status = %d", temp_info.status());
+
+    hailo_status status = m_device->stop_power_measurement();
+    GST_CHECK_SUCCESS(status, m_element, RESOURCE, "Stopping power measurement failed, status = %d", status);
+
+    status = m_device->set_power_measurement(0, HAILO_DVM_OPTIONS_AUTO, HAILO_POWER_MEASUREMENT_TYPES__AUTO);
+    GST_CHECK_SUCCESS(status, m_element, RESOURCE, "Setting power measurement parameters failed, status = %d", status);
+
+    status = m_device->start_power_measurement(POWER_MEASUREMENT_DELAY_MS, HAILO_DEFAULT_INIT_AVERAGING_FACTOR, HAILO_DEFAULT_INIT_SAMPLING_PERIOD_US);
+    GST_CHECK_SUCCESS(status, m_element, RESOURCE, "Starting power measurement failed, status = %d", status);
+
+    auto device_string = Device::pcie_device_info_to_string(m_device_info);
+    GST_CHECK_EXPECTED_AS_STATUS(device_string, m_element, RESOURCE, "Getting PCIe device ID string has failed, status = %d", device_string.status());
+    const char *device_raw_string = device_string->c_str();
+
+    while (m_is_thread_running.load()) {
+        auto measurement = m_device->get_power_measurement(0, true);
+        GST_CHECK_EXPECTED_AS_STATUS(measurement, m_element, RESOURCE, "Getting power measurement failed, status = %d", measurement.status());
+
+        if (!m_is_silent) {
+            GST_DEBUG("[%s] Power measurement: %f", device_raw_string, measurement->average_value);
+        }
+
+        auto temp_info = m_device->get_chip_temperature();
+        GST_CHECK_EXPECTED_AS_STATUS(temp_info, m_element, RESOURCE, "Temperature measurement failed, status = %d", temp_info.status());
+
+        float32_t ts_avg = ((temp_info->ts0_temperature + temp_info->ts1_temperature) / 2);
+        if (!m_is_silent) {
+            GST_DEBUG("[%s] Temperature = %f", device_raw_string, ts_avg);
+        }
+
+        {
+            std::unique_lock<std::mutex> lock(m_mutex);
+            m_power_measure = measurement->average_value;
+            m_avg_temp = ts_avg;
+        }
+
+        GstStructure *str = gst_structure_new(HailoDeviceStatsMessage::name,
+                                              "device_id", G_TYPE_STRING, device_raw_string,
+                                              "temperature", G_TYPE_FLOAT, m_avg_temp,
+                                              "power", G_TYPE_FLOAT, m_power_measure,
+                                              NULL);
+        GstMessage *msg = gst_message_new_custom(GST_MESSAGE_ELEMENT, GST_OBJECT(GST_ELEMENT_PARENT(m_element)), str);
+        gst_element_post_message(GST_ELEMENT_PARENT(m_element), msg);
+
+        std::this_thread::sleep_for(std::chrono::seconds(m_sampling_interval));
+    }
+
+    status = m_device->stop_power_measurement();
+    GST_CHECK_SUCCESS(status, m_element, RESOURCE, "Stopping power measurement failed, status = %d", status);
+
+    return HAILO_SUCCESS;
+}
+
+static void gst_hailodevicestats_init(GstHailoDeviceStats *self)
+{
+    auto hailodevicestats_impl = HailoDeviceStatsImpl::create(self);
+    if (!hailodevicestats_impl) {
+        GST_ELEMENT_ERROR(self, RESOURCE, FAILED, ("Creating hailodevicestats implementation has failed! status = %d", hailodevicestats_impl.status()), (NULL));
+        return;
+    }
+
+    self->impl = hailodevicestats_impl.release();
+}
+
+static void gst_hailodevicestats_finalize(GObject *object)
+{
+    GST_HAILODEVICESTATS(object)->impl.reset();
+    G_OBJECT_CLASS(gst_hailodevicestats_parent_class)->finalize(object);
+}
+
+void gst_hailodevicestats_set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec)
+{
+    GST_HAILODEVICESTATS(object)->impl->set_property(object, property_id, value, pspec);
+}
+
+void gst_hailodevicestats_get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec)
+{
+    GST_HAILODEVICESTATS(object)->impl->get_property(object, property_id, value, pspec);
+}
+
+static GstStateChangeReturn gst_hailodevicestats_change_state(GstElement *element, GstStateChange transition)
+{
+    GstStateChangeReturn ret = GST_ELEMENT_CLASS(gst_hailodevicestats_parent_class)->change_state(element, transition);
+    if (GST_STATE_CHANGE_FAILURE == ret) {
+        return ret;
+    }
+
+    switch (transition) {
+    case GST_STATE_CHANGE_READY_TO_PAUSED:
+    {
+        hailo_status status = GST_HAILODEVICESTATS(element)->impl->start_thread();
+        if (HAILO_SUCCESS != status) {
+            g_critical("start hailodevicestats thread failed, status = %d", status);
+        }
+        break;
+    }
+    case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
+        GST_HAILODEVICESTATS(element)->impl->join_thread();
+        break;
+    case GST_STATE_CHANGE_READY_TO_NULL:
+        // Cleanup all of hailodevicestats memory
+        GST_HAILODEVICESTATS(element)->impl.reset();
+        break;
+    default:
+        break;
+    }
+
+    return ret;
+}
\ No newline at end of file
diff --git a/hailort/libhailort/bindings/gstreamer/gst-hailo/gsthailodevicestats.hpp b/hailort/libhailort/bindings/gstreamer/gst-hailo/gsthailodevicestats.hpp
new file mode 100644 (file)
index 0000000..90c244d
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2021-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the LGPL 2.1 license (https://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+#ifndef _GST_HAILODEVICESTATS_HPP_
+#define _GST_HAILODEVICESTATS_HPP_
+
+#include "common.hpp"
+#include "hailo/expected.hpp"
+
+#include <memory>
+#include <mutex>
+
+G_BEGIN_DECLS
+
+#define GST_TYPE_HAILODEVICESTATS (gst_hailodevicestats_get_type())
+#define GST_HAILODEVICESTATS(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_HAILODEVICESTATS,GstHailoDeviceStats))
+#define GST_HAILODEVICESTATS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_HAILODEVICESTATS,GstHailoDeviceStatsClass))
+#define GST_IS_HAILODEVICESTATS(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_HAILODEVICESTATS))
+#define GST_IS_HAILODEVICESTATS_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_HAILODEVICESTATS))
+
+class HailoDeviceStatsImpl;
+struct GstHailoDeviceStats
+{
+    GstElement parent;
+    std::unique_ptr<HailoDeviceStatsImpl> impl;
+};
+
+struct GstHailoDeviceStatsClass
+{
+    GstElementClass parent;
+};
+
+// Message to send hailo device stats measurement data.
+// This event should be sent from gsthailodevicestats element.
+struct HailoDeviceStatsMessage {
+    gchar *device_id;
+    float32_t temperature;
+    float32_t power;
+
+    static constexpr const gchar *name = "HailoDeviceStatsMessage";
+};
+
+class HailoDeviceStatsImpl final
+{
+public:
+    static Expected<std::unique_ptr<HailoDeviceStatsImpl>> create(GstHailoDeviceStats *element);
+    HailoDeviceStatsImpl(GstHailoDeviceStats *element);
+    ~HailoDeviceStatsImpl();
+
+    void set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec);
+    void get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec);
+    hailo_status start_thread();
+    void join_thread();
+
+private:
+    hailo_status run_measure_loop();
+
+    Expected<std::unique_ptr<Device>> create_device(const char *device_id, hailo_pcie_device_info_t &device_info);
+
+    GstHailoDeviceStats *m_element;
+    guint32 m_sampling_interval;
+    gchar *m_device_id;
+    hailo_pcie_device_info_t m_device_info;
+    bool m_is_silent;
+    bool m_was_configured;
+    float32_t m_power_measure;
+    float32_t m_avg_temp;
+    std::thread m_thread;
+    std::atomic_bool m_is_thread_running;
+    std::unique_ptr<Device> m_device;
+    std::mutex m_mutex;
+};
+
+GType gst_hailodevicestats_get_type(void);
+
+G_END_DECLS
+
+#endif /* _GST_HAILODEVICESTATS_HPP_ */
diff --git a/hailort/libhailort/bindings/gstreamer/gst-hailo/gsthailonet.cpp b/hailort/libhailort/bindings/gstreamer/gst-hailo/gsthailonet.cpp
new file mode 100644 (file)
index 0000000..cd1fa98
--- /dev/null
@@ -0,0 +1,738 @@
+/*
+ * Copyright (c) 2021-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the LGPL 2.1 license (https://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+#include "gsthailonet.hpp"
+#include "gsthailosend.hpp"
+#include "gsthailorecv.hpp"
+#include "hailo_events/hailo_events.hpp"
+#include "metadata/hailo_buffer_flag_meta.hpp"
+#include "hailo/hailort_common.hpp"
+
+#include <sstream>
+#include <algorithm>
+
+GST_DEBUG_CATEGORY_STATIC(gst_hailonet_debug_category);
+#define GST_CAT_DEFAULT gst_hailonet_debug_category
+
+constexpr std::chrono::milliseconds WAIT_FOR_FLUSH_TIMEOUT_MS(1000);
+
+static void gst_hailonet_set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec);
+static void gst_hailonet_get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec);
+static gboolean gst_hailorecv_src_pad_event(GstPad *pad, GstObject *parent, GstEvent *event);
+static GstPadProbeReturn gst_hailonet_sink_probe(GstPad *pad, GstPadProbeInfo *info, gpointer user_data);
+static GstStateChangeReturn gst_hailonet_change_state(GstElement *element, GstStateChange transition);
+static void gst_hailonet_flush_callback(GstHailoNet *hailonet, gpointer data);
+static void gst_hailonet_inner_queue_overrun_callback(GstElement *queue, gpointer udata);
+static void gst_hailonet_inner_queue_underrun_callback(GstElement *queue, gpointer udata);
+
+enum
+{
+    PROP_0,
+    PROP_DEBUG,
+    PROP_DEVICE_ID,
+    PROP_HEF_PATH,
+    PROP_NETWORK_NAME,
+    PROP_BATCH_SIZE,
+    PROP_OUTPUTS_MIN_POOL_SIZE,
+    PROP_OUTPUTS_MAX_POOL_SIZE,
+    PROP_IS_ACTIVE,
+    PROP_DEVICE_COUNT,
+    PROP_VDEVICE_KEY
+};
+
+G_DEFINE_TYPE(GstHailoNet, gst_hailonet, GST_TYPE_BIN);
+
+static void gst_hailonet_class_init(GstHailoNetClass *klass)
+{
+    GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
+    GstElementClass *element_class = GST_ELEMENT_CLASS(klass);
+
+    GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE("src", GST_PAD_SRC, GST_PAD_ALWAYS, GST_STATIC_CAPS_ANY);
+    gst_element_class_add_pad_template(element_class, gst_static_pad_template_get(&src_template));
+
+    GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE("sink", GST_PAD_SINK, GST_PAD_ALWAYS, GST_STATIC_CAPS_ANY);
+    gst_element_class_add_pad_template(element_class, gst_static_pad_template_get(&sink_template));
+
+    gst_element_class_set_static_metadata(element_class,
+        "hailonet element", "Hailo/Network",
+        "Configure and Activate Hailo Network. "
+            "Supports the \"flush\" signal which blocks until there are no buffers currently processesd in the element. "
+            "When deactivating a hailonet during runtime (via set_property of \"is-active\" to False), make sure that no frames are being pushed into the "
+            "hailonet, since this operation waits until there are no frames coming in.",
+        PLUGIN_AUTHOR);
+
+    element_class->change_state = GST_DEBUG_FUNCPTR(gst_hailonet_change_state);
+    
+    gobject_class->set_property = gst_hailonet_set_property;
+    gobject_class->get_property = gst_hailonet_get_property;
+    g_object_class_install_property(gobject_class, PROP_DEBUG,
+        g_param_spec_boolean("debug", "Debug flag", "Should print debug information", false,
+            (GParamFlags)(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
+    g_object_class_install_property(gobject_class, PROP_DEVICE_ID,
+        g_param_spec_string("device-id", "Device ID", "Device ID ([<domain>]:<bus>:<device>.<func>, same as in lspci command). Excludes device-count.", NULL,
+            (GParamFlags)(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
+    g_object_class_install_property(gobject_class, PROP_DEVICE_COUNT,
+        g_param_spec_uint("device-count", "Number of devices to use", "Number of physical devices to use. Excludes device-id.", HAILO_DEFAULT_DEVICE_COUNT,
+            std::numeric_limits<uint16_t>::max(), HAILO_DEFAULT_DEVICE_COUNT, (GParamFlags)(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
+    g_object_class_install_property(gobject_class, PROP_VDEVICE_KEY,
+        g_param_spec_uint("vdevice-key",
+            "Indicate whether to re-use or re-create vdevice",
+            "Relevant only when 'device-count' is passed. If not passed, the created vdevice will be unique to this hailonet." \
+            "if multiple hailonets share 'vdevice-key' and 'device-count', the created vdevice will be shared between those hailonets",
+            MIN_VALID_VDEVICE_KEY, std::numeric_limits<uint32_t>::max(), MIN_VALID_VDEVICE_KEY, (GParamFlags)(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
+    g_object_class_install_property(gobject_class, PROP_HEF_PATH,
+        g_param_spec_string("hef-path", "HEF Path Location", "Location of the HEF file to read", NULL,
+            (GParamFlags)(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
+    g_object_class_install_property(gobject_class, PROP_NETWORK_NAME,
+        g_param_spec_string("net-name", "Network Name",
+            "Configure and run this specific network. "
+            "If not passed, configure and run the default network - ONLY if there is one network in the HEF!", NULL,
+            (GParamFlags)(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
+    g_object_class_install_property(gobject_class, PROP_BATCH_SIZE,
+        g_param_spec_uint("batch-size", "Inference Batch", "How many frame to send in one batch", MIN_GSTREAMER_BATCH_SIZE, MAX_GSTREAMER_BATCH_SIZE, HAILO_DEFAULT_BATCH_SIZE,
+            (GParamFlags)(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
+    g_object_class_install_property(gobject_class, PROP_OUTPUTS_MIN_POOL_SIZE,
+        g_param_spec_uint("outputs-min-pool-size", "Outputs Minimun Pool Size", "The minimum amount of buffers to allocate for each output layer",
+            0, std::numeric_limits<uint32_t>::max(), DEFAULT_OUTPUTS_MIN_POOL_SIZE, (GParamFlags)(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
+    g_object_class_install_property(gobject_class, PROP_OUTPUTS_MAX_POOL_SIZE,
+        g_param_spec_uint("outputs-max-pool-size", "Outputs Maximum Pool Size",
+            "The maximum amount of buffers to allocate for each output layer or 0 for unlimited", 0, std::numeric_limits<uint32_t>::max(),
+            DEFAULT_OUTPUTS_MAX_POOL_SIZE, (GParamFlags)(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
+    g_object_class_install_property(gobject_class, PROP_IS_ACTIVE,
+        g_param_spec_boolean("is-active", "Is Network Activated", "Controls whether this element should be active. "
+            "By default, the hailonet element will not be active unless there is only one hailonet in the pipeline", false,
+        (GParamFlags)(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
+
+    // See information about the "flush" signal in the element description
+    g_signal_new(
+        "flush",
+        GST_TYPE_HAILONET,
+        G_SIGNAL_ACTION,
+        0, nullptr, nullptr, nullptr, G_TYPE_NONE, 0
+    );
+}
+
+Expected<std::unique_ptr<HailoNetImpl>> HailoNetImpl::create(GstHailoNet *element)
+{
+    if (nullptr == element) {
+        return make_unexpected(HAILO_INVALID_ARGUMENT);
+    }
+
+    GstElement *hailosend = gst_element_factory_make("hailosend", "hailosend");
+    if (nullptr == hailosend) {
+        GST_ELEMENT_ERROR(element, RESOURCE, FAILED, ("Failed creating hailosend element in bin!"), (NULL));
+        return make_unexpected(HAILO_INTERNAL_FAILURE);
+    }
+
+    g_object_set(hailosend, "qos", FALSE, NULL);
+
+    GstElement *queue = gst_element_factory_make("queue", "hailo_infer_q_0");
+    if (nullptr == queue) {
+        GST_ELEMENT_ERROR(element, RESOURCE, FAILED, ("Failed creating queue element in bin!"), (NULL));
+        gst_object_unref(hailosend);
+        return make_unexpected(HAILO_INTERNAL_FAILURE);
+    }
+
+    // Passing 0 disables the features here
+    g_object_set(queue, "max-size-time", 0, NULL);
+    g_object_set(queue, "max-size-bytes", 0, NULL);
+    g_signal_connect(queue, "overrun", G_CALLBACK(gst_hailonet_inner_queue_overrun_callback), nullptr);
+    g_signal_connect(queue, "underrun", G_CALLBACK(gst_hailonet_inner_queue_underrun_callback), nullptr);
+
+    GstElement *hailorecv = gst_element_factory_make("hailorecv", "hailorecv");
+    if (nullptr == hailorecv) {
+        GST_ELEMENT_ERROR(element, RESOURCE, FAILED, ("Failed creating hailorecv element in bin!"), (NULL));
+        gst_object_unref(hailosend);
+        gst_object_unref(queue);
+        return make_unexpected(HAILO_INTERNAL_FAILURE);
+    }
+
+    g_object_set(hailorecv, "qos", FALSE, NULL);
+
+    g_signal_connect(element, "flush", G_CALLBACK(gst_hailonet_flush_callback), nullptr);
+
+    auto was_flushed_event = Event::create_shared(Event::State::not_signalled);
+    GST_CHECK(nullptr != was_flushed_event, make_unexpected(HAILO_OUT_OF_HOST_MEMORY), element, RESOURCE, "Failed allocating memory for event!");
+
+    auto ptr = make_unique_nothrow<HailoNetImpl>(element, hailosend, queue, hailorecv, was_flushed_event);
+    if (nullptr == ptr) {
+        return make_unexpected(HAILO_OUT_OF_HOST_MEMORY);
+    }
+
+    return ptr;
+}
+
+std::atomic_uint32_t HailoNetImpl::m_hailonet_count(0);
+std::mutex HailoNetImpl::m_mutex;
+HailoNetImpl::HailoNetImpl(GstHailoNet *element, GstElement *hailosend, GstElement *queue, GstElement *hailorecv, EventPtr was_flushed_event) :
+    m_element(element), m_props(), m_output_formats(), m_hailosend(hailosend), m_queue(queue), m_hailorecv(hailorecv),
+    m_net_group_handle(nullptr), m_was_configured(false), m_has_called_activate(false),
+    m_was_flushed_event(was_flushed_event), m_pool(nullptr)
+{
+    GST_DEBUG_CATEGORY_INIT(gst_hailonet_debug_category, "hailonet", 0, "debug category for hailonet element");
+
+    /* gst_bin_add_many cannot fail. I use this function because the elements are created here and does not come from the outside so,
+     * gst_bin_add will not fail */
+    gst_bin_add_many(GST_BIN(m_element), m_hailosend, m_queue, m_hailorecv, NULL);
+    init_ghost_sink();
+    init_ghost_src();
+
+    ++m_hailonet_count;
+}
+
+HailoNetImpl::~HailoNetImpl()
+{
+    if (nullptr != m_pool) {
+        (void)gst_buffer_pool_set_active(m_pool, FALSE);
+    }
+}
+
+void HailoNetImpl::init_ghost_sink()
+{
+    GstPad *pad = gst_element_get_static_pad(m_hailosend, "sink");
+
+    GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE("sink", GST_PAD_SINK, GST_PAD_ALWAYS, GST_STATIC_CAPS_ANY);
+    GstPadTemplate *pad_tmpl = gst_static_pad_template_get(&sink_template);
+
+    GstPad *ghost_pad = gst_ghost_pad_new_from_template("sink", pad, pad_tmpl);
+    gst_pad_set_active(ghost_pad, TRUE);
+    gst_element_add_pad(GST_ELEMENT(m_element), ghost_pad);
+
+    gst_pad_add_probe(pad, GST_PAD_PROBE_TYPE_BUFFER, static_cast<GstPadProbeCallback>(gst_hailonet_sink_probe), nullptr, nullptr);
+
+    gst_object_unref(pad_tmpl);
+    gst_object_unref(pad);
+}
+
+void HailoNetImpl::init_ghost_src()
+{
+    GstPad *pad = gst_element_get_static_pad(m_hailorecv, "src");
+
+    GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE("src", GST_PAD_SRC, GST_PAD_ALWAYS, GST_STATIC_CAPS_ANY);
+    GstPadTemplate *pad_tmpl = gst_static_pad_template_get(&src_template);
+
+    GstPad *ghost_pad = gst_ghost_pad_new_from_template("src", pad, pad_tmpl);
+    gst_pad_set_active(ghost_pad, TRUE);
+    gst_element_add_pad(GST_ELEMENT(m_element), ghost_pad);
+
+    gst_pad_set_event_function(pad, gst_hailorecv_src_pad_event);
+
+    gst_object_unref(pad_tmpl);
+    gst_object_unref(pad);
+}
+
+void HailoNetImpl::set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec)
+{
+    GST_DEBUG_OBJECT(m_element, "set_property");
+
+    if ((object == nullptr) || (value == nullptr) || (pspec == nullptr)) {
+        g_error("set_property got null parameter!");
+        return;
+    }
+
+    switch (property_id) {
+    case PROP_DEBUG:
+    {
+        gboolean debug = g_value_get_boolean(value);
+        g_object_set(m_hailosend, "debug", debug, NULL);
+        g_object_set(m_hailorecv, "debug", debug, NULL);
+        break;
+    }
+    case PROP_DEVICE_ID:
+        if (0 != m_props.m_device_count.get()) {
+            g_error("device-id and device-count excludes eachother. received device-id=%s, device-count=%d",
+                g_value_get_string(value), m_props.m_device_count.get());
+            break;
+        }
+        if (m_was_configured) {
+            g_warning("The network was already configured so changing the device ID will not take place!");
+            break;
+        }
+        if (nullptr != m_props.m_device_id.get()) {
+            g_free(m_props.m_device_id.get());
+        }
+        m_props.m_device_id = g_strdup(g_value_get_string(value));
+        break;
+    case PROP_DEVICE_COUNT:
+        if (nullptr != m_props.m_device_id.get()) {
+            g_error("device-id and device-count excludes eachother. received device-id=%s, device-count=%d",
+                m_props.m_device_id.get(), g_value_get_uint(value));
+            break;
+        }
+        if (m_was_configured) {
+            g_warning("The network was already configured so changing the device count will not take place!");
+            break;
+        }
+        m_props.m_device_count = static_cast<guint16>(g_value_get_uint(value));
+        break;
+    case PROP_VDEVICE_KEY:
+        if (m_was_configured) {
+            g_warning("The network was already configured so changing the vdevice key will not take place!");
+            break;
+        }
+        m_props.m_vdevice_key = static_cast<guint32>(g_value_get_uint(value));
+        break;
+    case PROP_HEF_PATH:
+        if (m_was_configured) {
+            g_warning("The network was already configured so changing the HEF path will not take place!");
+            break;
+        }
+        if (nullptr != m_props.m_hef_path.get()) {
+            g_free(m_props.m_hef_path.get());
+        }
+        m_props.m_hef_path = g_strdup(g_value_get_string(value));
+        break;
+    case PROP_NETWORK_NAME:
+        if (m_was_configured) {
+            g_warning("The network was already configured so changing the network name will not take place!");
+            break;
+        }
+        if (nullptr != m_props.m_network_name.get()) {
+            g_free(m_props.m_network_name.get());
+        }
+        m_props.m_network_name = g_strdup(g_value_get_string(value));
+        break;
+    case PROP_BATCH_SIZE:
+        if (m_was_configured) {
+            g_warning("The network was already configured so changing the batch size will not take place!");
+            break;
+        }
+        m_props.m_batch_size = static_cast<guint16>(g_value_get_uint(value));
+        break;
+    case PROP_OUTPUTS_MIN_POOL_SIZE:
+        if (m_was_configured) {
+            g_warning("The network was already configured so changing the outputs minimum pool size will not take place!");
+            break;
+        }
+        g_object_set(m_hailorecv, "outputs-min-pool-size", g_value_get_uint(value), NULL);
+        break;
+    case PROP_OUTPUTS_MAX_POOL_SIZE:
+        if (m_was_configured) {
+            g_warning("The network was already configured so changing the outputs maximum pool size will not take place!");
+            break;
+        }
+        g_object_set(m_hailorecv, "outputs-max-pool-size", g_value_get_uint(value), NULL);
+        break;
+    case PROP_IS_ACTIVE:
+    {
+        gboolean new_is_active = g_value_get_boolean(value);
+
+        if (m_has_called_activate) {
+            if (m_props.m_is_active.get() && !new_is_active) {
+                // Setting this to false before deactivating to signal hailosend and hailorecv to stop inferring
+                m_props.m_is_active = false;
+                hailo_status status = deactivate_network_group();
+                if (HAILO_SUCCESS != status) {
+                    g_error("Deactivating network group failed, status = %d", status);
+                    return;
+                }
+            } else if (!m_props.m_is_active.get() && new_is_active) {
+                hailo_status status = m_net_group_handle->activate_network_group();
+                if (HAILO_SUCCESS != status) {
+                    g_error("Failed activating network group, status = %d", status);
+                    break;
+                }
+                m_props.m_is_active = true;
+            } else {
+                g_warning("Trying to change is-active property state from %d to %d", m_props.m_is_active.get(), new_is_active);
+                break;
+            }
+        } else {
+            m_props.m_is_active = new_is_active;
+        }
+        break;
+    }
+    default:
+        G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
+        break;
+    }
+}
+
+void HailoNetImpl::get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec)
+{
+    GST_DEBUG_OBJECT(m_element, "get_property");
+
+    if ((object == nullptr) || (value == nullptr) || (pspec == nullptr)) {
+        g_error("get_property got null parameter!");
+        return;
+    }
+
+    switch (property_id) {
+    case PROP_DEBUG:
+    {
+        gboolean debug;
+        g_object_get(m_hailosend, "debug", &debug, nullptr);
+        g_value_set_boolean(value, debug);
+        break;
+    }
+    case PROP_DEVICE_ID:
+        g_value_set_string(value, m_props.m_device_id.get());
+        break;
+    case PROP_DEVICE_COUNT:
+        g_value_set_uint(value, m_props.m_device_count.get());
+        break;
+    case PROP_VDEVICE_KEY:
+        g_value_set_uint(value, m_props.m_vdevice_key.get());
+        break;
+    case PROP_HEF_PATH:
+        g_value_set_string(value, m_props.m_hef_path.get());
+        break;
+    case PROP_NETWORK_NAME:
+        g_value_set_string(value, m_props.m_network_name.get());
+        break;
+    case PROP_BATCH_SIZE:
+        g_value_set_uint(value, m_props.m_batch_size.get());
+        break;
+    case PROP_OUTPUTS_MIN_POOL_SIZE:
+    {
+        guint outputs_min_pool_size;
+        g_object_get(m_hailorecv, "outputs-min-pool-size", &outputs_min_pool_size, nullptr);
+        g_value_set_uint(value, outputs_min_pool_size);
+        break;
+    }
+    case PROP_OUTPUTS_MAX_POOL_SIZE:
+    {
+        guint outputs_max_pool_size;
+        g_object_get(m_hailorecv, "outputs-max-pool-size", &outputs_max_pool_size, nullptr);
+        g_value_set_uint(value, outputs_max_pool_size);
+        break;
+    }
+    case PROP_IS_ACTIVE:
+        g_value_set_boolean(value, m_props.m_is_active.get());
+        break;
+    default:
+        G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
+        break;
+    }
+}
+
+hailo_status HailoNetImpl::set_hef()
+{
+    m_net_group_handle = make_unique_nothrow<NetworkGroupHandle>(GST_ELEMENT(m_element));
+    GST_CHECK(nullptr != m_net_group_handle, HAILO_OUT_OF_HOST_MEMORY, m_element, RESOURCE, "Failed allocating memory for network handle!");
+
+    hailo_status status = m_net_group_handle->set_hef(m_props.m_device_id.get(), m_props.m_device_count.get(), m_props.m_vdevice_key.get(),
+        m_props.m_hef_path.get());
+    if (HAILO_SUCCESS != status) {
+        return status;
+    }
+
+    if (nullptr == m_props.m_network_name.get()) {
+        // TODO: HRT-4957
+        GST_CHECK(m_net_group_handle->hef()->get_network_groups_names().size() == 1, HAILO_INVALID_ARGUMENT, m_element, RESOURCE,
+            "Network group has to be specified when there are more than one network groups in the HEF!");
+        auto networks_infos = m_net_group_handle->hef()->get_network_infos(m_net_group_handle->hef()->get_network_groups_names()[0].c_str());
+        GST_CHECK_EXPECTED_AS_STATUS(networks_infos, m_element, RESOURCE, "Getting network infos from network group name was failed, status %d", networks_infos.status());
+        GST_CHECK(networks_infos.value().size() == 1, HAILO_INVALID_ARGUMENT, m_element, RESOURCE,
+            "Network has to be specified when there are more than one network in the network group!");
+        m_props.m_network_name = g_strdup(networks_infos.release()[0].name);
+    }
+
+    auto input_vstream_infos = m_net_group_handle->hef()->get_input_vstream_infos(m_props.m_network_name.get());
+    GST_CHECK_EXPECTED_AS_STATUS(input_vstream_infos, m_element, RESOURCE, "Getting input vstream infos from HEF has failed, status = %d",
+        input_vstream_infos.status());
+
+    // TODO: HRT-4095
+    GST_CHECK(1 == input_vstream_infos->size(), HAILO_INVALID_OPERATION, m_element, RESOURCE, "hailonet element supports only HEFs with one input for now!");
+
+    auto input_vstream_info = input_vstream_infos.value()[0];
+    GST_HAILOSEND(m_hailosend)->impl->set_input_vstream_infos(std::move(input_vstream_infos.release()));
+    GST_HAILOSEND(m_hailosend)->impl->set_batch_size(m_props.m_batch_size.get());
+
+    GstBufferPool *pool = gst_buffer_pool_new();
+    GstStructure *config = gst_buffer_pool_get_config(pool);
+
+    auto frame_size = HailoRTCommon::get_frame_size(input_vstream_info, input_vstream_info.format);
+    gst_buffer_pool_config_set_params(config, nullptr, frame_size, 1, 1);
+
+    gboolean result = gst_buffer_pool_set_config(pool, config);
+    GST_CHECK(result, HAILO_INTERNAL_FAILURE, m_element, RESOURCE, "Could not set config buffer pool");
+
+    result = gst_buffer_pool_set_active(pool, TRUE);
+    GST_CHECK(result, HAILO_INTERNAL_FAILURE, m_element, RESOURCE, "Could not set buffer pool active");
+
+    m_pool = pool;
+
+    return HAILO_SUCCESS;
+}
+
+hailo_status HailoNetImpl::configure_network_group()
+{
+    std::unique_lock<std::mutex> lock(m_mutex);
+    g_object_set(m_queue, "max-size-buffers", MAX_BUFFER_COUNT(m_props.m_batch_size.get()), NULL);
+
+    auto network_group_name = get_network_group_name(m_props.m_network_name.get());
+    GST_CHECK_EXPECTED_AS_STATUS(network_group_name, m_element, RESOURCE, "Could not get network group name from name %s, status = %d",
+        m_props.m_network_name.get(), network_group_name.status());
+
+    hailo_status status = m_net_group_handle->configure_network_group(network_group_name->c_str(), m_props.m_batch_size.get());
+    if (HAILO_SUCCESS != status) {
+        return status;
+    }
+    m_was_configured = true;
+
+    auto vstreams = m_net_group_handle->create_vstreams(m_props.m_network_name.get(), m_output_formats);
+    GST_CHECK_EXPECTED_AS_STATUS(vstreams, m_element, RESOURCE, "Creating vstreams failed, status = %d", status);
+
+    GST_HAILOSEND(m_hailosend)->impl->set_input_vstreams(std::move(vstreams->first));
+
+    status = GST_HAILORECV(m_hailorecv)->impl->set_output_vstreams(std::move(vstreams->second), m_props.m_batch_size.get());
+    GST_CHECK_SUCCESS(status, m_element, RESOURCE, "Setting output vstreams failed, status = %d", status);
+
+    return HAILO_SUCCESS;
+}
+
+hailo_status HailoNetImpl::activate_network_group()
+{
+    if ((1 == m_hailonet_count) && (!m_props.m_is_active.was_changed())) {
+        m_props.m_is_active = true;
+    }
+
+    if (m_props.m_is_active.get()) {
+        std::unique_lock<std::mutex> lock(m_mutex);
+        hailo_status status = m_net_group_handle->activate_network_group();
+        if (HAILO_SUCCESS != status) {
+            return status;
+        }
+    }
+
+    m_has_called_activate = true;
+
+    return HAILO_SUCCESS;
+}
+
+Expected<std::string> HailoNetImpl::get_network_group_name(const std::string &network_name)
+{
+    for (const auto &network_group_name : m_net_group_handle->hef()->get_network_groups_names()) {
+        // Look for network_group with the given name
+        if (network_name == network_group_name) {
+            return std::string(network_group_name);
+        }
+
+        auto network_infos = m_net_group_handle->hef()->get_network_infos(network_group_name);
+        GST_CHECK_EXPECTED(network_infos, m_element, RESOURCE, "Could not get network infos of group %s, status = %d", network_group_name.c_str(),
+            network_infos.status());
+
+        // Look for network with the given name
+        for (const auto &network_info : network_infos.value()) {
+            if (network_name == network_info.name) {
+                return std::string(network_group_name);
+            }
+        }
+    }
+
+    GST_ELEMENT_ERROR(m_element, RESOURCE, FAILED, ("Failed to get network group name from the name %s!", network_name.c_str()), (NULL));
+    return make_unexpected(HAILO_NOT_FOUND);
+}
+
+hailo_status HailoNetImpl::link_elements()
+{
+    /* Link elements here because only here we have the HEF and the Caps format */
+    if (!gst_element_link_many(m_hailosend, m_queue, m_hailorecv, NULL)) {
+        GST_ELEMENT_ERROR(m_element, RESOURCE, FAILED, ("Could not add link elements in bin!"), (NULL));
+        return HAILO_INTERNAL_FAILURE;
+    }
+
+    return HAILO_SUCCESS;
+}
+
+hailo_status HailoNetImpl::abort_streams()
+{
+    if (!m_props.m_is_active.get()) {
+        return HAILO_SUCCESS;
+    }
+
+    return m_net_group_handle->abort_streams();
+}
+
+hailo_status HailoNetImpl::deactivate_network_group()
+{
+    auto was_deactivated = m_net_group_handle->remove_network_group();
+    GST_CHECK_EXPECTED_AS_STATUS(was_deactivated, m_element, RESOURCE, "Failed removing network, status = %d", was_deactivated.status());
+
+    if (was_deactivated.value()) {
+        if (nullptr != GST_HAILOSEND(m_hailosend)->impl) {
+            hailo_status status = GST_HAILOSEND(m_hailosend)->impl->clear_vstreams();
+            GST_CHECK_SUCCESS(status, m_element, RESOURCE, "Failed clearing input VStreams of hailosend, status = %d", status);
+        }
+
+        if (nullptr != GST_HAILORECV(m_hailorecv)->impl) {
+            hailo_status status = GST_HAILORECV(m_hailorecv)->impl->clear_vstreams();
+            GST_CHECK_SUCCESS(status, m_element, RESOURCE, "Failed clearing output VStreams of hailorecv, status = %d", status);
+        }
+    }
+
+    return HAILO_SUCCESS;
+}
+
+gboolean HailoNetImpl::src_pad_event(GstEvent *event)
+{
+    assert(nullptr != event);
+
+    auto parsed_event = HailoSetOutputFormatEvent::parse(event);
+    if (HAILO_SUCCESS != parsed_event.status()) {
+         return FALSE;
+    }
+
+    m_output_formats = std::move(parsed_event->formats);
+    return TRUE;
+}
+
+GstPadProbeReturn HailoNetImpl::sink_probe()
+{
+    hailo_status status = activate_network_group();
+    GST_CHECK(HAILO_SUCCESS == status, GST_PAD_PROBE_REMOVE, m_element, RESOURCE, "Failed activating network, status = %d", status);
+    return GST_PAD_PROBE_REMOVE;
+}
+
+gboolean HailoNetImpl::is_active()
+{
+    return m_props.m_is_active.get();
+}
+
+hailo_status HailoNetImpl::flush()
+{
+    GstBuffer *buffer = nullptr;
+    GstFlowReturn flow_result = gst_buffer_pool_acquire_buffer(m_pool, &buffer, nullptr);
+    GST_CHECK(GST_FLOW_OK == flow_result, HAILO_INTERNAL_FAILURE, m_element, RESOURCE, "Acquire buffer failed!");
+
+    GstHailoBufferFlagMeta *buffer_meta = GST_HAILO_BUFFER_FLAG_META_ADD(buffer);
+    buffer_meta->flag = BUFFER_FLAG_FLUSH;
+    GST_BUFFER_TIMESTAMP(buffer) = GST_HAILOSEND(m_hailosend)->impl->last_frame_pts();
+
+    GstPad *pad = gst_element_get_static_pad(m_hailosend, "src");
+    flow_result = gst_pad_push(pad, buffer);
+    GST_CHECK(GST_FLOW_OK == flow_result, HAILO_INTERNAL_FAILURE, m_element, RESOURCE, "Pushing buffer to queue has failed!");
+
+    hailo_status status = m_was_flushed_event->wait(WAIT_FOR_FLUSH_TIMEOUT_MS);
+    GST_CHECK_SUCCESS(status, m_element, RESOURCE, "Failed waiting for flushed event, status = %d", status);
+
+    status = m_was_flushed_event->reset();
+    GST_CHECK_SUCCESS(status, m_element, RESOURCE, "Failed resetting flushed event, status = %d", status);
+
+    return HAILO_SUCCESS;
+}
+
+hailo_status HailoNetImpl::signal_was_flushed_event()
+{
+    return m_was_flushed_event->signal();
+}
+
+static void gst_hailonet_init(GstHailoNet *self)
+{
+    auto hailonet_impl = HailoNetImpl::create(self);
+    if (!hailonet_impl) {
+        GST_ELEMENT_ERROR(self, RESOURCE, FAILED, ("Creating hailonet implementation has failed! status = %d", hailonet_impl.status()), (NULL));
+        return;
+    }
+
+    self->impl = hailonet_impl.release();
+}
+
+static void gst_hailonet_set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec)
+{
+    GST_HAILONET(object)->impl->set_property(object, property_id, value, pspec);
+}
+
+static void gst_hailonet_get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec)
+{
+    GST_HAILONET(object)->impl->get_property(object, property_id, value, pspec);
+}
+
+static gboolean gst_hailorecv_src_pad_event(GstPad */*pad*/, GstObject *parent, GstEvent *event)
+{
+    gboolean result = GST_HAILONET(GST_ELEMENT_PARENT(parent))->impl->src_pad_event(event);
+    if (result) {
+        return TRUE;
+    }
+
+    GstBaseTransform *trans = GST_BASE_TRANSFORM_CAST(parent);
+    return GST_BASE_TRANSFORM_GET_CLASS(trans)->src_event(trans, event);
+}
+
+static GstPadProbeReturn gst_hailonet_sink_probe(GstPad *pad, GstPadProbeInfo */*info*/, gpointer /*user_data*/)
+{
+    return GST_HAILONET(GST_ELEMENT_PARENT(gst_pad_get_parent(pad)))->impl->sink_probe();
+}
+
+static GstStateChangeReturn gst_hailonet_change_state(GstElement *element, GstStateChange transition)
+{
+    GstStateChangeReturn ret = GST_ELEMENT_CLASS(gst_hailonet_parent_class)->change_state(element, transition);
+    if (GST_STATE_CHANGE_FAILURE == ret) {
+        return ret;
+    }
+
+    auto &hailonet = GST_HAILONET(element)->impl;
+    switch (transition) {
+    case GST_STATE_CHANGE_NULL_TO_READY:
+    {
+        hailo_status status = hailonet->link_elements();
+        GST_CHECK(HAILO_SUCCESS == status, GST_STATE_CHANGE_FAILURE, element, RESOURCE, "Linking elements has failed, status = %d\n", status);
+        break;
+    }
+    case GST_STATE_CHANGE_READY_TO_PAUSED:
+    {
+        hailo_status status = hailonet->configure_network_group();
+        GST_CHECK(HAILO_SUCCESS == status, GST_STATE_CHANGE_FAILURE, element, RESOURCE, "Configuring network group failed, status = %d\n", status);
+        break;
+    }
+    case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
+    {
+        hailo_status status = hailonet->abort_streams();
+        GST_CHECK(HAILO_SUCCESS == status, GST_STATE_CHANGE_FAILURE, element, RESOURCE, "Aborting streams has failed, status = %d\n", status);
+        break;
+    }
+    case GST_STATE_CHANGE_READY_TO_NULL:
+    {
+        // We abort streams again because deactivation of the activated network group calls flush, and it can fail on timeout unless we call abort
+        hailo_status status = hailonet->abort_streams();
+        GST_CHECK(HAILO_SUCCESS == status, GST_STATE_CHANGE_FAILURE, element, RESOURCE, "Aborting streams has failed, status = %d\n", status);
+
+        status = hailonet->deactivate_network_group();
+        GST_CHECK(HAILO_SUCCESS == status, GST_STATE_CHANGE_FAILURE, element, RESOURCE, "Deactivating network group has failed, status = %d\n", status);
+
+        // Cleanup all of hailonet memory
+        hailonet.reset();
+        break;
+    }
+    default:
+        break;
+    }
+
+    return ret;
+}
+
+static void gst_hailonet_flush_callback(GstHailoNet *hailonet, gpointer /*data*/)
+{
+    (void)hailonet->impl->flush();
+}
+
+static void gst_hailonet_inner_queue_overrun_callback(GstElement *queue, gpointer /*udata*/)
+{
+    if (GST_HAILONET(GST_ELEMENT_PARENT(queue))->impl->is_active()) {
+        GST_INFO("Inner queue of %s is overrun!", GST_ELEMENT_NAME(GST_ELEMENT_PARENT(queue)));
+    }
+}
+
+static void gst_hailonet_inner_queue_underrun_callback(GstElement *queue, gpointer /*udata*/)
+{
+    if (GST_HAILONET(GST_ELEMENT_PARENT(queue))->impl->is_active()) {
+        GST_INFO("Inner queue of %s is underrun!", GST_ELEMENT_NAME(GST_ELEMENT_PARENT(queue)));
+    }
+}
\ No newline at end of file
diff --git a/hailort/libhailort/bindings/gstreamer/gst-hailo/gsthailonet.hpp b/hailort/libhailort/bindings/gstreamer/gst-hailo/gsthailonet.hpp
new file mode 100644 (file)
index 0000000..810e37d
--- /dev/null
@@ -0,0 +1,112 @@
+/*
+ * Copyright (c) 2021-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the LGPL 2.1 license (https://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+#ifndef _GST_HAILONET_HPP_
+#define _GST_HAILONET_HPP_
+
+#include "common.hpp"
+#include "network_group_handle.hpp"
+#include "hailo/expected.hpp"
+#include "hailo/event.hpp"
+
+#include <atomic>
+#include <condition_variable>
+
+G_BEGIN_DECLS
+
+#define GST_TYPE_HAILONET (gst_hailonet_get_type())
+#define GST_HAILONET(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_HAILONET,GstHailoNet))
+#define GST_HAILONET_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_HAILONET,GstHailoNetClass))
+#define GST_IS_HAILONET(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_HAILONET))
+#define GST_IS_HAILONET_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_HAILONET))
+
+class HailoNetImpl;
+struct GstHailoNet
+{
+    GstBin parent;
+    std::unique_ptr<HailoNetImpl> impl;
+};
+
+struct GstHailoNetClass
+{
+    GstBinClass parent;
+};
+
+struct HailoNetProperties final
+{
+public:
+    HailoNetProperties() : m_device_id(nullptr), m_hef_path(nullptr), m_network_name(nullptr), m_batch_size(HAILO_DEFAULT_BATCH_SIZE),
+        m_is_active(false), m_device_count(0), m_vdevice_key(DEFAULT_VDEVICE_KEY)
+    {}
+
+    HailoElemProperty<gchar*> m_device_id;
+    HailoElemProperty<gchar*> m_hef_path;
+    HailoElemProperty<gchar*> m_network_name; // This property can be network group name or a network name
+    HailoElemProperty<guint16> m_batch_size;
+    HailoElemProperty<gboolean> m_is_active;
+    HailoElemProperty<guint16> m_device_count;
+    HailoElemProperty<guint32> m_vdevice_key;
+};
+
+class HailoNetImpl final
+{
+public:
+    static Expected<std::unique_ptr<HailoNetImpl>> create(GstHailoNet *element);
+    HailoNetImpl(GstHailoNet *element, GstElement *hailosend, GstElement *queue, GstElement *hailorecv, EventPtr was_flushed_event);
+    ~HailoNetImpl();
+
+    void set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec);
+    void get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec);
+    hailo_status set_hef();
+    hailo_status link_elements();
+    hailo_status configure_network_group();
+    hailo_status activate_network_group();
+    hailo_status abort_streams();
+    hailo_status deactivate_network_group();
+    gboolean src_pad_event(GstEvent *event);
+    GstPadProbeReturn sink_probe();
+    gboolean is_active();
+    hailo_status flush();
+    hailo_status signal_was_flushed_event();
+
+private:
+    void init_ghost_sink();
+    void init_ghost_src();
+    Expected<std::string> get_network_group_name(const std::string &network_name);
+
+    static std::atomic_uint32_t m_hailonet_count;
+    static std::mutex m_mutex;
+    GstHailoNet *m_element;
+    HailoNetProperties m_props;
+    std::vector<hailo_format_with_name_t> m_output_formats;
+    GstElement *m_hailosend;
+    GstElement *m_queue;
+    GstElement *m_hailorecv;
+    std::unique_ptr<NetworkGroupHandle> m_net_group_handle;
+    bool m_was_configured;
+    bool m_has_called_activate;
+    EventPtr m_was_flushed_event;
+    GstBufferPool *m_pool;
+};
+
+GType gst_hailonet_get_type(void);
+
+G_END_DECLS
+
+#endif /* _GST_HAILONET_HPP_ */
diff --git a/hailort/libhailort/bindings/gstreamer/gst-hailo/gsthailoplugin.cpp b/hailort/libhailort/bindings/gstreamer/gst-hailo/gsthailoplugin.cpp
new file mode 100644 (file)
index 0000000..dea6cdc
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2021-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the LGPL 2.1 license (https://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+#include "gsthailonet.hpp"
+#include "gsthailosend.hpp"
+#include "gsthailorecv.hpp"
+#include "gsthailodevicestats.hpp"
+#include "metadata/tensor_meta.hpp"
+
+static gboolean plugin_init(GstPlugin *plugin)
+{
+    (void)gst_tensor_meta_get_info();
+    (void)gst_tensor_meta_api_get_type();
+
+    return gst_element_register(plugin, "hailonet", GST_RANK_PRIMARY, GST_TYPE_HAILONET) &&
+        gst_element_register(plugin, "hailodevicestats", GST_RANK_PRIMARY, GST_TYPE_HAILODEVICESTATS) &&
+        gst_element_register(nullptr, "hailosend", GST_RANK_PRIMARY, GST_TYPE_HAILOSEND) &&
+        gst_element_register(nullptr, "hailorecv", GST_RANK_PRIMARY, GST_TYPE_HAILORECV);
+}
+
+GST_PLUGIN_DEFINE(GST_VERSION_MAJOR, GST_VERSION_MINOR, hailo, "hailo gstreamer plugin", plugin_init, VERSION,
+    GST_LICENSE_UNKNOWN, "GStreamer", "http://gstreamer.net/")
diff --git a/hailort/libhailort/bindings/gstreamer/gst-hailo/gsthailorecv.cpp b/hailort/libhailort/bindings/gstreamer/gst-hailo/gsthailorecv.cpp
new file mode 100644 (file)
index 0000000..34496e9
--- /dev/null
@@ -0,0 +1,390 @@
+/*
+ * Copyright (c) 2021-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the LGPL 2.1 license (https://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+#include "gsthailorecv.hpp"
+#include "gsthailonet.hpp"
+#include "common.hpp"
+#include "network_group_handle.hpp"
+#include "metadata/hailo_buffer_flag_meta.hpp"
+#include "metadata/tensor_meta.hpp"
+#include "hailo_events/hailo_events.hpp"
+
+#include <iostream>
+#include <numeric>
+#include <algorithm>
+
+GST_DEBUG_CATEGORY_STATIC(gst_hailorecv_debug_category);
+#define GST_CAT_DEFAULT gst_hailorecv_debug_category
+
+static void gst_hailorecv_set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec);
+static void gst_hailorecv_get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec);
+static GstFlowReturn gst_hailorecv_transform_frame_ip(GstVideoFilter *filter, GstVideoFrame *frame);
+static GstStateChangeReturn gst_hailorecv_change_state(GstElement *element, GstStateChange transition);
+static GstFlowReturn gst_hailorecv_buffer_pool_acquire_callback(GstBufferPool *pool, GstBuffer **buffer, GstBufferPoolAcquireParams *params);
+static void gst_hailorecv_buffer_pool_release_callback(GstBufferPool *pool, GstBuffer *buffer);
+
+G_DEFINE_TYPE(GstHailoBufferPool, gst_hailo_buffer_pool, GST_TYPE_BUFFER_POOL);
+
+static void gst_hailo_buffer_pool_class_init(GstHailoBufferPoolClass *klass)
+{
+    GstBufferPoolClass *buffer_pool_class = GST_BUFFER_POOL_CLASS(klass);
+    klass->parent_acquire_callback = buffer_pool_class->acquire_buffer;
+    klass->parent_release_callback = buffer_pool_class->release_buffer;
+    buffer_pool_class->acquire_buffer = gst_hailorecv_buffer_pool_acquire_callback;
+    buffer_pool_class->release_buffer = gst_hailorecv_buffer_pool_release_callback;
+}
+
+static void gst_hailo_buffer_pool_init(GstHailoBufferPool *self)
+{
+    self->element_name = nullptr;
+    self->buffers_acquired = 0;
+}
+
+enum
+{
+    PROP_0,
+    PROP_DEBUG,
+    PROP_OUTPUTS_MIN_POOL_SIZE,
+    PROP_OUTPUTS_MAX_POOL_SIZE
+};
+
+G_DEFINE_TYPE(GstHailoRecv, gst_hailorecv, GST_TYPE_VIDEO_FILTER);
+
+static void gst_hailorecv_class_init(GstHailoRecvClass *klass)
+{
+    GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
+    GstElementClass *element_class = GST_ELEMENT_CLASS(klass);
+    GstVideoFilterClass *video_filter_class = GST_VIDEO_FILTER_CLASS(klass);
+
+    /* Setting up pads and setting metadata should be moved to
+       base_class_init if you intend to subclass this class. */
+    gst_element_class_add_pad_template(GST_ELEMENT_CLASS(klass),
+        gst_pad_template_new("src", GST_PAD_SRC, GST_PAD_ALWAYS,
+            gst_caps_from_string(HAILO_VIDEO_CAPS)));
+    gst_element_class_add_pad_template(GST_ELEMENT_CLASS(klass),
+        gst_pad_template_new("sink", GST_PAD_SINK, GST_PAD_ALWAYS,
+        gst_caps_from_string(HAILO_VIDEO_CAPS)));
+
+    gst_element_class_set_static_metadata(GST_ELEMENT_CLASS(klass),
+        "hailorecv element", "Hailo/Filter/Video", "Receive data from HailoRT", PLUGIN_AUTHOR);
+    
+    element_class->change_state = GST_DEBUG_FUNCPTR(gst_hailorecv_change_state);
+
+    gobject_class->set_property = gst_hailorecv_set_property;
+    gobject_class->get_property = gst_hailorecv_get_property;
+    g_object_class_install_property(gobject_class, PROP_DEBUG,
+        g_param_spec_boolean("debug", "debug", "debug", false,
+            (GParamFlags)(GST_PARAM_CONTROLLABLE | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
+    g_object_class_install_property(gobject_class, PROP_OUTPUTS_MIN_POOL_SIZE,
+        g_param_spec_uint("outputs-min-pool-size", "Outputs Minimun Pool Size", "The minimum amount of buffers to allocate for each output layer",
+            0, std::numeric_limits<uint32_t>::max(), 1, (GParamFlags)(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
+    g_object_class_install_property(gobject_class, PROP_OUTPUTS_MAX_POOL_SIZE,
+        g_param_spec_uint("outputs-max-pool-size", "Outputs Maximum Pool Size",
+            "The maximum amount of buffers to allocate for each output layer or 0 for unlimited", 0, std::numeric_limits<uint32_t>::max(), 1,
+            (GParamFlags)(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
+    video_filter_class->transform_frame_ip = GST_DEBUG_FUNCPTR(gst_hailorecv_transform_frame_ip);
+}
+
+Expected<std::unique_ptr<HailoRecvImpl>> HailoRecvImpl::create(GstHailoRecv *element)
+{
+    if (nullptr == element) {
+        return make_unexpected(HAILO_INVALID_ARGUMENT);
+    }
+
+    auto ptr = make_unique_nothrow<HailoRecvImpl>(element);
+    if (nullptr == ptr) {
+        return make_unexpected(HAILO_OUT_OF_HOST_MEMORY);
+    }
+
+    return ptr;
+}
+
+HailoRecvImpl::HailoRecvImpl(GstHailoRecv *element) : m_element(element), m_props()
+{
+    GST_DEBUG_CATEGORY_INIT(gst_hailorecv_debug_category, "hailorecv", 0, "debug category for hailorecv element");
+}
+
+void HailoRecvImpl::set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec)
+{
+    GST_DEBUG_OBJECT(m_element, "set_property");
+
+    if ((object == nullptr) || (value == nullptr) || (pspec == nullptr)) {
+        g_error("set_property got null parameter!");
+        return;
+    }
+
+    switch (property_id) {
+    case PROP_DEBUG:
+        m_props.m_debug = g_value_get_boolean(value);
+        break;
+    case PROP_OUTPUTS_MIN_POOL_SIZE:
+        m_props.m_outputs_min_pool_size = g_value_get_uint(value);
+        break;
+    case PROP_OUTPUTS_MAX_POOL_SIZE:
+        m_props.m_outputs_max_pool_size = g_value_get_uint(value);
+        break;
+    default:
+        G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
+        break;
+    }
+}
+
+void HailoRecvImpl::get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec)
+{
+    GST_DEBUG_OBJECT(m_element, "get_property");
+
+    if ((object == nullptr) || (value == nullptr) || (pspec == nullptr)) {
+        g_error("get_property got null parameter!");
+        return;
+    }
+
+    switch (property_id) {
+    case PROP_DEBUG:
+        g_value_set_boolean(value, m_props.m_debug.get());
+        break;
+    case PROP_OUTPUTS_MIN_POOL_SIZE:
+        g_value_set_uint(value, m_props.m_outputs_min_pool_size.get());
+        break;
+    case PROP_OUTPUTS_MAX_POOL_SIZE:
+        g_value_set_uint(value, m_props.m_outputs_max_pool_size.get());
+        break;
+    default:
+        G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
+        break;
+    }
+}
+
+GstFlowReturn HailoRecvImpl::handle_frame(GstVideoFilter */*filter*/, GstVideoFrame *frame)
+{
+    assert(nullptr != frame);
+
+    gpointer state = nullptr;
+    GstHailoBufferFlagMeta *meta = GST_HAILO_BUFFER_FLAG_META_ITERATE(frame->buffer, &state);
+
+    // Frames without metadata should get a metadata with the output tensors
+    if  (nullptr != meta) {
+        switch (meta->flag) {
+        case BUFFER_FLAG_FLUSH:
+        {
+            hailo_status status = GST_HAILONET(GST_ELEMENT_PARENT(m_element))->impl->signal_was_flushed_event();
+            GST_CHECK(HAILO_SUCCESS == status, GST_FLOW_ERROR, m_element, RESOURCE, "Signalling was flushed event has failed, status = %d", status);
+            return GST_BASE_TRANSFORM_FLOW_DROPPED;
+        }
+        case BUFFER_FLAG_SKIP:
+            return GST_FLOW_OK;
+        case BUFFER_FLAG_NONE:
+        default:
+            g_error("Unknown metadata type = %d", meta->flag);
+            break;
+        }
+    }
+
+    if (!GST_HAILONET(GST_ELEMENT_PARENT(m_element))->impl->is_active()) {
+        return GST_FLOW_OK;
+    }
+
+    hailo_status status = read_from_vstreams(m_props.m_debug.get());
+    if (HAILO_SUCCESS != status) {
+        return GST_FLOW_ERROR;
+    }
+
+    status = write_tensors_to_metadata(frame, m_props.m_debug.get());
+    if (HAILO_SUCCESS != status) {
+        return GST_FLOW_ERROR;
+    }
+
+    return GST_FLOW_OK;
+}
+
+hailo_status HailoRecvImpl::read_from_vstreams(bool should_print_latency)
+{
+    std::chrono::time_point<std::chrono::system_clock> overall_start_time = std::chrono::system_clock::now();
+    std::chrono::system_clock::time_point start_time;
+
+    for (auto &output_info : m_output_infos) {
+        if (should_print_latency) {
+            start_time = std::chrono::system_clock::now();
+        }
+
+        GstMapInfo buffer_info;
+        auto buffer = output_info.acquire_buffer();
+        GST_CHECK_EXPECTED_AS_STATUS(buffer, m_element, RESOURCE, "Failed to acquire buffer!");
+
+        gboolean result = gst_buffer_map(*buffer, &buffer_info, GST_MAP_WRITE);
+        GST_CHECK(result, HAILO_INTERNAL_FAILURE, m_element, RESOURCE, "Failed mapping buffer!");
+
+        auto status = output_info.vstream().read(MemoryView(buffer_info.data, buffer_info.size));
+        if (should_print_latency) {
+            std::chrono::duration<double, std::milli> latency = std::chrono::system_clock::now() - start_time;
+            GST_DEBUG("%s latency: %f milliseconds", output_info.vstream().name().c_str(), latency.count());
+        }
+        GST_CHECK_SUCCESS(status, m_element, STREAM, "Reading from vstream failed, status = %d", status);
+
+        gst_buffer_unmap(*buffer, &buffer_info);
+    }
+
+    if (should_print_latency) {
+        std::chrono::duration<double, std::milli>  latency = std::chrono::system_clock::now() - overall_start_time;
+        GST_DEBUG("hailorecv read latency: %f milliseconds", latency.count());
+    }
+
+    return HAILO_SUCCESS;
+}
+
+hailo_status HailoRecvImpl::write_tensors_to_metadata(GstVideoFrame *frame, bool should_print_latency)
+{
+    std::chrono::time_point<std::chrono::system_clock> start_time = std::chrono::system_clock::now();
+    for (auto &output_info : m_output_infos) {
+        GstHailoTensorMeta *buffer_meta = GST_TENSOR_META_ADD(output_info.last_acquired_buffer());
+        buffer_meta->info = output_info.vstream_info();
+
+        (void)gst_buffer_add_parent_buffer_meta(frame->buffer, output_info.last_acquired_buffer());
+        output_info.unref_last_acquired_buffer();
+    }
+
+    if (should_print_latency) {
+        std::chrono::duration<double, std::milli> latency = std::chrono::system_clock::now() - start_time;
+        GST_DEBUG("hailorecv metadata latency: %f milliseconds", latency.count());
+    }
+
+    return HAILO_SUCCESS;
+}
+
+hailo_status HailoRecvImpl::set_output_vstreams(std::vector<OutputVStream> &&output_vstreams, uint32_t batch_size)
+{
+    GST_CHECK((0 == m_props.m_outputs_max_pool_size.get()) || (m_props.m_outputs_min_pool_size.get() <= m_props.m_outputs_max_pool_size.get()),
+        HAILO_INVALID_ARGUMENT, m_element, RESOURCE, "Minimum pool size (=%d) is bigger than maximum (=%d)!", m_props.m_outputs_min_pool_size.get(),
+        m_props.m_outputs_max_pool_size.get());
+    
+    if ((0 != m_props.m_outputs_max_pool_size.get()) && (m_props.m_outputs_max_pool_size.get() < batch_size)) {
+        g_warning("outputs-max-pool-size is smaller than the batch size! Overall performance might be affected!");
+    }
+
+    m_output_vstreams = std::move(output_vstreams);
+
+    for (auto &out_vstream : m_output_vstreams) {
+        GstHailoBufferPool *hailo_pool = GST_HAILO_BUFFER_POOL(g_object_new(GST_TYPE_HAILO_BUFFER_POOL, NULL));
+        gst_object_ref_sink(hailo_pool);
+        memcpy(hailo_pool->vstream_name, out_vstream.name().c_str(), sizeof(hailo_pool->vstream_name));
+        hailo_pool->element_name = GST_ELEMENT_NAME(GST_ELEMENT_PARENT(m_element));
+
+        GstBufferPool *pool = GST_BUFFER_POOL(hailo_pool);
+
+        GstStructure *config = gst_buffer_pool_get_config(pool);
+
+        gst_buffer_pool_config_set_params(config, nullptr, static_cast<guint>(out_vstream.get_frame_size()), m_props.m_outputs_min_pool_size.get(),
+            m_props.m_outputs_max_pool_size.get());
+
+        gboolean result = gst_buffer_pool_set_config(pool, config);
+        GST_CHECK(result, HAILO_INTERNAL_FAILURE, m_element, RESOURCE, "Could not set config for vstream %s buffer pool", out_vstream.name().c_str());
+
+        result = gst_buffer_pool_set_active(pool, TRUE);
+        GST_CHECK(result, HAILO_INTERNAL_FAILURE, m_element, RESOURCE, "Could not set buffer pool active for vstream %s", out_vstream.name().c_str());
+
+        m_output_infos.emplace_back(out_vstream, pool);
+    }
+
+    return HAILO_SUCCESS;
+}
+
+hailo_status HailoRecvImpl::clear_vstreams()
+{
+    return OutputVStream::clear(m_output_vstreams);
+}
+
+static void gst_hailorecv_init(GstHailoRecv *self)
+{
+    auto hailorecv_impl = HailoRecvImpl::create(self);
+    if (!hailorecv_impl) {
+        GST_ELEMENT_ERROR(self, RESOURCE, FAILED, ("Creating hailorecv implementation has failed! status = %d", hailorecv_impl.status()), (NULL));
+        return;
+    }
+
+    self->impl = hailorecv_impl.release();
+}
+
+static void gst_hailorecv_set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec)
+{
+    GST_HAILORECV(object)->impl->set_property(object, property_id, value, pspec);
+}
+
+static void gst_hailorecv_get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec)
+{
+    GST_HAILORECV(object)->impl->get_property(object, property_id, value, pspec);
+}
+
+static GstFlowReturn gst_hailorecv_transform_frame_ip(GstVideoFilter *filter, GstVideoFrame *frame)
+{
+    GST_DEBUG_OBJECT(filter, "transform_frame_ip");
+    return GST_HAILORECV(filter)->impl->handle_frame(filter, frame);
+}
+
+static GstStateChangeReturn gst_hailorecv_change_state(GstElement *element, GstStateChange transition)
+{
+    GstStateChangeReturn ret = GST_ELEMENT_CLASS(gst_hailorecv_parent_class)->change_state(element, transition);
+    if (GST_STATE_CHANGE_FAILURE == ret) {
+        return ret;
+    }
+
+    if (GST_STATE_CHANGE_READY_TO_NULL == transition) {
+        // Cleanup all of hailorecv memory
+        GST_HAILORECV(element)->impl.reset();
+    }
+
+    return ret;
+}
+
+static GstFlowReturn gst_hailorecv_buffer_pool_acquire_callback(GstBufferPool *pool, GstBuffer **buffer, GstBufferPoolAcquireParams *params)
+{
+    GstHailoBufferPool *hailo_pool = GST_HAILO_BUFFER_POOL(pool);
+
+    GstFlowReturn status = GST_HAILO_BUFFER_POOL_CLASS(GST_BUFFER_POOL_GET_CLASS(pool))->parent_acquire_callback(pool, buffer, params);
+    if (GST_FLOW_OK == status) {
+        ++hailo_pool->buffers_acquired;
+
+        GstStructure *pool_config = gst_buffer_pool_get_config(pool);
+        GstCaps *caps = nullptr;
+        guint size = 0;
+        guint min_buffers = 0;
+        guint max_buffers = 0;
+        gboolean result = gst_buffer_pool_config_get_params(pool_config, &caps, &size, &min_buffers, &max_buffers);
+        if (!result) {
+            g_error("Failed getting config params from buffer pool!");
+            return GST_FLOW_ERROR;
+        }
+
+        if (hailo_pool->buffers_acquired.load() == max_buffers) {
+            GST_INFO("Buffer pool of vstream %s in element %s is overrun!", hailo_pool->vstream_name, hailo_pool->element_name);
+        }
+    }
+    return status;
+}
+
+static void gst_hailorecv_buffer_pool_release_callback(GstBufferPool *pool, GstBuffer *buffer)
+{
+    GstHailoBufferPool *hailo_pool = GST_HAILO_BUFFER_POOL(pool);
+
+    GST_HAILO_BUFFER_POOL_CLASS(GST_BUFFER_POOL_GET_CLASS(pool))->parent_release_callback(pool, buffer);
+    if (hailo_pool->buffers_acquired > 0) {
+        --hailo_pool->buffers_acquired;
+        if (hailo_pool->buffers_acquired.load() == 0) {
+            GST_INFO("Buffer pool of vstream %s in element %s is underrun!", hailo_pool->vstream_name, hailo_pool->element_name);
+        }
+    }
+}
\ No newline at end of file
diff --git a/hailort/libhailort/bindings/gstreamer/gst-hailo/gsthailorecv.hpp b/hailort/libhailort/bindings/gstreamer/gst-hailo/gsthailorecv.hpp
new file mode 100644 (file)
index 0000000..892610a
--- /dev/null
@@ -0,0 +1,110 @@
+/*
+ * Copyright (c) 2021-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the LGPL 2.1 license (https://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+#ifndef _GST_HAILORECV_HPP_
+#define _GST_HAILORECV_HPP_
+
+#include "common.hpp"
+#include "hailo_output_info.hpp"
+
+#include <gst/video/video.h>
+#include <gst/video/gstvideofilter.h>
+
+#include <vector>
+
+G_BEGIN_DECLS
+
+#define GST_TYPE_HAILO_BUFFER_POOL (gst_hailo_buffer_pool_get_type())
+#define GST_HAILO_BUFFER_POOL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_HAILO_BUFFER_POOL, GstHailoBufferPool))
+#define GST_HAILO_BUFFER_POOL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_HAILO_BUFFER_POOL, GstHailoBufferPoolClass))
+#define GST_IS_HAILO_BUFFER_POOL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_HAILO_BUFFER_POOL))
+#define GST_IS_HAILO_BUFFER_POOL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_HAILO_BUFFER_POOL))
+
+struct GstHailoBufferPool
+{
+    GstBufferPool parent;
+    // This is a copy of the vstream name because vstreams can be freed before the pool has finished freeing all the buffers
+    char vstream_name[HAILO_MAX_STREAM_NAME_SIZE];
+    const char *element_name;
+    std::atomic_uint buffers_acquired;
+};
+
+struct GstHailoBufferPoolClass
+{
+    GstBufferPoolClass parent;
+    GstFlowReturn (*parent_acquire_callback)(GstBufferPool *, GstBuffer **buffer, GstBufferPoolAcquireParams *params);
+    void (*parent_release_callback)(GstBufferPool *pool, GstBuffer *buffer);
+};
+
+GType gst_hailo_buffer_pool_get_type(void);
+
+#define GST_TYPE_HAILORECV (gst_hailorecv_get_type())
+#define GST_HAILORECV(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_HAILORECV,GstHailoRecv))
+#define GST_HAILORECV_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_HAILORECV,GstHailoRecvClass))
+#define GST_IS_HAILORECV(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_HAILORECV))
+#define GST_IS_HAILORECV_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_HAILORECV))
+
+class HailoRecvImpl;
+struct GstHailoRecv
+{
+    GstVideoFilter parent;
+    std::unique_ptr<HailoRecvImpl> impl;
+};
+
+struct GstHailoRecvClass
+{
+    GstVideoFilterClass parent;
+};
+
+struct HailoRecvProperties final
+{
+public:
+    HailoRecvProperties() : m_debug(false), m_outputs_min_pool_size(DEFAULT_OUTPUTS_MIN_POOL_SIZE), m_outputs_max_pool_size(DEFAULT_OUTPUTS_MAX_POOL_SIZE)
+    {}
+
+    HailoElemProperty<gboolean> m_debug;
+    HailoElemProperty<guint> m_outputs_min_pool_size;
+    HailoElemProperty<guint> m_outputs_max_pool_size;
+};
+
+class HailoRecvImpl final {
+public:
+    static Expected<std::unique_ptr<HailoRecvImpl>> create(GstHailoRecv *element);
+    HailoRecvImpl(GstHailoRecv *element);
+    void set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec);
+    void get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec);
+    GstFlowReturn handle_frame(GstVideoFilter *filter, GstVideoFrame *frame);
+    hailo_status set_output_vstreams(std::vector<OutputVStream> &&output_vstreams, uint32_t batch_size);
+    hailo_status clear_vstreams();
+
+private:
+    hailo_status read_from_vstreams(bool should_print_latency);
+    hailo_status write_tensors_to_metadata(GstVideoFrame *frame, bool should_print_latency);
+
+    GstHailoRecv *m_element;
+    HailoRecvProperties m_props;
+    std::vector<OutputVStream> m_output_vstreams;
+    std::vector<HailoOutputInfo> m_output_infos;
+};
+
+GType gst_hailorecv_get_type(void);
+
+G_END_DECLS
+
+#endif /* _GST_HAILORECV_HPP_ */
diff --git a/hailort/libhailort/bindings/gstreamer/gst-hailo/gsthailosend.cpp b/hailort/libhailort/bindings/gstreamer/gst-hailo/gsthailosend.cpp
new file mode 100644 (file)
index 0000000..3330b21
--- /dev/null
@@ -0,0 +1,290 @@
+/*
+ * Copyright (c) 2021-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the LGPL 2.1 license (https://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+#include "gsthailosend.hpp"
+#include "gsthailonet.hpp"
+#include "metadata/hailo_buffer_flag_meta.hpp"
+
+#include <chrono>
+#include <iostream>
+#include <gst/video/video.h>
+#include <gst/video/gstvideofilter.h>
+
+GST_DEBUG_CATEGORY_STATIC(gst_hailosend_debug_category);
+#define GST_CAT_DEFAULT gst_hailosend_debug_category
+#define RGB_FEATURES_SIZE (3)
+#define YUY2_FEATURES_SIZE (2)
+
+static void gst_hailosend_set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec);
+static void gst_hailosend_get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec);
+static GstCaps *gst_hailosend_transform_caps(GstBaseTransform *trans, GstPadDirection direction, GstCaps *caps, GstCaps *filter);
+static GstFlowReturn gst_hailosend_transform_frame_ip(GstVideoFilter *filter, GstVideoFrame *frame);
+static gboolean gst_hailosend_propose_allocation(GstBaseTransform *trans, GstQuery *decide_query, GstQuery *query);
+static GstStateChangeReturn gst_hailosend_change_state(GstElement *element, GstStateChange transition);
+
+enum
+{
+    PROP_0,
+    PROP_DEBUG
+};
+
+G_DEFINE_TYPE(GstHailoSend, gst_hailosend, GST_TYPE_VIDEO_FILTER);
+
+static void gst_hailosend_class_init(GstHailoSendClass *klass)
+{
+    GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
+    GstElementClass *element_class = GST_ELEMENT_CLASS(klass);
+    GstBaseTransformClass *base_transform_class = GST_BASE_TRANSFORM_CLASS(klass);
+    GstVideoFilterClass *video_filter_class = GST_VIDEO_FILTER_CLASS(klass);
+
+    /* Setting up pads and setting metadata should be moved to
+       base_class_init if you intend to subclass this class. */
+    gst_element_class_add_pad_template(GST_ELEMENT_CLASS(klass),
+        gst_pad_template_new("src", GST_PAD_SRC, GST_PAD_ALWAYS, gst_caps_from_string(HAILO_VIDEO_CAPS)));
+    gst_element_class_add_pad_template(GST_ELEMENT_CLASS(klass),
+        gst_pad_template_new("sink", GST_PAD_SINK, GST_PAD_ALWAYS, gst_caps_from_string(HAILO_VIDEO_CAPS)));
+
+    gst_element_class_set_static_metadata(GST_ELEMENT_CLASS(klass),
+        "hailosend element", "Hailo/Filter/Video", "Send RGB/YUY2 video to HailoRT", PLUGIN_AUTHOR);
+    
+    element_class->change_state = GST_DEBUG_FUNCPTR(gst_hailosend_change_state);
+
+    gobject_class->set_property = gst_hailosend_set_property;
+    gobject_class->get_property = gst_hailosend_get_property;
+    g_object_class_install_property (gobject_class, PROP_DEBUG,
+        g_param_spec_boolean ("debug", "debug", "debug", false,
+            (GParamFlags)(GST_PARAM_CONTROLLABLE | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
+    base_transform_class->transform_caps = GST_DEBUG_FUNCPTR(gst_hailosend_transform_caps);
+    base_transform_class->propose_allocation = GST_DEBUG_FUNCPTR(gst_hailosend_propose_allocation);
+    video_filter_class->transform_frame_ip = GST_DEBUG_FUNCPTR(gst_hailosend_transform_frame_ip);
+}
+
+Expected<std::unique_ptr<HailoSendImpl>> HailoSendImpl::create(GstHailoSend *element)
+{
+    if (nullptr == element) {
+        return make_unexpected(HAILO_INVALID_ARGUMENT);
+    }
+
+    auto ptr = make_unique_nothrow<HailoSendImpl>(element);
+    if (nullptr == ptr) {
+        return make_unexpected(HAILO_OUT_OF_HOST_MEMORY);
+    }
+
+    return ptr;
+}
+
+HailoSendImpl::HailoSendImpl(GstHailoSend *element) : m_element(element), m_hailonet(nullptr), m_props(),
+    m_batch_size(HAILO_DEFAULT_BATCH_SIZE), m_last_frame_pts(0)
+{
+    GST_DEBUG_CATEGORY_INIT(gst_hailosend_debug_category, "hailosend", 0, "debug category for hailosend element");
+}
+
+void HailoSendImpl::set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec)
+{
+    GST_DEBUG_OBJECT(m_element, "set_property");
+
+    if ((object == nullptr) || (value == nullptr) || (pspec == nullptr)) {
+        g_error("set_property got null parameter!");
+        return;
+    }
+
+    switch (property_id) {
+    case PROP_DEBUG:
+        m_props.m_debug = g_value_get_boolean(value);
+        break;
+    default:
+        G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
+        break;
+    }
+}
+
+void HailoSendImpl::get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec)
+{
+    GST_DEBUG_OBJECT(m_element, "get_property");
+
+    if ((object == nullptr) || (value == nullptr) || (pspec == nullptr)) {
+        g_error("get_property got null parameter!");
+        return;
+    }
+
+    switch (property_id) {
+    case PROP_DEBUG:
+        g_value_set_boolean(value, m_props.m_debug.get());
+        break;
+    default:
+        G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
+        break;
+    }
+}
+
+GstFlowReturn HailoSendImpl::handle_frame(GstVideoFilter */*filter*/, GstVideoFrame *frame)
+{
+    assert(nullptr != frame);
+    m_last_frame_pts = GST_BUFFER_TIMESTAMP(frame->buffer);
+
+    if (!GST_HAILONET(GST_ELEMENT_PARENT(m_element))->impl->is_active()) {
+        GstHailoBufferFlagMeta *meta = GST_HAILO_BUFFER_FLAG_META_ADD(frame->buffer);
+        meta->flag = BUFFER_FLAG_SKIP;
+        return GST_FLOW_OK;
+    }
+
+    guint8 *frame_buffer = reinterpret_cast<guint8*>(GST_VIDEO_FRAME_PLANE_DATA(frame, 0));
+    hailo_status status = HAILO_UNINITIALIZED;
+
+    if (m_props.m_debug.get()) {
+        std::chrono::duration<double, std::milli> latency;
+        std::chrono::time_point<std::chrono::system_clock> start_time;
+        start_time = std::chrono::system_clock::now();
+        status = write_to_vstreams(frame_buffer, GST_VIDEO_FRAME_SIZE(frame));
+        latency = std::chrono::system_clock::now() - start_time;
+        GST_DEBUG("hailosend latency: %f milliseconds", latency.count());
+    } else {
+        status = write_to_vstreams(frame_buffer, GST_VIDEO_FRAME_SIZE(frame));
+    }
+
+    if (HAILO_SUCCESS != status) {
+        return GST_FLOW_ERROR;
+    }
+    return GST_FLOW_OK;
+}
+
+hailo_status HailoSendImpl::write_to_vstreams(void *buf, size_t size)
+{
+    for (auto &in_vstream : m_input_vstreams) {
+        auto status = in_vstream.write(MemoryView(buf, size));
+        GST_CHECK_SUCCESS(status, m_element, STREAM, "Failed writing to input vstream %s, status = %d", in_vstream.name().c_str(), status);
+    }
+    return HAILO_SUCCESS;
+}
+
+GstCaps *HailoSendImpl::get_caps(GstBaseTransform */*trans*/, GstPadDirection /*direction*/, GstCaps */*caps*/, GstCaps */*filter*/)
+{
+    GST_DEBUG_OBJECT(m_element, "transform_caps");
+
+    if (0 == m_input_vstream_infos.size()) {
+        // Init here because it is guaranteed that we have a parent element
+        m_hailonet = GST_HAILONET(GST_ELEMENT_PARENT(m_element));
+
+        hailo_status status = m_hailonet->impl->set_hef();
+        if (HAILO_SUCCESS != status) {
+            return NULL;
+        }
+    }
+    
+    const gchar *format = nullptr;
+    switch (m_input_vstream_infos[0].format.order) {
+    case HAILO_FORMAT_ORDER_NHWC:
+    case HAILO_FORMAT_ORDER_NHCW:
+    case HAILO_FORMAT_ORDER_FCR:
+    case HAILO_FORMAT_ORDER_F8CR:
+        format = "RGB";
+        GST_CHECK(RGB_FEATURES_SIZE == m_input_vstream_infos[0].shape.features, NULL, m_element, STREAM,
+            "Features of input vstream %s is not %d for RGB format! (features=%d)", m_input_vstream_infos[0].name, RGB_FEATURES_SIZE,
+            m_input_vstream_infos[0].shape.features);
+        break;
+    case HAILO_FORMAT_ORDER_YUY2:
+        format = "YUY2";
+        GST_CHECK(YUY2_FEATURES_SIZE == m_input_vstream_infos[0].shape.features, NULL, m_element, STREAM,
+            "Features of input vstream %s is not %d for YUY2 format! (features=%d)", m_input_vstream_infos[0].name, YUY2_FEATURES_SIZE,
+            m_input_vstream_infos[0].shape.features);
+        break;
+    default:
+        GST_ELEMENT_ERROR(m_element, RESOURCE, FAILED,
+            ("Input VStream %s has an unsupported format order! order = %d", m_input_vstream_infos[0].name, m_input_vstream_infos[0].format.order), (NULL));
+        return NULL;
+    }
+
+    /* filter against set allowed caps on the pad */
+    return gst_caps_new_simple("video/x-raw",
+                               "format", G_TYPE_STRING, format,
+                               "width", G_TYPE_INT, m_input_vstream_infos[0].shape.width,
+                               "height", G_TYPE_INT, m_input_vstream_infos[0].shape.height,
+                               NULL);
+}
+
+void HailoSendImpl::set_input_vstream_infos(std::vector<hailo_vstream_info_t> &&input_vstream_infos)
+{
+    m_input_vstream_infos = std::move(input_vstream_infos);
+}
+
+void HailoSendImpl::set_input_vstreams(std::vector<InputVStream> &&input_vstreams)
+{
+    m_input_vstreams = std::move(input_vstreams);
+}
+
+hailo_status HailoSendImpl::clear_vstreams()
+{
+    return InputVStream::clear(m_input_vstreams);
+}
+
+static void gst_hailosend_init(GstHailoSend *self)
+{
+    auto hailosend_impl = HailoSendImpl::create(self);
+    if (!hailosend_impl) {
+        GST_ELEMENT_ERROR(self, RESOURCE, FAILED, ("Creating hailosend implementation has failed! status = %d", hailosend_impl.status()), (NULL));
+        return;
+    }
+
+    self->impl = hailosend_impl.release();
+}
+
+void gst_hailosend_set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec)
+{
+    GST_HAILOSEND(object)->impl->set_property(object, property_id, value, pspec);
+}
+
+void gst_hailosend_get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec)
+{
+    GST_HAILOSEND(object)->impl->get_property(object, property_id, value, pspec);
+}
+
+static GstFlowReturn gst_hailosend_transform_frame_ip(GstVideoFilter *filter, GstVideoFrame *frame)
+{
+    GST_DEBUG_OBJECT(filter, "transform_frame_ip");
+    return GST_HAILOSEND(filter)->impl->handle_frame(filter, frame);
+}
+
+static GstCaps *gst_hailosend_transform_caps(GstBaseTransform *trans, GstPadDirection direction, GstCaps *caps, GstCaps *filter)
+{
+    return GST_HAILOSEND(trans)->impl->get_caps(trans, direction, caps, filter);
+}
+
+static gboolean gst_hailosend_propose_allocation(GstBaseTransform *trans, GstQuery *decide_query, GstQuery *query)
+{
+    if (GST_HAILOSEND(trans)->impl->batch_size() > 1) {
+        return FALSE;
+    }
+
+    return GST_BASE_TRANSFORM_CLASS(gst_hailosend_parent_class)->propose_allocation(trans, decide_query, query);
+}
+
+static GstStateChangeReturn gst_hailosend_change_state(GstElement *element, GstStateChange transition)
+{
+    GstStateChangeReturn ret = GST_ELEMENT_CLASS(gst_hailosend_parent_class)->change_state(element, transition);
+    if (GST_STATE_CHANGE_FAILURE == ret) {
+        return ret;
+    }
+
+    if (GST_STATE_CHANGE_READY_TO_NULL == transition) {
+        // Cleanup all of hailosend memory
+        GST_HAILOSEND(element)->impl.reset();
+    }
+
+    return ret;
+}
\ No newline at end of file
diff --git a/hailort/libhailort/bindings/gstreamer/gst-hailo/gsthailosend.hpp b/hailort/libhailort/bindings/gstreamer/gst-hailo/gsthailosend.hpp
new file mode 100644 (file)
index 0000000..3e2167a
--- /dev/null
@@ -0,0 +1,106 @@
+/*
+ * Copyright (c) 2021-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the LGPL 2.1 license (https://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+#ifndef _GST_HAILOSEND_HPP_
+#define _GST_HAILOSEND_HPP_
+
+#include "common.hpp"
+#include "network_group_handle.hpp"
+#include "gsthailonet.hpp"
+
+#include <gst/video/video.h>
+#include <gst/video/gstvideofilter.h>
+
+#include <vector>
+
+G_BEGIN_DECLS
+
+#define GST_TYPE_HAILOSEND (gst_hailosend_get_type())
+#define GST_HAILOSEND(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_HAILOSEND,GstHailoSend))
+#define GST_HAILOSEND_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_HAILOSEND,GstHailoSendClass))
+#define GST_IS_HAILOSEND(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_HAILOSEND))
+#define GST_IS_HAILOSEND_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_HAILOSEND))
+
+class HailoSendImpl;
+struct GstHailoSend
+{
+    GstVideoFilter parent;
+    std::unique_ptr<HailoSendImpl> impl;
+};
+
+struct GstHailoSendClass
+{
+    GstVideoFilterClass parent;
+};
+
+struct HailoSendProperties final
+{
+public:
+    HailoSendProperties() : m_debug(false)
+    {}
+
+    HailoElemProperty<gboolean> m_debug;
+};
+
+class HailoSendImpl final
+{
+public:
+    static Expected<std::unique_ptr<HailoSendImpl>> create(GstHailoSend *element);
+    HailoSendImpl(GstHailoSend *element);
+
+    void set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec);
+    void get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec);
+    GstFlowReturn handle_frame(GstVideoFilter *filter, GstVideoFrame *frame);
+    GstCaps *get_caps(GstBaseTransform *trans, GstPadDirection direction, GstCaps *caps, GstCaps *filter);
+    void set_input_vstream_infos(std::vector<hailo_vstream_info_t> &&input_vstream_infos);
+    void set_input_vstreams(std::vector<InputVStream> &&input_vstreams);
+    hailo_status clear_vstreams();
+
+    void set_batch_size(uint32_t batch_size)
+    {
+        m_batch_size = batch_size;
+    }
+
+    uint32_t batch_size()
+    {
+        return m_batch_size;
+    }
+
+    GstClockTime last_frame_pts()
+    {
+        return m_last_frame_pts;
+    }
+
+private:
+    hailo_status write_to_vstreams(void *buf, size_t size);
+    
+    GstHailoSend *m_element;
+    GstHailoNet *m_hailonet;
+    HailoSendProperties m_props;
+    std::vector<hailo_vstream_info_t> m_input_vstream_infos;
+    uint32_t m_batch_size;
+    std::vector<InputVStream> m_input_vstreams;
+    GstClockTime m_last_frame_pts;
+};
+
+GType gst_hailosend_get_type(void);
+
+G_END_DECLS
+
+#endif /* _GST_HAILOSEND_HPP_ */
diff --git a/hailort/libhailort/bindings/gstreamer/gst-hailo/hailo_events/hailo_events.cpp b/hailort/libhailort/bindings/gstreamer/gst-hailo/hailo_events/hailo_events.cpp
new file mode 100644 (file)
index 0000000..952dda4
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2021-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the LGPL 2.1 license (https://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+#include "hailo_events.hpp"
+
+GstEvent *HailoSetOutputFormatEvent::build(std::vector<hailo_format_with_name_t> &&formats)
+{
+    GVariant *v = g_variant_new_fixed_array(G_VARIANT_TYPE_BYTE, &formats[0], sizeof(formats[0]) * formats.size(), 1);
+    GstStructure *str = gst_structure_new(HailoSetOutputFormatEvent::name,
+        "formats", G_TYPE_VARIANT, v,
+        NULL);
+    return gst_event_new_custom(GST_EVENT_CUSTOM_UPSTREAM, str);
+}
+
+Expected<HailoSetOutputFormatEvent> HailoSetOutputFormatEvent::parse(GstEvent *event)
+{
+    if (GST_EVENT_CUSTOM_UPSTREAM != GST_EVENT_TYPE(event)) {
+        return make_unexpected(HAILO_INVALID_ARGUMENT);
+    }
+
+    if (!gst_event_has_name(event, HailoSetOutputFormatEvent::name)) {
+        return make_unexpected(HAILO_INVALID_ARGUMENT);
+    }
+
+    const GstStructure *str = gst_event_get_structure(event);
+    const GValue *f = gst_structure_get_value(str, "formats");
+    if (nullptr == f) {
+        return make_unexpected(HAILO_INVALID_ARGUMENT);
+    }
+    GVariant *v = g_value_get_variant(f);
+
+    gsize nbytes = 0;
+    const hailo_format_with_name_t *formats = reinterpret_cast<const hailo_format_with_name_t*>(g_variant_get_fixed_array(v, &nbytes, 1));
+    size_t num_of_formats = nbytes / sizeof(*formats);
+
+    HailoSetOutputFormatEvent event_data = {};
+    event_data.formats.reserve(num_of_formats);
+
+    for (uint32_t i = 0; i < num_of_formats; i++) {
+        event_data.formats.emplace_back(formats[i]);
+    }
+
+    return event_data;
+}
\ No newline at end of file
diff --git a/hailort/libhailort/bindings/gstreamer/gst-hailo/hailo_events/hailo_events.hpp b/hailort/libhailort/bindings/gstreamer/gst-hailo/hailo_events/hailo_events.hpp
new file mode 100644 (file)
index 0000000..70f5cb4
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2021-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the LGPL 2.1 license (https://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+#ifndef _GST_HAILO_EVENTS_HPP_
+#define _GST_HAILO_EVENTS_HPP_
+
+#include "hailo/hailort.h"
+#include "hailo/expected.hpp"
+
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wconversion"
+#include <gst/gst.h>
+#pragma GCC diagnostic pop
+
+#include <vector>
+
+using namespace hailort;
+
+struct hailo_format_with_name_t {
+    hailo_format_t format;
+    char name[HAILO_MAX_STREAM_NAME_SIZE];
+};
+
+// Event to set the output format of the output layers.
+// This event should be sent before the pipeline changes from READY to PAUSED.
+struct HailoSetOutputFormatEvent {
+    std::vector<hailo_format_with_name_t> formats;
+
+    static constexpr const gchar *name = "HailoSetOutputFormatEvent";
+    static GstEvent *build(std::vector<hailo_format_with_name_t> &&formats);
+    static Expected<HailoSetOutputFormatEvent> parse(GstEvent *event);
+};
+
+#endif /* _GST_HAILO_EVENTS_HPP_ */
\ No newline at end of file
diff --git a/hailort/libhailort/bindings/gstreamer/gst-hailo/hailo_output_info.hpp b/hailort/libhailort/bindings/gstreamer/gst-hailo/hailo_output_info.hpp
new file mode 100644 (file)
index 0000000..e62d49b
--- /dev/null
@@ -0,0 +1,97 @@
+/*
+ * Copyright (c) 2021-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the LGPL 2.1 license (https://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+#ifndef _HAILO_OUTPUT_INFO_HPP_
+#define _HAILO_OUTPUT_INFO_HPP_
+
+#include "common.hpp"
+#include "hailo/platform.h"
+#include "hailo/buffer.hpp"
+#include "hailo/vstream.hpp"
+
+
+// TODO: The name info is confusing because one might think that it holds only the info, so change it to "HailoOutputBuffer" or something like that
+class HailoOutputInfo {
+public:
+    HailoOutputInfo(OutputVStream &vstream, GstBufferPool *pool)
+        : m_vstream(vstream), m_pool(pool), m_last_acquired_buffer(nullptr), m_vstream_info(vstream.get_info())
+    {}
+
+    ~HailoOutputInfo()
+    {
+        if (nullptr != m_pool) {
+            (void)gst_buffer_pool_set_active(m_pool, FALSE);
+        }
+    }
+
+    HailoOutputInfo(const HailoOutputInfo &other) = delete;
+    HailoOutputInfo &operator=(const HailoOutputInfo &other) = delete;
+    HailoOutputInfo& operator=(HailoOutputInfo &&other) = delete;
+
+    HailoOutputInfo(HailoOutputInfo &&other) : m_vstream(other.m_vstream), m_pool(std::exchange(other.m_pool, nullptr)),
+        m_last_acquired_buffer(std::exchange(other.m_last_acquired_buffer, nullptr)), m_vstream_info(std::move(other.m_vstream_info))
+    {}
+
+    OutputVStream &vstream()
+    {
+        return m_vstream;
+    }
+
+    const hailo_vstream_info_t &vstream_info() const
+    {
+        return m_vstream_info;
+    }
+
+    Expected<GstBuffer*> acquire_buffer()
+    {
+        GstBuffer *buffer = nullptr;
+        GstFlowReturn result = gst_buffer_pool_acquire_buffer(m_pool, &buffer, nullptr);
+        if (GST_FLOW_OK != result) {
+            g_critical("Acquiring buffer failed with flow status %d!", result);
+            return make_unexpected(HAILO_INTERNAL_FAILURE);
+        }
+
+        m_last_acquired_buffer = buffer;
+        return buffer;
+    }
+
+    GstBuffer *last_acquired_buffer()
+    {
+        return m_last_acquired_buffer;
+    }
+
+    void unref_last_acquired_buffer()
+    {
+        if (nullptr == m_last_acquired_buffer) {
+            return;
+        }
+
+        gst_buffer_unref(m_last_acquired_buffer);
+        m_last_acquired_buffer = nullptr;
+    }
+
+private:
+    OutputVStream &m_vstream;
+    GstBufferPool *m_pool;
+    GstBuffer *m_last_acquired_buffer;
+    hailo_vstream_info_t m_vstream_info;
+};
+
+
+#endif /* _HAILO_OUTPUT_INFO_HPP_ */
diff --git a/hailort/libhailort/bindings/gstreamer/gst-hailo/metadata/hailo_buffer_flag_meta.cpp b/hailort/libhailort/bindings/gstreamer/gst-hailo/metadata/hailo_buffer_flag_meta.cpp
new file mode 100644 (file)
index 0000000..a500f17
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2021-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the LGPL 2.1 license (https://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+#include "hailo_buffer_flag_meta.hpp"
+
+GType gst_hailo_buffer_flag_meta_api_get_type(void)
+{
+    static volatile GType type;
+    static const gchar *tags[] = {HAILO_BUFFER_FLAG_META_TAG, NULL};
+
+    if (g_once_init_enter(&type)) {
+        GType _type = gst_meta_api_type_register(HAILO_BUFFER_FLAG_META_API_NAME, tags);
+        g_once_init_leave(&type, _type);
+    }
+    return type;
+}
+
+gboolean gst_hailo_buffer_flag_meta_init(GstMeta *meta, gpointer /*params*/, GstBuffer */*buffer*/)
+{
+    GstHailoBufferFlagMeta *hailo_buffer_flag_meta = (GstHailoBufferFlagMeta *)meta;
+    hailo_buffer_flag_meta->flag = BUFFER_FLAG_NONE;
+    return TRUE;
+}
+
+void gst_hailo_buffer_flag_meta_free(GstMeta */*meta*/, GstBuffer */*buffer*/)
+{}
+
+gboolean gst_hailo_buffer_flag_meta_transform(GstBuffer *dest_buf, GstMeta *src_meta, GstBuffer */*src_buf*/, GQuark /*type*/, gpointer /*data*/)
+{
+    g_return_val_if_fail(gst_buffer_is_writable(dest_buf), FALSE);
+
+    GstHailoBufferFlagMeta *dst = GST_HAILO_BUFFER_FLAG_META_ADD(dest_buf);
+    GstHailoBufferFlagMeta *src = (GstHailoBufferFlagMeta *)src_meta;
+
+    dst->flag = src->flag;
+    return TRUE;
+}
+
+const GstMetaInfo *gst_hailo_buffer_flag_meta_get_info(void)
+{
+    static const GstMetaInfo *meta_info = NULL;
+
+    if (g_once_init_enter(&meta_info)) {
+        const GstMetaInfo *meta = gst_meta_register(
+            gst_hailo_buffer_flag_meta_api_get_type(), HAILO_BUFFER_FLAG_META_IMPL_NAME, sizeof(GstHailoBufferFlagMeta),
+            (GstMetaInitFunction)gst_hailo_buffer_flag_meta_init, (GstMetaFreeFunction)gst_hailo_buffer_flag_meta_free,
+            (GstMetaTransformFunction)gst_hailo_buffer_flag_meta_transform);
+        g_once_init_leave(&meta_info, meta);
+    }
+    return meta_info;
+}
\ No newline at end of file
diff --git a/hailort/libhailort/bindings/gstreamer/gst-hailo/metadata/hailo_buffer_flag_meta.hpp b/hailort/libhailort/bindings/gstreamer/gst-hailo/metadata/hailo_buffer_flag_meta.hpp
new file mode 100644 (file)
index 0000000..f0e1e6b
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2021-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the LGPL 2.1 license (https://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+#ifndef __HAILO_BUFFER_FLAG_META_HPP__
+#define __HAILO_BUFFER_FLAG_META_HPP__
+
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wconversion"
+#include <gst/gst.h>
+#pragma GCC diagnostic pop
+
+#define HAILO_BUFFER_FLAG_META_API_NAME "GstHailoBufferFlagMetaAPI"
+#define HAILO_BUFFER_FLAG_META_IMPL_NAME "GstHailoBufferFlagMeta"
+#define HAILO_BUFFER_FLAG_META_TAG "hailo_buffer_flag_meta"
+
+G_BEGIN_DECLS
+
+enum BufferFlag {
+    BUFFER_FLAG_NONE,
+    BUFFER_FLAG_SKIP,
+    BUFFER_FLAG_FLUSH
+};
+
+struct GstHailoBufferFlagMeta {
+    GstMeta meta;
+    BufferFlag flag;
+};
+
+const GstMetaInfo *gst_hailo_buffer_flag_meta_get_info(void);
+
+GType gst_hailo_buffer_flag_meta_api_get_type(void);
+
+#define GST_HAILO_BUFFER_FLAG_META_API_TYPE (gst_hailo_buffer_flag_meta_api_get_type())
+#define GST_HAILO_BUFFER_FLAG_META_INFO (gst_hailo_buffer_flag_meta_get_info())
+#define GST_HAILO_BUFFER_FLAG_META_GET(buf) ((GstHailoBufferFlagMeta *)gst_buffer_get_meta(buf, gst_hailo_buffer_flag_meta_api_get_type()))
+#define GST_HAILO_BUFFER_FLAG_META_ITERATE(buf, state)                                                                        \
+    ((GstHailoBufferFlagMeta *)gst_buffer_iterate_meta_filtered(buf, state, gst_hailo_buffer_flag_meta_api_get_type()))
+#define GST_HAILO_BUFFER_FLAG_META_ADD(buf)                                                                                   \
+    ((GstHailoBufferFlagMeta *)gst_buffer_add_meta(buf, gst_hailo_buffer_flag_meta_get_info(), NULL))
+#define GST_HAILO_BUFFER_FLAG_META_COUNT(buf) (gst_buffer_get_n_meta(buf, gst_hailo_buffer_flag_meta_api_get_type()))
+
+G_END_DECLS
+
+#endif /* __HAILO_BUFFER_FLAG_META_HPP__ */
diff --git a/hailort/libhailort/bindings/gstreamer/gst-hailo/metadata/tensor_meta.cpp b/hailort/libhailort/bindings/gstreamer/gst-hailo/metadata/tensor_meta.cpp
new file mode 100644 (file)
index 0000000..060ad6d
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2021-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the LGPL 2.1 license (https://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+#include <string.h>
+#include <iostream>
+
+#include "tensor_meta.hpp"
+
+GType gst_tensor_meta_api_get_type(void)
+{
+    static volatile GType type;
+    static const gchar *tags[] = {TENSOR_META_TAG, NULL};
+
+    if (g_once_init_enter(&type)) {
+        GType _type = gst_meta_api_type_register(TENSOR_META_API_NAME, tags);
+        g_once_init_leave(&type, _type);
+    }
+    return type;
+}
+
+gboolean gst_tensor_meta_init(GstMeta */*meta*/, gpointer /*params*/, GstBuffer */*buffer*/) {
+    return TRUE;
+}
+
+void gst_tensor_meta_free(GstMeta */*meta*/, GstBuffer */*buffer*/)
+{}
+
+gboolean gst_tensor_meta_transform(GstBuffer *dest_buf, GstMeta *src_meta, GstBuffer */*src_buf*/, GQuark /*type*/, gpointer /*data*/)
+{
+    g_return_val_if_fail(gst_buffer_is_writable(dest_buf), FALSE);
+
+    GstHailoTensorMeta *dst = GST_TENSOR_META_ADD(dest_buf);
+    GstHailoTensorMeta *src = (GstHailoTensorMeta *)src_meta;
+
+    dst->info = src->info;
+    return TRUE;
+}
+
+const GstMetaInfo *gst_tensor_meta_get_info(void)
+{
+    static const GstMetaInfo *meta_info = NULL;
+
+    if (g_once_init_enter(&meta_info)) {
+        const GstMetaInfo *meta = gst_meta_register(
+            gst_tensor_meta_api_get_type(), TENSOR_META_IMPL_NAME, sizeof(GstHailoTensorMeta),
+            (GstMetaInitFunction)gst_tensor_meta_init, (GstMetaFreeFunction)gst_tensor_meta_free,
+            (GstMetaTransformFunction)gst_tensor_meta_transform);
+        g_once_init_leave(&meta_info, meta);
+    }
+    return meta_info;
+}
\ No newline at end of file
diff --git a/hailort/libhailort/bindings/gstreamer/gst-hailo/metadata/tensor_meta.hpp b/hailort/libhailort/bindings/gstreamer/gst-hailo/metadata/tensor_meta.hpp
new file mode 100644 (file)
index 0000000..82bb7d5
--- /dev/null
@@ -0,0 +1,140 @@
+/*
+ * Copyright (c) 2021-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the LGPL 2.1 license (https://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+#ifndef __TENSOR_META_HPP__
+#define __TENSOR_META_HPP__
+
+#include "hailo/hailort.h"
+
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wconversion"
+#include <gst/gst.h>
+#pragma GCC diagnostic pop
+
+#define TENSOR_META_API_NAME "GstHailoTensorMetaAPI"
+#define TENSOR_META_IMPL_NAME "GstHailoTensorMeta"
+#define TENSOR_META_TAG "tensor_meta"
+
+G_BEGIN_DECLS
+
+/**
+ * @brief This function returns a pointer to the fixed array of tensor bytes
+ * @param s GstStructure* to get tensor from. It's assumed that tensor data is stored in "data_buffer" field
+ * @param[out] nbytes pointer to the location to store the number of bytes in returned array
+ * @return void* to tensor data as bytes, NULL if s has no "data_buffer" field
+ */
+inline const void *get_tensor_data(GstStructure *s) {
+    gsize nbytes=0;
+    (void)gst_structure_get(s, "size", G_TYPE_LONG, &nbytes, NULL);
+    const GValue *f = gst_structure_get_value(s, "data_buffer");
+    if (!f)
+        return NULL;
+    GVariant *v = g_value_get_variant(f);
+    return g_variant_get_fixed_array(v, &nbytes, 1);
+}
+
+/**
+ * @brief This struct represents raw tensor metadata and contains instance of parent GstMeta and fields describing
+ * inference result tensor. This metadata instances is attached to buffer by gvainference elements
+ */
+struct GstHailoTensorMeta {
+    GstMeta meta;              /**< parent meta object */
+    hailo_vstream_info_t info; /**< struct that holds vstream info, e.g. shape, quant_info, layer_name etc... */
+};
+
+/**
+ * @brief This function registers, if needed, and returns GstMetaInfo for _GstHailoTensorMeta
+ * @return GstMetaInfo* for registered type
+ */
+const GstMetaInfo *gst_tensor_meta_get_info(void);
+
+/**
+ * @brief This function registers, if needed, and returns a GType for api "GstHailoTensorMetaAPI" and associate it with
+ * TENSOR_META_TAG tag
+ * @return GType type
+ */
+GType gst_tensor_meta_api_get_type(void);
+#define GST_TENSOR_META_API_TYPE (gst_tensor_meta_api_get_type())
+
+/**
+ * @def GST_TENSOR_META_INFO
+ * @brief This macro calls gst_tensor_meta_get_info
+ * @return const GstMetaInfo* for registered type
+ */
+#define GST_TENSOR_META_INFO (gst_tensor_meta_get_info())
+
+/**
+ * @def GST_TENSOR_META_GET
+ * @brief This macro retrieves ptr to _GstHailoTensorMeta instance for passed buf
+ * @param buf GstBuffer* of which metadata is retrieved
+ * @return _GstHailoTensorMeta* instance attached to buf
+ */
+#define GST_TENSOR_META_GET(buf) ((GstHailoTensorMeta *)gst_buffer_get_meta(buf, gst_tensor_meta_api_get_type()))
+
+/**
+ * @def GST_TENSOR_META_ITERATE
+ * @brief This macro iterates through _GstHailoTensorMeta instances for passed buf, retrieving the next _GstHailoTensorMeta.
+ * If state points to NULL, the first _GstHailoTensorMeta is returned
+ * @param buf GstBuffer* of which metadata is iterated and retrieved
+ * @param state gpointer* that updates with opaque pointer after macro call.
+ * @return _GstHailoTensorMeta* instance attached to buf
+ */
+#define GST_TENSOR_META_ITERATE(buf, state)                                                                        \
+    ((GstHailoTensorMeta *)gst_buffer_iterate_meta_filtered(buf, state, gst_tensor_meta_api_get_type()))
+
+/**
+ * @def GST_TENSOR_META_ADD
+ * @brief This macro attaches new _GstHailoTensorMeta instance to passed buf
+ * @param buf GstBuffer* to which metadata will be attached
+ * @return _GstHailoTensorMeta* of the newly added instance attached to buf
+ */
+#define GST_TENSOR_META_ADD(buf)                                                                                   \
+    ((GstHailoTensorMeta *)gst_buffer_add_meta(buf, gst_tensor_meta_get_info(), NULL))
+
+/**
+ * @def GST_TENSOR_META_COUNT
+ * @brief This macro counts the number of _GstHailoTensorMeta instances attached to passed buf
+ * @param buf GstBuffer* of which metadata instances are counted
+ * @return guint number of _GstHailoTensorMeta instances attached to passed buf
+ */
+#define GST_TENSOR_META_COUNT(buf) (gst_buffer_get_n_meta(buf, gst_tensor_meta_api_get_type()))
+
+/**
+ * @brief This function searches for first _GstHailoTensorMeta instance that satisfies passed parameters
+ * @param buffer GstBuffer* that is searched for metadata
+ * @param model_name substring that should be in _GstHailoTensorMeta instance's model_name
+ * @param output_layer substring that should be in _GstHailoTensorMeta instance's layer_name
+ * @return GstHailoTensorMeta* for found instance or NULL if none are found
+ */
+//GstHailoTensorMeta *find_tensor_meta(GstBuffer *buffer, const char *model_name, const char *output_layer);
+
+/**
+ * @brief This function searches for first _GstHailoTensorMeta instance that satisfies passed parameters
+ * @param buffer GstBuffer* that is searched for metadata
+ * @param model_name substring that should be in _GstHailoTensorMeta instance's model_name
+ * @param output_layer substring that should be in _GstHailoTensorMeta instance's layer_name
+ * @param element_id element_id substring that should be in _GstHailoTensorMeta instance's element_id
+ * @return GstHailoTensorMeta* for found instance or NULL if none are found
+ */
+// GstHailoTensorMeta *find_tensor_meta_ext(GstBuffer *buffer, const char *model_name, const char *output_layer,
+//                                        const char *element_id);
+
+G_END_DECLS
+
+#endif /* __TENSOR_META_HPP__ */
diff --git a/hailort/libhailort/bindings/gstreamer/gst-hailo/network_group_handle.cpp b/hailort/libhailort/bindings/gstreamer/gst-hailo/network_group_handle.cpp
new file mode 100644 (file)
index 0000000..eec733b
--- /dev/null
@@ -0,0 +1,390 @@
+/*
+ * Copyright (c) 2021-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the LGPL 2.1 license (https://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+#include "network_group_handle.hpp"
+
+#include <sstream>
+#include <chrono>
+
+VDeviceManager NetworkGroupHandle::m_vdevice_manager;
+NetworkGroupConfigManager NetworkGroupHandle::m_net_group_config_manager;
+NetworkGroupActivationManager NetworkGroupHandle::m_net_group_activation_manager;
+
+Expected<std::shared_ptr<VDevice>> NetworkGroupHandle::create_vdevice(const std::string &device_id, uint16_t device_count, uint32_t vdevice_key)
+{
+    auto expected_device = m_vdevice_manager.create_vdevice(m_element, device_id, device_count, vdevice_key);
+    GST_CHECK_EXPECTED(expected_device, m_element, RESOURCE, "Failed creating vdevice, status = %d", expected_device.status());
+    return expected_device;
+}
+
+hailo_status NetworkGroupHandle::set_hef(const char *device_id, uint16_t device_count, uint32_t vdevice_key, const char *hef_path)
+{
+    if (0 == device_count) {
+        device_count = HAILO_DEFAULT_DEVICE_COUNT;
+    }
+
+    std::string device_id_str = (nullptr == device_id) ? "" : device_id;
+
+    auto vdevice = create_vdevice(device_id_str, device_count, vdevice_key);
+    GST_CHECK_EXPECTED_AS_STATUS(vdevice, m_element, RESOURCE, "Failed creating vdevice, status = %d", vdevice.status());
+    m_vdevice = vdevice.release();
+
+    // Setting m_shared_device_id only if a non-default vdevice_key or explicit device_id is given
+    if (!device_id_str.empty()) {
+        m_shared_device_id = device_id;
+    } else if (DEFAULT_VDEVICE_KEY != vdevice_key) {
+        m_shared_device_id = std::to_string(device_count) + "-" + std::to_string(vdevice_key);
+    } else {
+        m_shared_device_id = "";
+    }
+
+    auto hef = Hef::create(hef_path);
+    GST_CHECK_EXPECTED_AS_STATUS(hef, m_element, RESOURCE, "Failed reading hef file %s, status = %d", hef_path, hef.status());
+
+    m_hef = make_shared_nothrow<Hef>(std::move(hef.release()));
+    GST_CHECK(nullptr != m_hef, HAILO_OUT_OF_HOST_MEMORY, m_element, RESOURCE, "Allocating memory for HEF has failed!");
+
+    return HAILO_SUCCESS;
+}
+
+hailo_status NetworkGroupHandle::configure_network_group(const char *net_group_name, uint16_t batch_size)
+{
+    auto net_groups_params_map = get_configure_params(*m_hef, net_group_name, batch_size);
+    GST_CHECK_EXPECTED_AS_STATUS(net_groups_params_map, m_element, RESOURCE, "Failed getting configure params, status = %d", net_groups_params_map.status());
+
+    auto expected_cng = m_net_group_config_manager.configure_network_group(m_element, m_shared_device_id, net_group_name, batch_size, m_vdevice, m_hef, net_groups_params_map.value());
+    GST_CHECK_EXPECTED_AS_STATUS(expected_cng, m_element, RESOURCE, "Failed configuring network, status = %d", expected_cng.status());
+
+    m_cng = expected_cng.release();
+    m_net_group_name = net_group_name;
+    m_batch_size = batch_size;
+    return HAILO_SUCCESS;
+}
+
+Expected<std::pair<std::vector<InputVStream>, std::vector<OutputVStream>>> NetworkGroupHandle::create_vstreams(const char *network_name,
+    const std::vector<hailo_format_with_name_t> &output_formats)
+{
+    GST_CHECK(nullptr != network_name, make_unexpected(HAILO_INVALID_ARGUMENT), m_element, RESOURCE, "Got nullptr in network name!");
+
+    m_network_name = network_name;
+    hailo_status status = m_net_group_config_manager.add_network(m_network_name, m_element);
+    GST_CHECK(HAILO_SUCCESS == status, make_unexpected(status), m_element, RESOURCE,
+        "Inserting network name to configured networks has failed, status = %d", status);
+
+    auto input_params_map = m_cng->make_input_vstream_params(true, HAILO_FORMAT_TYPE_AUTO, HAILO_DEFAULT_VSTREAM_TIMEOUT_MS,
+        HAILO_DEFAULT_VSTREAM_QUEUE_SIZE, m_network_name);
+    GST_CHECK_EXPECTED(input_params_map, m_element, RESOURCE, "Failed making input vstream params, status = %d",
+        input_params_map.status());
+
+    auto input_vstreams = VStreamsBuilder::create_input_vstreams(*m_cng, input_params_map.release());
+    GST_CHECK_EXPECTED(input_vstreams, m_element, RESOURCE, "Failed creating input vstreams, status = %d", input_vstreams.status());
+
+    // TODO: HRT-4095
+    GST_CHECK(1 == input_vstreams->size(), make_unexpected(HAILO_INVALID_OPERATION), m_element, RESOURCE,
+        "hailosend element supports only HEFs with one input for now!");
+
+    auto output_params_map = m_cng->make_output_vstream_params(true, HAILO_FORMAT_TYPE_AUTO, HAILO_DEFAULT_VSTREAM_TIMEOUT_MS,
+        HAILO_DEFAULT_VSTREAM_QUEUE_SIZE, m_network_name);
+    GST_CHECK_EXPECTED(output_params_map, m_element, RESOURCE, "Failed making output vstream params, status = %d",
+        output_params_map.status());
+    
+    if (output_formats.size() > 0) {
+        std::unordered_map<std::string, hailo_format_t> output_formats_map;
+        for (const auto &format_with_name : output_formats) {
+            GST_CHECK(output_formats_map.find(format_with_name.name) == output_formats_map.end(), make_unexpected(HAILO_INVALID_ARGUMENT), m_element, RESOURCE,
+                "Got duplicate output format from event! (name = %s)", format_with_name.name);
+            GST_CHECK(output_params_map->find(format_with_name.name) != output_params_map->end(), make_unexpected(HAILO_INVALID_ARGUMENT),
+                m_element, RESOURCE, "Got unknown output format from event! (name = %s)", format_with_name.name);
+            output_formats_map[format_with_name.name] = format_with_name.format;
+        }
+        for (auto &vstream_params : output_params_map.value()) {
+            vstream_params.second.user_buffer_format = output_formats_map[vstream_params.first];
+        }
+    }
+
+    auto output_vstreams = VStreamsBuilder::create_output_vstreams(*m_cng, output_params_map.release());
+    GST_CHECK_EXPECTED(output_vstreams, m_element, RESOURCE, "Failed creating output vstreams, status = %d", output_vstreams.status());
+
+    return std::pair<std::vector<InputVStream>, std::vector<OutputVStream>>(
+        std::move(input_vstreams.release()), std::move(output_vstreams.release()));
+}
+
+Expected<NetworkGroupsParamsMap> NetworkGroupHandle::get_configure_params(Hef &hef, const char *net_group_name, uint16_t batch_size)
+{
+    auto params = hef.create_configure_params(HAILO_STREAM_INTERFACE_PCIE, net_group_name);
+    GST_CHECK_EXPECTED(params, m_element, RESOURCE, "Failed creating configure params, status = %d", params.status());
+    params->batch_size = batch_size;
+
+    NetworkGroupsParamsMap net_groups_params_map;
+    net_groups_params_map[net_group_name] = std::move(params.release());
+    return net_groups_params_map;
+}
+
+hailo_status NetworkGroupHandle::activate_network_group()
+{
+    auto expected_ang = m_net_group_activation_manager.activate_network_group(m_element, m_shared_device_id, m_net_group_name.c_str(), m_batch_size, m_cng);
+    GST_CHECK_EXPECTED_AS_STATUS(expected_ang, m_element, RESOURCE, "Failed activating network, status = %d", expected_ang.status());
+    m_ang = expected_ang.release();
+    return HAILO_SUCCESS;
+}
+
+hailo_status NetworkGroupHandle::abort_streams()
+{
+    if (nullptr == m_cng) {
+        return HAILO_SUCCESS;
+    }
+
+    hailo_status final_status = HAILO_SUCCESS;
+    auto input_streams = m_cng->get_input_streams_by_network(m_network_name);
+    GST_CHECK_EXPECTED_AS_STATUS(input_streams, m_element, RESOURCE, "Getting input streams by network name %s failed, status = %d",
+        m_network_name.c_str(), input_streams.status());
+
+    for (auto &input_stream : input_streams.value()) {
+        hailo_status status = input_stream.get().abort();
+        if (HAILO_SUCCESS != status) {
+            g_warning("Abort of input stream %s has failed, status = %d", input_stream.get().name().c_str(), status);
+            final_status = status;
+        }
+    }
+
+    auto output_streams = m_cng->get_output_streams_by_network(m_network_name);
+    GST_CHECK_EXPECTED_AS_STATUS(output_streams, m_element, RESOURCE, "Getting output streams by network name %s failed, status = %d",
+        m_network_name.c_str(), output_streams.status());
+
+    for (auto &output_stream : output_streams.value()) {
+        hailo_status status = output_stream.get().abort();
+        if (HAILO_SUCCESS != status) {
+            g_warning("Abort of output stream %s has failed, status = %d", output_stream.get().name().c_str(), status);
+            final_status = status;
+        }
+    }
+    return final_status;
+}
+
+Expected<bool> NetworkGroupHandle::remove_network_group()
+{
+    bool was_network_deactivated = false;
+
+    // If use count is 2, it means the only references to the activated network group is in the manager and the one here, meaning that we can clear it
+    // from the manager
+    if (m_ang.use_count() == 2) {
+        hailo_status status = m_net_group_activation_manager.remove_activated_network(m_shared_device_id, m_net_group_name.c_str(), m_batch_size);
+        GST_CHECK(HAILO_SUCCESS == status, make_unexpected(status), m_element, RESOURCE, "Cound not find activated network group! status = %d", status);
+
+        was_network_deactivated = true;
+    }
+
+    // Delete local activated network group
+    m_ang.reset();
+
+    return was_network_deactivated;
+}
+
+
+Expected<std::shared_ptr<VDevice>> VDeviceManager::create_vdevice(const void *element, const std::string &device_id, uint16_t device_count,
+    uint32_t vdevice_key)
+{
+    std::unique_lock<std::mutex> lock(m_mutex);
+    if (!device_id.empty()) {
+        return create_shared_vdevice(element, device_id);
+    }
+    if (DEFAULT_VDEVICE_KEY != vdevice_key) {
+        return create_shared_vdevice(element, device_count, vdevice_key);
+    }
+    return create_unique_vdevice(element, device_count);
+}
+
+Expected<std::shared_ptr<VDevice>> VDeviceManager::create_shared_vdevice(const void *element, const std::string &device_id)
+{
+    // If passing device_id, than device_count must be 1
+    const auto device_count = 1;
+
+    // If vdevice already exist, use it
+    auto found_vdevice = get_vdevice(device_id);
+    if (found_vdevice) {
+        return found_vdevice.release();
+    }
+
+    auto device_info_expected = Device::parse_pcie_device_info(device_id);
+    GST_CHECK_EXPECTED(device_info_expected, element, RESOURCE, "Failed parsing pcie device info, status = %d", device_info_expected.status());
+
+    hailo_vdevice_params_t params = {};
+    params.device_count = device_count;
+    params.device_infos = &(device_info_expected.value());
+    auto vdevice = VDevice::create(params);
+    GST_CHECK_EXPECTED(vdevice, element, RESOURCE, "Failed creating vdevice, status = %d", vdevice.status());
+    std::shared_ptr<VDevice> vdevice_ptr = std::move(vdevice.release());
+
+    m_shared_vdevices[device_id] = vdevice_ptr;
+    return vdevice_ptr;
+}
+
+Expected<std::shared_ptr<VDevice>> VDeviceManager::create_shared_vdevice(const void *element, uint16_t device_count, uint32_t vdevice_key)
+{
+    auto device_id = std::to_string(device_count) + "-" + std::to_string(vdevice_key);
+
+    // If vdevice already exist, use it
+    auto found_vdevice = get_vdevice(device_id);
+    if (found_vdevice) {
+        return found_vdevice.release();
+    }
+
+    hailo_vdevice_params_t params = {};
+    params.device_count = device_count;
+    params.device_infos = nullptr;
+    auto vdevice = VDevice::create(params);
+    GST_CHECK_EXPECTED(vdevice, element, RESOURCE, "Failed creating vdevice, status = %d", vdevice.status());
+    std::shared_ptr<VDevice> vdevice_ptr = std::move(vdevice.release());
+
+    m_shared_vdevices[device_id] = vdevice_ptr;
+    return vdevice_ptr;
+}
+
+Expected<std::shared_ptr<VDevice>> VDeviceManager::create_unique_vdevice(const void *element, uint16_t device_count)
+{
+    hailo_vdevice_params_t params = {};
+    params.device_count = device_count;
+    params.device_infos = nullptr;
+    auto vdevice = VDevice::create(params);
+    GST_CHECK_EXPECTED(vdevice, element, RESOURCE, "Failed creating vdevice, status = %d", vdevice.status());
+
+    // Unique vdevices are not saved in VDeviceManager, only in their hailonet
+    std::shared_ptr<VDevice> vdevice_ptr = std::move(vdevice.release());
+    m_unique_vdevices.push_back(vdevice_ptr);
+    return vdevice_ptr;
+}
+
+Expected<std::shared_ptr<VDevice>> VDeviceManager::get_vdevice(const std::string &device_id)
+{
+    auto found = m_shared_vdevices.find(device_id);
+    if (found == m_shared_vdevices.end()) {
+        return make_unexpected(HAILO_NOT_FOUND);
+    }
+    auto vdevice_cpy = found->second;
+    return vdevice_cpy;
+}
+
+Expected<std::shared_ptr<ConfiguredNetworkGroup>> NetworkGroupConfigManager::configure_network_group(const void *element, const std::string &device_id,
+    const char *network_group_name, uint16_t batch_size, std::shared_ptr<VDevice> &vdevice, std::shared_ptr<Hef> hef,
+    NetworkGroupsParamsMap &net_groups_params_map)
+{
+    std::unique_lock<std::mutex> lock(m_mutex);
+
+    std::shared_ptr<ConfiguredNetworkGroup> found_cng = get_configured_network_group(device_id, network_group_name, batch_size);
+    if (nullptr != found_cng) {
+        return found_cng;
+    }
+
+    auto network_group_list = vdevice->configure(*hef, net_groups_params_map);
+    GST_CHECK_EXPECTED(network_group_list, element, RESOURCE, "Failed configure device from hef, status = %d",
+        network_group_list.status());
+
+    m_configured_net_groups[get_configure_string(device_id, network_group_name, batch_size)] = network_group_list->at(0);
+
+    return std::move(network_group_list->at(0));
+}
+
+hailo_status NetworkGroupConfigManager::add_network(const std::string &network_name, const GstElement *owner_element)
+{
+    std::unique_lock<std::mutex> lock(m_mutex);
+
+    auto found = m_configured_networks.find(network_name);
+    GST_CHECK(found == m_configured_networks.end(), HAILO_INVALID_OPERATION, owner_element, RESOURCE,
+        "Network %s was already configured by %s!", network_name.c_str(), found->second.c_str());
+
+    m_configured_networks[network_name] = GST_ELEMENT_NAME(owner_element);
+    return HAILO_SUCCESS;
+}
+
+std::shared_ptr<ConfiguredNetworkGroup> NetworkGroupConfigManager::get_configured_network_group(const std::string &device_id,
+    const char *network_group_name, uint16_t batch_size)
+{
+    auto found = m_configured_net_groups.find(get_configure_string(device_id, network_group_name, batch_size));
+    if (found == m_configured_net_groups.end()) {
+        return nullptr;
+    }
+
+    return found->second;
+}
+
+std::string NetworkGroupConfigManager::get_configure_string(const std::string &device_id, const char *network_group_name, uint16_t batch_size)
+{
+    const char *EMPTY_FIELD = "NULL,";
+    std::ostringstream oss;
+
+    if (device_id.empty()) {
+        oss << EMPTY_FIELD;
+    } else {
+        oss << device_id << ",";
+    }
+
+    if (nullptr == network_group_name) {
+        oss << EMPTY_FIELD;
+    } else {
+        oss << network_group_name << ",";
+    }
+
+    oss << batch_size;
+    return oss.str();
+}
+
+Expected<std::shared_ptr<ActivatedNetworkGroup>> NetworkGroupActivationManager::activate_network_group(const void *element, const std::string &device_id,
+    const char *net_group_name, uint16_t batch_size, std::shared_ptr<ConfiguredNetworkGroup> cng)
+{
+    std::unique_lock<std::mutex> lock(m_mutex);
+
+    std::shared_ptr<ActivatedNetworkGroup> found_ang = get_activated_network_group(device_id, net_group_name, batch_size);
+    if (nullptr != found_ang) {
+        return found_ang;
+    }
+
+    auto activated_network_group = cng->activate();
+    GST_CHECK_EXPECTED(activated_network_group, element, RESOURCE, "Failed activating network group, status = %d",
+        activated_network_group.status());
+
+    std::shared_ptr<ActivatedNetworkGroup> ang = std::move(activated_network_group.release());
+    m_activated_net_groups[NetworkGroupConfigManager::get_configure_string(device_id, net_group_name, batch_size)] = ang;
+
+    return ang;
+}
+
+std::shared_ptr<ActivatedNetworkGroup> NetworkGroupActivationManager::get_activated_network_group(const std::string &device_id,
+    const char *net_group_name, uint16_t batch_size)
+{
+    auto found = m_activated_net_groups.find(NetworkGroupConfigManager::get_configure_string(device_id, net_group_name, batch_size));
+    if (found == m_activated_net_groups.end()) {
+        return nullptr;
+    }
+
+    return found->second;
+}
+
+hailo_status NetworkGroupActivationManager::remove_activated_network(const std::string &device_id, const char *net_group_name,
+    uint16_t batch_size)
+{
+    std::unique_lock<std::mutex> lock(m_mutex);
+
+    auto found = m_activated_net_groups.find(NetworkGroupConfigManager::get_configure_string(device_id, net_group_name, batch_size));
+    if (found == m_activated_net_groups.end()) {
+        return HAILO_NOT_FOUND;
+    }
+
+    m_activated_net_groups.erase(found);
+    return HAILO_SUCCESS;
+}
\ No newline at end of file
diff --git a/hailort/libhailort/bindings/gstreamer/gst-hailo/network_group_handle.hpp b/hailort/libhailort/bindings/gstreamer/gst-hailo/network_group_handle.hpp
new file mode 100644 (file)
index 0000000..b83dd24
--- /dev/null
@@ -0,0 +1,130 @@
+/*
+ * Copyright (c) 2021-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the LGPL 2.1 license (https://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+#ifndef _NETWORK_GROUP_HANDLE_HPP_
+#define _NETWORK_GROUP_HANDLE_HPP_
+
+#include "common.hpp"
+#include "hailo_events/hailo_events.hpp"
+#include "hailo/vdevice.hpp"
+
+#include <unordered_map>
+#include <mutex>
+
+
+class VDeviceManager final
+{
+public:
+    VDeviceManager() : m_shared_vdevices(), m_unique_vdevices() {}
+
+    Expected<std::shared_ptr<VDevice>> create_vdevice(const void *element, const std::string &device_id, uint16_t device_count, uint32_t vdevice_key);
+
+private:
+    Expected<std::shared_ptr<VDevice>> create_shared_vdevice(const void *element, const std::string &device_id);
+    Expected<std::shared_ptr<VDevice>> create_shared_vdevice(const void *element, uint16_t device_count, uint32_t vdevice_key);
+    Expected<std::shared_ptr<VDevice>> create_unique_vdevice(const void *element, uint16_t device_count);
+    Expected<std::shared_ptr<VDevice>> get_vdevice(const std::string &device_id);
+
+    /* Contains only the shared vdevices (either created by bdf, or with device-count && vdevice-key)
+       Keys are either "<BDF>" or "<device_count>-<vdevice_key>" */
+    std::unordered_map<std::string, std::shared_ptr<VDevice>> m_shared_vdevices;
+    std::vector<std::shared_ptr<VDevice>> m_unique_vdevices;
+    std::mutex m_mutex;
+};
+
+using network_name_t = std::string;
+using hailonet_name_t = std::string;
+
+class NetworkGroupConfigManager final
+{
+public:
+    NetworkGroupConfigManager() : m_configured_net_groups() {}
+    Expected<std::shared_ptr<ConfiguredNetworkGroup>> configure_network_group(const void *element, const std::string &device_id,
+        const char *network_group_name, uint16_t batch_size, std::shared_ptr<VDevice> &vdevice, std::shared_ptr<Hef> hef,
+        NetworkGroupsParamsMap &net_groups_params_map);
+    hailo_status add_network(const std::string &network_name, const GstElement *owner_element);
+    
+private:
+    static std::string get_configure_string(const std::string &device_id, const char *network_group_name, uint16_t batch_size);
+    friend class NetworkGroupActivationManager;
+
+    std::shared_ptr<ConfiguredNetworkGroup> get_configured_network_group(const std::string &device_id, const char *net_group_name,
+        uint16_t batch_size);
+
+    // TODO: change this map to store only the shared network_groups (used by multiple hailonets with the same vdevices)
+    std::unordered_map<std::string, std::shared_ptr<ConfiguredNetworkGroup>> m_configured_net_groups;
+    std::unordered_map<network_name_t, hailonet_name_t> m_configured_networks;
+    std::mutex m_mutex;
+};
+
+class NetworkGroupActivationManager final
+{
+public:
+    NetworkGroupActivationManager() : m_activated_net_groups() {}
+    Expected<std::shared_ptr<ActivatedNetworkGroup>> activate_network_group(const void *element, const std::string &device_id,
+        const char *net_group_name, uint16_t batch_size, std::shared_ptr<ConfiguredNetworkGroup> cng);
+    hailo_status remove_activated_network(const std::string &device_id, const char *net_group_name, uint16_t batch_size);
+    
+private:
+    std::shared_ptr<ActivatedNetworkGroup> get_activated_network_group(const std::string &device_id,  const char *net_group_name,
+        uint16_t batch_size);
+
+    // TODO: change this map to store only the shared network_groups (used by multiple hailonets with the same vdevices)
+    std::unordered_map<std::string, std::shared_ptr<ActivatedNetworkGroup>> m_activated_net_groups;
+    std::mutex m_mutex;
+};
+
+class NetworkGroupHandle final
+{
+public:
+    NetworkGroupHandle(const GstElement *element) : m_element(element), m_shared_device_id(), m_net_group_name(), m_network_name(), m_batch_size(0),
+        m_vdevice(nullptr), m_hef(nullptr), m_cng(nullptr), m_ang(nullptr) {}
+
+    hailo_status set_hef(const char *device_id, uint16_t device_count, uint32_t vdevice_key, const char *hef_path);
+    hailo_status configure_network_group(const char *net_group_name, uint16_t batch_size);
+    Expected<std::pair<std::vector<InputVStream>, std::vector<OutputVStream>>> create_vstreams(const char *network_name,
+        const std::vector<hailo_format_with_name_t> &output_formats);
+    hailo_status activate_network_group();
+    hailo_status abort_streams();
+    Expected<bool> remove_network_group();
+
+    std::shared_ptr<Hef> hef()
+    {
+        return m_hef;
+    }
+
+private:
+    Expected<NetworkGroupsParamsMap> get_configure_params(Hef &hef, const char *net_group_name, uint16_t batch_size);
+    Expected<std::shared_ptr<VDevice>> create_vdevice(const std::string &device_id, uint16_t device_count, uint32_t vdevice_key);
+
+    static VDeviceManager m_vdevice_manager;
+    static NetworkGroupConfigManager m_net_group_config_manager;
+    static NetworkGroupActivationManager m_net_group_activation_manager;
+    const GstElement *m_element;
+    std::string m_shared_device_id;
+    std::string m_net_group_name;
+    std::string m_network_name;
+    uint16_t m_batch_size;
+    std::shared_ptr<VDevice> m_vdevice;
+    std::shared_ptr<Hef> m_hef;
+    std::shared_ptr<ConfiguredNetworkGroup> m_cng;
+    std::shared_ptr<ActivatedNetworkGroup> m_ang;
+};
+
+#endif /* _NETWORK_GROUP_HANDLE_HPP_ */
\ No newline at end of file
diff --git a/hailort/libhailort/bindings/python/CMakeLists.txt b/hailort/libhailort/bindings/python/CMakeLists.txt
new file mode 100644 (file)
index 0000000..febd4f0
--- /dev/null
@@ -0,0 +1 @@
+add_subdirectory(src)
diff --git a/hailort/libhailort/bindings/python/__init__.py b/hailort/libhailort/bindings/python/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/hailort/libhailort/bindings/python/examples/hef_infer_pipeline_vstream.py b/hailort/libhailort/bindings/python/examples/hef_infer_pipeline_vstream.py
new file mode 100644 (file)
index 0000000..b01c038
--- /dev/null
@@ -0,0 +1,30 @@
+from hailo_platform import (HEF, PcieDevice, ConfigureParams, InferVStreams, InputVStreamParams,
+    OutputVStreamParams, FormatType)
+from hailo_platform.drivers.hailort.pyhailort import HailoStreamInterface
+import numpy as np
+import argparse
+
+def parse_args():
+    parser = argparse.ArgumentParser(description='Streaming API example')
+    parser.add_argument('hef_path', type=str, help='Path of the HEF to run')
+    parser.add_argument('-n', '--num-frames', type=int, default=10, help='Number of frames to send')
+    return parser.parse_args()
+
+def main():
+    args = parse_args()
+    with PcieDevice() as target:
+        hef = HEF(args.hef_path)
+        configure_params = ConfigureParams.create_from_hef(hef, interface=HailoStreamInterface.PCIe)
+        network_groups = target.configure(hef, configure_params)
+        network_group = network_groups[0]
+        network_group_params = network_group.create_params()
+        input_vstreams_params = InputVStreamParams.make(network_group, quantized=False, format_type=FormatType.FLOAT32)
+        output_vstreams_params = OutputVStreamParams.make(network_group, quantized=True, format_type=FormatType.UINT8)
+        with InferVStreams(network_group, input_vstreams_params, output_vstreams_params) as infer_pipeline:
+            input_names_to_shape = {vstream_info.name: vstream_info.shape for vstream_info in hef.get_input_vstream_infos()}
+            input_data = {name : 1 + np.ndarray([args.num_frames] + list(shape), dtype=np.float32) for name, shape in input_names_to_shape.items()}
+            with network_group.activate(network_group_params):
+                _ = infer_pipeline.infer(input_data)
+
+if __name__ == '__main__':
+    main()
\ No newline at end of file
diff --git a/hailort/libhailort/bindings/python/examples/hef_infer_virtual_stream.py b/hailort/libhailort/bindings/python/examples/hef_infer_virtual_stream.py
new file mode 100644 (file)
index 0000000..a447f39
--- /dev/null
@@ -0,0 +1,61 @@
+import argparse
+from multiprocessing import Process
+
+import numpy as np
+from hailo_platform import (HEF, PcieDevice, HailoStreamInterface, ConfigureParams, InputVStreamParams, InputVStreams,
+                            OutputVStreamParams, OutputVStreams)
+
+def send(configured_network, num_frames):
+    vstreams_params = InputVStreamParams.make(configured_network)
+    configured_network.wait_for_activation(1000)
+    with InputVStreams(configured_network, vstreams_params) as vstreams:
+        vstream_to_buffer = {vstream: np.ndarray([1] + list(vstream.shape), dtype=vstream.dtype) for vstream in vstreams}
+        for _ in range(num_frames):
+            for vstream, buff in vstream_to_buffer.items():
+                vstream.send(buff)
+        # Flushing is not mandatory here
+        for vstream in vstreams:
+            vstream.flush()
+
+def recv(configured_network, vstreams_params, num_frames):
+    configured_network.wait_for_activation(1000)
+    with OutputVStreams(configured_network, vstreams_params) as vstreams:
+        for _ in range(num_frames):
+            for vstream in vstreams:
+                _ = vstream.recv()
+
+def recv_all(configured_network, num_frames):
+    vstreams_params_groups = OutputVStreamParams.make_groups(configured_network)
+    recv_procs = []
+    for vstreams_params in vstreams_params_groups:
+        proc = Process(target=recv, args=(configured_network, vstreams_params, num_frames))
+        proc.start()
+        recv_procs.append(proc)
+
+    for proc in recv_procs:
+        proc.join()
+
+def parse_args():
+    parser = argparse.ArgumentParser(description='vStream API example')
+    parser.add_argument('hef_path', type=str, help='Path of the HEF to run')
+    parser.add_argument('-n', '--num-frames', type=int, default=1000, help='Number of frames to send')
+    return parser.parse_args()
+
+def main():
+    args = parse_args()
+    hef = HEF(args.hef_path)
+
+    with PcieDevice() as device:
+        configure_params = ConfigureParams.create_from_hef(hef, interface=HailoStreamInterface.PCIe)
+        network_group = device.configure(hef, configure_params)[0]
+        network_group_params = network_group.create_params()
+        send_process = Process(target=send, args=(network_group, args.num_frames))
+        recv_process = Process(target=recv_all, args=(network_group, args.num_frames))
+        recv_process.start()
+        send_process.start()
+        with network_group.activate(network_group_params):
+            send_process.join()
+            recv_process.join()
+
+if __name__ == '__main__':
+    main()
diff --git a/hailort/libhailort/bindings/python/platform/hailo_platform/__init__.py b/hailort/libhailort/bindings/python/platform/hailo_platform/__init__.py
new file mode 100644 (file)
index 0000000..1c6ca2e
--- /dev/null
@@ -0,0 +1,83 @@
+#!/usr/bin/env python
+import os
+import sys
+import pathlib
+import pprint
+
+from hailo_platform.common.paths_manager.version import get_version
+from hailo_platform.common.paths_manager.paths import PackingInfo, PackingStatus
+
+class MissingPyHRTLib(Exception):
+    pass
+
+
+# This hack checks which modules the user has on his computer.
+# packing status set to "packed_client" if it can't import the PLATFORM_INTERNALS module
+# it set the packing status to "standalone_platform" if it can't import the SDK_COMMON module
+
+try:
+    import hailo_platform_internals  # noqa F401
+except ImportError:
+    PackingInfo().status = PackingStatus.packed_client
+
+try:
+    import hailo_sdk_common # noqa F401
+except:
+    PackingInfo().status = PackingStatus.standalone_platform
+
+
+
+# This hack only affects internals users with hailo_validation installed.
+# It changes the packing status to PACKED if it thinks SDK was installed from
+# wheel.
+try:
+    import hailo_validation  # noqa F401
+    if 'site-packages/hailo_sdk' in __path__[0] :
+        PackingInfo().status = PackingStatus.packed_client
+except ImportError:
+    pass
+
+
+# Must appear before other imports:
+def join_drivers_path(path):
+    _ROOT = os.path.abspath(os.path.dirname(os.path.dirname(__file__)))
+    return os.path.join(_ROOT, 'hailo_platform', 'drivers', path)
+
+
+from hailo_platform.tools.udp_rate_limiter import UDPRateLimiter
+from hailo_platform.drivers.hw_object import PcieDevice, EthernetDevice
+from hailo_platform.drivers.hailo_controller.power_measurement import (DvmTypes,
+                                                                       PowerMeasurementTypes,
+                                                                       SamplingPeriod, AveragingFactor)
+from hailo_platform.drivers.hailort.pyhailort import (HEF, ConfigureParams,
+                                                      FormatType, FormatOrder,
+                                                      MipiDataTypeRx, MipiPixelsPerClock,
+                                                      MipiClockSelection, MipiIspImageInOrder,
+                                                      MipiIspImageOutDataType, IspLightFrequency, HailoPowerMode,
+                                                      Endianness, HailoStreamInterface,
+                                                      InputVStreamParams, OutputVStreamParams,
+                                                      InputVStreams, OutputVStreams,
+                                                      InferVStreams, HailoStreamDirection, HailoFormatFlags, HailoCpuId, VDevice)
+
+def _verify_pyhailort_lib_exists():
+    python_version = "".join(str(i) for i in sys.version_info[:2])
+    lib_extension = {
+        "posix": "so",
+        "nt": "pyd",  # Windows
+    }[os.name]
+
+    path = f"{__path__[0]}/drivers/hailort/"
+    if next(pathlib.Path(path).glob(f"_pyhailort*.{lib_extension}"), None) is None:
+        raise MissingPyHRTLib(f"{path} should include a _pyhailort library (_pyhailort*{python_version}*.{lib_extension}). Includes: {pprint.pformat(list(pathlib.Path(path).iterdir()))}")
+
+_verify_pyhailort_lib_exists()
+
+
+__version__ = get_version('hailo_platform')
+__all__ = ['EthernetDevice', 'DvmTypes', 'PowerMeasurementTypes',
+           'SamplingPeriod', 'AveragingFactor', 'UDPRateLimiter', 'PcieDevice', 'HEF',
+           'ConfigureParams', 'FormatType', 'FormatOrder', 'MipiDataTypeRx', 'MipiPixelsPerClock', 'MipiClockSelection',
+           'MipiIspImageInOrder', 'MipiIspImageOutDataType', 'join_drivers_path', 'IspLightFrequency', 'HailoPowerMode',
+           'Endianness', 'HailoStreamInterface', 'InputVStreamParams', 'OutputVStreamParams',
+           'InputVStreams', 'OutputVStreams', 'InferVStreams', 'HailoStreamDirection', 'HailoFormatFlags', 'HailoCpuId',
+           'VDevice']
diff --git a/hailort/libhailort/bindings/python/platform/hailo_platform/common/__init__.py b/hailort/libhailort/bindings/python/platform/hailo_platform/common/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/hailort/libhailort/bindings/python/platform/hailo_platform/common/compatibility/__init__.py b/hailort/libhailort/bindings/python/platform/hailo_platform/common/compatibility/__init__.py
new file mode 100644 (file)
index 0000000..3dd945d
--- /dev/null
@@ -0,0 +1,27 @@
+#!/usr/bin/env python
+import six
+from io import IOBase
+
+# Based on: https://portingguide.readthedocs.io/en/latest/builtins.html#removed-file
+try:
+    # Python 2
+    file_types = (file, IOBase,)
+except NameError:
+    # "file" isn't a built-in type in Python 3
+    file_types = (IOBase,)
+
+# Exporting types and functions from six
+string_types = six.string_types
+integer_types = six.integer_types
+class_types = six.class_types
+text_type = six.text_type
+binary_type = six.binary_type
+
+def ensure_binary(s, encoding='utf-8', errors='strict'):
+    return six.ensure_binary(s, encoding=encoding, errors=errors)
+
+def ensure_str(s, encoding='utf-8', errors='strict'):
+    return six.ensure_str(s, encoding=encoding, errors=errors)
+
+def ensure_text(s, encoding='utf-8', errors='strict'):
+    return six.ensure_text(s, encoding=encoding, errors=errors)
\ No newline at end of file
diff --git a/hailort/libhailort/bindings/python/platform/hailo_platform/common/logger/__init__.py b/hailort/libhailort/bindings/python/platform/hailo_platform/common/logger/__init__.py
new file mode 100644 (file)
index 0000000..8b13789
--- /dev/null
@@ -0,0 +1 @@
+
diff --git a/hailort/libhailort/bindings/python/platform/hailo_platform/common/logger/logger.py b/hailort/libhailort/bindings/python/platform/hailo_platform/common/logger/logger.py
new file mode 100644 (file)
index 0000000..a368291
--- /dev/null
@@ -0,0 +1,67 @@
+#!/usr/bin/env python
+__all__ = ['default_logger', 'set_default_logger_level', 'create_custom_logger', 'create_server_logger', 'SERVER_LOG_PATH']
+
+import os
+import sys
+import logging
+
+import verboselogs
+
+SERVER_LOG_DIR = "/tmp/"
+SERVER_LOG_PATH = os.path.join(SERVER_LOG_DIR, "hailo_server{}.log")
+
+
+def _create_logger(level="INFO", logger_path="./pyhailort.log", fmt=None, console=True):
+    """
+    Creates a logging object and returns it
+    """
+    logger_name = "".join(os.path.basename(logger_path).split(".")[:-1])
+    logger = verboselogs.VerboseLogger(logger_name)
+    logger.setLevel(level)
+
+    # create the logging file handler
+    fh = logging.FileHandler(logger_path)
+    if fmt is None:
+        fmt = "%(asctime)s - %(levelname)s - %(filename)s:%(lineno)s - %(message)s"
+    formatter = logging.Formatter(fmt)
+    fh.setFormatter(formatter)
+    if console:
+        console_fh = logging.StreamHandler(sys.stdout)
+        logger.addHandler(console_fh)
+    logger.addHandler(fh)
+    return logger
+
+
+_g_logger = None
+
+
+def create_server_logger(port, console=False):
+    global _g_logger
+    if not os.path.exists(SERVER_LOG_DIR):
+        os.mkdir(SERVER_LOG_DIR)
+    _g_logger = _create_logger("INFO", SERVER_LOG_PATH.format(port), console=console)
+    _g_logger.verbose("Created the log, pid:{}".format(os.getpid()))
+
+
+def default_logger():
+    global _g_logger
+    if _g_logger is None:
+        _g_logger = _create_logger()
+    return _g_logger
+
+
+def create_custom_logger(log_path, fmt=None, console=False):
+    """Returns a logger to the specified path, creating it if needed."""
+    return _create_logger(logger_path=log_path, fmt=fmt, console=console)
+
+
+def set_default_logger_level(level):
+    """
+    Set log level of the default log.
+    Should only be called from initialization code to avoid log level collisions.
+    """
+    if level == "ERROR":
+        _g_logger.verbose("Logger verbosity has been decreased to {}s only ".format(level.lower()))
+    else:
+        _g_logger.verbose("Logger verbosity is: {}".format(level))
+    _g_logger.setLevel(level)
diff --git a/hailort/libhailort/bindings/python/platform/hailo_platform/common/paths_manager/__init__.py b/hailort/libhailort/bindings/python/platform/hailo_platform/common/paths_manager/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/hailort/libhailort/bindings/python/platform/hailo_platform/common/paths_manager/config.py b/hailort/libhailort/bindings/python/platform/hailo_platform/common/paths_manager/config.py
new file mode 100644 (file)
index 0000000..5e1d7fc
--- /dev/null
@@ -0,0 +1,52 @@
+#!/usr/bin/env python
+import os
+from configparser import ConfigParser
+from hailo_platform.common.paths_manager.paths import PackingInfo, PackingStatus, SDKPaths
+from hailo_platform.paths_manager.paths import PlatformPaths
+
+
+class ConfigFileNotFoundException(Exception):
+    pass
+
+
+def get_home_hailo_dir():
+    return os.path.expanduser('~/.hailo')
+
+def get_parsed_config_from_path(config_path=None):
+    actual_config_path = _get_config_path_with_default(config_path)
+    config = ConfigParser()
+    with open(actual_config_path, 'r') as config_file:
+        config.read_file(config_file)
+    return config
+
+
+def _get_config_path_with_default(config_path=None):
+    if config_path is not None and os.path.isfile(config_path):
+        return config_path
+    default_path = _get_config_path()
+    if os.path.isfile(default_path):
+        return default_path
+    raise ConfigFileNotFoundException('Could not find configuration file at default path: {}.'.format(default_path))
+
+
+def _get_config_path():
+    config_file_name = 'config'
+
+    if PackingInfo().status in [PackingStatus.unpacked]:
+        full_path = os.path.join(SDKPaths().join_sdk('../'), config_file_name)
+        if os.path.exists(full_path):
+            return full_path
+        #This is a CI nightly workaround because we are in unpack mode but installing whl's 
+        #In this case SDKPaths() is inside the site packages and not the sdk root. the workaround is to look for local dir
+        elif os.path.exists(config_file_name):
+            return config_file_name
+
+    elif PackingInfo().status in [PackingStatus.standalone_platform]:
+        full_path = os.path.join(PlatformPaths().join_platform('../../'), config_file_name)
+        if os.path.exists(full_path) and os.path.isfile(full_path):
+            return full_path
+
+    elif os.path.exists(config_file_name):
+        return config_file_name
+
+    return os.path.join(get_home_hailo_dir(), config_file_name)
diff --git a/hailort/libhailort/bindings/python/platform/hailo_platform/common/paths_manager/paths.py b/hailort/libhailort/bindings/python/platform/hailo_platform/common/paths_manager/paths.py
new file mode 100644 (file)
index 0000000..3dada52
--- /dev/null
@@ -0,0 +1,207 @@
+#!/usr/bin/env python
+from builtins import object
+import os
+import shutil
+import copy
+from enum import Enum
+
+
+from hailo_platform.common.logger.logger import default_logger
+from future.utils import with_metaclass
+
+logger = default_logger()
+
+
+class ConfigStageNotSetException(Exception):
+    pass
+
+class PackagingException(Exception):
+    pass
+
+class Singleton(type):
+    _instances = {}
+
+    def __call__(cls, *args, **kwargs):
+        if cls not in cls._instances:
+            cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs)
+        return cls._instances[cls]
+
+
+class PackingStatus(Enum):
+    unpacked = 'unpacked'
+    packed_server = 'packed_server'
+    packed_client = 'packed_client'
+    standalone_platform = 'standalone_platform'
+
+
+class PackingInfo(with_metaclass(Singleton, object)):
+    def __init__(self):
+        self._status = PackingStatus.unpacked
+        self._has_graphviz = True
+
+    def is_packed(self):
+        return self._status not in [PackingStatus.unpacked]
+
+    @property
+    def status(self):
+        return self._status
+
+    @status.setter
+    def status(self, val):
+        self._status = val
+
+    @property
+    def has_graphviz(self):
+        return self._has_graphviz
+
+    @has_graphviz.setter
+    def has_graphviz(self, val):
+        self._has_graphviz = val
+
+
+class SDKPaths(with_metaclass(Singleton, object)):
+    DEFAULT_BUILD_DIR = 'build'
+
+    def __init__(self):
+        self._build_dir_name = type(self).DEFAULT_BUILD_DIR
+        self._build_dir_path = '.'
+        self._custom_build_dir = None
+
+    @property
+    def _sdk_path(self):
+        packaging_status = PackingInfo().status
+        if packaging_status == PackingStatus.packed_server:
+            import hailo_sdk_common
+            return os.path.dirname(hailo_sdk_common.__path__[0])
+        if packaging_status == PackingStatus.packed_client:
+            return ''
+        if packaging_status == PackingStatus.standalone_platform:
+            raise PackagingException(
+                'the packaging status is \'standalone_platform\', and there was a call to a sdk method')
+        import hailo_sdk_common
+        return os.path.join(os.path.dirname(os.path.dirname(hailo_sdk_common.__path__[0])), 'sdk_server')
+
+    @property
+    def custom_build_dir(self):
+        return self._custom_build_dir
+
+    @custom_build_dir.setter
+    def custom_build_dir(self, custom_build_dir):
+        self._custom_build_dir = custom_build_dir
+
+    def join_sdk(self, path):
+        return os.path.join(self._sdk_path, path)
+
+    def join_sdk_common(self, path):
+        import hailo_sdk_common
+        if PackingInfo().status == PackingStatus.packed_server:
+            return self.join_sdk(os.path.join('hailo_sdk_common', path))
+        return os.path.join(os.path.abspath(hailo_sdk_common.__path__[0]), path)
+
+    def set_client_build_dir_path(self):
+        if PackingInfo().status == PackingStatus.unpacked:
+            self._build_dir_path = '../sdk_client'
+
+    def set_server_build_dir_path(self):
+        if PackingInfo().status == PackingStatus.unpacked:
+            self._build_dir_path = '../sdk_server'
+
+    def set_build_dir(self, build_dir_name=None, clean=False):
+        self._build_dir_name = build_dir_name if build_dir_name is not None else type(self).DEFAULT_BUILD_DIR
+        logger.debug('Build dir name: {}'.format(self._build_dir_name))
+        build_dir = self.build_dir
+        if os.path.exists(build_dir):
+            if clean:
+                logger.debug('Deleting build dir : {}'.format(build_dir))
+                shutil.rmtree(build_dir)
+                self._make_build_dir(build_dir)
+            return
+        self._make_build_dir(build_dir)
+
+    @property
+    def build_dir(self):
+        if self._custom_build_dir:
+            return self._custom_build_dir
+        return os.path.join(self._sdk_path, self._build_dir_path, self._build_dir_name)
+
+    def join_build_sdk(self, path):
+        build_dir = self.build_dir
+        if os.path.exists(build_dir):
+            return os.path.join(build_dir, path)
+        logger.debug('Creating build dir : {}'.format(build_dir))
+        self._make_build_dir(build_dir)
+        return os.path.join(build_dir, path)
+
+    def _make_build_dir(self, build_dir):
+        os.makedirs(build_dir)
+
+
+class BaseConfigDirs(object):
+
+    DIRS_BUILD_ONLY = {
+        'outputs_dir': ['outputs'],
+        'bin_dir': ['bin'],
+        'weights_dir': ['data', 'weights'],
+        'inputs_dir': ['data', 'inputs'],
+    }
+
+    DIRS_SDK_ONLY = {}
+
+    DIRS_BOTH = {}
+
+    def __init__(self, hw_arch):
+        self._hw_arch = hw_arch.name
+        self._paths = SDKPaths()
+        self._dirs = {}
+        for d in [type(self).DIRS_BUILD_ONLY, type(self).DIRS_SDK_ONLY, type(self).DIRS_BOTH]:
+            self._dirs.update(self._format_dirs(d))
+
+    def get_dir(self, name, in_build=True):
+        return self._join_base(self._dirs[name], in_build)
+
+    def _format_dirs(self, input_dirs):
+        result = {}
+        for name, dir_path in input_dirs.items():
+            result[name] = os.path.join(*dir_path).format(hw_arch=self._hw_arch)
+        return result
+
+    def _join_base(self, path, in_build=True):
+        base_dir = self._paths.build_dir if in_build else 'microcode'
+        whole_path = os.path.join(base_dir, path)
+        return self._paths.join_sdk(whole_path)
+
+    @property
+    def build_dirs_keys(self):
+        return list(type(self).DIRS_BUILD_ONLY.keys()) + list(type(self).DIRS_BOTH.keys())
+
+
+class BaseConfigPaths(BaseConfigDirs):
+
+    PATHS = {
+        'bin': ['{bin_dir}', '{network_name}.mem'],
+    }
+
+    def __init__(self, hw_arch, model_name):
+        super(BaseConfigPaths, self).__init__(hw_arch)
+        self._model_name = model_name
+        self._stage = None
+        self._stage_only = False
+
+    def set_stage(self, stage, stage_only=False):
+        self._stage = stage
+        self._stage_only = stage_only
+
+    def get_path(self, path_name, in_build=True):
+        template = os.path.join(*type(self).PATHS[path_name])
+        if self._stage is None and '{network_name}' in template:
+            raise ConfigStageNotSetException('Set the stage before trying to get paths')
+        format_dict = copy.deepcopy(self._dirs)
+        format_dict['model_name'] = self._model_name
+        if self._stage_only:
+            format_dict['network_name'] = self._stage
+        else:
+            format_dict['network_name'] = '{model_name}.{stage}'.format(
+                model_name=self._model_name, stage=self._stage
+            )
+        config_path = template.format(**format_dict)
+        return self._join_base(config_path, in_build)
diff --git a/hailort/libhailort/bindings/python/platform/hailo_platform/common/paths_manager/version.py b/hailort/libhailort/bindings/python/platform/hailo_platform/common/paths_manager/version.py
new file mode 100644 (file)
index 0000000..04d40ee
--- /dev/null
@@ -0,0 +1,15 @@
+#!/usr/bin/env python
+
+def get_version(package_name):
+    # See: https://packaging.python.org/guides/single-sourcing-package-version/ (Option 5)
+    # We assume that the installed package is actually the same one we import. This assumption may
+    # break in some edge cases e.g. if the user modifies sys.path manually.
+
+    # hailo_platform package has been renamed to hailort, but the import is still hailo_platform
+    if package_name == "hailo_platform":
+        package_name = "hailort"
+    try:
+        import pkg_resources
+        return pkg_resources.get_distribution(package_name).version
+    except:
+        return 'unknown'
diff --git a/hailort/libhailort/bindings/python/platform/hailo_platform/common/targets/__init__.py b/hailort/libhailort/bindings/python/platform/hailo_platform/common/targets/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/hailort/libhailort/bindings/python/platform/hailo_platform/common/targets/inference_targets.py b/hailort/libhailort/bindings/python/platform/hailo_platform/common/targets/inference_targets.py
new file mode 100644 (file)
index 0000000..80335b4
--- /dev/null
@@ -0,0 +1,111 @@
+#!/usr/bin/env python
+from builtins import object
+import json
+
+from contextlib2 import contextmanager
+
+
+class InferenceTargetException(Exception):
+    """Raised when an error related to the inference target has occurred."""
+    pass
+
+
+class InferenceTargets(object):
+    """Enum-like class with all inference targets supported by the Hailo SDK.
+    See the classes themselves for details about each target.
+    """
+    UNINITIALIZED = 'uninitialized'
+    SDK_NATIVE = 'sdk_native'
+    SDK_NATIVE_CLIPPED = 'sdk_native_clipped'
+    SDK_NUMERIC = 'sdk_numeric'
+    SDK_DEBUG_PRECISE_NUMERIC = 'sdk_debug_precise_numeric'
+    SDK_PARTIAL_NUMERIC = 'sdk_partial_numeric'
+    SDK_FINE_TUNE = 'sdk_fine_tune'
+    SDK_MIXED = 'sdk_mixed'
+    HW_SIMULATION = 'hw_sim'
+    HW_SIMULATION_MULTI_CLUSTER = 'hw_sim_mc'
+    FPGA = 'fpga'
+    UDP_CONTROLLER = 'udp'
+    PCIE_CONTROLLER = 'pcie'
+    HW_DRY = 'hw_dry'
+    HW_DRY_UPLOAD = 'hw_dry_upload'
+    UV_WORKER = 'uv'
+    DANNOX = 'dannox'
+
+
+class InferenceObject(object):
+    """Represents a target that can run models inference. The target can be either software based
+    (eventually running on CPU/GPU), or Hailo hardware based.
+
+    .. note:: This class should not be used directly. Use only its inherited classes.
+    """
+    NAME = InferenceTargets.UNINITIALIZED
+    IS_NUMERIC = False
+    IS_HARDWARE = False
+    IS_SIMULATION = False
+
+    def __new__(cls, *args, **kwargs):
+        if cls.NAME == InferenceTargets.UNINITIALIZED:
+            raise InferenceTargetException(
+                '{} is an abstract target and cannot be used directly.'.format(cls.__name__))
+        # object's __new__() takes no parameters
+        return super(type(cls), cls).__new__(cls)
+
+    def __init__(self):
+        """Inference object constructor."""
+        self._is_device_used = False
+
+    def __eq__(self, other):
+        return type(self).NAME == other
+
+    # TODO: Required for Python2 BW compatibility (SDK-10038)
+    # This impl' comes by default in Python3
+    def __ne__(self, other):
+        return not self.__eq__(other)
+
+    @contextmanager
+    def use_device(self, *args, **kwargs):
+        """A context manager that should wrap any usage of the target."""
+        self._is_device_used = True
+        yield
+        self._is_device_used = False
+
+    @property
+    def name(self):
+        """str: The name of this target. Valid values are defined by
+        :class:`InferenceObject <hailo_sdk_common.targets.inference_targets.InferenceTargets>`.
+        """
+        return type(self).NAME
+
+    @property
+    def is_numeric(self):
+        """bool: Determines whether this target is working in numeric mode.
+        """
+        return type(self).IS_NUMERIC
+
+    @property
+    def is_hardware(self):
+        """bool: Determines whether this target runs on a physical hardware device.
+        """
+        return type(self).IS_HARDWARE
+
+    @property
+    def is_simulation(self):
+        """bool: Determines whether this target is used for HW simulation.
+        """
+        return type(self).IS_SIMULATION
+
+    def _get_json_dict(self):
+        json_dict = {'name': self.name,
+                     'is_numeric': self.is_numeric,
+                     'is_hardware': self.is_hardware,
+                     'is_simulation': self.is_simulation}
+        return json_dict
+
+    def to_json(self):
+        """Get a JSON representation of this object.
+
+        Returns:
+            str: A JSON dump.
+        """
+        return json.dumps(self._get_json_dict())
diff --git a/hailort/libhailort/bindings/python/platform/hailo_platform/common/tools/__init__.py b/hailort/libhailort/bindings/python/platform/hailo_platform/common/tools/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/hailort/libhailort/bindings/python/platform/hailo_platform/common/tools/cmd_utils/__init__.py b/hailort/libhailort/bindings/python/platform/hailo_platform/common/tools/cmd_utils/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/hailort/libhailort/bindings/python/platform/hailo_platform/common/tools/cmd_utils/base_utils.py b/hailort/libhailort/bindings/python/platform/hailo_platform/common/tools/cmd_utils/base_utils.py
new file mode 100644 (file)
index 0000000..911ae3d
--- /dev/null
@@ -0,0 +1,55 @@
+#!/usr/bin/env python
+from __future__ import print_function
+from builtins import object
+import subprocess
+
+from hailo_platform.common.logger.logger import default_logger
+
+
+class CmdUtilsBaseUtilError(Exception):
+    pass
+
+
+class HailortCliUtilError(CmdUtilsBaseUtilError):
+    pass
+
+
+# Note: CmdUtilsBaseUtil and CmdUtilsBaseUtilError are external dependencies in phase2-sdk/demos repo; don't change!
+class CmdUtilsBaseUtil(object):
+    def __init__(self, args_parser):
+        pass
+
+
+class Helper(CmdUtilsBaseUtil):
+    def __init__(self, commands):
+        self._commands = commands
+
+    def __call__(self, parser):
+        parser.set_defaults(func=self.run)
+
+    def run(self, args):
+        for command_name, (help_, _) in self._commands.items():
+            print("{} - {}".format(command_name, help_))
+
+
+class HailortCliUtil(CmdUtilsBaseUtil):
+    def __init__(self, args_parser, command):
+        self._command = command
+        self._logger = default_logger()
+
+    @classmethod
+    def _run_hailortcli(cls, binary_path, command, command_args):
+        cmd_args = [binary_path, command] + command_args
+
+        process = subprocess.Popen(cmd_args)
+        try:
+            _ = process.communicate()
+            return process.returncode
+        except KeyboardInterrupt:
+            process.terminate()
+            raise HailortCliUtilError('hailo has been interrupted by the user')
+
+    def run(self, argv):
+        hailortcli_cmd = 'hailortcli'
+        self._logger.info('(hailo) Running command \'{}\' with \'hailortcli\''.format(self._command))
+        return self._run_hailortcli(hailortcli_cmd, self._command, argv)
diff --git a/hailort/libhailort/bindings/python/platform/hailo_platform/common/tools/cmd_utils/hailo_device_utils.py b/hailort/libhailort/bindings/python/platform/hailo_platform/common/tools/cmd_utils/hailo_device_utils.py
new file mode 100644 (file)
index 0000000..fd33bb1
--- /dev/null
@@ -0,0 +1,69 @@
+#!/usr/bin/env python
+from enum import Enum
+
+from hailo_platform.common.tools.cmd_utils.base_utils import CmdUtilsBaseUtil
+from hailo_platform.drivers.hw_object import EthernetDevice, PcieDevice
+from hailo_platform.common.logger.logger import default_logger
+from hailo_platform.drivers.hailort.pyhailort import PcieDeviceInfo, InternalPcieDevice
+
+logger = default_logger()
+
+class HailoCLITargets(Enum):
+    udp = 'udp'
+    pcie = 'pcie'
+
+
+class HailoDeviceCmdUtil(CmdUtilsBaseUtil):
+    """
+    Base class for any cmd utility that use a specific hailo device
+    """
+    def __init__(self, args_parser, set_target_args=True):
+        super().__init__(args_parser)
+        self._parser = args_parser
+        if set_target_args:
+            self.add_target_args(args_parser)
+
+    def get_target(self, args):
+        self.validate_args(args)
+        target_type = self.get_target_type(args)
+        if target_type == HailoCLITargets.udp.value:
+            return EthernetDevice(args.ip)
+        else:
+            try:
+                return PcieDevice(device_info=args.board_location)
+            except Exception as e:
+                logger.error('Internal PCIe error (No PCIe device connected?)')
+                logger.error('Error code: {}'.format(e))
+                raise
+
+    def get_target_type(self, args):
+        if args.target is not None:
+            return args.target
+
+        if args.ip is not None:
+            # If IP is given, we assume that the target is udp
+            return HailoCLITargets.udp.value
+
+        # Otherwise the default target is pcie
+        return HailoCLITargets.pcie.value
+
+
+    def validate_args(self, args):
+        if args.target == HailoCLITargets.udp.value:
+            if not args.ip:
+                self._parser.error('When using --target udp, you must supply --ip')
+            if args.board_location:
+                self._parser.error("When using --target udp, you must not supply --board-location")
+
+        if args.board_location:
+            all_devices = InternalPcieDevice.scan_devices()
+            if args.board_location not in all_devices:
+                self._parser.error('Device {} does not appear on your host, please run `hailo scan -d pcie` to see all available devices'
+                    .format(args.board_location))
+
+    def add_target_args(self, args_parser):
+        args_parser.add_argument('--target', type=str, choices=[t.value for t in HailoCLITargets],
+                                 default=None, help='Device type to use')
+        args_parser.add_argument('--ip', type=str, default=None, help='IP address of the target (udp)')
+        args_parser.add_argument('-s', '--board-location', help=PcieDeviceInfo.BOARD_LOCATION_HELP_STRING,
+                                 type=PcieDeviceInfo.argument_type)
diff --git a/hailort/libhailort/bindings/python/platform/hailo_platform/config_definitions.json b/hailort/libhailort/bindings/python/platform/hailo_platform/config_definitions.json
new file mode 100644 (file)
index 0000000..f1efdca
--- /dev/null
@@ -0,0 +1,79 @@
+{
+    "_comment": 
+        [
+        "This file defines the available fields of the firmwares config. It is not used to serialize any data.",
+        "WARNING! DO NOT CHANGE THE ORDER OF THE DEFINITIONS AS IT WILL CHANGE THEIR GENERATED VALUES!"
+        ],
+    "version": 0,
+    "categories": 
+    {
+        "network": 
+        {
+            "entries":
+            {
+                "should_use_dhcp": {"size": 1, "deserialize_as": "bool"},
+                "mac_address": {"size": 1, "length": 6, "deserialize_as": "mac_address"},
+                "static_ip_address": {"size": 4, "deserialize_as": "ipv4"},
+                "static_gw_address": {"size": 4, "deserialize_as": "ipv4"},
+                "static_netmask": {"size": 4, "deserialize_as": "ipv4"},
+                "rx_pause_frames_enable": {"size": 1, "deserialize_as": "bool"}
+            }
+        },
+        "system":
+        {
+            "entries":
+            {
+                "name": {"size": 1, "length": 32, "deserialize_as": "str"},
+                "app_watchdog_enable": {"size": 1, "deserialize_as": "bool"},
+                "app_watchdog_cycles": {"size": 4, "deserialize_as": "int"},
+                "core_watchdog_enable": {"size": 1, "deserialize_as": "bool"},
+                "core_watchdog_cycles": {"size": 4, "deserialize_as": "int"},
+                "watchdog_mode" : {"size": 1, "deserialize_as": "watchdog_mode"},
+                "max_neural_network_core_clock_rate": {"size": 4, "deserialize_as": "clock_frequency"},
+                "supported_aspm_states": {"size": 1, "deserialize_as": "supported_aspm_states"},
+                "bus_0_i2c_speed": {"size": 1, "deserialize_as": "i2c_speed"},
+                "bus_1_i2c_speed": {"size": 1, "deserialize_as": "i2c_speed"},
+                "bus_2_i2c_speed": {"size": 1, "deserialize_as": "i2c_speed"},
+                "bus_3_i2c_speed": {"size": 1, "deserialize_as": "i2c_speed"},
+                "supported_aspm_l1_substates": {"size": 1, "deserialize_as": "supported_aspm_l1_substates"},
+                "overcurrent_parameters_source": {"size": 1, "deserialize_as": "overcurrent_parameters_source"},
+                "overcurrent_monitoring_red_threshold": {"size": 4, "deserialize_as": "int"},
+                "overcurrent_conversion_time_microseconds": {"size": 4, "deserialize_as": "conversion_time"},
+                "temperature_parameters_source": {"size": 1, "deserialize_as": "temperature_parameters_source"},
+                "temperature_red_threshold": {"size": 1, "deserialize_as": "int"},
+                "temperature_red_hysteresis_threshold": {"size": 1, "deserialize_as": "int"},
+                "temperature_orange_threshold": {"size": 1, "deserialize_as": "int"},
+                "temperature_orange_hysteresis_threshold": {"size": 1, "deserialize_as": "int"},
+                "temperature_throttling_enable": {"size": 1, "deserialize_as": "bool"},
+                "overcurrent_monitoring_orange_threshold_enable": {"size": 1, "deserialize_as": "bool"}
+            }
+        },
+        "control":
+        {
+            "entries":
+            {
+                "udp_port": {"size": 2, "deserialize_as": "int"}
+            }
+        },
+        "d2h_event":
+        {
+            "entries":
+            {
+                "host_udp_port": {"size": 2, "deserialize_as": "int"},
+                "src_udp_port": {"size": 2, "deserialize_as": "int"},
+                "host_ip_address": {"size": 4, "deserialize_as": "ipv4"},
+                "connection_type": {"size": 1, "deserialize_as": "bool"}
+            }
+        },
+        "logger":
+        {
+            "entries":
+            {
+                "send_via_pci": {"size": 1, "deserialize_as": "bool"},
+                "send_via_uart": {"size": 1, "deserialize_as": "bool"},
+                "logger_level": {"size": 4, "deserialize_as": "logger_level"}
+            }
+        }
+    }
+}
+
diff --git a/hailort/libhailort/bindings/python/platform/hailo_platform/drivers/__init__.py b/hailort/libhailort/bindings/python/platform/hailo_platform/drivers/__init__.py
new file mode 100644 (file)
index 0000000..22a799f
--- /dev/null
@@ -0,0 +1,6 @@
+#!/usr/bin/env python
+
+""":class:`~hailo_platform.drivers.hw_object.HailoHWObject` is the high level base class of the
+platform API. :class:`~hailo_platform.drivers.hw_object.PcieDevice` inherits from it
+and implements control and dataflow over PCIe.
+"""
diff --git a/hailort/libhailort/bindings/python/platform/hailo_platform/drivers/control_object.py b/hailort/libhailort/bindings/python/platform/hailo_platform/drivers/control_object.py
new file mode 100644 (file)
index 0000000..0e7d4f3
--- /dev/null
@@ -0,0 +1,655 @@
+#!/usr/bin/env python
+
+"""Control operations for the Hailo hardware device."""
+import struct
+from builtins import object
+from abc import ABCMeta, abstractmethod
+from future.utils import with_metaclass
+
+from hailo_platform.drivers.hailo_controller.hailo_control_protocol import HailoResetTypes, DeviceArchitectureTypes
+from hailo_platform.drivers.hailo_controller.power_measurement import SamplingPeriod, AveragingFactor, DvmTypes, PowerMeasurementTypes, DEFAULT_POWER_MEASUREMENT_DELAY_PERIOD_MS
+from hailo_platform.drivers.hailort.pyhailort import Control, InternalPcieDevice
+from hailo_platform.common.logger.logger import default_logger
+
+class ControlObjectException(Exception):
+    """Raised on illegal ContolObject operation."""
+    pass
+
+
+class FirmwareUpdateException(Exception):
+    pass
+
+
+class HailoControl(with_metaclass(ABCMeta, object)):
+    """Control object that sends control operations to a Hailo hardware device."""
+
+    def __init__(self):
+        """Initializes a new HailoControl object."""
+        self._logger = default_logger()
+        self._controller = None
+
+    @abstractmethod
+    def open(self):
+        """Initializes the resources needed for using a control device."""
+        pass
+
+    @abstractmethod
+    def close(self):
+        """Releases the resources that were allocated for the control device."""
+        pass
+
+    def configure(self, hef, configure_params_by_name={}):
+        """
+        Configures device from HEF object.
+
+        Args:
+            hef (:class:`~hailo_platform.drivers.hailort.pyhailort.HEF`): HEF to configure the
+                device from.
+            configure_params_by_name (dict, optional): Maps between each net_group_name to
+                configure_params. In case of a mismatch with net_groups_names, default params will
+                be used.
+        """     
+        return self._controller.configure_device_from_hef(hef, configure_params_by_name)
+
+    @abstractmethod
+    def chip_reset(self):
+        """Resets the device (chip reset)."""
+        pass
+
+    @abstractmethod
+    def read_memory(self, address, data_length):
+        """Reads memory from the Hailo chip.
+        Byte order isn't changed. The core uses little-endian byte order.
+
+        Args:
+            address (int): Physical address to read from.
+            data_length (int): Size to read in bytes.
+
+        Returns:
+            list of str: Memory read from the chip, each index in the list is a byte.
+        """
+        pass
+
+    @abstractmethod
+    def write_memory(self, address, data_buffer):
+        """Write memory to Hailo chip.
+        Byte order isn't changed. The core uses little-endian byte order.
+
+        Args:
+            address (int): Physical address to write to.
+            data_buffer (list of str): Data to write.
+        """
+        pass
+
+
+class HcpControl(HailoControl):
+    """Control object that uses the HCP protocol for controlling the device."""
+
+    WORD_SIZE = 4
+
+
+    def __init__(self):
+        super(HcpControl, self).__init__()
+
+    @property
+    def device_id(self):
+        """Getter for the device_id.
+
+        Returns:
+            str: A string ID of the device. BDF for PCIe devices, MAC address for Ethernet devices, "Core" for core devices.
+        """
+        return self._device_id
+
+    def open(self):
+        """Initializes the resources needed for using a control device."""
+        pass
+
+    def close(self):
+        """Releases the resources that were allocated for the control device."""
+        pass
+
+    def chip_reset(self):
+        """Resets the device (chip reset)."""
+        return self._controller.reset(reset_type=HailoResetTypes.CHIP)
+
+    def nn_core_reset(self):
+        """Resets the nn_core."""
+        return self._controller.reset(reset_type=HailoResetTypes.NN_CORE)
+
+    def soft_reset(self):
+        """reloads the device firmware (soft reset)"""
+        return self._controller.reset(reset_type=HailoResetTypes.SOFT)
+        
+    def forced_soft_reset(self):
+        """reloads the device firmware (forced soft reset)"""
+        return self._controller.reset(reset_type=HailoResetTypes.FORCED_SOFT) 
+
+    def read_memory(self, address, data_length):
+        """Reads memory from the Hailo chip. Byte order isn't changed. The core uses little-endian
+        byte order.
+
+        Args:
+            address (int): Physical address to read from.
+            data_length (int): Size to read in bytes.
+
+        Returns:
+            list of str: Memory read from the chip, each index in the list is a byte
+        """
+        return self._controller.read_memory(address, data_length)
+
+    def write_memory(self, address, data_buffer):
+        """Write memory to Hailo chip. Byte order isn't changed. The core uses little-endian byte
+        order.
+
+        Args:
+            address (int): Physical address to write to.
+            data_buffer (list of str): Data to write.
+        """
+        return self._controller.write_memory(address, data_buffer)
+
+    def power_measurement(self, dvm=DvmTypes.AUTO, measurement_type=PowerMeasurementTypes.AUTO):
+        """Perform a single power measurement on an Hailo chip. It works with the default settings
+        where the sensor returns a new value every 2.2 ms without averaging the values.
+
+        Args:
+            dvm (:class:`~hailo_platform.drivers.hailort.pyhailort.DvmTypes`):
+                Which DVM will be measured. Default (:class:`~hailo_platform.drivers.hailort.pyhailort.DvmTypes.AUTO`) will be different according to the board: \n
+                 Default (:class:`~hailo_platform.drivers.hailort.pyhailort.DvmTypes.AUTO`) for EVB is an approximation to the total power consumption of the chip in PCIe setups.
+                 It sums :class:`~hailo_platform.drivers.hailort.pyhailort.DvmTypes.VDD_CORE`,
+                 :class:`~hailo_platform.drivers.hailort.pyhailort.DvmTypes.MIPI_AVDD` and :class:`~hailo_platform.drivers.hailort.pyhailort.DvmTypes.AVDD_H`.
+                 Only :class:`~hailo_platform.drivers.hailort.pyhailort.PowerMeasurementTypes.POWER` can measured with this option. \n
+                 Default (:class:`~hailo_platform.drivers.hailort.pyhailort.DvmTypes.AUTO`) for platforms supporting current monitoring (such as M.2 and mPCIe): :class:`~hailo_platform.drivers.hailort.pyhailort.DvmTypes.OVERCURRENT_PROTECTION`
+            measurement_type
+             (:class:`~hailo_platform.drivers.hailort.pyhailort.PowerMeasurementTypes`):
+             The type of the measurement.
+
+        Returns:
+            float: The measured power. \n
+            For :class:`~hailo_platform.drivers.hailort.pyhailort.PowerMeasurementTypes`: \n
+            - :class:`~hailo_platform.drivers.hailort.pyhailort.PowerMeasurementTypes.SHUNT_VOLTAGE`: Unit is mV. \n
+            - :class:`~hailo_platform.drivers.hailort.pyhailort.PowerMeasurementTypes.BUS_VOLTAGE`: Unit is mV. \n
+            - :class:`~hailo_platform.drivers.hailort.pyhailort.PowerMeasurementTypes.POWER`: Unit is W. \n
+            - :class:`~hailo_platform.drivers.hailort.pyhailort.PowerMeasurementTypes.CURRENT`: Unit is mA. \n
+
+
+        Note:
+            This function can perform measurements for more than just power. For all supported
+            measurement types, please look at
+            :class:`~hailo_platform.drivers.hailort.pyhailort.PowerMeasurementTypes`.
+        """
+        if self.device_id.device_architecture != DeviceArchitectureTypes.HAILO8_B0:
+            raise ControlObjectException("Invalid device architecture: {}".format(self.device_id.device_architecture))
+        return self._controller.power_measurement(dvm, measurement_type)
+
+    def start_power_measurement(self, delay=DEFAULT_POWER_MEASUREMENT_DELAY_PERIOD_MS, averaging_factor=AveragingFactor.AVERAGE_256, sampling_period=SamplingPeriod.PERIOD_1100us):
+        """Start performing a long power measurement.
+
+        Args:
+            delay (int): Amount of time between each measurement interval (in milliseconds) This
+                time period is sleep time of the core. The default value depends on the other
+                default values, plus a factor of 20 percent.
+            averaging_factor (:class:`~hailo_platform.drivers.hailort.pyhailort.AveragingFactor`):
+                Number of samples per time period, sensor configuration value.
+            sampling_period (:class:`~hailo_platform.drivers.hailort.pyhailort.SamplingPeriod`):
+                Related conversion time, sensor configuration value. The sensor samples the power
+                every ``sampling_period`` [ms] and averages every ``averaging_factor`` samples. The
+                sensor provides a new value every: 2 * sampling_period * averaging_factor [ms]. The
+                firmware wakes up every ``delay`` [ms] and checks the sensor. If there is a new
+                value to read from the sensor, the firmware reads it.  Note that the average
+                calculated by the firmware is "average of averages", because it averages values
+                that have already been averaged by the sensor.
+        """
+        return self._controller.start_power_measurement(delay, averaging_factor, sampling_period)
+
+    def stop_power_measurement(self):
+        """Stop performing a long power measurement. Deletes all saved results from the firmware.
+        Calling the function eliminates the start function settings for the averaging the samples,
+        and returns to the default values, so the sensor will return a new value every 2.2 ms
+        without averaging values.
+        """
+        return self._controller.stop_power_measurement()
+
+    def set_power_measurement(self, index, dvm=DvmTypes.AUTO, measurement_type=PowerMeasurementTypes.AUTO):
+        """Set parameters for long power measurement on an Hailo chip.
+
+        Args:
+            index (int): Index of the buffer on the firmware the data would be saved at.
+            dvm (:class:`~hailo_platform.drivers.hailort.pyhailort.DvmTypes`):
+                Which DVM will be measured. Default (:class:`~hailo_platform.drivers.hailort.pyhailort.DvmTypes.AUTO`) will be different according to the board: \n
+                 Default (:class:`~hailo_platform.drivers.hailort.pyhailort.DvmTypes.AUTO`) for EVB is an approximation to the total power consumption of the chip in PCIe setups.
+                 It sums :class:`~hailo_platform.drivers.hailort.pyhailort.DvmTypes.VDD_CORE`,
+                 :class:`~hailo_platform.drivers.hailort.pyhailort.DvmTypes.MIPI_AVDD` and :class:`~hailo_platform.drivers.hailort.pyhailort.DvmTypes.AVDD_H`.
+                 Only :class:`~hailo_platform.drivers.hailort.pyhailort.PowerMeasurementTypes.POWER` can measured with this option. \n
+                 Default (:class:`~hailo_platform.drivers.hailort.pyhailort.DvmTypes.AUTO`) for platforms supporting current monitoring (such as M.2 and mPCIe): :class:`~hailo_platform.drivers.hailort.pyhailort.DvmTypes.OVERCURRENT_PROTECTION`
+            measurement_type
+             (:class:`~hailo_platform.drivers.hailort.pyhailort.PowerMeasurementTypes`):
+             The type of the measurement.
+
+        Note:
+            This function can perform measurements for more than just power. For all supported measurement types
+            view :class:`~hailo_platform.drivers.hailort.pyhailort.PowerMeasurementTypes`
+        """
+        return self._controller.set_power_measurement(index, dvm, measurement_type)
+
+    def get_power_measurement(self, index, should_clear=True):
+        """Read measured power from a long power measurement
+
+        Args:
+            index (int): Index of the buffer on the firmware the data would be saved at.
+            should_clear (bool): Flag indicating if the results saved at the firmware will be deleted after reading.
+
+        Returns:
+            :class:`~hailo_platform.drivers.hailort.pyhailort.PowerMeasurementData`:
+             Object containing measurement data \n
+            For :class:`~hailo_platform.drivers.hailort.pyhailort.PowerMeasurementTypes`: \n
+            - :class:`~hailo_platform.drivers.hailort.pyhailort.PowerMeasurementTypes.SHUNT_VOLTAGE`: Unit is mV. \n
+            - :class:`~hailo_platform.drivers.hailort.pyhailort.PowerMeasurementTypes.BUS_VOLTAGE`: Unit is mV. \n
+            - :class:`~hailo_platform.drivers.hailort.pyhailort.PowerMeasurementTypes.POWER`: Unit is W. \n
+            - :class:`~hailo_platform.drivers.hailort.pyhailort.PowerMeasurementTypes.CURRENT`: Unit is mA. \n
+
+        Note:
+            This function can perform measurements for more than just power.
+            For all supported measurement types view
+            :class:`~hailo_platform.drivers.hailort.pyhailort.PowerMeasurementTypes`.
+        """
+        if self.device_id.device_architecture != DeviceArchitectureTypes.HAILO8_B0:
+            raise ControlObjectException("Invalid device architecture: {}".format(self.device_id.device_architecture))
+        return self._controller.get_power_measurement(
+            index,
+            should_clear=should_clear)
+
+    def _examine_user_config(self):
+        return self._controller.examine_user_config()
+    
+    def read_user_config(self):
+        """Read the user configuration section as binary data.
+
+        Returns:
+            str: User config as a binary buffer.
+        """
+        return self._controller.read_user_config()
+
+    def write_user_config(self, configuration):
+        """Write the user configuration.
+
+        Args:
+            configuration (str): A binary representation of a Hailo device configuration.
+        """
+        return self._controller.write_user_config(configuration)
+    
+    def _erase_user_config(self):
+        return self._controller.erase_user_config()
+    
+    def read_board_config(self):
+        """Read the board configuration section as binary data.
+
+        Returns:
+            str: Board config as a binary buffer.
+        """
+        return self._controller.read_board_config()
+
+    def write_board_config(self, configuration):
+        """Write the static confuration.
+
+        Args:
+            configuration (str): A binary representation of a Hailo device configuration.
+        """
+        return self._controller.write_board_config(configuration)
+
+    def identify(self):
+        """Gets the Hailo chip identification.
+
+        Returns:
+            class HailoIdentifyResponse with Protocol version.
+        """
+        return self._controller.identify()
+
+    def core_identify(self):
+        """Gets the Core Hailo chip identification.
+
+        Returns:
+            class HailoIdentifyResponse with Protocol version.
+        """
+        return self._controller.core_identify()
+
+    def set_fw_logger(self, level, interface_mask):
+        """Configure logger level and interface of sending.
+
+        Args:
+            level (FwLoggerLevel):    The minimum logger level.
+            interface_mask (int):     Output interfaces (mix of FwLoggerInterface).
+        """
+        return self._controller.set_fw_logger(level, interface_mask)
+
+    def set_throttling_state(self, should_activate):
+        """Change throttling state of temperature protection component.
+
+        Args:
+            should_activate (bool):   Should be true to enable or false to disable. 
+        """
+        return self._controller.set_throttling_state(should_activate)
+
+    def get_throttling_state(self):
+        """Get the current throttling state of temperature protection component.
+        
+        Returns:
+            bool: true if temperature throttling is enabled, false otherwise.
+        """
+        return self._controller.get_throttling_state()
+
+    def _set_overcurrent_state(self, should_activate):
+        """Control whether the overcurrent protection is enabled or disabled.
+
+        Args:
+            should_activate (bool):   Should be true to enable or false to disable. 
+        """
+        return self._controller._set_overcurrent_state(should_activate)
+
+    def _get_overcurrent_state(self):
+        """Get the overcurrent protection state.
+        
+        Returns:
+            bool: true if overcurrent protection is enabled, false otherwise.
+        """
+        return self._controller._get_overcurrent_state()
+
+    def i2c_write(self, slave, register_address, data):
+        """Write data to an I2C slave.
+
+        Args:
+            slave (:class:`hailo_platform.drivers.hailo_controller.i2c_slaves.I2CSlave`): I2C slave
+                configuration.
+            register_address (int): The address of the register to which the data will be written.
+            data (str): The data that will be written.
+        """
+        return self._controller.i2c_write(slave, register_address, data)
+        
+    def i2c_read(self, slave, register_address, data_length):
+        """Read data from an I2C slave.
+
+        Args:
+            slave (:class:`hailo_platform.drivers.hailo_controller.i2c_slaves.I2CSlave`): I2C slave
+                configuration.
+            register_address (int): The address of the register from which the data will be read.
+            data_length (int): The number of bytes to read.
+
+        Returns:
+            str: Data read from the I2C slave.
+        """
+        return self._controller.i2c_read(slave, register_address, data_length)
+        
+    def read_register(self, address):
+        """Read the value of a register from a given address.
+
+        Args:
+            address (int): Address to read register from.
+
+        Returns:
+            int: Value of the register
+        """
+        register_value, = struct.unpack('!I', self.read_memory(address, type(self).WORD_SIZE))
+        return register_value
+
+    def set_bit(self, address, bit_index):
+        """Set (turn on) a specific bit at a register from a given address.
+
+        Args:
+            address (int) : Address of the register to modify.
+            bit_index (int) : Index of the bit that would be set.
+        """
+        register_value = self.read_register(address)
+        register_value |= 1 << bit_index
+        self.write_memory(address, struct.pack('!I', register_value))
+
+    def reset_bit(self, address, bit_index):
+        """Reset (turn off) a specific bit at a register from a given address.
+
+        Args:
+            address (int) :  Address of the register to modify.
+            bit_index (int) : Index of the bit that would be reset.
+        """
+        register_value = self.read_register(address)
+        register_value &= ~(1 << bit_index)
+        self.write_memory(address, struct.pack('!I', register_value))
+    
+    def firmware_update(self, firmware_binary, should_reset=True):
+        """Update firmware binary on the flash. 
+        
+        Args:
+            firmware_binary (bytes): firmware binary stream.
+            should_reset (bool): Should a reset be performed after the update (to load the new firmware)
+        """
+        return self._controller.firmware_update(firmware_binary, should_reset)
+
+    def second_stage_update(self, second_stage_binary):
+        """Update second stage binary on the flash
+        
+        Args:
+            second_stage_binary (bytes): second stage binary stream.
+        """
+        return self._controller.second_stage_update(second_stage_binary)
+
+    def store_sensor_config(self, section_index, reset_data_size, sensor_type, config_file_path,
+                            config_height=0, config_width=0, config_fps=0, config_name=None):
+            
+        """Store sensor configuration to Hailo chip flash memory.
+        
+        Args:
+            section_index (int): Flash section index to write to. [0-6]
+            reset_data_size (int): Size of reset configuration.
+            sensor_type (:class:`~hailo_platform.drivers.hailort.pyhailort.SensorConfigTypes`): Sensor type.
+            config_file_path (str): Sensor configuration file path.
+            config_height (int): Configuration resolution height.
+            config_width (int): Configuration resolution width.
+            config_fps (int): Configuration FPS.
+            config_name (str): Sensor configuration name.
+        """
+        if config_name is None:
+            config_name = "UNINITIALIZED"
+
+        return self._controller.sensor_store_config(section_index, reset_data_size, sensor_type, config_file_path,
+            config_height, config_width, config_fps, config_name)
+    
+    def store_isp_config(self, reset_config_size, isp_static_config_file_path, isp_runtime_config_file_path,
+                         config_height=0, config_width=0, config_fps=0, config_name=None):
+        """Store sensor isp configuration to Hailo chip flash memory.
+
+        Args:
+            reset_config_size (int): Size of reset configuration.
+            isp_static_config_file_path (str): Sensor isp static configuration file path.
+            isp_runtime_config_file_path (str): Sensor isp runtime configuration file path.
+            config_height (int): Configuration resolution height.
+            config_width (int): Configuration resolution width.
+            config_fps (int): Configuration FPS.
+            config_name (str): Sensor configuration name.
+        """
+        if config_name is None:
+            config_name = "UNINITIALIZED"
+
+        return self._controller.store_isp_config(reset_config_size, config_height, config_width, 
+            config_fps, isp_static_config_file_path, isp_runtime_config_file_path, config_name)
+
+    def get_sensor_sections_info(self):
+        """Get sensor sections info from Hailo chip flash memory.
+
+        Returns:
+            Sensor sections info read from the chip flash memory.
+        """
+        return self._controller.sensor_get_sections_info()
+    
+    def sensor_set_generic_i2c_slave(self, slave_address, register_address_size, bus_index, should_hold_bus, endianness):
+        """Set a generic I2C slave for sensor usage.
+
+        Args:
+            sequence (int): Request/response sequence.
+            slave_address (int): The address of the I2C slave.
+            register_address_size (int): The size of the offset (in bytes).
+            bus_index (int): The number of the bus the I2C slave is behind.
+            should_hold_bus (bool): Hold the bus during the read.
+            endianness (:class:`~hailo_platform.drivers.hailort.pyhailort.Endianness`):
+                Big or little endian.
+        """
+        return self._controller.sensor_set_generic_i2c_slave(slave_address, register_address_size, bus_index, should_hold_bus, endianness)
+
+    def set_sensor_i2c_bus_index(self, sensor_type, i2c_bus_index):
+        """Set the I2C bus to which the sensor of the specified type is connected.
+  
+        Args:
+            sensor_type (:class:`~hailo_platform.drivers.hailort.pyhailort.SensorConfigTypes`): The sensor type.
+            i2c_bus_index (int): The I2C bus index of the sensor.
+        """
+        return self._controller.sensor_set_i2c_bus_index(sensor_type, i2c_bus_index)
+
+    def load_and_start_sensor(self, section_index):
+        """Load the configuration with I2C in the section index.
+  
+        Args:
+            section_index (int): Flash section index to load config from. [0-6]
+        """
+        return self._controller.sensor_load_and_start_config(section_index)
+
+    def reset_sensor(self, section_index):
+        """Reset the sensor that is related to the section index config.
+
+        Args:
+            section_index (int): Flash section index to reset. [0-6]
+        """
+        return self._controller.sensor_reset(section_index)
+
+    def wd_enable(self, cpu_id):
+        """Enable firmware watchdog.
+
+        Args:
+            cpu_id (:class:`~hailo_platform.drivers.hailort.pyhailort.HailoCpuId`): 0 for App CPU, 1 for Core CPU.
+        """
+        self._controller.wd_enable(cpu_id)
+
+    def wd_disable(self, cpu_id):
+        """Disable firmware watchdog.
+
+        Args:
+            cpu_id (:class:`~hailo_platform.drivers.hailort.pyhailort.HailoCpuId`): 0 for App CPU, 1 for Core CPU.
+        """
+        self._controller.wd_disable(cpu_id)
+
+    def wd_config(self, cpu_id, wd_cycles, wd_mode):
+        """Configure a firmware watchdog.
+
+        Args:
+            cpu_id (:class:`~hailo_platform.drivers.hailort.pyhailort.HailoCpuId`): 0 for App CPU, 1 for Core CPU.
+            wd_cycles (int): number of cycles until watchdog is triggered.
+            wd_mode (int): 0 - HW/SW mode, 1 -  HW only mode
+        """
+        return self._controller.wd_config(cpu_id, wd_cycles, wd_mode)
+
+    def previous_system_state(self, cpu_id):
+        """Read the FW previous system state.
+
+        Args:
+            cpu_id (:class:`~hailo_platform.drivers.hailort.pyhailort.HailoCpuId`): 0 for App CPU, 1 for Core CPU.
+        """
+        return self._controller.previous_system_state(cpu_id)
+
+    def get_chip_temperature(self):
+        """Returns the latest temperature measurements from the 2 internal temperature sensors of the Hailo chip.
+
+        Returns:
+            :class:`~hailo_platform.drivers.hailort.pyhailort.TemperatureInfo`:
+             Temperature in celsius of the 2 internal temperature sensors (TS), and a sample
+             count (a running 16-bit counter)
+        """
+        return self._controller.get_chip_temperature()
+
+    def get_extended_device_information(self):
+        return self._controller.get_extended_device_information()
+
+    def _get_health_information(self):
+        return self._controller._get_health_information()
+
+    def set_pause_frames(self, rx_pause_frames_enable):
+        """Enable/Disable Pause frames.
+
+        Args:
+            rx_pause_frames_enable (bool): False for disable, True for enable.
+        """
+        self._controller.set_pause_frames(rx_pause_frames_enable)
+
+    def test_chip_memories(self):
+        """test all chip memories using smart BIST
+
+        """
+        self._controller.test_chip_memories()
+
+    def _get_device_handle(self):
+        return self._controller._get_device_handle()
+
+class UdpHcpControl(HcpControl):
+    """Control object that uses a HCP over UDP controller interface."""
+
+    def __init__(self, remote_ip, device=None, remote_control_port=22401, retries=2, response_timeout_seconds=10.0, ignore_socket_errors=False):
+        """Initializes a new UdpControllerControl object.
+
+        Args:
+            remote_ip (str): The IPv4 address of the remote Hailo device (X.X.X.X).
+            remote_control_port (int, optional): The port that the remote Hailo device listens on.
+            response_timeout_seconds (float, optional): Number of seconds to wait until a response is received.
+            ignore_socket_errors (bool, optional): Ignore socket error (might be usefull for debugging).
+        """
+        super(UdpHcpControl, self).__init__()
+
+        # In the C API we define the total amount of attempts, instead of the amount of retries.
+        max_number_of_attempts = retries + 1
+        self._controller = Control(Control.Type.ETH, remote_ip, remote_control_port, response_timeout_seconds=response_timeout_seconds, max_number_of_attempts=max_number_of_attempts)
+        self.set_udp_device(device)
+        self._device_id = self.identify()
+
+    def set_udp_device(self, device):
+        self._controller.set_device(device)
+
+
+class PcieHcpControl(HcpControl):
+    """Control object that uses a HCP over PCIe controller interface."""
+
+    def __init__(self, device=None, device_info=None):
+        """Initializes a new HailoPcieController object."""
+        super(PcieHcpControl, self).__init__()
+
+        if device_info is None:
+            device_info = InternalPcieDevice.scan_devices()[0]
+        
+        self._controller = Control(Control.Type.PCIE, None, None, pcie_device_info=device_info)
+        self.set_pcie_device(device)
+        self._device_id = self.identify()
+
+    def release(self):
+        if self._controller is None:
+            return
+        self._controller.release()
+        self._controller = None
+
+    def set_pcie_device(self, pcie_device):
+        """Prepare the pcie device to be used after creating it."""
+        self._controller.set_device(pcie_device)
+    
+    def set_notification_callback(self, callback_func, notification_id, opaque):
+        """Set a callback function to be called when a notification is received.
+
+        Args:
+            callback_func (function): Callback function with the parameters (device, notification, opaque).
+                Note that throwing exceptions is not supported and will cause the program to terminate with an error!
+            notification_id (NotificationId): Notification ID to register the callback to.
+            opauqe (object): User defined data.
+
+        Note:
+            The notifications thread is started and closed in the use_device() context, so
+            notifications can only be received there.
+        """
+        return self._controller.set_notification_callback(callback_func, notification_id, opaque)
+
+    def remove_notification_callback(self, notification_id):
+        """Remove a notification callback which was already set.
+
+        Args:
+            notification_id (NotificationId): Notification ID to remove the callback from.
+        """
+        return self._controller.remove_notification_callback(notification_id)
diff --git a/hailort/libhailort/bindings/python/platform/hailo_platform/drivers/ethernet_utils.py b/hailort/libhailort/bindings/python/platform/hailo_platform/drivers/ethernet_utils.py
new file mode 100644 (file)
index 0000000..0b0bb84
--- /dev/null
@@ -0,0 +1,66 @@
+#!/usr/bin/env python
+from builtins import str
+import netifaces as ni
+
+from netaddr import IPAddress, IPNetwork
+
+
+# As defined in sockios.h
+SIOCGIFTXQLEN = 0x8942
+# Interface name is 16 bytes (including NULL)
+SIOCGIFTXQLEN_FMT = "16sI"
+
+class NoInterfaceError(Exception):
+    """Raised by get_interface_from_ip when no matching interface was found"""
+    pass
+
+def get_interface_from_ip(ip_address):
+    """Returns the interface name associated with the given ip addressself.
+
+    Args:
+        ip_address (str): the IP address to query.
+
+    Returns:
+        str: The name of the interface matching the given IP address.
+    """
+
+    skipped_ifaces = []
+    for interface in ni.interfaces():
+        if ni.AF_INET not in ni.ifaddresses(interface):
+            skipped_ifaces.append(interface)
+            continue
+        af_inet_values = ni.ifaddresses(interface)[ni.AF_INET][0]
+        ip_addr, netmask = af_inet_values['addr'], af_inet_values['netmask']
+        if is_ip_in_network(ip_addr, netmask, ip_address):
+            return str(interface)
+
+    raise NoInterfaceError('No interface for {} found among {}'.format(ip_address, skipped_ifaces))
+
+
+def get_interface_address(interface_name):
+    """Returns the interface address associated with the given interface name.
+
+        Args:
+            interface_name (str): the name of the interface to query.
+
+        Returns:
+            str: The IP address of the interface matching the given interface_name.
+        """
+    af_inet_values = ni.ifaddresses(interface_name)[ni.AF_INET][0]
+    return af_inet_values['addr']
+
+
+def is_ip_in_network(network_ip, netmask, ip_in_question):
+    """Checks whether an IP address is located in a given network.
+
+    Args:
+        network_ip (str): the IP address of the network interface.
+        netmask (str): the netmask of the given networkself.
+        ip_in_question (str): the IP address to compare against the network.
+
+    Returns:
+        bool: whether the IP address belongs to the given network.
+    """
+
+    netmask_bits = IPAddress(netmask).netmask_bits()
+    return IPAddress(ip_in_question) in IPNetwork('{}/{}'.format(network_ip, netmask_bits))
diff --git a/hailort/libhailort/bindings/python/platform/hailo_platform/drivers/hailo_controller/__init__.py b/hailort/libhailort/bindings/python/platform/hailo_platform/drivers/hailo_controller/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/hailort/libhailort/bindings/python/platform/hailo_platform/drivers/hailo_controller/hailo_control_protocol.py b/hailort/libhailort/bindings/python/platform/hailo_platform/drivers/hailo_controller/hailo_control_protocol.py
new file mode 100644 (file)
index 0000000..bdfc8cf
--- /dev/null
@@ -0,0 +1,331 @@
+#!/usr/bin/env python
+"""
+.. module:: hailo_control_protocol
+   :synopsis: Implements a Hailo Control Protocol message.
+"""
+
+from builtins import object
+from enum import Enum, IntEnum
+
+import struct
+
+# Supported protocol and Firmware version of current SDK.
+SUPPORTED_PROTOCOL_VERSION = 2
+SUPPORTED_FW_MAJOR = 4
+SUPPORTED_FW_MINOR = 6
+SUPPORTED_FW_REVISION = 0
+
+MEGA_MULTIPLIER = 1000.0 * 1000.0
+
+
+class HailoControlProtocolException(Exception):
+    pass
+
+
+class DeviceArchitectureTypes(IntEnum):
+    HAILO8_A0 = 0
+    HAILO8_B0 = 1
+    MERCURY_CA = 2
+
+    def __str__(self):
+        return self.name
+
+class BoardInformation(object):
+    def __init__(self, protocol_version, fw_version_major, fw_version_minor, fw_version_revision,
+                 logger_version, board_name, is_release, device_architecture, serial_number, part_number, product_name):
+        self.protocol_version = protocol_version
+        self.firmware_version = HailoFirmwareVersion.construct_from_params(fw_version_major, fw_version_minor, fw_version_revision, is_release, HailoFirmwareType.APP)
+        self.logger_version = logger_version
+        self.board_name = board_name
+        self.is_release = is_release
+        self.device_architecture = DeviceArchitectureTypes(device_architecture)
+        self.serial_number = serial_number
+        self.part_number = part_number
+        self.product_name = product_name
+    
+    def _string_field_str(self, string_field):
+        # Return <Not Configured> if the string field is empty
+        return string_field.rstrip('\x00') or "<Not Configured>"
+
+    def __str__(self):
+        """Returns:
+            str: Human readable string.
+        """
+        return 'Control Protocol Version: {}\n' \
+               'Firmware Version: {}\n' \
+               'Logger Version: {}\n' \
+               'Board Name: {}\n' \
+               'Device Architecture: {}\n' \
+               'Serial Number: {}\n' \
+               'Part Number: {}\n' \
+               'Product Name: {}\n'.format(
+            self.protocol_version,
+            self.firmware_version,
+            self.logger_version,
+            self.board_name.rstrip('\x00'),
+            str(self.device_architecture),
+            self._string_field_str(self.serial_number),
+            self._string_field_str(self.part_number),
+            self._string_field_str(self.product_name))
+       
+    def __repr__(self):
+        """Returns:
+            str: Human readable string.
+        """ 
+        return self.__str__()
+
+    @staticmethod
+    def get_hw_arch_str(device_arch):
+        if device_arch == DeviceArchitectureTypes.HAILO8_B0:
+            return 'hailo8'
+        elif device_arch == DeviceArchitectureTypes.MERCURY_CA:
+            return 'mercury'
+        else:
+            raise HailoControlProtocolException("Unsupported device architecture.")
+
+class CoreInformation(object):
+    def __init__(self, fw_version_major, fw_version_minor, fw_version_revision, is_release):
+        self.firmware_version = HailoFirmwareVersion.construct_from_params(fw_version_major, fw_version_minor, fw_version_revision, is_release, HailoFirmwareType.CORE)
+        self.is_release = is_release
+    
+    def __str__(self):
+        """Returns:
+            str: Human readable string.
+        """
+        return 'Core Firmware Version: {}'.format(
+            self.firmware_version)
+
+    def __repr__(self):
+        """Returns:
+            str: Human readable string.
+        """
+        return self.__str__()
+
+class TemperatureThrottlingLevel(object):
+    def __init__(self, level_number, temperature_threshold, hysteresis_temperature_threshold, throttling_nn_clock_freq):
+        self.level_number = level_number
+        self.temperature_threshold = temperature_threshold
+        self.hysteresis_temperature_threshold = hysteresis_temperature_threshold
+        self.throttling_nn_clock_freq = throttling_nn_clock_freq
+
+    def __str__(self):
+        """Returns:
+            str: Human readable string.
+        """
+        return 'Temperature Throttling Level {}: \n' \
+               'Temperature Threshold: {}\n' \
+               'Hysteresis Temperature Threshold: {}\n' \
+               'Throttling NN Clock Frequency: {}\n' \
+               .format(self.level_number, self.temperature_threshold, self.hysteresis_temperature_threshold, self.throttling_nn_clock_freq)
+        
+    def __repr__(self):
+        return self.__str__()
+
+class HealthInformation(object):
+    def __init__(self, overcurrent_protection_active, current_overcurrent_zone, red_overcurrent_threshold, orange_overcurrent_threshold, 
+                       temperature_throttling_active, current_temperature_zone, current_temperature_throttling_level,
+                       temperature_throttling_levels, orange_temperature_threshold, orange_hysteresis_temperature_threshold, 
+                       red_temperature_threshold, red_hysteresis_temperature_threshold):
+        self.overcurrent_protection_active = overcurrent_protection_active
+        self.current_overcurrent_zone = current_overcurrent_zone
+        self.red_overcurrent_threshold = red_overcurrent_threshold
+        self.orange_overcurrent_threshold = orange_overcurrent_threshold
+        self.temperature_throttling_active = temperature_throttling_active
+        self.current_temperature_zone = current_temperature_zone
+        self.current_temperature_throttling_level = current_temperature_throttling_level
+        self.orange_temperature_threshold = orange_temperature_threshold
+        self.orange_hysteresis_temperature_threshold = orange_hysteresis_temperature_threshold
+        self.red_temperature_threshold = red_temperature_threshold
+        self.red_hysteresis_temperature_threshold = red_hysteresis_temperature_threshold
+        
+        # Add TemperatureThrottlingLevel in case it has new throttling_nn_clock_freq. level_number can be used as only last
+        # levels can be with the same freq
+        self.temperature_throttling_levels = []
+        if self.temperature_throttling_active:
+            throttling_nn_clock_frequencies = []
+            for level_number, temperature_throttling_level in enumerate(temperature_throttling_levels):
+                if temperature_throttling_level.throttling_nn_clock_freq not in throttling_nn_clock_frequencies:
+                    throttling_nn_clock_frequencies.append(temperature_throttling_level.throttling_nn_clock_freq)
+                    self.temperature_throttling_levels.append(TemperatureThrottlingLevel(level_number,
+                                                                                        temperature_throttling_level.temperature_threshold, 
+                                                                                        temperature_throttling_level.hysteresis_temperature_threshold, 
+                                                                                        temperature_throttling_level.throttling_nn_clock_freq))
+    def __repr__(self):
+        return self.__str__()
+
+    def __str__(self):
+        """Returns:
+            str: Human readable string.
+        """
+        temperature_throttling_levels_str = "\n".join(["\n\n{}\n".format(str(temperature_throttling_level)) for temperature_throttling_level in self.temperature_throttling_levels]) \
+                                            if self.temperature_throttling_active else "<Temperature throttling is disabled>"
+        return 'Overcurrent Protection Active: {}\n' \
+               'Overcurrent Protection Current Overcurrent Zone: {}\n' \
+               'Overcurrent Protection Red Threshold: {}\n' \
+               'Overcurrent Protection Orange Threshold: {}\n' \
+               'Temperature Protection Red Threshold: {}\n' \
+               'Temperature Protection Red Hysteresis Threshold: {}\n' \
+               'Temperature Protection Orange Threshold: {}\n' \
+               'Temperature Protection Orange Hysteresis Threshold: {}\n' \
+               'Temperature Protection Throttling State: {}\n' \
+               'Temperature Protection Current Zone: {}\n' \
+               'Temperature Protection Current Throttling Level: {}\n' \
+               'Temperature Protection Throttling Levels: {}' \
+               .format(self.overcurrent_protection_active, self.current_overcurrent_zone, self.red_overcurrent_threshold, 
+                       self.orange_overcurrent_threshold, self.red_temperature_threshold, 
+                       self.red_hysteresis_temperature_threshold, self.orange_temperature_threshold, 
+                       self.orange_hysteresis_temperature_threshold, self.temperature_throttling_active,
+                       self.current_temperature_zone, self.current_temperature_throttling_level, temperature_throttling_levels_str)
+
+class ExtendedDeviceInformation(object):
+    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):
+        self.neural_network_core_clock_rate = neural_network_core_clock_rate
+        self.supported_features = SupportedFeatures(supported_features)
+        self.boot_source = boot_source
+        self.lcs = lcs
+        self.soc_id = soc_id
+        self.eth_mac_address = eth_mac_address
+        self.unit_level_tracking_id = unit_level_tracking_id
+        self.soc_pm_values = soc_pm_values
+
+    def __str__(self):
+        """Returns:
+            str: Human readable string.
+        """
+        string = 'Neural Network Core Clock Rate: {}MHz\n' \
+                 '{}' \
+                 'Boot source: {}\n' \
+                 'LCS: {}\n'.format(
+            self.neural_network_core_clock_rate / MEGA_MULTIPLIER,
+            str(self.supported_features),
+            str(self.boot_source.name),
+            str(self.lcs))
+        if any(self.soc_id):
+            string += 'SoC ID: ' + (self.soc_id.hex())
+
+        if any(self.eth_mac_address):
+            string += '\nMAC Address: ' + (":".join("{:02X}".format(i) for i in self.eth_mac_address))
+        
+        if any(self.unit_level_tracking_id):
+            string += '\nULT ID: ' + (self.unit_level_tracking_id.hex())
+        
+        if any(self.soc_pm_values):
+            string += '\nPM Values: ' + (self.soc_pm_values.hex())
+
+
+        return string
+
+    def __repr__(self):
+        """Returns:
+            str: Human readable string.
+        """
+        return self.__str__()
+
+class HailoFirmwareMode(Enum):
+    """Indication that firmware version is stable and official  """
+    DEVELOP = 'develop'
+    RELEASE = 'release'
+
+
+class HailoFirmwareType(Enum):
+    """Indication the firmware type """
+    CORE = 'core'
+    APP = 'app'
+
+
+class HailoResetTypes(Enum):
+    """Defines the available reset types."""
+    CHIP = 'chip'
+    NN_CORE = 'nn_core'
+    SOFT = 'soft'
+    FORCED_SOFT = 'forced_soft'
+
+
+class HailoFirmwareVersion(object):
+    """Represents a Hailo chip firmware version."""
+    DEV_BIT  = 0x80000000
+    CORE_BIT = 0x08000000
+    FW_VERSION_FORMAT = '<III'
+
+    def __init__(self, firmware_version_buffer, is_release, fw_type):
+        """Initialize a new Hailo Firmware Version object.
+
+        Args:
+            firmware_version_buffer (str): A buffer containing the firmware version struct.
+            is_release (bool, optional): Flag indicating if firmware is at develop/release mode.
+                                        None indicates unknown
+        """
+        self.major, self.minor, self.revision = struct.unpack(
+            self.FW_VERSION_FORMAT,
+            firmware_version_buffer)
+        
+        self.fw_type = fw_type
+        self.mode = HailoFirmwareMode.RELEASE if is_release else HailoFirmwareMode.DEVELOP
+        
+        self.revision &= ~(self.CORE_BIT | self.DEV_BIT)
+
+    def __str__(self):
+        """Returns:
+            str: Firmware version in a human readable format.
+        """
+        return '{}.{}.{} ({},{})'.format(self.major, self.minor, self.revision, self.mode.value, self.fw_type.value)
+
+    @classmethod
+    def construct_from_params(cls, major, minor, revision, is_release, fw_type):
+        """Returns:
+            class HailoFirmwareVersion : with the given Firmware version.
+        """
+        return cls(struct.pack(HailoFirmwareVersion.FW_VERSION_FORMAT, major, minor, revision), is_release, fw_type)
+
+    @property
+    def comparable_value(self):
+        """A value that could be compared to other firmware versions."""
+        return (self.major << 64) + (self.minor << 32) + (self.revision)
+
+    def __hash__(self):
+        return self.comparable_value
+
+    def __eq__(self, other):
+        return self.comparable_value == other.comparable_value
+
+    # TODO: Required for Python2 BW compatibility (SDK-10038)
+    # This impl' comes by default in Python3
+    def __ne__(self, other):
+        return not (self == other)
+
+    def __lt__(self, other):
+        return self.comparable_value < other.comparable_value
+
+    def check_protocol_compatibility(self, other):
+        return ((self.major == other.major) and (self.minor == other.minor))
+
+class SupportedFeatures(object):
+    def __init__(self, supported_features):
+        self.ethernet = supported_features.ethernet
+        self.mipi = supported_features.mipi
+        self.pcie = supported_features.pcie
+        self.current_monitoring = supported_features.current_monitoring
+        self.mdio = supported_features.mdio
+    
+    def _feature_str(self, feature_name, is_feature_enabled):
+        return '{}: {}\n'.format(feature_name, 'Enabled' if is_feature_enabled else 'Disabled')
+
+    def __str__(self):
+        """Returns:
+            str: Human readable string.
+        """
+        return 'Device supported features: \n' + \
+            self._feature_str('Ethernet', self.ethernet) + \
+            self._feature_str('MIPI', self.mipi) + \
+            self._feature_str('PCIE', self.pcie) + \
+            self._feature_str('Current Monitoring', self.current_monitoring) + \
+            self._feature_str('MDIO', self.mdio)
+
+    def __repr__(self):
+        """Returns:
+            str: Human readable string.
+        """
+        return self.__str__()
+
+    def _is_feature_enabled(self, feature):
+        return (self.supported_features & feature) != 0
diff --git a/hailort/libhailort/bindings/python/platform/hailo_platform/drivers/hailo_controller/i2c_slaves.py b/hailort/libhailort/bindings/python/platform/hailo_platform/drivers/hailo_controller/i2c_slaves.py
new file mode 100644 (file)
index 0000000..b76cd5c
--- /dev/null
@@ -0,0 +1,191 @@
+#!/usr/bin/env python
+from builtins import object
+import struct
+
+from hailo_platform.common.logger.logger import default_logger
+from hailo_platform.drivers.hailort.pyhailort import Endianness
+logger = default_logger()
+
+#: Variable which defines that the I2C slave is not behind a switch.
+NO_I2C_SWITCH = 5
+
+class I2CSlavesException(Exception):
+    pass
+
+
+class I2CSlave(object):
+    def __init__(self, name, bus_index, slave_address, switch_number=NO_I2C_SWITCH,
+                 register_address_size=1, endianness=Endianness.LITTLE_ENDIAN,
+                 should_hold_bus=False):
+        """Initialize a class which describes an I2C slave.
+
+        Args:
+            name (str): The name of the I2C slave.
+            bus_index (int): The bus number the I2C slave is connected to.
+            slave_address (int): The address of the I2C slave.
+            switch_number (int): The number of the switch the i2c salve is connected to.
+            register_address_size (int): Slave register address length (in bytes).
+            endianness (:class:`~hailo_platform.drivers.hailort.pyhailort.Endianness`): The endianness of the slave.
+            should_hold_bus (bool): Should hold the bus during the read.
+
+        """
+        self._name = name
+        self._bus_index = bus_index
+        self._slave_address = slave_address
+        self._switch_number = switch_number
+        self._register_address_size = register_address_size
+        self._endianness = endianness
+        self._should_hold_bus = should_hold_bus
+
+    def __repr__(self):
+        # Returning '' for the sphinx doc
+        return ''
+
+    @property
+    def name(self):
+        """Get the name of the I2C slave.
+
+        Returns:
+            str: Name of the I2C slave.
+        """
+        return self._name
+
+    @property
+    def bus_index(self):
+        """Get bus index the I2C slave is connected to.
+
+        Returns:
+            int: Index of the bus the I2C slave is connected to.
+        """
+        return self._bus_index
+
+    @property
+    def slave_address(self):
+        """Get the address of the salve.
+
+        Returns:
+            int: The address of the I2C slave.
+        """
+        return self._slave_address
+
+    @property
+    def register_address_size(self):
+        """Get the slave register address length (in bytes). This number represents how many bytes are in the
+        register address the slave can access.
+
+        Returns:
+            int: Slave register address length.
+
+        Note:
+            Pay attention to the slave endianness (:class:`~hailo_platform.drivers.hailort.pyhailort.Endianness`).
+        """
+        return self._register_address_size
+
+    @property
+    def switch_number(self):
+        """Get the switch number the slave is connected to.
+
+        Returns:
+            int: The number of the switch the I2C is behind.
+
+        Note:
+            If :data:`NO_I2C_SWITCH` is returned, it means the slave is not behind a switch.
+        """
+        return self._switch_number
+
+    @property
+    def endianness(self):
+        """Get the slave endianness.
+
+        Returns:
+            :class:`~hailo_platform.drivers.hailort.pyhailort.Endianness`: The slave endianness.
+        """
+        return self._endianness
+
+    @property
+    def should_hold_bus(self):
+        """Returns a Boolean indicating if the bus will be held while reading from the slave.
+
+        Returns:
+            bool: True if the bus would be held, otherwise False.
+        """
+        return self._should_hold_bus
+
+# DVM's
+#: Class which represents the MIPI AVDD I2C slave.
+I2C_SLAVE_MIPI_AVDD = I2CSlave("DVM_MIPI_AVDD", 0, 0x40)
+#: Class which represents the USB AVDD IO slave.
+I2C_SLAVE_USB_AVDD_IO = I2CSlave("DVM_USB_AVDD_IO", 0, 0x41)
+#: Class which represents the V_CORE slave.
+I2C_SLAVE_VDD_CORE = I2CSlave("DVM_VDD_CORE", 0, 0x42)
+#: Class which represents the VDD TOP slave.
+I2C_SLAVE_VDD_TOP = I2CSlave("DVM_VDD_TOP", 0, 0x43)
+#: Class which represents the MIPI AVDD_H I2C slave.
+I2C_SLAVE_MIPI_AVDD_H = I2CSlave("DVM_MIPI_AVDD_H", 0, 0x44)
+#: Class which represents the DVM USB AVDD IO HV slave.
+I2C_SLAVE_USB_AVDD_IO_HV = I2CSlave("DVM_USB_AVDD_IO_HV", 0, 0x45)
+#: Class which represents the DVM_VDDIO slave.
+I2C_SLAVE_VDD_IO = I2CSlave("DVM_VDD_IO", 0, 0x46)
+#: Class which represents the DVM_AVDD_H slave.
+I2C_SLAVE_AVDD_H = I2CSlave("DVM_AVDD_H", 0, 0x47)
+#: Class which represents the DVM_SDIO_VDDIO slave.
+I2C_SLAVE_SDIO_VDD_IO = I2CSlave("DVM_SDIO_VDD_IO", 0, 0x4d)
+
+#: Class which represents the DVM_SDIO_VDDIO slave.
+I2C_SLAVE_M_DOT_2_OVERCURREN_PROTECTION = I2CSlave("M_DOT_2_OVERCURREN_PROTECTION", 0, 0x40)
+
+#: Class which represents the I2S codec I2C slave.
+I2C_SLAVE_I2S_CODEC = I2CSlave("I2S_codec", 1, 0x18, should_hold_bus=True)
+
+#: Class which represents the I2C to gpio I2C slave.
+I2C_SLAVE_I2C_TO_GPIO = I2CSlave("I2C_to_GPIO", 0, 0x22)
+#: Class which represents the I2C switch slave.
+I2C_SLAVE_SWITCH = I2CSlave("I2C_SWITCH", 1, 0x70)
+
+#: Class which represents the I2C TEMP_sensor_0 slave.
+I2C_SLAVE_TEMP_SENSOR_0 = I2CSlave("TEMP_sensor_0", 0, 0x29)
+#: Class which represents the I2S TEMP_sensor_1 slave.
+I2C_SLAVE_TEMP_SENSOR_1 = I2CSlave("TEMP_sensor_1", 0, 0x2A)
+
+#: Class which represents the EEPROM I2C slave.
+I2C_SLAVE_EEPROM = I2CSlave("EEPROM", 0, 0x50, register_address_size=2,
+                              endianness=Endianness.BIG_ENDIAN)
+
+# External hardware
+#: Class which represents the raspicam I2C slave.
+I2C_SLAVE_RASPICAM = I2CSlave("RaspiCam", 1, 0x36, switch_number=1, register_address_size=2,
+                              endianness=Endianness.BIG_ENDIAN)
+
+I2C_SLAVE_ONSEMI_CAMERA_AR0220 = I2CSlave('Onsemi', 1, 0x10, switch_number=0, register_address_size=2,
+                              endianness=Endianness.BIG_ENDIAN)
+
+I2C_SLAVE_ONSEMI_CAMERA_AS0149 = I2CSlave('Onsemi', 1, (0x90 >> 1), switch_number=0, register_address_size=2,
+                              endianness=Endianness.BIG_ENDIAN)
+
+def set_i2c_switch(control_object, slave, slave_switch=None):
+    """Set the I2C switch in order to perform actions from the I2C slave.
+
+    Args:
+        control_object (:class:`~hailo_platform.drivers.control_object.HcpControl`): Control object
+            which communicates with the Hailo chip.
+        slave (:class:`I2CSlave`): Slave which the switch is set for.
+        slave_switch (:class:`I2CSlave`): The I2C slave for the switch it self. Defaults to
+            :data:`I2C_SLAVE_SWITCH`.
+    """
+    I2C_SWITCH_REGISTER_SIZE = 1
+    if NO_I2C_SWITCH != slave.switch_number:
+        if not slave_switch:
+            slave_switch = I2C_SLAVE_SWITCH
+
+        # Set the switch value that should be written
+        switch_value = 1 << slave.switch_number
+
+        # Write new value to the switch
+        control_object.i2c_write(slave_switch, switch_value, struct.pack('b', switch_value))
+
+        # Read data from the switch, make sure write was successful
+        read_data, = struct.unpack('b', control_object.i2c_read(slave_switch, switch_value, I2C_SWITCH_REGISTER_SIZE))
+        if read_data != switch_value:
+            raise I2CSlavesException("Switch writing has failed. Read data is different then expected %s != %s" % (
+                read_data,
+                switch_value))
diff --git a/hailort/libhailort/bindings/python/platform/hailo_platform/drivers/hailo_controller/power_measurement.py b/hailort/libhailort/bindings/python/platform/hailo_platform/drivers/hailo_controller/power_measurement.py
new file mode 100644 (file)
index 0000000..b9da002
--- /dev/null
@@ -0,0 +1,11 @@
+from hailo_platform.drivers.hailort.pyhailort import (DvmTypes, PowerMeasurementTypes,  # noqa F401
+                                                      SamplingPeriod, AveragingFactor,
+                                                      HailoPowerMeasurementUtils)
+
+""" Amount of time between each power measurement interval.
+        The default values for provides by the sensor a new value every:
+        2 * sampling_period (1.1) * averaging_factor (256) [ms].
+        Therefore we want it to be the period of time that the core will sleep between samples,
+        plus a factor of 20 percent  """
+DEFAULT_POWER_MEASUREMENT_DELAY_PERIOD_MS = int((HailoPowerMeasurementUtils.return_real_sampling_period(SamplingPeriod.PERIOD_1100us) / 1000.0 *
+                                                HailoPowerMeasurementUtils.return_real_averaging_factor(AveragingFactor.AVERAGE_256)  * 2) * 1.2)
\ No newline at end of file
diff --git a/hailort/libhailort/bindings/python/platform/hailo_platform/drivers/hailort/__init__.py b/hailort/libhailort/bindings/python/platform/hailo_platform/drivers/hailort/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/hailort/libhailort/bindings/python/platform/hailo_platform/drivers/hailort/pyhailort.py b/hailort/libhailort/bindings/python/platform/hailo_platform/drivers/hailort/pyhailort.py
new file mode 100644 (file)
index 0000000..ca2461e
--- /dev/null
@@ -0,0 +1,2123 @@
+import sys
+
+from hailo_platform.drivers.hailo_controller.hailo_control_protocol import BoardInformation, CoreInformation, HailoResetTypes, ExtendedDeviceInformation, HealthInformation
+
+from argparse import ArgumentTypeError
+import numpy
+import signal
+import time
+from hailo_platform.common.logger.logger import default_logger
+import gc
+import os
+
+import hailo_platform.drivers.hailort._pyhailort as _pyhailort
+from hailo_platform.drivers.hailort._pyhailort import (BootloaderVersion, TemperatureInfo, # noqa F401
+                                                        DvmTypes, PowerMeasurementTypes,  # noqa F401
+                                                        PowerMeasurementData, NotificationId,  # noqa F401
+                                                        OvercurrentAlertState,
+                                                        FormatOrder,
+                                                        AveragingFactor, SamplingPeriod,
+                                                        FormatType, WatchdogMode,
+                                                        MipiDataTypeRx, MipiPixelsPerClock,
+                                                        MipiClockSelection, MipiIspImageInOrder,
+                                                        MipiIspImageOutDataType, IspLightFrequency,
+                                                        BootSource, HailoSocketDefs, Endianness,
+                                                        MipiInputStreamParams, SensorConfigTypes,
+                                                        SensorConfigOpCode)
+
+BBOX_PARAMS = _pyhailort.HailoRTDefaults.BBOX_PARAMS()
+HAILO_DEFAULT_ETH_CONTROL_PORT = _pyhailort.HailoRTDefaults.HAILO_DEFAULT_ETH_CONTROL_PORT()
+INPUT_DATAFLOW_BASE_PORT = _pyhailort.HailoRTDefaults.DEVICE_BASE_INPUT_STREAM_PORT()
+OUTPUT_DATAFLOW_BASE_PORT = _pyhailort.HailoRTDefaults.DEVICE_BASE_OUTPUT_STREAM_PORT()
+PCIE_ANY_DOMAIN = _pyhailort.HailoRTDefaults.PCIE_ANY_DOMAIN()
+DEFAULT_VSTREAM_TIMEOUT_MS = 10000
+DEFAULT_VSTREAM_QUEUE_SIZE = 2
+
+class HailoSocket(object):
+    MAX_UDP_PAYLOAD_SIZE = HailoSocketDefs.MAX_UDP_PAYLOAD_SIZE()
+    MIN_UDP_PAYLOAD_SIZE = HailoSocketDefs.MIN_UDP_PAYLOAD_SIZE()
+    MAX_UDP_PADDED_PAYLOAD_SIZE = HailoSocketDefs.MAX_UDP_PADDED_PAYLOAD_SIZE()
+    MIN_UDP_PADDED_PAYLOAD_SIZE = HailoSocketDefs.MIN_UDP_PADDED_PAYLOAD_SIZE()
+    MAX_ALIGNED_UDP_PAYLOAD_SIZE_RTP = HailoSocketDefs.MAX_ALIGNED_UDP_PAYLOAD_SIZE_RTP()
+
+
+class HailoRTException(Exception):
+    pass
+
+class UdpRecvError(HailoRTException):
+    pass
+
+class InvalidProtocolVersionException(HailoRTException):
+    pass
+
+class HailoRTFirmwareControlFailedException(HailoRTException):
+    pass
+
+class HailoRTInvalidFrameException(HailoRTException):
+    pass
+
+class HailoRTUnsupportedOpcodeException(HailoRTException):
+    pass
+
+class HailoRTTimeout(HailoRTException):
+    pass
+
+class HailoRTStreamAborted(HailoRTException):
+    pass
+
+class HailoRTInvalidOperationException(HailoRTException):
+    pass
+
+class HailoRTInvalidArgumentException(HailoRTException):
+    pass
+
+class HailoRTNotFoundException(HailoRTException):
+    pass
+
+class HailoRTInvalidHEFException(HailoRTException):
+    pass
+
+class HailoRTEthException(HailoRTException):
+    pass
+
+class HailoRTPCIeDriverException(HailoRTException):
+    pass
+
+class HailoRTNetworkGroupNotActivatedException(HailoRTException):
+    pass
+
+class HailoStatusInvalidValueException(Exception):
+    pass
+
+class ExceptionWrapper(object):
+    def __enter__(self):
+        pass
+
+    def __exit__(self, exception_type, value, traceback):
+        if value is not None:
+            if exception_type is _pyhailort.HailoRTStatusException:
+                self._raise_indicative_status_exception(int(value.args[0]))
+            else:
+                raise
+
+    def _raise_indicative_status_exception(self, error_code):
+        string_error_code = get_status_message(error_code)
+        if string_error_code == "HAILO_ETH_RECV_FAILURE":
+            raise UdpRecvError("Failed to receive data")
+        if string_error_code == "HAILO_UNSUPPORTED_CONTROL_PROTOCOL_VERSION":
+            raise InvalidProtocolVersionException("HailoRT has failed because an invalid protocol version was received from device")
+        if string_error_code == "HAILO_FW_CONTROL_FAILURE":
+            raise HailoRTFirmwareControlFailedException("libhailort control operation failed")
+        if string_error_code == "HAILO_UNSUPPORTED_OPCODE":
+            raise HailoRTUnsupportedOpcodeException("HailoRT has failed because an unsupported opcode was sent to device")
+        if string_error_code == "HAILO_INVALID_FRAME":
+            raise HailoRTInvalidFrameException("An invalid frame was received")
+        if string_error_code == "HAILO_TIMEOUT":
+            raise HailoRTTimeout("Received a timeout - hailort has failed because a timeout had occurred")
+        if string_error_code == "HAILO_STREAM_ABORTED":
+            raise HailoRTStreamAborted("Stream aborted due to an external event")
+
+        if string_error_code == "HAILO_INVALID_OPERATION":
+            raise HailoRTInvalidOperationException("Invalid operation. See hailort.log for more information")
+        if string_error_code == "HAILO_INVALID_ARGUMENT":
+            raise HailoRTInvalidArgumentException("Invalid argument. See hailort.log for more information")
+        if string_error_code == "HAILO_NOT_FOUND":
+            raise HailoRTNotFoundException("Item not found. See hailort.log for more information")
+
+        if string_error_code == "HAILO_INVALID_HEF":
+            raise HailoRTInvalidHEFException("Invalid HEF. See hailort.log for more information")
+
+        if string_error_code == "HAILO_ETH_FAILURE":
+            raise HailoRTEthException("Ethernet failure. See hailort.log for more information")
+        if string_error_code == "HAILO_PCIE_DRIVER_FAIL":
+            raise HailoRTPCIeDriverException("PCIe driver failure. run 'dmesg | grep hailo' for more information")
+
+        if string_error_code == "HAILO_NETWORK_GROUP_NOT_ACTIVATED":
+            raise HailoRTNetworkGroupNotActivatedException("Network group is not activated")
+        else:
+            raise HailoRTException("libhailort failed with error: {} ({})".format(error_code, string_error_code))
+
+def get_status_message(status_code):
+    status_str = _pyhailort.get_status_message(status_code)
+    if status_str == "":
+        raise HailoStatusInvalidValueException("Value {} is not a valid status".format(status_code))
+    return status_str
+
+class Control(object):
+    class Type(object):
+        PCIE = 0
+        ETH = 1
+    
+    def __init__(self, control_type, address, port, pcie_device_info=None, response_timeout_seconds=10,
+     max_number_of_attempts=3):
+        self.device = None
+        self.control_type = control_type
+        self._eth_address = address
+        self._eth_port = port
+        self._eth_response_timeout_milliseconds = int(response_timeout_seconds * 1000)
+        self._eth_max_number_of_attempts = max_number_of_attempts
+        self._pcie_device_info = pcie_device_info
+
+        if sys.platform != "win32":
+            signal.pthread_sigmask(signal.SIG_BLOCK, [signal.SIGWINCH])
+
+    def ensure_device(method):
+        def _ensure_device(self, *args, **kw):
+            if self.device is not None:
+                return method(self, *args, **kw)
+            
+            with ExceptionWrapper():
+                if self.control_type == Control.Type.PCIE:
+                    self.device = _pyhailort.create_pcie_device(self._pcie_device_info)
+                    _pyhailort.identify(self.device)
+                elif self.control_type == Control.Type.ETH:
+                    self.device = _pyhailort.create_eth_device(self._eth_address, len(self._eth_address), self._eth_port,
+                        self._eth_response_timeout_milliseconds, self._eth_max_number_of_attempts)
+                else:
+                    raise HailoRTException("Unsupported control type")
+            try:
+                result = method(self, *args, **kw)
+            finally:
+                if self.device is not None:
+                    with ExceptionWrapper():
+                        _pyhailort.release_device(self.device)
+                self.device = None
+
+            return result
+        return _ensure_device
+    
+    def set_device(self, device_object):
+        if device_object is None:
+            self.device = None
+        else:
+            self.device = device_object.device
+        
+    @ensure_device
+    def identify(self):
+        with ExceptionWrapper():
+            response = _pyhailort.identify(self.device)
+        board_information = BoardInformation(response.protocol_version, response.fw_version.major,
+            response.fw_version.minor, response.fw_version.revision, response.logger_version,
+            response.board_name, response.is_release,  int(response.device_architecture), response.serial_number,
+            response.part_number, response.product_name)
+        return board_information
+
+    @ensure_device
+    def core_identify(self):
+        with ExceptionWrapper():
+            response = _pyhailort.core_identify(self.device)
+        core_information = CoreInformation(response.fw_version.major, response.fw_version.minor, 
+            response.fw_version.revision, response.is_release)
+        return core_information
+
+    @ensure_device
+    def set_fw_logger(self, level, interface_mask):
+        with ExceptionWrapper():
+            return _pyhailort.set_fw_logger(self.device, level, interface_mask)
+
+    @ensure_device
+    def set_throttling_state(self, should_activate):
+        with ExceptionWrapper():
+            return _pyhailort.set_throttling_state(self.device, should_activate)
+
+    @ensure_device
+    def get_throttling_state(self):
+        with ExceptionWrapper():
+            return _pyhailort.get_throttling_state(self.device)
+
+    @ensure_device
+    def _set_overcurrent_state(self, should_activate):
+        with ExceptionWrapper():
+            return _pyhailort._set_overcurrent_state(self.device, should_activate)
+
+    @ensure_device
+    def _get_overcurrent_state(self):
+        with ExceptionWrapper():
+            return _pyhailort._get_overcurrent_state(self.device)
+
+    @ensure_device
+    def read_memory(self, address, length):
+        with ExceptionWrapper():
+            return _pyhailort.read_memory(self.device, int(address), int(length))
+
+    @ensure_device
+    def write_memory(self, address, data):
+        with ExceptionWrapper():
+            return _pyhailort.write_memory(self.device, int(address), data, len(data))
+    
+    @ensure_device
+    def configure_device_from_hef(self, hef, configure_params_by_name={}):
+        with ExceptionWrapper():
+            return _pyhailort.configure_device_from_hef(self.device, hef._hef, configure_params_by_name)
+
+    @ensure_device
+    def _create_c_i2c_slave(self, pythonic_slave):
+        c_slave = _pyhailort.I2CSlaveConfig()
+        c_slave.endianness = pythonic_slave.endianness
+        c_slave.slave_address = pythonic_slave.slave_address
+        c_slave.register_address_size = pythonic_slave.register_address_size
+        c_slave.bus_index = pythonic_slave.bus_index
+        return c_slave
+
+    @ensure_device
+    def i2c_write(self, pythonic_slave, register_address, data):
+        c_slave = self._create_c_i2c_slave(pythonic_slave)
+        with ExceptionWrapper():
+            return _pyhailort.i2c_write(self.device, c_slave, register_address, data, len(data))
+
+    @ensure_device
+    def i2c_read(self, pythonic_slave, register_address, data_length):
+        c_slave = self._create_c_i2c_slave(pythonic_slave)
+        with ExceptionWrapper():
+            return _pyhailort.i2c_read(self.device, c_slave, register_address, data_length)
+
+    @ensure_device
+    def power_measurement(self, dvm, measurement_type):
+        with ExceptionWrapper():
+            return _pyhailort.power_measurement(self.device, dvm, measurement_type)
+    
+    @ensure_device
+    def start_power_measurement(self, delay_milliseconds, averaging_factor, sampling_period):
+        with ExceptionWrapper():
+            return _pyhailort.start_power_measurement(self.device, delay_milliseconds,
+                                                      averaging_factor, sampling_period)
+    
+    @ensure_device
+    def set_power_measurement(self, index, dvm, measurement_type):
+        with ExceptionWrapper():
+            return _pyhailort.set_power_measurement(self.device, index, dvm, measurement_type)
+
+    @ensure_device
+    def get_power_measurement(self, index, should_clear):
+        with ExceptionWrapper():
+            return _pyhailort.get_power_measurement(self.device, index, should_clear)
+
+    @ensure_device
+    def stop_power_measurement(self):
+        with ExceptionWrapper():
+            return _pyhailort.stop_power_measurement(self.device)
+    
+    @ensure_device
+    def examine_user_config(self):
+        with ExceptionWrapper():
+            return _pyhailort.examine_user_config(self.device)
+
+    @ensure_device
+    def read_user_config(self):
+        with ExceptionWrapper():
+            return _pyhailort.read_user_config(self.device)
+    
+    @ensure_device
+    def write_user_config(self, data):
+        with ExceptionWrapper():
+            return _pyhailort.write_user_config(self.device, data)
+    
+    @ensure_device
+    def erase_user_config(self):
+        with ExceptionWrapper():
+            return _pyhailort.erase_user_config(self.device)
+
+    @ensure_device
+    def read_board_config(self):
+        with ExceptionWrapper():
+            return _pyhailort.read_board_config(self.device)
+
+    @ensure_device
+    def write_board_config(self, data):
+        with ExceptionWrapper():
+            return _pyhailort.write_board_config(self.device, data)
+
+    @ensure_device
+    def reset(self, reset_type):
+        map_mode = {
+            HailoResetTypes.CHIP    : _pyhailort.ResetDeviceMode.CHIP,
+            HailoResetTypes.NN_CORE : _pyhailort.ResetDeviceMode.NN_CORE,
+            HailoResetTypes.SOFT : _pyhailort.ResetDeviceMode.SOFT,
+            HailoResetTypes.FORCED_SOFT : _pyhailort.ResetDeviceMode.FORCED_SOFT
+            }
+        
+        mode = map_mode[reset_type]
+        with ExceptionWrapper():
+            return _pyhailort.reset(self.device, mode)
+
+    @ensure_device
+    def sensor_store_config(self, section_index, reset_data_size, sensor_type, config_file_path, config_height, config_width, 
+                            config_fps, config_name):
+        with ExceptionWrapper():
+            return _pyhailort.sensor_store_config(self.device, section_index, reset_data_size, sensor_type, config_file_path, 
+                config_height, config_width, config_fps, config_name)
+
+    @ensure_device
+    def store_isp_config(self, reset_config_size, config_height, config_width, config_fps, isp_static_config_file_path, 
+                                isp_runtime_config_file_path, config_name):
+        with ExceptionWrapper():
+            return _pyhailort.store_isp_config(self.device, reset_config_size, config_height, config_width, config_fps, 
+                isp_static_config_file_path, isp_runtime_config_file_path, config_name)
+
+    @ensure_device
+    def sensor_get_sections_info(self):
+        with ExceptionWrapper():
+            return _pyhailort.sensor_get_sections_info(self.device)
+
+    @ensure_device
+    def sensor_set_i2c_bus_index(self, sensor_type, bus_index):
+        with ExceptionWrapper():
+            return _pyhailort.sensor_set_i2c_bus_index(self.device, sensor_type, bus_index)
+        
+    @ensure_device
+    def sensor_load_and_start_config(self, section_index):
+        with ExceptionWrapper():
+            return _pyhailort.sensor_load_and_start_config(self.device, section_index)
+
+    @ensure_device
+    def sensor_reset(self, section_index):
+        with ExceptionWrapper():
+            return _pyhailort.sensor_reset(self.device, section_index)
+
+    @ensure_device
+    def sensor_set_generic_i2c_slave(self, slave_address, register_address_size, bus_index, should_hold_bus, endianness):
+        with ExceptionWrapper():
+            return _pyhailort.sensor_set_generic_i2c_slave(self.device, slave_address, register_address_size, bus_index, should_hold_bus, endianness)
+
+    @ensure_device
+    def firmware_update(self, firmware_binary, should_reset):
+        with ExceptionWrapper():
+            return _pyhailort.firmware_update(self.device, firmware_binary, len(firmware_binary), should_reset)
+
+    @ensure_device
+    def second_stage_update(self, second_stage_binary):
+        with ExceptionWrapper():
+            return _pyhailort.second_stage_update(self.device, second_stage_binary, len(second_stage_binary))
+
+    @ensure_device
+    def set_pause_frames(self, rx_pause_frames_enable):
+        with ExceptionWrapper():
+            return _pyhailort.set_pause_frames(self.device, rx_pause_frames_enable)
+
+    @ensure_device
+    def wd_enable(self, cpu_id):
+        with ExceptionWrapper():
+            return _pyhailort.wd_enable(self.device, cpu_id)
+
+    @ensure_device
+    def wd_disable(self, cpu_id):
+        with ExceptionWrapper():
+            return _pyhailort.wd_disable(self.device, cpu_id)
+
+    @ensure_device
+    def wd_config(self, cpu_id, wd_cycles, wd_mode):
+        with ExceptionWrapper():
+            return _pyhailort.wd_config(self.device, cpu_id, wd_cycles, WatchdogMode(wd_mode))
+
+    @ensure_device
+    def previous_system_state(self, cpu_id):
+        with ExceptionWrapper():
+            return _pyhailort.previous_system_state(self.device, cpu_id)
+
+    @ensure_device
+    def get_chip_temperature(self):
+        with ExceptionWrapper():
+            return _pyhailort.get_chip_temperature(self.device)
+
+    @ensure_device
+    def get_extended_device_information(self):
+        with ExceptionWrapper():
+            response = _pyhailort.get_extended_device_information(self.device)
+        device_information = ExtendedDeviceInformation(response.neural_network_core_clock_rate,
+            response.supported_features, response.boot_source, response.lcs, response.soc_id,  response.eth_mac_address , response.unit_level_tracking_id, response.soc_pm_values)
+        return device_information
+
+    @ensure_device
+    def _get_health_information(self):
+        with ExceptionWrapper():
+            response = _pyhailort._get_health_information(self.device)
+        health_information = HealthInformation(response.overcurrent_protection_active, response.current_overcurrent_zone, response.red_overcurrent_threshold,
+                    response.orange_overcurrent_threshold, response.temperature_throttling_active, response.current_temperature_zone, response.current_temperature_throttling_level, 
+                    response.temperature_throttling_levels, response.orange_temperature_threshold, response.orange_hysteresis_temperature_threshold,
+                    response.red_temperature_threshold, response.red_hysteresis_temperature_threshold)
+        return health_information
+    
+    @ensure_device
+    def set_notification_callback(self, callback_func, notification_id, opaque):
+        with ExceptionWrapper():
+            _pyhailort.set_notification_callback(self.device, callback_func, notification_id, opaque)
+
+    @ensure_device
+    def remove_notification_callback(self, notification_id):
+        with ExceptionWrapper():
+            _pyhailort.remove_notification_callback(self.device, notification_id)
+
+    @ensure_device
+    def test_chip_memories(self):
+        """
+        Test chip memories using smart BIST mechanism.
+        """
+        with ExceptionWrapper():
+            return _pyhailort.test_chip_memories(self.device)
+
+    @ensure_device
+    def _get_device_handle(self):
+        return self.device
+
+class HailoUdpScan(object):
+    def __init__(self):
+        self._logger = default_logger()
+        with ExceptionWrapper():
+            self._scan = _pyhailort.UdpScan()
+
+    def scan_devices(self, interface_name, timeout_seconds=3):
+        self._logger.info('Scanning over interface {iface}'.format(iface=interface_name))
+        timeout_milliseconds = int(timeout_seconds * 1000)
+        device_ip_addresses =  self._scan.scan_devices(interface_name, timeout_milliseconds)
+        for ip in device_ip_addresses:
+            self._logger.debug("Found board at: {}".format(ip))
+        return device_ip_addresses
+
+
+class TrafficControl(object):
+    def __init__(self, ip, port, rate_bytes_per_sec):
+        if sys.platform != 'linux':
+            raise HailoRTInvalidOperationException('TrafficControl is supported only on UNIX os')
+        with ExceptionWrapper():
+            self._tc_util = _pyhailort.TrafficControlUtil(ip, port, int(rate_bytes_per_sec))
+    
+    def set_rate_limit(self):
+        self._tc_util.set_rate_limit()
+    
+    def reset_rate_limit(self):
+        self._tc_util.reset_rate_limit()
+
+    def get_interface_name(ip):
+        "get the interface corresponding to the given ip"
+        with ExceptionWrapper():
+            return _pyhailort.TrafficControlUtil.get_interface_name(ip)
+
+
+class ConfigureParams(object):
+
+    @staticmethod
+    def create_from_hef(hef, interface):
+        """Create configure params from HEF. These params affects the HEF configuration into a device.
+
+        Args:
+            hef (:class:`HEF`): The HEF to create the parameters from.
+            interface (:class:`HailoStreamInterface`): The stream_interface to create stream_params for.
+
+        Returns:
+            dict: The created stream params. The keys are the network_group names in the HEF. The values are default params, which can be changed.
+        """
+        with ExceptionWrapper():
+            return hef._hef.create_configure_params(interface)
+
+    @staticmethod
+    def create_mipi_inputs_from_hef(hef, output_interface, mipi_rx_id=0, data_type=MipiDataTypeRx.RAW_8,
+            img_width_pixels=1920, img_height_pixels=1080,
+            pixels_per_clock=MipiPixelsPerClock.PIXELS_PER_CLOCK_4, number_of_lanes=2,
+            clock_selection=MipiClockSelection.SELECTION_AUTOMATIC, data_rate=260, virtual_channel_index=0,
+            isp_enable=False, isp_img_in_order=MipiIspImageInOrder.GR_FIRST,
+            isp_img_out_data_type=MipiIspImageOutDataType.RGB_888, isp_crop_enable=False,
+            isp_crop_output_width_pixels=1920, isp_crop_output_height_pixels=1080,
+            isp_crop_output_width_start_offset_pixels=0, isp_crop_output_height_start_offset_pixels=0,
+            isp_test_pattern_enable=True, isp_configuration_bypass=False,
+            isp_run_time_ae_enable=True, isp_run_time_awb_enable=True, isp_run_time_adt_enable=True,
+            isp_run_time_af_enable=False, isp_run_time_calculations_interval_ms=0,
+            isp_light_frequency=IspLightFrequency.LIGHT_FREQ_50_HZ):
+        """Create configure params from HEF. These params affects the HEF configuration into a device.
+
+        .. attention:: The ISP and its features are not officially supported yet.
+
+        Args:
+            hef (:class:`HEF`): The HEF to create the parameters from.
+            output_interface (:class:`HailoStreamInterface`): The stream_interface to create output stream_params for.
+            mipi_rx_id (int): Selection of which MIPI Rx device to use.
+            data_type (:class:`~hailo_platform.drivers.hailort.pyhailort.MipiDataTypeRx`): The data type which will be passed over the MIPI.
+            img_width_pixels (int): The width in pixels of the image that enter to the mipi CSI. The sensor output.
+                                        When isp_enable and isp_crop_enable is false, is also the stream input.
+            img_height_pixels (int): The height in pixels of the image that enter to the mipi CSI. The sensor output.
+                                        When isp_enable and isp_crop_enable is false, is also the stream input.
+            pixels_per_clock (:class:`~hailo_platform.drivers.hailort.pyhailort.MipiPixelsPerClock`): Number of pixels transmitted at each
+                clock.
+            number_of_lanes (int): Number of lanes to use.
+            clock_selection (:class:`~hailo_platform.drivers.hailort.pyhailort.MipiClockSelection`): Selection of clock range that would be
+                used. Setting :class:`~hailo_platform.drivers.hailort.pyhailort.MipiClockSelection.SELECTION_AUTOMATIC` means that the
+                clock selection is calculated from the data rate.
+            data_rate (int): Rate of the passed data (MHz).
+            virtual_channel_index (int): The virtual channel index of the MIPI dphy.
+            isp_enable (bool): Enable the ISP block in the MIPI dataflow. The ISP is not supported yet.
+            isp_img_in_order (:class:`~hailo_platform.drivers.hailort.pyhailort.MipiIspImageInOrder`):
+                The ISP Rx bayer pixel order. Only relevant when the ISP is enabled.
+            isp_img_out_data_type (:class:`~hailo_platform.drivers.hailort.pyhailort.MipiIspImageOutDataType`):
+                The data type that the mipi will take out. Only relevant when the ISP is enabled.
+            isp_crop_enable (bool): Enable the crop feature in the ISP. Only relevant when the ISP is enabled.
+            isp_crop_output_width_pixels (int): The width in pixels of the output window that the ISP take out. The stream input.
+                                        Useful when isp_crop_enable is True. Only relevant when the ISP is enabled.
+            isp_crop_output_height_pixels (int): The height in pixels of the output window that the ISP take out. The stream input.
+                                        Useful when isp_crop_enable is True. Only relevant when the ISP is enabled.
+            isp_crop_output_width_start_offset_pixels (int): The width start point of the output window that the ISP take out. 
+                                        Useful when isp_crop_enable is True. Only relevant when the ISP is enabled.
+            isp_crop_output_height_start_offset_pixels (int): The height start point of the output window that the ISP take out. 
+                                        Useful when isp_crop_enable is True. Only relevant when the ISP is enabled.
+            isp_test_pattern_enable (bool): Enable Test pattern from the ISP. Only relevant when the ISP is enabled.
+            isp_configuration_bypass (bool): Don't load the ISP configuration file from the FLASH. Only relevant when the ISP is enabled.
+            isp_run_time_ae_enable (bool): Enable the run-time Auto Exposure in the ISP. Only relevant when the ISP is enabled.
+            isp_run_time_awb_enable (bool): Enable the run-time Auto White Balance in the ISP. Only relevant when the ISP is enabled.
+            isp_run_time_adt_enable (bool): Enable the run-time Adaptive Function in the ISP. Only relevant when the ISP is enabled.
+            isp_run_time_af_enable (bool): Enable the run-time Auto Focus in the ISP. Only relevant when the ISP is enabled.
+            isp_run_time_calculations_interval_ms (int): Interval in milliseconds between ISP run time calculations. Only relevant when the ISP is enabled.
+            isp_light_frequency (:class:`~hailo_platform.drivers.hailort.pyhailort.IspLightFrequency`):
+                                        Selection of the light frequency. This parameter varies depending on the power grid of the country where 
+                                        the product is running. Only relevant when the ISP is enabled.
+        Returns:
+            dict: The created stream params. The keys are the network_group names in the HEF. The values are default params, which can be changed.
+        """
+
+        mipi_params = MipiInputStreamParams()
+        mipi_params.mipi_rx_id = mipi_rx_id
+        mipi_params.data_type = data_type
+        mipi_params.isp_enable = isp_enable
+        mipi_params.mipi_common_params.pixels_per_clock = pixels_per_clock
+        mipi_params.mipi_common_params.number_of_lanes = number_of_lanes
+        mipi_params.mipi_common_params.clock_selection = clock_selection
+        mipi_params.mipi_common_params.virtual_channel_index = virtual_channel_index
+        mipi_params.mipi_common_params.data_rate = data_rate
+        mipi_params.mipi_common_params.img_width_pixels = img_width_pixels
+        mipi_params.mipi_common_params.img_height_pixels = img_height_pixels
+        mipi_params.isp_params.img_in_order = isp_img_in_order
+        mipi_params.isp_params.img_out_data_type = isp_img_out_data_type
+        mipi_params.isp_params.crop_enable = isp_crop_enable
+        mipi_params.isp_params.crop_output_width_pixels = isp_crop_output_width_pixels
+        mipi_params.isp_params.crop_output_height_pixels = isp_crop_output_height_pixels
+        mipi_params.isp_params.crop_output_width_start_offset_pixels = isp_crop_output_width_start_offset_pixels
+        mipi_params.isp_params.crop_output_height_start_offset_pixels = isp_crop_output_height_start_offset_pixels
+        mipi_params.isp_params.test_pattern_enable = isp_test_pattern_enable
+        mipi_params.isp_params.configuration_bypass = isp_configuration_bypass
+        mipi_params.isp_params.run_time_ae_enable = isp_run_time_ae_enable
+        mipi_params.isp_params.run_time_awb_enable = isp_run_time_awb_enable
+        mipi_params.isp_params.run_time_adt_enable = isp_run_time_adt_enable
+        mipi_params.isp_params.run_time_af_enable = isp_run_time_af_enable
+        mipi_params.isp_params.isp_run_time_calculations_interval_ms = isp_run_time_calculations_interval_ms
+        mipi_params.isp_params.isp_light_frequency = isp_light_frequency
+        with ExceptionWrapper():
+            return hef._hef.create_configure_params_mipi_input(output_interface, mipi_params)
+
+def _get_name_as_str(name):
+    return name if name is not None else ""
+
+class HEF(object):
+    """Python representation of the Hailo Executable Format, which contains one or more compiled
+    models.
+    """
+
+    def __init__(self, hef_source):
+        """Constructor for the HEF class.
+
+        Args:
+            hef_source (str or bytes): The source from which the HEF object will be created. If the
+                source type is `str`, it is treated as a path to an hef file. If the source type is
+                `bytes`, it is treated as a buffer. Any other type will raise a ValueError.
+        """
+
+        with ExceptionWrapper():
+            if isinstance(hef_source, str):
+                self._hef = _pyhailort.Hef.create_from_file(hef_source)
+                self._path = hef_source
+            elif isinstance(hef_source, bytes):
+                self._hef = _pyhailort.Hef.create_from_buffer(hef_source)
+                self._path = None
+            else: 
+                raise ValueError("HEF can only be created from a file path (str) or a buffer (bytes)")
+        self._sorted_output_names = {}
+
+    def get_networks_names(self, network_group_name=None):
+        """Gets the names of all networks in a specific network group.
+
+        Args:
+            network_group_name (str, optional): The name of the network group to access. If not given, first network_group is addressed.
+
+        Returns:
+            list of str: The names of the networks.
+        """
+        name = _get_name_as_str(network_group_name)
+        with ExceptionWrapper():
+            return self._hef.get_networks_names(name)
+
+    @property
+    def path(self):
+        """HEF file path."""
+        return self._path
+    
+    def get_network_group_names(self):
+        """Get the names of the network groups in this HEF."""
+        with ExceptionWrapper():
+            return self._hef.get_network_group_names()
+  
+    def get_network_groups_infos(self):
+        """Get information about the network groups in this HEF."""
+        with ExceptionWrapper():
+            return self._hef.get_network_groups_infos()
+
+    def get_input_vstream_infos(self, name=None):
+        """Get input vstreams information.
+
+        Args:
+            name (str, optional): The name of the network or network_group to access. In case network_group name is given,
+                Address all networks of the given network_group. In case not given, first network_group is addressed.
+
+        Returns:
+            list of :obj:`hailo_platform.drivers.hailort._pyhailort.VStreamInfo`: with all the information objects of all input vstreams.
+        """
+        name = _get_name_as_str(name)
+        return self._hef.get_input_vstream_infos(name)
+
+    def get_output_vstream_infos(self, name=None):
+        """Get output vstreams information.
+
+        Args:
+            name (str, optional): The name of the network or network_group to access. In case network_group name is given,
+                Address all networks of the given network_group. In case not given, first network_group is addressed.
+
+        Returns:
+            list of :obj:`hailo_platform.drivers.hailort._pyhailort.VStreamInfo`: with all the information objects of all output vstreams
+        """
+        name = _get_name_as_str(name)
+        return self._hef.get_output_vstream_infos(name)
+
+    def get_all_vstream_infos(self, name=None):
+        """Get input and output vstreams information.
+
+        Args:
+            name (str, optional): The name of the network or network_group to access. In case network_group name is given,
+                Address all networks of the given network_group. In case not given, first network_group is addressed.
+
+        Returns:
+            list of :obj:`hailo_platform.drivers.hailort._pyhailort.VStreamInfo`: with all the information objects of all input and output vstreams
+        """
+        name = _get_name_as_str(name)
+        return self._hef.get_all_vstream_infos(name)
+
+    def get_input_stream_infos(self, name=None):
+        """Get the input low-level streams information.
+
+        Args:
+            name (str, optional): The name of the network or network_group to access. In case network_group name is given,
+                Address all networks of the given network_group. In case not given, first network_group is addressed.
+
+        Returns:
+            List of :obj:`hailo_platform.drivers.hailort._pyhailort.StreamInfo`: with information objects
+            of all input low-level streams.
+        """
+        name = _get_name_as_str(name)
+        return self._hef.get_input_stream_infos(name)
+
+
+    def get_output_stream_infos(self, name=None):
+        """Get the output low-level streams information of a specific network group.
+
+        Args:
+            name (str, optional): The name of the network or network_group to access. In case network_group name is given,
+                Address all networks of the given network_group. In case not given, first network_group is addressed.
+
+        Returns:
+            List of :obj:`hailo_platform.drivers.hailort._pyhailort.StreamInfo`: with information objects
+            of all output low-level streams.
+        """
+        name = _get_name_as_str(name)
+        return self._hef.get_output_stream_infos(name)
+
+    def get_all_stream_infos(self, name=None):
+        """Get input and output streams information of a specific network group.
+
+        Args:
+            name (str, optional): The name of the network or network_group to access. In case network_group name is given,
+                Address all networks of the given network_group. In case not given, first network_group is addressed.
+
+        Returns:
+            list of :obj:`hailo_platform.drivers.hailort._pyhailort.StreamInfo`: with all the information objects of all input and output streams
+        """
+        name = _get_name_as_str(name)
+        return self._hef.get_all_stream_infos(name)
+
+    def get_sorted_output_names(self, network_group_name=None):
+        """Get the names of the outputs in a network group. The order of names is determined by
+        the SDK. If the network group is not given, the first one is used.
+        """
+        if network_group_name is None:
+            network_group_name = self.get_network_group_names()[0]
+
+        if network_group_name not in self._sorted_output_names:
+            with ExceptionWrapper():
+                self._sorted_output_names[network_group_name] = self._hef.get_sorted_output_names(network_group_name)
+        return self._sorted_output_names[network_group_name]
+
+    def bottleneck_fps(self, network_group_name=None):
+        if network_group_name is None:
+            network_group_name = self.get_network_group_names()[0]
+        with ExceptionWrapper():
+            bottleneck_fps = self._hef.get_bottleneck_fps(network_group_name)
+            if bottleneck_fps == 0:
+                raise HailoRTException("bottleneck_fps is zero")
+            return bottleneck_fps
+
+    def get_udp_rates_dict(self, fps, max_supported_rate_bytes, network_group_name=None):
+        if network_group_name is None:
+            network_group_name = self.get_network_group_names()[0]
+        with ExceptionWrapper():
+            return self._hef.get_udp_rates_dict(network_group_name, fps, int(max_supported_rate_bytes))
+
+    def get_vstream_name_from_original_name(self, original_name, network_group_name=None):
+        """Get vstream name from original layer name for a specific network group.
+
+        Args:
+            original_name (str): The original layer name.
+            network_group_name (str, optional): The name of the network group to access. If not given, first network_group is addressed.
+
+        Returns:
+            str: the matching vstream name for the provided original name.
+        """
+        if network_group_name is None:
+            network_group_name = self.get_network_group_names()[0]
+        with ExceptionWrapper():
+            return self._hef.get_vstream_name_from_original_name(original_name, network_group_name)
+
+    def get_original_names_from_vstream_name(self, vstream_name, network_group_name=None):
+        """Get original names list from vstream name for a specific network group.
+
+        Args:
+            vstream_name (str): The stream name.
+            network_group_name (str, optional): The name of the network group to access. If not given, first network_group is addressed.
+
+        Returns:
+            list of str: all the matching original layers names for the provided vstream name.
+        """
+        if network_group_name is None:
+            network_group_name = self.get_network_group_names()[0]
+        with ExceptionWrapper():
+            return self._hef.get_original_names_from_vstream_name(vstream_name, network_group_name)
+
+    def get_vstream_names_from_stream_name(self, stream_name, network_group_name=None):
+        """Get vstream names list from their underlying stream name for a specific network group.
+
+        Args:
+            stream_name (str): The underlying stream name.
+            network_group_name (str, optional): The name of the network group to access. If not given, first network_group is addressed.
+
+        Returns:
+            list of str: All the matching vstream names for the provided stream name.
+        """
+        if network_group_name is None:
+            network_group_name = self.get_network_group_names()[0]
+        with ExceptionWrapper():
+            return self._hef.get_vstream_names_from_stream_name(stream_name, network_group_name)
+
+    def get_stream_names_from_vstream_name(self, vstream_name, network_group_name=None):
+        """Get stream name from vstream name for a specific network group.
+
+        Args:
+            vstream_name (str): The name of the vstreams.
+            network_group_name (str, optional): The name of the network group to access. If not given, first network_group is addressed.
+
+        Returns:
+            list of str: All the underlying streams names for the provided vstream name.
+        """
+        if network_group_name is None:
+            network_group_name = self.get_network_group_names()[0]
+        with ExceptionWrapper():
+            return self._hef.get_stream_names_from_vstream_name(vstream_name, network_group_name)
+
+
+class ConfiguredNetwork(object):
+    """Represents a network group loaded to the device."""
+
+    def __init__(self, configured_network, target, hef):
+        self._configured_network = configured_network
+        self._target = target
+        self._hef = hef
+
+    def get_networks_names(self):
+        return self._hef.get_networks_names(self.name)
+
+    def activate(self, network_group_params=None):
+        """Activate this network group in order to infer data through it.
+
+        Args:
+            network_group_params (:obj:`hailo_platform.drivers.hailort._pyhailort.ActivateNetworkGroupParams`, optional):
+                Network group activation params. If not given, default params will be applied,
+
+        Returns:
+            :class:`ActivatedNetworkContextManager`: Context manager that returns the activated
+            network group.
+        """
+        network_group_params = network_group_params or self.create_params()
+
+        with ExceptionWrapper():
+            return ActivatedNetworkContextManager(self,
+                self._configured_network.activate(network_group_params),
+                self._target, self._hef)
+
+    def wait_for_activation(self, timeout_ms=None):
+        """Block until activated, or until ``timeout_ms`` is passed.
+
+        Args:
+            timeout_ms (int, optional): Timeout value in milliseconds to wait for activation.
+                Defaults to ``HAILO_INFINITE``.
+
+        Raises:
+            :class:`HailoRTTimeout`: In case of timeout.
+        """
+        MAX_INT = 0x7fffffff
+        with ExceptionWrapper():
+            if timeout_ms is None:
+                timeout_ms = MAX_INT
+            return self._configured_network.wait_for_activation(timeout_ms)
+
+    @staticmethod
+    def create_params():
+        """Create activation params for network_group.
+
+        Returns:
+            :obj:`hailo_platform.drivers.hailort._pyhailort.ActivateNetworkGroupParams`.
+        """
+        return _pyhailort.ActivateNetworkGroupParams.default()
+
+    @property
+    def name(self):
+        return self._configured_network.get_name()
+
+    def get_output_shapes(self):
+        name_to_shape = {vstream_info.name : vstream_info.shape for vstream_info in self.get_output_vstream_infos()}
+        results = []
+        for name in self.get_sorted_output_names():
+            results.append(name_to_shape[name])
+        return tuple(results)
+
+    def get_sorted_output_names(self):
+        return self._hef.get_sorted_output_names(self.name)
+
+    def get_input_vstream_infos(self, network_name=None):
+        """Get input vstreams information.
+
+        Args:
+            network_name (str, optional): The name of the network to access. In case not given, all the networks in the network group will be addressed.
+
+        Returns:
+            list of :obj:`hailo_platform.drivers.hailort._pyhailort.VStreamInfo`: with all the information objects of all input vstreams
+        """
+
+        name = network_name if network_name is not None else self.name
+        return self._hef.get_input_vstream_infos(name)
+
+    def get_output_vstream_infos(self, network_name=None):
+        """Get output vstreams information.
+
+        Args:
+            network_name (str, optional): The name of the network to access. In case not given, all the networks in the network group will be addressed.
+
+        Returns:
+            list of :obj:`hailo_platform.drivers.hailort._pyhailort.VStreamInfo`: with all the information objects of all output vstreams
+        """
+
+        name = network_name if network_name is not None else self.name
+        return self._hef.get_output_vstream_infos(name)
+
+    def get_all_vstream_infos(self, network_name=None):
+        """Get input and output vstreams information.
+
+        Args:
+            network_name (str, optional): The name of the network to access. In case not given, all the networks in the network group will be addressed.
+
+        Returns:
+            list of :obj:`hailo_platform.drivers.hailort._pyhailort.VStreamInfo`: with all the information objects of all input and output vstreams
+        """
+
+        name = network_name if network_name is not None else self.name
+        return self._hef.get_all_vstream_infos(name)
+
+    def get_input_stream_infos(self, network_name=None):
+        """Get the input low-level streams information of a specific network group.
+
+        Args:
+            network_name (str, optional): The name of the network to access. In case not given, all the networks in the network group will be addressed.
+
+        Returns:
+            List of :obj:`hailo_platform.drivers.hailort._pyhailort.StreamInfo`: with information objects
+            of all input low-level streams.
+        """
+
+        name = network_name if network_name is not None else self.name
+        return self._hef.get_input_stream_infos(name)
+
+    def get_output_stream_infos(self, network_name=None):
+        """Get the output low-level streams information of a specific network group.
+
+        Args:
+            network_name (str, optional): The name of the network to access. In case not given, all the networks in the network group will be addressed.
+
+        Returns:
+            List of :obj:`hailo_platform.drivers.hailort._pyhailort.StreamInfo`: with information objects
+            of all output low-level streams.
+        """
+
+        name = network_name if network_name is not None else self.name
+        return self._hef.get_output_stream_infos(name)
+
+    def get_all_stream_infos(self, network_name=None):
+        """Get input and output streams information of a specific network group.
+
+        Args:
+            network_name (str, optional): The name of the network to access. In case not given, all the networks in the network group will be addressed.
+
+        Returns:
+            list of :obj:`hailo_platform.drivers.hailort._pyhailort.StreamInfo`: with all the information objects of all input and output streams
+        """
+
+        name = network_name if network_name is not None else self.name
+        return self._hef.get_all_stream_infos(name)
+
+    def get_udp_rates_dict(self, fps, max_supported_rate_bytes):
+        with ExceptionWrapper():
+            return self._configured_network.get_udp_rates_dict(int(fps), int(max_supported_rate_bytes))
+
+    def _create_input_vstreams(self, input_vstreams_params):
+        return self._configured_network.InputVStreams(input_vstreams_params)
+
+    def _create_output_vstreams(self, output_vstreams_params):
+        return self._configured_network.OutputVStreams(output_vstreams_params)
+
+    def get_stream_names_from_vstream_name(self, vstream_name):
+        """Get stream name from vstream name for a specific network group.
+
+        Args:
+            vstream_name (str): The name of the vstreams.
+
+        Returns:
+            list of str: All the underlying streams names for the provided vstream name.
+        """
+        with ExceptionWrapper():
+            return self._hef.get_stream_names_from_vstream_name(vstream_name, self.name)
+
+    def get_vstream_names_from_stream_name(self, stream_name):
+        """Get vstream names list from their underlying stream name for a specific network group.
+
+        Args:
+            stream_name (str): The underlying stream name.
+
+        Returns:
+            list of str: All the matching vstream names for the provided stream name.
+        """
+        with ExceptionWrapper():
+            return self._hef.get_vstream_names_from_stream_name(stream_name, self.name)
+
+
+class ActivatedNetworkContextManager(object):
+    """A context manager that returns the activated network group upon enter."""
+
+    def __init__(self, configured_network, activated_network, target, hef):
+        self._configured_network = configured_network
+        self._activated_network = activated_network
+        self._target = target
+        self._hef = hef
+
+    def __enter__(self):
+        with ExceptionWrapper():
+            activated_network_group = ActivatedNetwork(self._configured_network, self._activated_network.__enter__(), self._target,
+                self._hef)
+        return activated_network_group
+    
+    def __exit__(self, *args):
+        self._activated_network.__exit__(*args)
+
+
+class ActivatedNetwork(object):
+    """The network group that is currently activated for inference."""
+
+    def __init__(self, configured_network, activated_network, target, hef):
+        self._configured_network = configured_network
+        self._activated_network = activated_network
+        self._target = target
+        self._hef = hef
+        self._last_number_of_invalid_frames_read = 0
+    
+    @property
+    def target(self):
+        return self._target
+
+    @property
+    def name(self):
+        return self._configured_network.name
+
+    def get_number_of_invalid_frames(self, clear=True):
+        """Returns number of invalid frames.
+
+        Args:
+            clear (bool): If set, the returned value will be the number of invalid frames read since the last call to this function.
+
+        Returns:
+            int: Number of invalid frames.
+        """
+        total_invalid_frames_count = self._activated_network.get_invalid_frames_count()
+        if clear:
+            value = total_invalid_frames_count - self._last_number_of_invalid_frames_read
+        self._last_number_of_invalid_frames_read = total_invalid_frames_count
+        return value if clear else total_invalid_frames_count
+
+    def validate_all_frames_are_valid(self):
+        """Validates that all of the frames so far are valid (no invalid frames)."""
+        number_of_invalid_frames = self.get_number_of_invalid_frames()
+        if number_of_invalid_frames != 0:
+            raise HailoRTException("There are {} invalid frames.".format(number_of_invalid_frames))
+
+    def get_sorted_output_names(self):
+        return self._hef.get_sorted_output_names(self.name)
+
+    def _get_intermediate_buffer(self, src_context_index, src_stream_index):
+        with ExceptionWrapper():
+            return self._activated_network.get_intermediate_buffer(src_context_index, src_stream_index)
+
+
+class InferVStreams(object):
+    """Pipeline that allows to call blocking inference, to be used as a context manager."""
+
+    def __init__(self, configured_net_group, input_vstreams_params, output_vstreams_params,
+        tf_nms_format=False):
+        """Constructor for the InferVStreams class.
+
+        Args:
+            configured_net_group (:class:`ConfiguredNetwork`): The configured network group for
+                which the pipeline is created.
+            input_vstreams_params (dict from str to :class:`InputVStreamParams`): Params for the
+                input vstreams in the pipeline. Only members of this dict will take part in the
+                inference.
+            output_vstreams_params (dict from str to :class:`OutputVStreamParams`): Params for the
+                output vstreams in the pipeline. Only members of this dict will take part in the
+                inference.
+            tf_nms_format (bool, optional): indicates whether the returned nms outputs should be in
+                Hailo format or TensorFlow format. Default is False (using Hailo format).
+
+                * Hailo format -- list of :obj:`numpy.ndarray`. Each element represents the
+                  detections (bboxes) for the class, and its shape is
+                  ``[number_of_detections, BBOX_PARAMS]``
+                * TensorFlow format -- :obj:`numpy.ndarray` of shape
+                  ``[class_count, BBOX_PARAMS, detections_count]`` padded with empty bboxes.
+        """
+
+        self._logger = default_logger()
+        self._configured_net_group = configured_net_group
+        self._net_group_name = configured_net_group.name
+        self._input_vstreams_params = input_vstreams_params
+        self._output_vstreams_params = output_vstreams_params
+        self._tf_nms_format = tf_nms_format
+        self._total_time = None
+        self._hw_time = None
+        self._network_name_to_outputs = InferVStreams._get_network_to_outputs_mapping(configured_net_group)
+        self._input_name_to_network_name = InferVStreams._get_input_name_to_network_mapping(configured_net_group)
+
+    @staticmethod
+    def _get_input_name_to_network_mapping(configured_net_group):
+        input_name_to_network_mapping = {}
+        for network_name in configured_net_group.get_networks_names():
+            for input_vstream_info in configured_net_group.get_input_vstream_infos(network_name):
+                input_name_to_network_mapping[input_vstream_info.name] = network_name
+        return input_name_to_network_mapping
+
+    @staticmethod
+    def _get_network_to_outputs_mapping(configured_net_group):
+        network_to_outputs_mapping = {}
+        for network_name in configured_net_group.get_networks_names():
+            network_to_outputs_mapping[network_name] = set()
+            for output_vstream_info in configured_net_group.get_output_vstream_infos(network_name):
+                network_to_outputs_mapping[network_name].add(output_vstream_info.name)
+        return network_to_outputs_mapping
+
+    def _make_output_buffers_and_infos(self, input_data, batch_size):
+        output_buffers = {}
+        output_buffers_info = {}
+        already_seen_networks = set()
+        for input_name in input_data.keys():
+            network_name = self._input_name_to_network_name[input_name]
+            if (network_name not in already_seen_networks) :
+                already_seen_networks.add(network_name)
+                for output_name in self._network_name_to_outputs[network_name]:
+                    output_buffers_info[output_name] = OutputLayerUtils(self._configured_net_group._hef, output_name, self._infer_pipeline,
+                        self._net_group_name)
+                    output_tensor_info = output_buffers_info[output_name].output_tensor_info
+                    shape, dtype = output_tensor_info
+                    output_buffers[output_name] = numpy.empty([batch_size] + list(shape), dtype=dtype)
+        return output_buffers, output_buffers_info
+
+    def __enter__(self):
+        self._infer_pipeline = _pyhailort.InferVStreams(self._configured_net_group._configured_network,
+            self._input_vstreams_params, self._output_vstreams_params)
+        return self
+    
+    def infer(self, input_data):
+        """Run inference on the hardware device.
+
+        Args:
+            input_data (dict of :obj:`numpy.ndarray`): Where the key is the name of the input_layer,
+                and the value is the data to run inference on.
+
+        Returns:
+            dict: Output tensors of all output layers. The keys are outputs names and the values
+            are output data tensors as :obj:`numpy.ndarray` (or list of :obj:`numpy.ndarray` in case of nms output and tf_nms_format=False).
+        """
+
+        time_before_infer_calcs = time.time()
+        if not isinstance(input_data, dict):
+            input_stream_infos = self._configured_net_group.get_input_stream_infos()
+            if len(input_stream_infos) != 1:
+                raise Exception("when there is more than one input, the input_data should be of type dict,"
+                                             " mapping between each input_name, and his input_data tensor. number of inputs: {}".format(len(input_stream_infos)))
+            input_data = {input_stream_infos[0].name : input_data}
+
+        batch_size = InferVStreams._get_number_of_frames(input_data)
+        output_buffers, output_buffers_info = self._make_output_buffers_and_infos(input_data, batch_size)
+
+        for input_layer_name in input_data:
+            # TODO: Remove cast after tests are updated and are working
+            self._cast_input_data_if_needed(input_layer_name, input_data)
+            self._validate_input_data_format_type(input_layer_name, input_data)
+            self._make_c_contiguous_if_needed(input_layer_name, input_data)
+
+        with ExceptionWrapper():
+            time_before_infer = time.time()
+            self._infer_pipeline.infer(input_data, output_buffers, batch_size)
+            self._hw_time = time.time() - time_before_infer
+
+        for name, result_array in output_buffers.items():
+            is_nms = output_buffers_info[name].is_nms
+            if not is_nms:
+                continue
+            nms_shape = output_buffers_info[name].vstream_info.nms_shape
+            if self._tf_nms_format:
+                shape = [batch_size] + output_buffers_info[name].old_nms_fomrat_shape
+                output_dtype = output_buffers_info[name].output_dtype
+                quantized_empty_bbox = output_buffers_info[name].quantized_empty_bbox
+                flat_result_array = result_array.reshape(-1)
+                output_buffers[name] = HailoRTTransformUtils.output_raw_buffer_to_nms_tf_format(flat_result_array, shape,
+                    output_dtype, quantized_empty_bbox)
+            else:
+                output_buffers[name] = HailoRTTransformUtils.output_raw_buffer_to_nms_format(result_array, nms_shape.number_of_classes)
+        
+        self._total_time = time.time() - time_before_infer_calcs
+        return output_buffers
+
+    def get_hw_time(self):
+        """Get the hardware device operation time it took to run inference over the last batch.
+
+        Returns:
+            float: Time in seconds.
+        """
+        return self._hw_time
+
+    def get_total_time(self):
+        """Get the total time it took to run inference over the last batch.
+
+        Returns:
+            float: Time in seconds.
+        """
+        return self._total_time
+
+    def _cast_input_data_if_needed(self, input_layer_name, input_data):
+        input_dtype = input_data[input_layer_name].dtype
+        with ExceptionWrapper():
+            input_expected_dtype = self._infer_pipeline.get_host_dtype(input_layer_name)
+        if input_dtype != input_expected_dtype:
+
+            self._logger.warning("Given input data dtype ({}) is different than inferred dtype ({}). "
+                "conversion for every frame will reduce performance".format(input_dtype,
+                    input_expected_dtype))
+            input_data[input_layer_name] = input_data[input_layer_name].astype(input_expected_dtype)
+    
+    def _validate_input_data_format_type(self, input_layer_name, input_data):
+        if input_layer_name not in self._input_vstreams_params:
+            return
+
+        input_data_format = self._input_vstreams_params[input_layer_name].user_buffer_format
+        input_expected_item_size = _pyhailort.get_format_data_bytes(input_data_format)
+        input_item_size = input_data[input_layer_name].dtype.itemsize
+
+        # TODO: Add distinction between float32 and int32 and others
+        if input_item_size != input_expected_item_size:
+            raise HailoRTException("{} numpy array item size is {}, not {}".format(input_layer_name,
+                input_item_size, input_expected_item_size))
+
+    @staticmethod
+    def _get_number_of_frames(input_data):
+        # Checks that all the batch-sizes of the input_data are equals for all input layers
+        if len(input_data) == 0:
+            raise ValueError("Input_data can't be empty")
+        batch_size_of_first_input = list(input_data.values())[0].shape[0]
+        for name, input_data_tensor in input_data.items():
+            if input_data_tensor.shape[0] != batch_size_of_first_input:
+                raise ValueError(
+                    "The number of frames on all input_tensors should be equal! different sizes detected: {} != {}".format(
+                        batch_size_of_first_input, input_data_tensor.shape[0]))
+        return batch_size_of_first_input
+
+    def _make_c_contiguous_if_needed(self, input_layer_name, input_data):
+        if not input_data[input_layer_name].flags.c_contiguous:
+            self._logger.warning("Converting {} numpy array to be C_CONTIGUOUS".format(
+                input_layer_name))
+            input_data[input_layer_name] = numpy.asarray(input_data[input_layer_name], order='C')
+
+    def __exit__(self, *args):
+        self._infer_pipeline.release()
+        return False
+
+
+class HailoRTTransformUtils(object):
+    @staticmethod
+    def get_dtype(data_bytes):
+        """Get data type from the number of bytes."""
+        if data_bytes == 1:
+            return numpy.uint8
+        elif data_bytes == 2:
+            return numpy.uint16
+        elif data_bytes == 4:
+            return numpy.float32
+        raise HailoRTException("unsupported data bytes value")
+
+    @staticmethod
+    def dequantize_output_buffer(src_buffer, dst_buffer, elements_count, quant_info):
+        """De-quantize the data in input buffer `src_buffer` and output it to the buffer `dst_buffer`
+
+        Args:
+            src_buffer (:obj:`numpy.ndarray`): The input buffer containing the data to be de-quantized.
+                The buffer's data type is the source data type.
+            dst_buffer (:obj:`numpy.ndarray`): The buffer that will contain the de-quantized data.
+                The buffer's data type is the destination data type.
+            elements_count (int): The number of elements to de-quantize. This number must not exceed 'src_buffer' or 'dst_buffer' sizes.
+            quant_info (:class:`~hailo_platform.drivers.hailort.pyhailort.QuantInfo`): The quantization info.
+        """
+        with ExceptionWrapper():
+            src_format_type = HailoRTTransformUtils._get_format_type(src_buffer.dtype)
+            dst_format_type = HailoRTTransformUtils._get_format_type(dst_buffer.dtype)
+            _pyhailort.dequantize_output_buffer(src_buffer, dst_buffer, src_format_type, dst_format_type, elements_count, quant_info)
+
+    @staticmethod
+    def dequantize_output_buffer_in_place(raw_buffer, dst_dtype, elements_count, quant_info):
+        """De-quantize the output buffer `raw_buffer` to data type `dst_dtype`.
+
+        Args:
+            raw_buffer (:obj:`numpy.ndarray`): The output buffer to be de-quantized. The buffer's data type is the source data type.
+            dst_dtype (:obj:`numpy.dtype`): The data type to de-quantize `raw_buffer` to.
+            elements_count (int): The number of elements to de-quantize. This number must not exceed 'raw_buffer' size.
+            quant_info (:class:`~hailo_platform.drivers.hailort.pyhailort.QuantInfo`): The quantization info.
+        """
+        with ExceptionWrapper():
+            src_format_type = HailoRTTransformUtils._get_format_type(raw_buffer.dtype)
+            dst_format_type = HailoRTTransformUtils._get_format_type(dst_dtype)
+            _pyhailort.dequantize_output_buffer_in_place(raw_buffer, src_format_type, dst_format_type, elements_count, quant_info)
+
+    @staticmethod
+    def quantize_input_buffer(src_buffer, dst_buffer, elements_count, quant_info):
+        """Quantize the data in input buffer `src_buffer` and output it to the buffer `dst_buffer`
+
+        Args:
+            src_buffer (:obj:`numpy.ndarray`): The input buffer containing the data to be quantized.
+                The buffer's data type is the source data type.
+            dst_buffer (:obj:`numpy.ndarray`): The buffer that will contain the quantized data.
+                The buffer's data type is the destination data type.
+            elements_count (int): The number of elements to quantize. This number must not exceed 'src_buffer' or 'dst_buffer' sizes.
+            quant_info (:class:`~hailo_platform.drivers.hailort.pyhailort.QuantInfo`): The quantization info.
+        """
+        with ExceptionWrapper():
+            src_format_type = HailoRTTransformUtils._get_format_type(src_buffer.dtype)
+            dst_format_type = HailoRTTransformUtils._get_format_type(dst_buffer.dtype)
+            _pyhailort.quantize_input_buffer(src_buffer, dst_buffer, src_format_type, dst_format_type, elements_count, quant_info)
+
+    @staticmethod
+    def output_raw_buffer_to_nms_tf_format(raw_output_buffer, shape, dtype, quantized_empty_bbox):
+        offset = 0
+        # We create the tf_format buffer with reversed width/features for preformance optimization
+        converted_output_buffer = numpy.empty([shape[0], shape[1], shape[3], shape[2]], dtype=dtype)
+        for frame in range(converted_output_buffer.shape[0]):
+            offset = frame * converted_output_buffer.shape[1] * (converted_output_buffer.shape[2] * converted_output_buffer.shape[3] + 1)
+            HailoRTTransformUtils.output_raw_buffer_to_nms_tf_format_single_frame(raw_output_buffer, converted_output_buffer[frame],
+                converted_output_buffer.shape[1], converted_output_buffer.shape[2], quantized_empty_bbox, offset)
+        converted_output_buffer = numpy.swapaxes(converted_output_buffer, 2, 3)
+        return converted_output_buffer
+
+    @staticmethod
+    def output_raw_buffer_to_nms_tf_format_single_frame(raw_output_buffer, converted_output_frame, number_of_classes,
+        max_bboxes_per_class, quantized_empty_bbox, offset=0):
+        for class_i in range(number_of_classes):
+            class_bboxes_amount = int(raw_output_buffer[offset])
+            offset += 1
+            if 0 != class_bboxes_amount:
+                converted_output_frame[class_i][ : class_bboxes_amount][:] = raw_output_buffer[offset : offset + (BBOX_PARAMS * class_bboxes_amount)].reshape(class_bboxes_amount, BBOX_PARAMS)
+                offset += BBOX_PARAMS * class_bboxes_amount
+            converted_output_frame[class_i][class_bboxes_amount : max_bboxes_per_class][:] = quantized_empty_bbox
+
+    @staticmethod
+    def output_raw_buffer_to_nms_format(raw_output_buffer, number_of_classes):
+        converted_output_buffer = []
+        for frame in raw_output_buffer:
+            converted_output_buffer.append(HailoRTTransformUtils.output_raw_buffer_to_nms_format_single_frame(frame, number_of_classes))
+        return converted_output_buffer
+
+    @staticmethod
+    def output_raw_buffer_to_nms_format_single_frame(raw_output_buffer, number_of_classes, offset=0):
+        converted_output_frame = []
+        for class_i in range(number_of_classes):
+            class_bboxes_amount = int(raw_output_buffer[offset])
+            offset += 1
+            if class_bboxes_amount == 0:
+                converted_output_frame.append(numpy.empty([0, BBOX_PARAMS]))
+            else:
+                converted_output_frame.append(raw_output_buffer[offset : offset + (BBOX_PARAMS * class_bboxes_amount)].reshape(
+                    class_bboxes_amount, BBOX_PARAMS))
+                offset += BBOX_PARAMS * class_bboxes_amount
+        return converted_output_frame
+
+    @staticmethod
+    def _get_format_type(dtype):
+        if dtype == numpy.uint8:
+            return FormatType.UINT8
+        elif dtype == numpy.uint16:
+            return FormatType.UINT16
+        elif dtype == numpy.float32:
+            return FormatType.FLOAT32
+        raise HailoRTException("unsupported data type {}".format(dtype))
+
+class InternalEthernetDevice(object):
+    def __init__(self, address, port, response_timeout_seconds=10, max_number_of_attempts=3):
+        self.device = None
+        self._address = address
+        self._port = port
+        self._response_timeout_milliseconds = int(response_timeout_seconds * 1000)
+        self._max_number_of_attempts = max_number_of_attempts
+        with ExceptionWrapper():
+            self.device = _pyhailort.create_eth_device(self._address, len(self._address), self._port,
+                self._response_timeout_milliseconds, self._max_number_of_attempts)
+
+    def __del__(self):
+        self.release()
+
+    def release(self):
+        if self.device is None:
+            return
+        with ExceptionWrapper():
+            _pyhailort.release_device(self.device)
+            self.device = None
+
+
+class PcieDeviceInfo(_pyhailort.PcieDeviceInfo):
+    """Represents pcie device info, includeing domain, bus, device and function.
+    """
+
+    BOARD_LOCATION_HELP_STRING = 'Board location in the format of the command: "lspci -d 1e60: | cut -d\' \' -f1" ([<domain>]:<bus>:<device>.<func>). If not specified the first board is taken.'
+
+    def __init__(self, bus, device, func, domain=None):
+        super(PcieDeviceInfo, self).__init__()
+        self.bus = bus
+        self.device = device
+        self.func = func
+        if domain is None:
+            self.domain = PCIE_ANY_DOMAIN
+        else:
+            self.domain = domain
+
+    def __eq__(self, other):
+        return (self.domain, self.bus, self.device, self.func) == (other.domain, other.bus, other.device, other.func)
+
+    def __str__(self):
+        with ExceptionWrapper():
+            return super().__str__()
+
+    def __repr__(self):
+        return 'PcieDeviceInfo({})'.format(str(self))
+
+    @classmethod
+    def from_string(cls, board_location_str):
+        """Parse pcie device info BDF from string. The format is [<domain>]:<bus>:<device>.<func>"""
+        with ExceptionWrapper():
+            device_info = _pyhailort.PcieDeviceInfo._parse(board_location_str)
+            return PcieDeviceInfo(device_info.bus, device_info.device, device_info.func, device_info.domain)
+
+    @classmethod
+    def argument_type(cls, board_location_str):
+        """PcieDeviceInfo Argument type for argparse parsers"""
+        try:
+            return cls.from_string(board_location_str)
+        except HailoRTException:
+            raise ArgumentTypeError('Invalid device info string, format is [<domain>]:<bus>:<device>.<func>')
+
+
+class InternalPcieDevice(object):
+    def __init__(self, device_info=None, send_identify=True):
+        self.device = None
+        if device_info is None:
+            device_info = InternalPcieDevice.scan_devices()[0]
+        self._device_handle = None
+        self._device_info = device_info
+        with ExceptionWrapper():
+            self.device = _pyhailort.create_pcie_device(self._device_info)
+            self._device_handle = _pyhailort.get_hlpcie_device(self.device)
+            if send_identify:
+                _pyhailort.identify(self.device)
+
+    def __del__(self):
+        self.release()
+
+    def release(self):
+        if self.device is None:
+            return
+        with ExceptionWrapper():
+            _pyhailort.release_device(self.device)
+            self._device_handle = None
+            self.device = None
+
+    @staticmethod
+    def scan_devices():
+        with ExceptionWrapper():
+            return [PcieDeviceInfo(dev_info.bus, dev_info.device, dev_info.func, dev_info.domain)
+                for dev_info in _pyhailort.scan_pcie_devices()]
+
+    def create_debug_log(self):
+        return PcieDebugLog(self)
+
+    def write_memory(self, address, data):
+        with ExceptionWrapper():
+            _pyhailort.direct_write_memory(self.device, address, data)
+
+    def read_memory(self, address, size):
+        with ExceptionWrapper():
+            return _pyhailort.direct_read_memory(self.device, address, size)
+
+
+class PcieDebugLog(object):
+    def __init__(self, pci_device):
+        self._pcie_device = pci_device
+
+    def read(self, count, cpu_id):
+        with ExceptionWrapper():
+            return _pyhailort.read_log(self._pcie_device.device, count, cpu_id)
+
+
+class HailoPowerMeasurementUtils(object):
+    @staticmethod
+    def return_real_sampling_period(sampling_period):
+        """Get a sampling period from the enum."""
+        SamplingPeriodDictionary = dict([
+                (SamplingPeriod.PERIOD_140us, 140),
+                (SamplingPeriod.PERIOD_204us, 204),
+                (SamplingPeriod.PERIOD_332us, 332),
+                (SamplingPeriod.PERIOD_588us, 588),
+                (SamplingPeriod.PERIOD_1100us, 1100),
+                (SamplingPeriod.PERIOD_2116us, 2116),
+                (SamplingPeriod.PERIOD_4156us, 4156),
+                (SamplingPeriod.PERIOD_8244us, 8244),
+        ])
+        return SamplingPeriodDictionary[sampling_period]
+
+    @staticmethod
+    def return_real_averaging_factor(averaging_factor):
+        """Get an averaging factor from the enum."""
+        AveragingFactorDictionary = dict([
+                (AveragingFactor.AVERAGE_1, 1),
+                (AveragingFactor.AVERAGE_4, 4),
+                (AveragingFactor.AVERAGE_16, 16),
+                (AveragingFactor.AVERAGE_64, 64),
+                (AveragingFactor.AVERAGE_128, 128),
+                (AveragingFactor.AVERAGE_256, 256),
+                (AveragingFactor.AVERAGE_512, 512),
+                (AveragingFactor.AVERAGE_1024, 1024),
+        ])
+        return AveragingFactorDictionary[averaging_factor]
+
+
+class HailoPowerMode(_pyhailort.PowerMode):
+    pass
+
+class HailoStreamInterface(_pyhailort.StreamInterface):
+    pass
+
+class HailoStreamDirection(_pyhailort.StreamDirection):
+    pass
+
+class HailoCpuId(_pyhailort.CpuId):
+    pass
+
+class HailoFormatFlags(_pyhailort.FormatFlags):
+    pass
+
+
+class VDevice(object):
+    """Hailo virtual device representation."""
+
+    def __init__(
+            self,
+            params=None, device_infos=None):
+
+        """Create the Hailo virtual device object.
+
+        Args:
+            params (:obj:`hailo_platform.drivers.hailort.pyhailort.VDeviceParams`, optional): VDevice params, call
+                :func:`VDevice.create_params` to get default params. Excludes 'device_infos'.
+            device_infos (list of :obj:`hailo_platform.drivers.hailort.pyhailort.PcieDeviceInfo`, optional): pcie devices infos to create VDevice from,
+                call :func:`PcieDevice.scan_devices` to get list of all available devices. Excludes 'params'.
+        """
+        gc.collect()
+        self._id = "VDevice"
+        self._params = params
+        self._device_infos = device_infos
+        if self._device_infos is not None:
+            if self._params is not None:
+                raise HailoRTException("VDevice can be created from params or device_infos. Both parameters was passed to the c'tor")
+        self._vdevice = None
+        self._loaded_network_groups = []
+        if self._vdevice is None:
+            self._open_vdevice()
+
+        self._creation_pid = os.getpid()
+
+    def _open_vdevice(self):
+        if self._device_infos is not None:
+            with ExceptionWrapper():
+                self._vdevice = _pyhailort.VDevice.create_from_infos(self._device_infos)
+        else:
+            if self._params is None:
+                self._params = VDevice.create_params()
+            with ExceptionWrapper():
+                self._vdevice = _pyhailort.VDevice.create(self._params)
+
+    def __enter__(self):
+        if self._vdevice is None:
+            self._open_vdevice()
+        return self
+
+    def release(self):
+        if self._vdevice is not None:
+            self._vdevice.release()
+            self._vdevice = None
+
+    def __exit__(self, *args):
+        self.release()
+        return False
+
+    def __del__(self):
+        self.release()
+
+    @staticmethod
+    def create_params():
+        with ExceptionWrapper():
+            return _pyhailort.VDeviceParams.default()
+
+    def configure(self, hef, configure_params_by_name={}):
+        """Configures target vdevice from HEF object.
+
+        Args:
+            hef (:class:`~hailo_platform.drivers.hailort.pyhailort.HEF`): HEF to configure the vdevice from
+            configure_params_by_name (dict, optional): Maps between each net_group_name to configure_params. If not provided, default params will be applied
+        """
+        if self._creation_pid != os.getpid():
+            raise HailoRTException("VDevice can only be configured from the process it was created in.")
+        with ExceptionWrapper():
+            configured_apps = self._vdevice.configure(hef._hef, configure_params_by_name)
+        configured_networks = [ConfiguredNetwork(configured_app, self, hef) for configured_app in configured_apps]
+        self._loaded_network_groups.extend(configured_networks)
+        return configured_networks
+
+    def get_physical_devices(self):
+        """Gets the underlying physical devices.
+
+        Return:
+            list of :obj:`~hailo_platform.drivers.hw_object.PcieDevice`: The underlying physical devices.
+        """
+        with ExceptionWrapper():
+            phys_dev_infos = self._vdevice.get_physical_devices_infos()
+        pythonic_dev_infos = [PcieDeviceInfo(dev_info.bus, dev_info.device, dev_info.func, dev_info.domain)
+            for dev_info in phys_dev_infos]
+
+        from hailo_platform.drivers.hw_object import PcieDevice
+        return [PcieDevice(info) for info in pythonic_dev_infos]
+
+    def get_physical_devices_infos(self):
+        """Gets the physical devices infos.
+
+        Return:
+            list of :obj:`~hailo_platform.drivers.hailort.pyhailort.PcieDeviceInfo`: The underlying physical devices infos.
+        """
+        with ExceptionWrapper():
+            return self._vdevice.get_physical_devices_infos()
+
+
+class InputVStreamParams(object):
+    """Parameters of an input virtual stream (host to device)."""
+
+    @staticmethod
+    def make(configured_network, quantized=True, format_type=None, timeout_ms=None, queue_size=None, network_name=None):
+        """Create input virtual stream params from a configured network group. These params determine the format of the
+        data that will be fed into the network group.
+
+        Args:
+            configured_network (:class:`ConfiguredNetwork`): The configured network group for which
+                the params are created.
+            quantized (bool): Whether the data fed into the chip is already quantized. True means
+                the data is already quantized. False means it's HailoRT's responsibility to quantize
+                (scale) the data. Defaults to True.
+            format_type (:class:`~hailo_platform.drivers.hailort.pyhailort.FormatType`): The
+                default format type of the data for all input virtual streams. If quantized is False,
+                the default is :attr:`~hailo_platform.drivers.hailort.pyhailort.FormatType.FLOAT32`. Otherwise,
+                the default is :attr:`~hailo_platform.drivers.hailort.pyhailort.FormatType.AUTO`,
+                which means the data is fed in the same format expected by the device (usually
+                uint8).
+            timeout_ms (int): The default timeout in milliseconds for all input virtual streams.
+                Defaults to DEFAULT_VSTREAM_TIMEOUT_MS. In case of timeout, :class:`HailoRTTimeout` will be raised.
+            queue_size (int): The pipeline queue size. Defaults to DEFAULT_VSTREAM_QUEUE_SIZE.
+            network_name (str): Network name of the requested virtual stream params.
+                If not passed, all the networks in the network group will be addressed.
+
+        Returns:
+            dict: The created virtual streams params. The keys are the vstreams names. The values are the
+            params.
+        """
+        if format_type is None:
+            if not quantized:
+                format_type = FormatType.FLOAT32
+            else:
+                format_type = FormatType.AUTO
+        if timeout_ms is None:
+            timeout_ms = DEFAULT_VSTREAM_TIMEOUT_MS
+        if queue_size is None:
+            queue_size = DEFAULT_VSTREAM_QUEUE_SIZE
+        name = network_name if network_name is not None else configured_network.name
+        with ExceptionWrapper():
+            return configured_network._hef._hef.get_input_vstreams_params(name, quantized,
+                format_type, timeout_ms, queue_size)
+
+    @staticmethod
+    def make_from_network_group(configured_network, quantized=True, format_type=None, timeout_ms=None, queue_size=None, network_name=None):
+        """Create input virtual stream params from a configured network group. These params determine the format of the
+        data that will be fed into the network group.
+
+        Args:
+            configured_network (:class:`ConfiguredNetwork`): The configured network group for which
+                the params are created.
+            quantized (bool): Whether the data fed into the chip is already quantized. True means
+                the data is already quantized. False means it's HailoRT's responsibility to quantize
+                (scale) the data. Defaults to True.
+            format_type (:class:`~hailo_platform.drivers.hailort.pyhailort.FormatType`): The
+                default format type of the data for all input virtual streams. If quantized is False,
+                the default is :attr:`~hailo_platform.drivers.hailort.pyhailort.FormatType.FLOAT32`. Otherwise,
+                the default is :attr:`~hailo_platform.drivers.hailort.pyhailort.FormatType.AUTO`,
+                which means the data is fed in the same format expected by the device (usually
+                uint8).
+            timeout_ms (int): The default timeout in milliseconds for all input virtual streams.
+                Defaults to DEFAULT_VSTREAM_TIMEOUT_MS. In case of timeout, :class:`HailoRTTimeout` will be raised.
+            queue_size (int): The pipeline queue size. Defaults to DEFAULT_VSTREAM_QUEUE_SIZE.
+            network_name (str): Network name of the requested virtual stream params.
+                If not passed, all the networks in the network group will be addressed.
+
+        Returns:
+            dict: The created virtual streams params. The keys are the vstreams names. The values are the
+            params.
+        """
+        return InputVStreamParams.make(configured_network, quantized, format_type, timeout_ms, queue_size, network_name)
+
+
+class OutputVStreamParams(object):
+    """Parameters of an output virtual stream (device to host)."""
+
+    @staticmethod
+    def make(configured_network, quantized=True, format_type=None, timeout_ms=None, queue_size=None, network_name=None):
+        """Create output virtual stream params from a configured network group. These params determine the format of the
+        data that will be fed into the network group.
+
+        Args:
+            configured_network (:class:`ConfiguredNetwork`): The configured network group for which
+                the params are created.
+            quantized (bool): Whether the data fed into the chip is already quantized. True means
+                the data is already quantized. False means it's HailoRT's responsibility to quantize
+                (scale) the data. Defaults to True.
+            format_type (:class:`~hailo_platform.drivers.hailort.pyhailort.FormatType`): The
+                default format type of the data for all output virtual streams. If quantized is False,
+                the default is :attr:`~hailo_platform.drivers.hailort.pyhailort.FormatType.FLOAT32`. Otherwise,
+                the default is :attr:`~hailo_platform.drivers.hailort.pyhailort.FormatType.AUTO`,
+                which means the data is fed in the same format expected by the device (usually
+                uint8).
+            timeout_ms (int): The default timeout in milliseconds for all output virtual streams.
+                Defaults to DEFAULT_VSTREAM_TIMEOUT_MS. In case of timeout, :class:`HailoRTTimeout` will be raised.
+            queue_size (int): The pipeline queue size. Defaults to DEFAULT_VSTREAM_QUEUE_SIZE.
+            network_name (str): Network name of the requested virtual stream params.
+                If not passed, all the networks in the network group will be addressed.
+
+        Returns:
+            dict: The created virtual streams params. The keys are the vstreams names. The values are the
+            params.
+        """
+        if format_type is None:
+            if not quantized:
+                format_type = FormatType.FLOAT32
+            else:
+                format_type = FormatType.AUTO
+        if timeout_ms is None:
+            timeout_ms = DEFAULT_VSTREAM_TIMEOUT_MS
+        if queue_size is None:
+            queue_size = DEFAULT_VSTREAM_QUEUE_SIZE
+        name = network_name if network_name is not None else configured_network.name
+        with ExceptionWrapper():
+            return configured_network._hef._hef.get_output_vstreams_params(name, quantized,
+                format_type, timeout_ms, queue_size)
+
+    @staticmethod
+    def make_from_network_group(configured_network, quantized=True, format_type=None, timeout_ms=None, queue_size=None, network_name=None):
+        """Create output virtual stream params from a configured network group. These params determine the format of the
+        data that will be fed into the network group.
+
+        Args:
+            configured_network (:class:`ConfiguredNetwork`): The configured network group for which
+                the params are created.
+            quantized (bool): Whether the data fed into the chip is already quantized. True means
+                the data is already quantized. False means it's HailoRT's responsibility to quantize
+                (scale) the data. Defaults to True.
+            format_type (:class:`~hailo_platform.drivers.hailort.pyhailort.FormatType`): The
+                default format type of the data for all output virtual streams. If quantized is False,
+                the default is :attr:`~hailo_platform.drivers.hailort.pyhailort.FormatType.FLOAT32`. Otherwise,
+                the default is :attr:`~hailo_platform.drivers.hailort.pyhailort.FormatType.AUTO`,
+                which means the data is fed in the same format expected by the device (usually
+                uint8).
+            timeout_ms (int): The default timeout in milliseconds for all output virtual streams.
+                Defaults to DEFAULT_VSTREAM_TIMEOUT_MS. In case of timeout, :class:`HailoRTTimeout` will be raised.
+            queue_size (int): The pipeline queue size. Defaults to DEFAULT_VSTREAM_QUEUE_SIZE.
+            network_name (str): Network name of the requested virtual stream params.
+                If not passed, all the networks in the network group will be addressed.
+
+        Returns:
+            dict: The created virtual streams params. The keys are the vstreams names. The values are the
+            params.
+        """
+        return OutputVStreamParams.make(configured_network, quantized, format_type, timeout_ms, queue_size, network_name)
+
+    @staticmethod
+    def make_groups(configured_network, quantized=True, format_type=None, timeout_ms=None, queue_size=None):
+        """Create output virtual stream params from a configured network group. These params determine the format of the
+        data that will be fed into the network group. The params groups are splitted with respect to their underlying streams for multi process usges.
+
+        Args:
+            configured_network (:class:`ConfiguredNetwork`): The configured network group for which
+                the params are created.
+            quantized (bool): Whether the data fed into the chip is already quantized. True means
+                the data is already quantized. False means it's HailoRT's responsibility to quantize
+                (scale) the data. Defaults to True.
+            format_type (:class:`~hailo_platform.drivers.hailort.pyhailort.FormatType`): The
+                default format type of the data for all output virtual streams. If quantized is False,
+                the default is :attr:`~hailo_platform.drivers.hailort.pyhailort.FormatType.FLOAT32`. Otherwise,
+                the default is :attr:`~hailo_platform.drivers.hailort.pyhailort.FormatType.AUTO`,
+                which means the data is fed in the same format expected by the device (usually
+                uint8).
+            timeout_ms (int): The default timeout in milliseconds for all output virtual streams.
+                Defaults to DEFAULT_VSTREAM_TIMEOUT_MS. In case of timeout, :class:`HailoRTTimeout` will be raised.
+            queue_size (int): The pipeline queue size. Defaults to DEFAULT_VSTREAM_QUEUE_SIZE.
+
+        Returns:
+            list of dicts: Each element in the list represent a group of params, where the keys are the vstreams names, and the values are the
+            params. The params groups are splitted with respect to their underlying streams for multi process usges.
+        """
+        all_params = OutputVStreamParams.make(configured_network, quantized=quantized, format_type=format_type, timeout_ms=timeout_ms, queue_size=queue_size)
+        low_level_streams_names = [stream_info.name for stream_info in configured_network.get_output_stream_infos()]
+        stream_name_to_vstream_names = {stream_name: configured_network.get_vstream_names_from_stream_name(stream_name) for stream_name in low_level_streams_names}
+        results = []
+        for low_level_stream_name, vstream_names in stream_name_to_vstream_names.items():
+            params_group = {}
+            for vstream_name in vstream_names:
+                # Vstreams that were already seen should not be added to another params_group
+                if all_params[vstream_name] is not None:
+                    params_group[vstream_name] = all_params[vstream_name]
+                    all_params[vstream_name] = None
+            if 0 < len(params_group):
+                results.append(params_group)
+        return results
+
+
+class InputVStream(object):
+    """Represents a single virtual stream in the host to device direction."""
+
+    def __init__(self, send_object):
+        self._send_object = send_object
+        self._input_dtype = self._send_object.dtype
+
+    @property
+    def shape(self):
+        return self._send_object.shape
+    
+    @property
+    def dtype(self):
+        return self._send_object.dtype
+
+    @property
+    def name(self):
+        return self._send_object.info.name
+
+    @property
+    def network_name(self):
+        return self._send_object.info.network_name
+
+    def send(self, input_data):
+        """Send frames to inference.
+
+        Args:
+            input_data (:obj:`numpy.ndarray`): Data to run inference on.
+        """
+        
+        if input_data.dtype != self._input_dtype:
+            input_data = input_data.astype(self._input_dtype)
+
+        if not input_data.flags.c_contiguous:
+            logger = default_logger()
+            logger.warning("Warning - Converting input numpy array to be C_CONTIGUOUS")
+            input_data = numpy.asarray(input_data, order='C')
+
+        batch_number = 0
+        batch_size = 1
+        while batch_number < input_data.shape[0]:
+            data = input_data[batch_number:batch_number + batch_size]
+            with ExceptionWrapper():
+                self._send_object.send(data)
+            batch_number += batch_size
+
+    def flush(self):
+        """Blocks until there are no buffers in the input VStream pipeline."""
+        with ExceptionWrapper():
+            self._send_object.flush()
+
+    @property
+    def info(self):
+        with ExceptionWrapper():
+            return self._send_object.info
+
+class InputVStreams(object):
+    """Input vstreams pipelines that allows to send data, to be used as a context manager."""
+
+    def __init__(self, configured_network, input_vstreams_params):
+        """Constructor for the InputVStreams class.
+
+        Args:
+            configured_network (:class:`ConfiguredNetwork`): The configured network group for which the pipeline is created.
+            input_vstreams_params (dict from str to :class:`InputVStreamParams`): Params for the input vstreams in the pipeline.
+        """
+        self._configured_network = configured_network
+        self._input_vstreams_params = input_vstreams_params
+        self._vstreams = {}
+
+    def __enter__(self):
+        self._input_vstreams_holder = self._configured_network._create_input_vstreams(self._input_vstreams_params)
+        self._input_vstreams_holder.__enter__()
+        for name, vstream in self._input_vstreams_holder.get_all_inputs().items():
+            self._vstreams[name] = InputVStream(vstream)
+        return self
+
+    def get(self, name=None):
+        """Return a single input vstream by its name.
+        
+        Args:
+            name (str): The vstream name. If name=None and there is a single input vstream, that single (:class:`InputVStream`) will be returned.
+                Otherwise, if name=None and there are multiple input vstreams, an exception will be thrown.
+
+        Returns:
+            :class:`InputVStream`: The (:class:`InputVStream`) that corresponds to the given name.
+        """
+        if name is None:
+            if len(self._vstreams) != 1:
+                raise HailoRTException("There is no single input vStream. You must give a name")
+            name = list(self._vstreams.keys())[0]
+        return self._vstreams[name]
+
+    def clear(self):
+        """Clears the vstreams' pipeline buffers."""
+        with ExceptionWrapper():
+            self._input_vstreams_holder.clear()
+
+    def __exit__(self, *args):
+        self._input_vstreams_holder.__exit__(*args)
+        return False
+    
+    def __iter__(self):
+        return iter(self._vstreams.values())
+
+class OutputLayerUtils(object):
+    def __init__(self, hef, vstream_name, pipeline, net_group_name=""):
+        self._hef = hef
+        self._net_group_name = net_group_name
+        self._vstream_info = self._get_vstream_info(vstream_name)
+
+        if isinstance(pipeline, (_pyhailort.InferVStreams)):
+            # TODO: HRT-5754 - Save user buffer instead of dtype and flags.
+            self._output_dtype = pipeline.get_host_dtype(vstream_name)
+            self._output_shape = pipeline.get_shape(vstream_name)
+            self._output_flags = pipeline.get_user_buffer_format(vstream_name).flags
+        else:
+            self._output_dtype = pipeline.dtype
+            self._output_shape = pipeline.shape
+            self._output_flags = pipeline.get_user_buffer_format().flags
+
+        self._is_nms = (self._vstream_info.format.order == FormatOrder.HAILO_NMS)
+        if self._is_nms:
+            self._quantized_empty_bbox = numpy.asarray([0] * BBOX_PARAMS, dtype=self._output_dtype)
+            if not (self._output_flags & _pyhailort.FormatFlags.QUANTIZED):
+                HailoRTTransformUtils.dequantize_output_buffer_in_place(self._quantized_empty_bbox, self._output_dtype,
+                    BBOX_PARAMS, self._vstream_info.quant_info)
+    
+    @property
+    def output_dtype(self):
+        return self._output_dtype
+    
+    @property
+    def output_shape(self):
+        return self._output_shape
+
+    @property
+    def vstream_info(self):
+        return self._vstream_info
+    
+    @property
+    def output_tensor_info(self):
+        return self.output_shape, self.output_dtype
+    
+    @property
+    def is_nms(self):
+        return self._is_nms
+    
+    @property
+    def quantized_empty_bbox(self):
+        return self._quantized_empty_bbox
+
+    def _get_vstream_info(self, name):
+        output_vstream_infos = self._hef.get_output_vstream_infos(self._net_group_name)
+        for info in output_vstream_infos:
+            if info.name == name:
+                return info
+        raise HailoRTException("No vstream matches the given name {}".format(name))
+
+    @property
+    def old_nms_fomrat_shape(self):
+        nms_shape = self._vstream_info.nms_shape
+        return [nms_shape.number_of_classes, BBOX_PARAMS,
+                nms_shape.max_bboxes_per_class]
+
+class OutputVStream(object):
+    """Represents a single output virtual stream in the device to host direction."""
+
+    def __init__(self, configured_network, recv_object, name, tf_nms_format=False, net_group_name=""):
+        self._recv_object = recv_object
+        self._output_layer_utils = OutputLayerUtils(configured_network._hef, name, self._recv_object, net_group_name)
+        self._output_dtype = self._output_layer_utils.output_dtype
+        self._vstream_info = self._output_layer_utils._vstream_info
+        self._output_tensor_info = self._output_layer_utils.output_tensor_info
+        self._is_nms = self._output_layer_utils.is_nms
+        if self._is_nms:
+            self._quantized_empty_bbox = self._output_layer_utils.quantized_empty_bbox
+        self._tf_nms_format = tf_nms_format
+
+    @property
+    def shape(self):
+        return self._recv_object.shape
+
+    @property
+    def dtype(self):
+        return self._recv_object.dtype
+
+    @property
+    def name(self):
+        return self._vstream_info.name
+
+    @property
+    def network_name(self):
+        return self._vstream_info.network_name
+
+    def recv(self):
+        """Receive frames after inference.
+
+        Returns:
+            :obj:`numpy.ndarray`: The output of the inference for a single frame. The returned
+            tensor does not include the batch dimension.
+            In case of nms output and tf_nms_format=False, returns list of :obj:`numpy.ndarray`.
+        """
+        result_array = None
+        with ExceptionWrapper():
+            result_array = self._recv_object.recv()
+
+        if self._is_nms:
+            nms_shape = self._vstream_info.nms_shape
+            if self._tf_nms_format:
+                nms_results_tesnor = result_array
+                # We create the tf_format buffer with reversed width/features for preformance optimization
+                shape = self._output_layer_utils.old_nms_fomrat_shape
+                result_array = numpy.empty([shape[0], shape[2], shape[1]], dtype=self._output_dtype)
+                HailoRTTransformUtils.output_raw_buffer_to_nms_tf_format_single_frame(nms_results_tesnor, result_array,
+                    nms_shape.number_of_classes,
+                    nms_shape.max_bboxes_per_class, self._quantized_empty_bbox)
+                result_array = numpy.swapaxes(result_array, 1, 2)
+            else:
+                result_array = HailoRTTransformUtils.output_raw_buffer_to_nms_format_single_frame(result_array,
+                    nms_shape.number_of_classes)
+        return result_array
+
+    @property
+    def info(self):
+        with ExceptionWrapper():
+            return self._recv_object.info
+
+class OutputVStreams(object):
+    """Output virtual streams pipelines that allows to receive data, to be used as a context manager."""
+
+    def __init__(self, configured_network, output_vstreams_params, tf_nms_format=False):
+        """Constructor for the OutputVStreams class.
+
+        Args:
+            configured_network (:class:`ConfiguredNetwork`): The configured network group for which
+                the pipeline is created.
+            output_vstreams_params (dict from str to :class:`OutputVStreamParams`): Params for the
+                output vstreams in the pipeline.
+            tf_nms_format (bool, optional): indicates whether the returned nms outputs should be in
+                Hailo format or TensorFlow format. Default is False (using Hailo format).
+
+                * Hailo format -- list of :obj:`numpy.ndarray`. Each element represents th
+                  detections (bboxes) for the class, and its shape is
+                  ``[number_of_detections, BBOX_PARAMS]``
+                * TensorFlow format -- :obj:`numpy.ndarray` of shape
+                  ``[class_count, BBOX_PARAMS, detections_count]`` padded with empty bboxes.
+        """
+        self._configured_network = configured_network
+        self._net_group_name = configured_network.name
+        self._output_vstreams_params = output_vstreams_params
+        self._output_tensor_info = {}
+        self._tf_nms_format = tf_nms_format
+        self._vstreams = {}
+
+    def __enter__(self):
+        self._output_vstreams_holder = self._configured_network._create_output_vstreams(self._output_vstreams_params)
+        self._output_vstreams_holder.__enter__()
+        for name, vstream in self._output_vstreams_holder.get_all_outputs().items():
+            self._vstreams[name] = OutputVStream(self._configured_network, vstream, name,
+                tf_nms_format=self._tf_nms_format, net_group_name=self._net_group_name)
+        return self
+
+    def get(self, name=None):
+        """Return a single output vstream by its name.
+        
+        Args:
+            name (str): The vstream name. If name=None and there is a single output vstream, that single (:class:`OutputVStream`) will be returned.
+                Otherwise, if name=None and there are multiple output vstreams, an exception will be thrown.
+
+        Returns:
+            :class:`OutputVStream`: The (:class:`OutputVStream`) that corresponds to the given name.
+        """
+        if name is None:
+            if len(self._vstreams) != 1:
+                raise HailoRTException("There is no single output vStream. You must give a name")
+            name = list(self._vstreams.keys())[0]
+        return self._vstreams[name]
+
+    def clear(self):
+        """Clears the vstreams' pipeline buffers."""
+        with ExceptionWrapper():
+            self._output_vstreams_holder.clear()
+
+    def __exit__(self, *args):
+        self._output_vstreams_holder.__exit__(*args)
+        return False
+
+    def __iter__(self):
+        return iter(self._vstreams.values())
diff --git a/hailort/libhailort/bindings/python/platform/hailo_platform/drivers/hw_object.py b/hailort/libhailort/bindings/python/platform/hailo_platform/drivers/hw_object.py
new file mode 100644 (file)
index 0000000..c285caf
--- /dev/null
@@ -0,0 +1,465 @@
+#!/usr/bin/env python
+
+"""Hailo hardware API"""
+from __future__ import division
+
+import gc
+import os
+
+from contextlib import contextmanager
+
+from hailo_platform.drivers.control_object import UdpHcpControl, PcieHcpControl
+from hailo_platform.common.compatibility import ensure_str
+from hailo_platform.common.targets.inference_targets import InferenceTargets, InferenceObject, InferenceTargetException
+from hailo_platform.common.logger.logger import default_logger
+from hailo_platform.drivers.hailo_controller.hailo_control_protocol import BoardInformation
+
+from hailo_platform.drivers.hailort.pyhailort import ConfiguredNetwork, InternalEthernetDevice, InternalPcieDevice, HailoRTTransformUtils, HailoUdpScan, HailoRTException
+
+
+class HailoHWObjectException(InferenceTargetException):
+    """Raised in any error related to Hailo hardware."""
+    pass
+
+
+class HailoHWObject(InferenceObject):
+    """Abstract Hailo hardware device representation."""
+
+    IS_NUMERIC = True
+
+    def __init__(self):
+        """Create the Hailo hardware object."""
+        super(HailoHWObject, self).__init__()
+        self._last_interact_time = None
+        self._total_time = None
+        self._id = None
+        self._hw_arch = None
+        self._logger = default_logger()
+        self._debug = False
+        self._is_device_used = False
+        self._hef_loaded = False
+
+    @property
+    def device_id(self):
+        """Getter for the device_id.
+
+        Returns:
+            str: A string ID of the device. BDF for PCIe devices, MAC address for Ethernet devices, "Core" for core devices.
+        """
+        return self._id
+
+    @property
+    def sorted_output_layer_names(self):
+        """Getter for the property sorted_output_names.
+
+        Returns:
+            list of str: Sorted list of the output layer names.
+        """
+        if len(self._loaded_network_groups) != 1:
+            raise HailoHWObjectException("Access to sorted_output_layer_names is only allowed when there is a single loaded network group")
+        return self._loaded_network_groups[0].get_sorted_output_names()
+
+    @contextmanager
+    def use_device(self, *args, **kwargs):
+        """A context manager that wraps the usage of the device (deprecated)."""
+        self._is_device_used = True
+        yield
+        self._is_device_used = False
+
+    def get_output_device_layer_to_original_layer_map(self):
+        """Get a mapping between the device outputs to the layers' names they represent.
+
+        Returns:
+            dict: Keys are device output names and values are lists of layers' names.
+        """
+        if len(self._loaded_network_groups) != 1:
+            raise HailoHWObjectException("Access to layer names is only allowed when there is a single loaded network group")
+        return {stream_info.name : self._loaded_network_groups[0].get_vstream_names_from_stream_name(stream_info.name)
+            for stream_info in self.get_output_stream_infos()}
+
+    def get_original_layer_to_device_layer_map(self):
+        """Get a mapping between the layer names and the device outputs that contain them.
+
+        Returns:
+            dict: Keys are the names of the layers and values are device outputs names.
+        """
+        if len(self._loaded_network_groups) != 1:
+            raise HailoHWObjectException("Access to layer names is only allowed when there is a single loaded network group")
+        return {vstream_info.name : self._loaded_network_groups[0].get_stream_names_from_vstream_name(vstream_info.name)
+            for vstream_info in self.get_output_vstream_infos()}
+
+    @property
+    def device_input_layers(self):
+        """Get a list of the names of the device's inputs."""
+        return [layer.name for layer in self.get_input_stream_infos()]
+
+    @property
+    def device_output_layers(self):
+        """Get a list of the names of the device's outputs."""
+        return [layer.name for layer in self.get_output_stream_infos()]
+
+    def hef_loaded(self):
+        """Return True if this object has loaded the model HEF to the hardware device."""
+        return self._hef_loaded
+
+    def outputs_count(self):
+        """Return the amount of output tensors that are returned from the hardware device for every
+        input image.
+        """
+        return len(self.get_output_vstream_infos())
+
+    def _clear_shapes(self):
+        self._hw_consts = None
+
+    @property
+    def model_name(self):
+        """Get the name of the current model.
+
+        Returns:
+            str: Model name.
+        """
+        if len(self._loaded_network_groups) == 1:
+            return self._loaded_network_groups[0].name
+        raise HailoHWObjectException(
+            "This function is only supported when there is exactly 1 loaded network group. one should use HEF.get_network_group_names() / ConfiguredNetwork.name / ActivatedNetwork.name")
+
+    def get_output_shapes(self):
+        """Get the model output shapes, as returned to the user (without any hardware padding).
+
+        Returns:
+            Tuple of output shapes, sorted by the output names.
+        """
+        if len(self._loaded_network_groups) != 1:
+            raise HailoHWObjectException("Calling get_output_shapes is only allowed when there is a single loaded network group")
+        return self._loaded_network_groups[0].get_output_shapes()
+
+
+class HailoChipObject(HailoHWObject):
+    """Hailo hardware device representation"""
+    IS_NUMERIC = True
+    IS_HARDWARE = True
+
+    def __init__(self):
+        """Create the Hailo Chip hardware object."""
+        super(HailoChipObject, self).__init__()
+        self._id = "Generic Hailo Device"
+        self._control_object = None
+        self._loaded_network_groups = []
+        self._creation_pid = os.getpid()
+
+    @property
+    def control(self):
+        """:class:`HailoControl <hailo_platform.drivers.control_object.HailoControl>`: Returns
+        the control object of this device, which implements the control API of the Hailo device.
+
+        .. attention:: Use the low level control API with care.
+        """
+        return self._control_object
+
+    def get_all_input_layers_dtype(self):
+        """Get the model inputs dtype.
+
+        Returns:
+            dict of :obj:'numpy.dtype': where the key is model input_layer name, and the value is dtype as the device expect to get for this input. 
+        """
+        return {stream.name: HailoRTTransformUtils.get_dtype(stream.data_bytes) for stream in self.get_input_stream_infos()}
+
+    def get_input_vstream_infos(self, network_name=None):
+        """Get input vstreams information of a specific network group.
+
+        Args:
+            network_name (str, optional): The name of the network to access. In case not given, all the networks in the network group will be addressed.
+
+        Returns:
+            If there is exactly one configured network group, returns a list of
+            :obj:`hailo_platform.drivers.hailort._pyhailort.VStreamInfo`: with all the information objects of all input vstreams
+        """
+
+        if len(self._loaded_network_groups) != 1:
+            raise HailoHWObjectException("Access to network vstream info is only allowed when there is a single loaded network group")
+        return self._loaded_network_groups[0].get_input_vstream_infos(network_name=network_name)
+
+    def get_output_vstream_infos(self, network_name=None):
+        """Get output vstreams information of a specific network group.
+
+        Args:
+            network_name (str, optional): The name of the network to access. In case not given, all the networks in the network group will be addressed.
+
+        Returns:
+            If there is exactly one configured network group, returns a list of
+            :obj:`hailo_platform.drivers.hailort._pyhailort.VStreamInfo`: with all the information objects of all output vstreams
+        """
+
+        if len(self._loaded_network_groups) != 1:
+            raise HailoHWObjectException("Access to network vstream info is only allowed when there is a single loaded network group")
+        return self._loaded_network_groups[0].get_output_vstream_infos(network_name=network_name)
+
+    def get_all_vstream_infos(self, network_name=None):
+        """Get input and output vstreams information.
+
+        Args:
+            network_name (str, optional): The name of the network to access. In case not given, all the networks in the network group will be addressed.
+
+        Returns:
+            If there is exactly one configured network group, returns a list of
+            :obj:`hailo_platform.drivers.hailort._pyhailort.VStreamInfo`: with all the information objects of all input and output vstreams
+        """
+
+        if len(self._loaded_network_groups) != 1:
+            raise HailoHWObjectException("Access to network vstream info is only allowed when there is a single loaded network group")
+        return self._loaded_network_groups[0].get_all_vstream_infos(network_name=network_name)
+
+    def get_input_stream_infos(self, network_name=None):
+        """Get the input low-level streams information of a specific network group.
+
+        Args:
+            network_name (str, optional): The name of the network to access. In case not given, all the networks in the network group will be addressed.
+
+        Returns:
+            If there is exactly one configured network group, returns a list of
+            :obj:`hailo_platform.drivers.hailort._pyhailort.VStreamInfo`: with information objects
+            of all input low-level streams.
+        """
+        if len(self._loaded_network_groups) != 1:
+            raise HailoHWObjectException("Access to network stream info is only allowed when there is a single loaded network group")
+        return self._loaded_network_groups[0].get_input_stream_infos(network_name=network_name)
+
+    def get_output_stream_infos(self, network_name=None):
+        """Get the output low-level streams information of a specific network group.
+
+        Args:
+            network_name (str, optional): The name of the network to access. In case not given, all the networks in the network group will be addressed.
+
+        Returns:
+            If there is exactly one configured network group, returns a list of
+            :obj:`hailo_platform.drivers.hailort._pyhailort.VStreamInfo`: with information objects
+            of all output low-level streams.
+        """
+        if len(self._loaded_network_groups) != 1:
+            raise HailoHWObjectException("Access to network stream info is only allowed when there is a single loaded network group")
+        return self._loaded_network_groups[0].get_output_stream_infos(network_name=network_name)
+
+    def get_all_stream_infos(self, network_name=None):
+        """Get input and output streams information of a specific network group.
+
+        Args:
+            network_name (str, optional): The name of the network to access. In case not given, all the networks in the network group will be addressed.
+
+        Returns:
+            If there is exactly one configured network group, returns a list of
+            :obj:`hailo_platform.drivers.hailort._pyhailort.StreamInfo`: with all the information objects of all input and output streams
+        """
+
+        if len(self._loaded_network_groups) != 1:
+            raise HailoHWObjectException("Access to network stream info is only allowed when there is a single loaded network group")
+        return self._loaded_network_groups[0].get_all_stream_infos(network_name=network_name)
+
+    @property
+    def loaded_network_groups(self):
+        """Getter for the property _loaded_network_groups.
+
+        Returns:
+            list of :obj:`ConfiguredNetwork`: List of the the configured network groups loaded on the device.
+        """
+        return self._loaded_network_groups
+
+    @property
+    def _loaded_network_group(self):
+        if len(self._loaded_network_groups) != 1:
+            raise HailoHWObjectException("Access to network layer info is only allowed when there is a single loaded network group")
+        return self._loaded_network_groups[0]
+
+    def configure(self, hef, configure_params_by_name={}):
+        """Configures target device from HEF object.
+
+        Args:
+            hef (:class:`~hailo_platform.drivers.hailort.pyhailort.HEF`): HEF to configure the device from
+            configure_params_by_name (dict, optional): Maps between each net_group_name to configure_params. If not provided, default params will be applied
+        """
+        if self._creation_pid != os.getpid():
+            raise HailoRTException("Device can only be configured from the process it was created in.")
+        configured_apps = self._control_object.configure(hef, configure_params_by_name)
+        self._hef_loaded = True
+        configured_networks = [ConfiguredNetwork(configured_app, self, hef) for configured_app in configured_apps]
+        self._loaded_network_groups.extend(configured_networks)
+        return configured_networks
+
+    def get_input_shape(self, name=None):
+        """Get the input shape (not padded) of a network.
+
+        Args:
+            name (str, optional): The name of the desired input. If a name is not provided, return
+                the first input_dataflow shape.
+
+        Returns:
+            Tuple of integers representing the input_shape.
+        """
+        if name is None:
+            name = self.get_input_vstream_infos()[0].name
+
+        for input_vstream in self.get_input_vstream_infos():
+            if input_vstream.name == name:
+                return input_vstream.shape
+
+        raise HailoHWObjectException("There is no input named {}! the input names are: {}".format(name,
+            [input_vstream.name for input_vstream in self.get_input_vstream_infos()]))
+
+    def get_index_from_name(self, name):
+        """Get the index in the output list from the name.
+
+        Args:
+            name (str): The name of the output.
+
+        Returns:
+            int: The index of the layer name in the output list.
+        """
+        try:
+            return self.sorted_output_layer_names.index(ensure_str(name))
+        except ValueError:
+            if len(self.sorted_output_layer_names) == 1:
+                # Case regard to SDK-9366 - see Jira for details.
+                self._logger.warning('Incorrect meta item - layer defuse_name does not match layer name.')
+                return 0
+            else:
+                raise HailoHWObjectException("Could not get index for outputs properly.")
+
+
+class EthernetDevice(HailoChipObject):
+    """Represents any Hailo hardware device that supports UDP control and dataflow."""
+
+    NAME = InferenceTargets.UDP_CONTROLLER
+
+    def __init__(
+            self,
+            remote_ip,
+            remote_control_port=22401):
+        """Create the Hailo UDP hardware object.
+
+        Args:
+            remote_ip (str): Device IP address.
+            remote_control_port (int, optional): UDP port to which the device listens for control.
+                Defaults to 22401.
+        """
+
+        super(EthernetDevice, self).__init__()
+
+        self._remote_ip = remote_ip
+        gc.collect()
+        self._remote_control_port = remote_control_port
+        # EthernetDevice __del__ function tries to release self._eth_device.
+        # to avoid AttributeError if the __init__ func fails, we set it to None first.
+        # https://stackoverflow.com/questions/6409644/is-del-called-on-an-object-that-doesnt-complete-init
+        self._eth_device = None
+        self._id = "{}".format(self._remote_ip)
+
+        if not self.hef_loaded():
+            self._open_device()
+
+        identity = self._control_object._device_id
+        self._hw_arch = BoardInformation.get_hw_arch_str(identity.device_architecture)
+
+    @staticmethod
+    def scan_devices(interface_name, timeout_seconds=3):
+        """Scans for all eth devices on a specific network interface.
+
+        Args:
+            interface_name (str): Interface to scan.
+            timeout_seconds (int, optional): timeout for scan operation. Defaults to 3.
+        Returns:
+            list of str: IPs of scanned devices.
+        """
+        udp_scanner = HailoUdpScan()
+        return udp_scanner.scan_devices(interface_name, timeout_seconds=timeout_seconds)
+
+    def _open_device(self):
+        self._eth_device = InternalEthernetDevice(self._remote_ip, self._remote_control_port)
+        self._control_object = UdpHcpControl(self._remote_ip, device=self._eth_device, remote_control_port=self._remote_control_port)
+
+    def _close(self):
+        if self._eth_device is not None:
+            self._eth_device.release()
+            self._eth_device = None
+
+    def __enter__(self):
+        if not self.hef_loaded:
+            return None
+        self._open_device()
+        return self
+
+    def __exit__(self, *args):
+        self._close()
+        return False
+
+    def __del__(self):
+        self._close()
+
+    @property
+    def remote_ip(self):
+        """Return the IP of the remote device."""
+        return self._remote_ip
+
+
+class PcieDevice(HailoChipObject):
+    """Hailo PCIe production device representation."""
+
+    NAME = InferenceTargets.PCIE_CONTROLLER
+
+    def __init__(
+            self,
+            device_info=None):
+
+        """Create the Hailo PCIe hardware object.
+
+        Args:
+            device_info (:obj:`hailo_platform.drivers.hailort.pyhailort.PcieDeviceInfo`, optional): Device info to create, call
+                :func:`PcieDevice.scan_devices` to get list of all available devices.
+        """
+        super(PcieDevice, self).__init__()
+
+        gc.collect()
+        # PcieDevice __del__ function tries to release self._pcie_device.
+        # to avoid AttributeError if the __init__ func fails, we set it to None first.
+        # https://stackoverflow.com/questions/6409644/is-del-called-on-an-object-that-doesnt-complete-init
+        self._pcie_device = None
+        self._device_info = device_info
+
+        if not self.hef_loaded():
+            self._open_device()
+
+        # At this point self._device_info is already initialized
+        self._id = "{}".format(self._device_info)
+
+        identity = self._control_object._device_id
+        self._hw_arch = BoardInformation.get_hw_arch_str(identity.device_architecture)
+
+    @staticmethod
+    def scan_devices():
+        """Scans for all pcie devices on the system.
+
+        Returns:
+            list of :obj:`hailo_platform.drivers.hailort.pyhailort.PcieDeviceInfo`
+        """
+        return InternalPcieDevice.scan_devices()
+
+    def _open_device(self):
+        self._pcie_device = InternalPcieDevice(self._device_info)
+        self._device_info = self._pcie_device._device_info
+        self._control_object = PcieHcpControl(device=self._pcie_device, device_info=self._device_info)
+
+    def _close(self):
+        if self._pcie_device is not None:
+            self._pcie_device.release()
+            self._pcie_device = None
+
+    def __enter__(self):
+        if not self.hef_loaded:
+            return None
+        self._open_device()
+        return self
+
+    def __exit__(self, *args):
+        self._close()
+        return False
+
+    def __del__(self):
+        self._close()
diff --git a/hailort/libhailort/bindings/python/platform/hailo_platform/paths_manager/__init__.py b/hailort/libhailort/bindings/python/platform/hailo_platform/paths_manager/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/hailort/libhailort/bindings/python/platform/hailo_platform/paths_manager/paths.py b/hailort/libhailort/bindings/python/platform/hailo_platform/paths_manager/paths.py
new file mode 100644 (file)
index 0000000..6855145
--- /dev/null
@@ -0,0 +1,10 @@
+#!/usr/bin/env python
+import hailo_platform
+from hailo_platform.common.paths_manager.paths import SDKPaths, Singleton
+import os
+from future.utils import with_metaclass
+
+
+class PlatformPaths(with_metaclass(Singleton, SDKPaths)):
+    def join_platform(self, path):
+        return os.path.join(os.path.abspath(hailo_platform.__path__[0]), path)
diff --git a/hailort/libhailort/bindings/python/platform/hailo_platform/tools/__init__.py b/hailort/libhailort/bindings/python/platform/hailo_platform/tools/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/hailort/libhailort/bindings/python/platform/hailo_platform/tools/benchmark_command.py b/hailort/libhailort/bindings/python/platform/hailo_platform/tools/benchmark_command.py
new file mode 100644 (file)
index 0000000..cc5eb6a
--- /dev/null
@@ -0,0 +1,5 @@
+from hailo_platform.common.tools.cmd_utils.base_utils import HailortCliUtil
+
+class BenchmarkCommandCLI(HailortCliUtil):
+    def __init__(self, parser):
+        super().__init__(parser, 'benchmark')
diff --git a/hailort/libhailort/bindings/python/platform/hailo_platform/tools/cmd_utils/__init__.py b/hailort/libhailort/bindings/python/platform/hailo_platform/tools/cmd_utils/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/hailort/libhailort/bindings/python/platform/hailo_platform/tools/cmd_utils/main.py b/hailort/libhailort/bindings/python/platform/hailo_platform/tools/cmd_utils/main.py
new file mode 100644 (file)
index 0000000..8133f35
--- /dev/null
@@ -0,0 +1,108 @@
+#!/usr/bin/env python
+
+import argparse
+import argcomplete
+import sys
+
+import hailo_platform
+from hailo_platform.common.tools.cmd_utils.base_utils import HailortCliUtil, Helper, HailortCliUtilError
+from hailo_platform.tools.firmware.update_firmware import FWUpdaterCLI
+from hailo_platform.tools.firmware.update_second_stage import SSBUpdaterCLI
+from hailo_platform.tools.udp_rate_limiter import UDPRateLimiterCLI
+from hailo_platform.tools.fw_control import ControlCommandCLI, ScanCommandCLI, LoggerCommandCLI, MeasurePowerCommandCLI
+from hailo_platform.tools.run_command import RunCommandCLI
+from hailo_platform.tools.infer_cli import InferCLI
+from hailo_platform.tools.firmware.sensor_config import SensorConfigCommandCLI
+from hailo_platform.tools.firmware.configure_firmware import FWConfigCommandCLI
+from hailo_platform.tools.benchmark_command import BenchmarkCommandCLI
+
+
+# Note: PlatformCommands are external dependencies in phase2-sdk/demos repo; don't change!
+class PlatformCommands:
+    INVALID_COMMAND_EXIT_CODE = 1
+
+    PLATFORM_COMMANDS = {
+        'fw-update': ('Firmware update tool', FWUpdaterCLI),
+        'ssb-update': ('Second stage boot update tool', SSBUpdaterCLI),
+        'fw-config': ('Firmware configuration tool', FWConfigCommandCLI),
+        'udp-limiter': ('UDP rate limitation tool', UDPRateLimiterCLI),
+        'udp': ('Alias to udp-limiter', UDPRateLimiterCLI),
+        'fw-control': ('Useful firmware control operations', ControlCommandCLI),
+        'fw-logger': ('Download fw logs to a file', LoggerCommandCLI),
+        'scan': ('Scans for devices (Ethernet or PCIE)', ScanCommandCLI),
+        'broadcast': ('Scans for devices on a given interface (alias to \'hailo scan\')', ScanCommandCLI),
+        'sensor-config': ('Sensor configuration tool', SensorConfigCommandCLI),
+        'infer': ('Run a compiled network - infer command is deprecated and will be removed in a future release. Please use \'hailo run\' instead.', InferCLI),
+        'run': ('Run a compiled network', RunCommandCLI),
+        'benchmark': ('Measure basic performance on compiled network', BenchmarkCommandCLI),
+        'measure-power': ('Measures power consumption', MeasurePowerCommandCLI),
+    }
+
+    def __init__(self):
+        self.parser = argparse.ArgumentParser(description=self._get_description())
+        self.subparsers = self.parser.add_subparsers(help='Hailo utilities aimed to help with everything you need')
+        self.COMMANDS = {}
+        self.COMMANDS.update(type(self).PLATFORM_COMMANDS)
+
+        try:
+            # If sdk_client is importable - add its commands to this tool
+            from hailo_sdk_client.tools.cmd_utils.main import ClientCommands
+            self.COMMANDS.update(ClientCommands.SDK_COMMANDS)
+        except ImportError:
+            pass
+
+    @staticmethod
+    def _get_description():
+        return 'Hailo Platform SW v{} command line utilities'.format(hailo_platform.__version__)
+
+    def run(self):
+        argv = sys.argv[1:]
+        return self._run(argv)
+
+    # Dependency injection for testing
+    def _run(self, argv):
+        self.COMMANDS['help'] = ('show the list of commands', Helper(self.COMMANDS))
+        # Create the commands and let them set the arguments
+        commands = {}
+        for command_name, (help_, command_class) in self.COMMANDS.items():
+            commands[command_name] = command_class(self.subparsers.add_parser(command_name, help=help_))
+
+        argcomplete.autocomplete(self.parser)
+        # HailortCliUtil commands are parsed by hailortcli:
+        # * argv from after the command name are forwarded to hailortcli.
+        # * E.g. calling `hailo udp-limiter arg1 arg2` will result in the call `commands['udp-limiter'].run(['arg1', 'arg2'])`.
+        #   Inturn, this will create the process `hailortcli udp-rate-limiter arg1 arg2` (via the UDPRateLimiterCLI class).
+        # In order to support this, we first check if the first argument in argv is the name of a HailortCliUtil class.
+        # If so, we'll pass the arguments to the HailortCliUtil, otherwise we parse with argparse as usual.
+        if len(argv) == 0:
+            self.parser.print_help()
+            return self.INVALID_COMMAND_EXIT_CODE
+
+        command_name = argv[0]
+        if (command_name in commands) and isinstance(commands[command_name], HailortCliUtil):
+            # HailortCliUtil just passes the rest of the argv to hailortcli
+            try :
+                return commands[command_name].run(argv[1:])
+            except HailortCliUtilError as e:
+                print('\n'+ str(e))
+                return
+
+        # This isn't a HailortCliUtil commnad, parse with argparse
+        args = self.parser.parse_args(argv)
+        # Due to a bug in Python's handling of subparsers, we cannot require a sub-command using argparse. A manual
+        # check is performed here so we can print out a proper message. (https://bugs.python.org/issue9253)
+        if not vars(args):
+            self.parser.print_help()
+            return self.INVALID_COMMAND_EXIT_CODE
+
+        # The commands themself, set the default func.
+        return args.func(args)
+
+
+def main():
+    a = PlatformCommands()
+    return a.run()
+
+
+if __name__ == '__main__':
+    main()
diff --git a/hailort/libhailort/bindings/python/platform/hailo_platform/tools/firmware/__init__.py b/hailort/libhailort/bindings/python/platform/hailo_platform/tools/firmware/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/hailort/libhailort/bindings/python/platform/hailo_platform/tools/firmware/configure_firmware.py b/hailort/libhailort/bindings/python/platform/hailo_platform/tools/firmware/configure_firmware.py
new file mode 100644 (file)
index 0000000..d1e6bf8
--- /dev/null
@@ -0,0 +1,15 @@
+#!/usr/bin/env python
+
+"""Parses a configuration file that contains user or board configurations.
+"""
+from hailo_platform.common.tools.cmd_utils.base_utils import HailortCliUtil
+
+class FWConfigCommandCLI(HailortCliUtil):
+    """CLI tool for changing the FW configuration (User Config)"""
+    def __init__(self, parser):
+        super().__init__(parser, 'fw-config')
+
+class BoardConfigCommandCLI(HailortCliUtil):
+    """CLI tool for changing the FW configuration (Board Config)"""
+    def __init__(self, parser):
+        super().__init__(parser, 'board-config')
diff --git a/hailort/libhailort/bindings/python/platform/hailo_platform/tools/firmware/example_config.json b/hailort/libhailort/bindings/python/platform/hailo_platform/tools/firmware/example_config.json
new file mode 100644 (file)
index 0000000..f1a08bf
--- /dev/null
@@ -0,0 +1,14 @@
+{
+    "network":
+    {
+        "static_netmask": "255.255.255.0",
+        "should_use_dhcp": false,
+        "mac_address": "80:00:DE:AD:BE:EF"
+    },
+    "system":
+    {
+        "name": "Hailo-8",
+        "supported_aspm_states": "ASPM L1 ONLY",
+        "temperature_throttling_enable": true
+    }
+}
diff --git a/hailort/libhailort/bindings/python/platform/hailo_platform/tools/firmware/firmware_config.py b/hailort/libhailort/bindings/python/platform/hailo_platform/tools/firmware/firmware_config.py
new file mode 100644 (file)
index 0000000..7b5738a
--- /dev/null
@@ -0,0 +1,286 @@
+#!/usr/bin/env python
+
+"""Parses a configuration file that contains the definitions of possible user configurations
+    Availabe in the firmware.
+"""
+
+from builtins import object
+import json
+
+from abc import ABCMeta
+from collections import OrderedDict
+from future.utils import with_metaclass
+
+
+
+class ConfigSyntaxException(Exception):
+    """Raised when there is a syntax error in the configuration file."""
+    pass
+
+
+class UnsupportedConfigVersionException(Exception):
+    """Raised when the version of the configuration file is not supported."""
+    pass
+
+
+class ConfigFormattingException(Exception):
+    """Raised when there is a formatting error in the firmware's configuration."""
+    pass
+
+class ConfigDeserializationException(Exception):
+    """Raised when there is an error while parsing a binary configuration."""
+    pass
+
+
+class ConfigLine(with_metaclass(ABCMeta, object)):
+    """An abstract config line class. Implements logic that is shared between a few types
+        of configuration lines.
+    """
+
+    def __init__(self, config_name, config_id):
+        """Initializes a new ConfigLine object.
+
+        Args:
+            config_name(str): Name defined for the configuration in the line.
+            config_id(int): Config ID (the integer representing the config).
+        """
+        self._name = None
+        self.name = config_name
+        self._id = config_id
+
+    @property
+    def name(self):
+        """Returns:
+            str: Name of the category.
+        """
+        return self._name
+
+    @name.setter
+    def name(self, config_name):
+        if not (config_name.isalnum() or config_name[0].isalpha()):
+            raise ConfigSyntaxException('Configuration name must be alphanumeric (with a first '
+                                        'alphabetical character) received: {}'.format(
+                                            config_name))
+        self._name = config_name.lower()
+
+    @property
+    def id_value(self):
+        """Returns:
+            int: Value of the entry's ID.
+        """
+        return self._id
+
+
+class ConfigEntry(ConfigLine):
+    """A configuration entry."""
+
+    _PRIMITIVE_TYPES = {
+        1: 'int8_t',
+        2: 'int16_t',
+        4: 'int32_t'}
+
+    def __init__(self, entry_name, entry_id, category_id, size, length, sign, deserializer, length_before_serialization=None):
+        """Initializes a new ConfigEntry object.
+
+        Args:
+            entry_name(str): Entry name.
+            entry_id(int): Entry ID (the integer representing the entry). (Should be 0-65535)
+            category_id(int): ID of the category that contains this entry. (Should be 0-65535)
+            size(int): Size of the entry in bytes. Only primitive sizes are supported.
+            length(int): Length of the entry, defined as an array if different than 0.
+            sign(bool): Is the primitive signed.
+            deserializer(str): The formatter to use when deserializing the entry.
+            length_before_serialization(int): Length of the entry before serialization, should be given only if this length
+                                                is different than size
+        """
+        super(ConfigEntry, self).__init__(entry_name, entry_id)
+        self._category_id = category_id
+        if size not in self._PRIMITIVE_TYPES:
+            raise ConfigSyntaxException(
+                'Unsupported entry size. Supported sizes {}.'.format(list(self._PRIMITIVE_TYPES.keys())))
+        self._size = size
+        self._length = length
+        self._sign = sign
+        self._deserializer = deserializer
+        if length_before_serialization is None:
+            self._length_before_serialization = size
+        else:
+            self._length_before_serialization = length_before_serialization
+
+    @property
+    def deserializer(self):
+        """Returns:
+            str: The formatter to use when deserializing the entry.
+        """
+        return self._deserializer
+
+    @property
+    def primitive_type(self):
+        """Returns:
+            str: The string representing the primitve C type of the entry.
+        """
+        var_type = self._PRIMITIVE_TYPES[self._size]
+        if not self._sign:
+            var_type = 'u{}'.format(var_type)
+        return var_type
+
+    @property
+    def category_id(self):
+        """Returns:
+            int: Entry id value.
+        """
+        return self._category_id
+
+    @property
+    def length(self):
+        """Returns:
+            int: Length of the entry, if this is different than zero, then the entry is an array.
+        """
+        return self._length
+
+    @property
+    def total_size(self):
+        """Returns:
+            int: Entry's total size in bytes.
+        """
+        if self._length == 0:
+            return self._size
+        else:
+            return self._length * self._size
+
+    @property
+    def length_before_serialization(self):
+        """Returns:
+            int: The length of the entry before serialization
+        """
+        return self._length_before_serialization
+
+class ConfigCategory(ConfigLine):
+    """A configuration category that contains multiple configuration IDs"""
+
+    def __init__(self, category_name, category_id):
+        """Initializes a new ConfigCategory object.
+
+        Args:
+            category_name(str): Category name.
+            category_id(int): Category ID (the integer representing the category).
+        """
+        super(ConfigCategory, self).__init__(category_name, category_id)
+        self._config_entries = OrderedDict()
+        self._current_entry_id = 0
+
+    def append(self, entry_name, size, deserialize_as, length=0, sign=False, length_before_serialization=None):
+        """Adds a new entry to the category
+
+        Args:
+            entry_name(str): Name of the appended configuration entry.
+            size(int): Size of the entry in bytes. Only primitive sizes are supported.
+            length(int): Length of the entry, defined as an array if different than 0.
+            sign(bool): Is the primitive signed.
+            deserialize_as(str): The formatter to use when deserializing the entry.
+        """
+        config_entry = ConfigEntry(
+            entry_name,
+            self._current_entry_id,
+            self._id,
+            size,
+            length,
+            sign,
+            deserialize_as,
+            length_before_serialization)
+        self._config_entries[config_entry.name] = config_entry
+        self._current_entry_id += 1
+
+    @property
+    def entries(self):
+        """Returns:
+            list of :obj:`ConfigEntry`: The entries in this category object.
+        """
+        return self._config_entries
+
+    def __getitem__(self, key):
+        """Returns:
+            int: The ID value of the requested entry key.
+        """
+        return self._config_entries[key.lower()]
+
+
+class FirmwareConfig(object):
+    """This class wraps the configuration file that defines the firmware user config."""
+    _SUPPORTED_VERSION = [0]
+    _MAGIC_NUMBER = 0x1FF6A40B
+
+    def __init__(self, config_path):
+        """Initializes a new FirmwareConfig object.
+
+        Args:
+            config_path(str): Path to a configuration file.
+        """
+        config_file = open(config_path, 'r')
+        config_json = json.load(config_file, object_pairs_hook=OrderedDict)
+        self._current_category = None
+        self._next_category_id = 0
+        self._categories = OrderedDict()
+        self._parse_config_json(config_json)
+
+    def _parse_config_json(self, config_json):
+        """Parses a json dictionary containing the configuration and assigns it to the
+        object attributes.
+
+        Args:
+            config_json(dict of str): A dictionary containing the configration file's content.
+        """
+        try:
+            version = config_json['version']
+        except KeyError:
+            raise ConfigSyntaxException('Error: Version definition not found.')
+        if version not in self._SUPPORTED_VERSION:
+            raise UnsupportedConfigVersionException('Unsupported version: {}.\n'
+                                                    'Supported versions: {}'.format(
+                                                        version, self._SUPPORTED_VERSION))
+        self.version = version
+
+        try:
+            categories = config_json['categories']
+        except KeyError:
+            raise ConfigSyntaxException('Error: Categories definition not found.')
+
+        for i, category in enumerate(categories):
+            category_object = ConfigCategory(category, i)
+            try:
+                entries = config_json['categories'][category]['entries']
+            except KeyError:
+                raise ConfigSyntaxException('Error: Category {} does not contain entries.'.format(
+                    category))
+            for entry in entries:
+                category_object.append(entry, **entries[entry])
+            self._categories[category_object.name] = category_object
+
+    @property
+    def supported_version(self):
+        """Returns:
+            list of int: A list containing the supported configuration version.
+        """
+        return self._SUPPORTED_VERSION
+
+    @property
+    def magic(self):
+        """Returns:
+            int: Firmware configuration magic number value.
+        """
+        return self._MAGIC_NUMBER
+
+    @property
+    def categories(self):
+        """Returns:
+            list of :obj:`ConfigCategory`: List of configuration categories contained in the
+                firmware configuration.
+        """
+        return self._categories
+
+    def __getitem__(self, key):
+        """Returns:
+            :class:`sdk_client.hailo_sdk_client.tools.firmware.ConfigCategory: The category
+            corresponding to the requested key.
+        """
+        return self._categories[key]
\ No newline at end of file
diff --git a/hailort/libhailort/bindings/python/platform/hailo_platform/tools/firmware/sensor_config.py b/hailort/libhailort/bindings/python/platform/hailo_platform/tools/firmware/sensor_config.py
new file mode 100644 (file)
index 0000000..3588e28
--- /dev/null
@@ -0,0 +1,9 @@
+#!/usr/bin/env python
+
+"""Parses a configuration file that contains camera sensor configurations in the FW.
+"""
+from hailo_platform.common.tools.cmd_utils.base_utils import HailortCliUtil
+
+class SensorConfigCommandCLI(HailortCliUtil):
+    def __init__(self, parser):
+        super().__init__(parser, 'sensor-config')
\ No newline at end of file
diff --git a/hailort/libhailort/bindings/python/platform/hailo_platform/tools/firmware/update_firmware.py b/hailort/libhailort/bindings/python/platform/hailo_platform/tools/firmware/update_firmware.py
new file mode 100644 (file)
index 0000000..a15d218
--- /dev/null
@@ -0,0 +1,9 @@
+#!/usr/bin/env python
+
+from hailo_platform.common.tools.cmd_utils.base_utils import HailortCliUtil
+
+
+class FWUpdaterCLI(HailortCliUtil):
+    """Cli tool for firmware updates"""
+    def __init__(self, parser):
+        super().__init__(parser, 'fw-update')
diff --git a/hailort/libhailort/bindings/python/platform/hailo_platform/tools/firmware/update_second_stage.py b/hailort/libhailort/bindings/python/platform/hailo_platform/tools/firmware/update_second_stage.py
new file mode 100644 (file)
index 0000000..653d0d0
--- /dev/null
@@ -0,0 +1,9 @@
+#!/usr/bin/env python
+
+from hailo_platform.common.tools.cmd_utils.base_utils import HailortCliUtil
+
+
+class SSBUpdaterCLI(HailortCliUtil):
+    """Cli tool for second stage boot updates"""
+    def __init__(self, parser):
+        super().__init__(parser, 'ssb-update')
diff --git a/hailort/libhailort/bindings/python/platform/hailo_platform/tools/fw_control.py b/hailort/libhailort/bindings/python/platform/hailo_platform/tools/fw_control.py
new file mode 100644 (file)
index 0000000..d169039
--- /dev/null
@@ -0,0 +1,20 @@
+#!/usr/bin/env python
+from hailo_platform.common.tools.cmd_utils.base_utils import HailortCliUtil
+
+
+class ScanCommandCLI(HailortCliUtil):
+    def __init__(self, parser):
+        super().__init__(parser, 'scan')
+
+
+class ControlCommandCLI(HailortCliUtil):
+    def __init__(self, parser):
+        super().__init__(parser, 'fw-control')
+
+class LoggerCommandCLI(HailortCliUtil):
+    def __init__(self, parser):
+        super().__init__(parser, 'fw-logger')
+
+class MeasurePowerCommandCLI(HailortCliUtil):
+    def __init__(self, parser):
+        super().__init__(parser, 'measure-power')
\ No newline at end of file
diff --git a/hailort/libhailort/bindings/python/platform/hailo_platform/tools/infer_cli.py b/hailort/libhailort/bindings/python/platform/hailo_platform/tools/infer_cli.py
new file mode 100644 (file)
index 0000000..153ea8d
--- /dev/null
@@ -0,0 +1,54 @@
+from enum import Enum
+
+from hailo_platform.common.tools.cmd_utils.hailo_device_utils import HailoDeviceCmdUtil
+from hailo_platform.common.logger.logger import default_logger
+from hailo_platform.tools.run_command import RunCommandCLI
+from hailo_platform.common.tools.cmd_utils.base_utils import HailortCliUtilError
+
+logger = default_logger()
+
+class InferModes(Enum):
+    simple = 'simple'
+    performance = 'performance'
+
+class InferCLI(HailoDeviceCmdUtil):
+    def __init__(self, parser):
+        super().__init__(parser, set_target_args=False)
+        self._hailortcli_run_command = RunCommandCLI(parser)
+        self._parser = parser
+        subparsers = parser.add_subparsers(title="Inference mode", dest="mode")
+        subparsers.required = True
+        simple_parser =subparsers.add_parser(InferModes.simple.value, help="'simple' mode is unsupported, please use 'hailo run' instead")
+        simple_parser.add_argument('--input-data-path', type=str, default=None,
+                                   help="unsupported argument.")
+        simple_parser.add_argument('--results-path', type=str, default=None,
+                                   help='Unsupported argument.')
+        simple_parser.add_argument('--config-path', type=str, required=True, help='Path to config HEF to infer with')
+        performance_parser = subparsers.add_parser(InferModes.performance.value,
+            help="infer command is deprecated and will be removed in a future release, please use 'hailo run' instead")
+        performance_parser.add_argument('--config-path', type=str, required=True,
+                            help='Path to config HEF to infer with')
+        self.add_target_args(performance_parser)
+        performance_parser.add_argument('-t', '--streaming-time', type=float, default=10.0, help='For how long to stream in performance mode')
+        performance_parser.add_argument('--streaming-mode',
+                                        choices=['hw-only', 'full'],
+                                        default='full',
+                                        help='Whether to skip pre-infer and post-infer steps on host (hw-only) or do them (full)')
+        parser.set_defaults(func=self.run)
+
+    def run(self, args):
+        if InferModes[args.mode] == InferModes.simple:
+            logger.info("mode simple is deprecated please use \'hailo run\' instead.\n"
+                ".npz and .npy format are unsupported, use binary file instead, you can use the following example:\n"
+                "\'hailo run --input-files [Input file path] [hef]\'.\n"
+                "for more information use \'hailo run --help\'.")
+        else:
+            self.validate_args(args)
+            argv = [args.config_path, "-t", str(int(args.streaming_time)), '-m', "streaming" if args.streaming_mode == 'full' else 'hw_only']
+            if args.target == 'udp':
+                argv += ['-d', args.target, '--ip', args.ip]
+            try:
+                self._hailortcli_run_command.run(argv)
+            except HailortCliUtilError as e:
+                print('\n'+ str(e))
+                return
diff --git a/hailort/libhailort/bindings/python/platform/hailo_platform/tools/run_command.py b/hailort/libhailort/bindings/python/platform/hailo_platform/tools/run_command.py
new file mode 100644 (file)
index 0000000..1b16bbc
--- /dev/null
@@ -0,0 +1,5 @@
+from hailo_platform.common.tools.cmd_utils.base_utils import HailortCliUtil
+
+class RunCommandCLI(HailortCliUtil):
+    def __init__(self, parser):
+        super().__init__(parser, 'run')
diff --git a/hailort/libhailort/bindings/python/platform/hailo_platform/tools/udp_rate_limiter.py b/hailort/libhailort/bindings/python/platform/hailo_platform/tools/udp_rate_limiter.py
new file mode 100644 (file)
index 0000000..658edac
--- /dev/null
@@ -0,0 +1,122 @@
+#!/usr/bin/env python
+"""Tool for limiting the packet sending rate via UDP. Needed to ensure the board will not get more
+traffic than it can handle, which would cause packet loss.
+"""
+from __future__ import division
+from builtins import object
+
+
+from hailo_platform.common.tools.cmd_utils.base_utils import HailortCliUtil
+from hailo_platform.drivers.hailort.pyhailort import ConfiguredNetwork, HEF, TrafficControl, INPUT_DATAFLOW_BASE_PORT
+
+DEFAULT_MAX_KBPS = 850e3
+DEFAULT_MAX_KBPS_PAPRIKA_B0 = 160e3
+
+BYTES_IN_Kbits = 125.0
+
+
+class RateLimiterException(Exception):
+    """A problem has occurred during the rate setting."""
+    pass
+
+class BadTCParamError(Exception):
+    """One of shell's tc command params is wrong."""
+    pass
+
+
+class BadTCCallError(Exception):
+    """Shell's tc command has failed."""
+    pass
+
+
+def get_max_supported_kbps(hw_arch="hailo8"):
+    # TODO: What should be here?
+    if hw_arch == "paprika_b0":
+        return DEFAULT_MAX_KBPS_PAPRIKA_B0
+    return DEFAULT_MAX_KBPS
+
+class RateLimiterWrapper(object):
+    """UDPRateLimiter wrapper enabling ``with`` statements."""
+    def __init__(self, network_group, fps=1, fps_factor=1.0):
+        """RateLimiterWrapper constructor.
+
+        Args:
+            configured_network_group (:class:`~hailo_platform.drivers.hailort.pyhailort.ConfiguredNetwork`): The
+                target network_group.
+            fps (int): Frame rate.
+            fps_factor (float): Safety factor by which to multiply the calculated UDP rate.
+        """
+        if not isinstance(network_group, ConfiguredNetwork):
+            return RateLimiterException("The API was changed. RateLimiterWrapper accept ConfiguredNetwork instead of ActivatedNetwork")
+        self._network_group = network_group
+        self._remote_ip = network_group._target.remote_ip
+        self._fps = fps
+        self._fps_factor = fps_factor
+        self._hw_arch = network_group._target._hw_arch
+        self._rates_dict = {}
+        self._tc_dict = {}
+
+    def __enter__(self):
+        max_supported_kbps_rate = get_max_supported_kbps(self._hw_arch)
+
+        self._rates_dict = self._network_group.get_udp_rates_dict((self._fps * self._fps_factor),
+            (max_supported_kbps_rate * BYTES_IN_Kbits))
+        for port, rate in self._rates_dict.items():
+            self._tc_dict[port] = TrafficControl(self._remote_ip, port, rate)
+            self._tc_dict[port].reset_rate_limit()
+            self._tc_dict[port].set_rate_limit()
+
+    def __exit__(self, *args, **kwargs):
+        for tc in self._tc_dict.values():
+            tc.reset_rate_limit()
+        return False
+
+
+class UDPRateLimiter(object):
+    """Enables limiting or removing limits on UDP communication rate to a board."""
+    def __init__(self, remote_ip, port, rate_kbits_per_sec = 0):
+        self._tc = TrafficControl(remote_ip, port, rate_kbits_per_sec * BYTES_IN_Kbits)
+    
+    def set_rate_limit(self):
+        return self._tc.set_rate_limit()
+
+    def reset_rate_limit(self):
+        return self._tc.reset_rate_limit()
+
+    @staticmethod    
+    def calc_udp_rate(hef, network_group_name, fps, fps_factor=1, max_supported_kbps_rate=850e3):
+        """Calculates the proper UDP rate according to an HEF.
+
+        Args:
+            hef (str): Path to an HEF file containing the network_group.
+            network_group_name (str): Name of the network_group to configure rates for.
+            fps (int): Frame rate.
+            fps_factor (float, optional): Safety factor by which to multiply the calculated UDP
+                rate.
+            max_supported_kbps_rate (int, optional): Max supported Kbits per second. Defaults to 850
+                Mbit/s (850,000 Kbit/s).
+
+        Returns:
+            dict: Maps between each input default dport to its calculated Rate in Kbits/sec.
+        """
+        hef_object = HEF(hef)
+        input_stream_infos = hef_object.get_input_stream_infos(network_group_name)
+
+        input_rates = hef_object.get_udp_rates_dict(int(fps * fps_factor), (max_supported_kbps_rate * BYTES_IN_Kbits),
+            network_group_name)
+
+        if len(input_stream_infos) != len(input_rates.keys()):
+            raise RateLimiterException("There is a missmatch between the calculated rates and the network inputs.")
+        
+        results = {}
+        for stream_info in input_stream_infos:
+            port = stream_info.sys_index + INPUT_DATAFLOW_BASE_PORT
+            results[port] = input_rates[stream_info.name] / BYTES_IN_Kbits
+
+        return results
+
+
+class UDPRateLimiterCLI(HailortCliUtil):
+    """CLI tool for UDP rate limitation."""
+    def __init__(self, parser):
+        super().__init__(parser, 'udp-rate-limiter')
diff --git a/hailort/libhailort/bindings/python/platform/hailo_platform/tools/watchdog.py b/hailort/libhailort/bindings/python/platform/hailo_platform/tools/watchdog.py
new file mode 100644 (file)
index 0000000..acc485e
--- /dev/null
@@ -0,0 +1,33 @@
+from __future__ import print_function
+from builtins import object
+import threading
+
+
+class Watchdog(object):
+    def __init__(self, queues_to_close, timeout=20):
+        self.timeout = timeout
+        self._queues_to_close = queues_to_close
+        self._t = None
+
+    def do_expire(self):
+        for queue_to_close in self._queues_to_close:
+            queue_to_close.close()
+
+    def _expire(self):
+        print("\nWatchdog expire")
+        self.do_expire()
+
+    def start(self):
+        if self.timeout is not None:
+            self._t = threading.Timer(self.timeout, self._expire)
+            self._t.start()
+        else:
+            self._t = None
+
+    def stop(self):
+        if self._t is not None:
+            self._t.cancel()
+
+    def refresh(self):
+        self.stop()
+        self.start()
diff --git a/hailort/libhailort/bindings/python/platform/requirements.txt b/hailort/libhailort/bindings/python/platform/requirements.txt
new file mode 100644 (file)
index 0000000..10782be
--- /dev/null
@@ -0,0 +1,16 @@
+appdirs==1.4.4
+argcomplete==2.0.0
+contextlib2==0.6.0.post1
+distlib==0.3.4
+filelock==3.4.1
+future==0.18.2
+importlib-metadata==3.7.3
+importlib-resources==5.1.2
+netaddr==0.8.0
+netifaces==0.10.9
+numpy==1.19.4
+six==1.15.0
+typing_extensions==4.1.1
+verboselogs==1.7
+virtualenv==20.4.3
+zipp==3.4.1
diff --git a/hailort/libhailort/bindings/python/platform/setup.py b/hailort/libhailort/bindings/python/platform/setup.py
new file mode 100644 (file)
index 0000000..d269c63
--- /dev/null
@@ -0,0 +1,63 @@
+import os
+import sys
+from setuptools import setup, find_packages
+from wheel.bdist_wheel import bdist_wheel as orig_bdist_wheel
+
+
+class NonPurePythonBDistWheel(orig_bdist_wheel):
+    """makes the wheel platform-dependant so it can be based on the _pyhailort architecture"""
+
+    def finalize_options(self):
+        orig_bdist_wheel.finalize_options(self)
+        self.root_is_pure = False
+
+
+def _get_pyhailort_lib():
+    extension = {
+        "posix": "so",
+        "nt": "pyd",  # Windows
+    }[os.name]
+    py = "".join(map(str, sys.version_info[:2]))
+
+    return f"drivers/hailort/_pyhailort*{py}*.{extension}"
+
+
+if __name__ == "__main__":
+    setup(
+        author="Hailo team",
+        author_email="contact@hailo.ai",
+        cmdclass={
+            "bdist_wheel": NonPurePythonBDistWheel,
+        },
+        description="HailoRT",
+        entry_points={
+            "console_scripts": [
+                "hailo=hailo_platform.tools.cmd_utils.main:main",
+            ]
+        },
+        install_requires=[
+            "argcomplete",
+            "contextlib2",
+            "future",
+            "netaddr",
+            "netifaces",
+            "six",
+            "verboselogs",
+            # Pinned versions
+            "numpy==1.19.4",
+        ],
+        name="hailort",
+        package_data={
+            "hailo_platform": [
+                _get_pyhailort_lib(),  # packs _pyhailort library for _pyhailort imports
+            ],
+        },
+        packages=find_packages(),
+        platforms=[
+            "linux_x86_64",
+            "linux_aarch64",
+        ],
+        url="https://hailo.ai/",
+        version="4.6.0",
+        zip_safe=False,
+    )
diff --git a/hailort/libhailort/bindings/python/platform/tutorials/notebooks/Inference Tutorial.ipynb b/hailort/libhailort/bindings/python/platform/tutorials/notebooks/Inference Tutorial.ipynb
new file mode 100644 (file)
index 0000000..2f7d477
--- /dev/null
@@ -0,0 +1,202 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "\n",
+    "# Python inference tutorial\n",
+    "\n",
+    "This tutorial will walk you through the inference process.\n",
+    "\n",
+    "**Requirements:**\n",
+    "\n",
+    "* Run the notebook inside the Python virtual environment: ```source hailo_virtualenv/bin/activate```"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## Standalone hardware deployment\n",
+    "\n",
+    "The standalone flow allows direct access to the HW, developing applications directly on top of Hailo\n",
+    "core HW, using HailoRT. This way we can use the Hailo hardware without Tensorflow, and\n",
+    "even without the Hailo SDK (after the HEF is built).\n",
+    "\n",
+    "An HEF is Hailo’s binary format for neural networks. The HEF files contain:\n",
+    "\n",
+    "* Target HW configuration\n",
+    "* Weights\n",
+    "* Metadata for HailoRT (e.g. input/output scaling)\n",
+    "\n",
+    "First create the desired target object. In our example we use the Hailo-8 PCIe interface:\n"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import numpy as np\n",
+    "from multiprocessing import Process\n",
+    "from hailo_platform import (HEF, PcieDevice, HailoStreamInterface, InferVStreams, ConfigureParams,\n",
+    "    InputVStreamParams, OutputVStreamParams, InputVStreams, OutputVStreams, FormatType)\n",
+    "\n",
+    "# The target can be used as a context manager (\"with\" statement) to ensure it's released on time.\n",
+    "# Here it's avoided for the sake of simplicity\n",
+    "target = PcieDevice()\n",
+    "\n",
+    "# Loading compiled HEFs to device:\n",
+    "model_name = 'resnet_v1_18'\n",
+    "hef_path = '../hefs/{}.hef'.format(model_name) \n",
+    "hef = HEF(hef_path)\n",
+    "    \n",
+    "# Configure network groups\n",
+    "configure_params = ConfigureParams.create_from_hef(hef=hef, interface=HailoStreamInterface.PCIe)\n",
+    "network_groups = target.configure(hef, configure_params)\n",
+    "network_group = network_groups[0]\n",
+    "network_group_params = network_group.create_params()\n",
+    "\n",
+    "# Create input and output virtual streams params\n",
+    "# Quantized argument signifies whether or not the incoming data is already quantized.\n",
+    "# Data is quantized by HailoRT if and only if quantized == False .\n",
+    "input_vstreams_params = InputVStreamParams.make(network_group, quantized=False, format_type=FormatType.FLOAT32)\n",
+    "output_vstreams_params = OutputVStreamParams.make(network_group, quantized=True, format_type=FormatType.UINT8)\n",
+    "\n",
+    "# Define dataset params\n",
+    "input_vstream_info = hef.get_input_vstream_infos()[0]\n",
+    "output_vstream_info = hef.get_output_vstream_infos()[0]\n",
+    "image_height, image_width, channels = input_vstream_info.shape\n",
+    "num_of_images = 10\n",
+    "low, high = 2, 20\n",
+    "\n",
+    "# Generate random dataset\n",
+    "dataset = np.random.randint(low, high, (num_of_images, image_height, image_width, channels)).astype(np.float32)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "#### Running hardware inference\n",
+    "Infer the model and then display the output shape:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "# Infer \n",
+    "with InferVStreams(network_group, input_vstreams_params, output_vstreams_params) as infer_pipeline:\n",
+    "    input_data = {input_vstream_info.name: dataset}\n",
+    "    with network_group.activate(network_group_params):\n",
+    "        infer_results = infer_pipeline.infer(input_data)\n",
+    "        print('Stream output shape is {}'.format(infer_results[output_vstream_info.name].shape))"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## Streaming inference\n",
+    "\n",
+    "This section shows how to run streaming inference using multiple processes in Python.\n",
+    "\n",
+    "We will not use infer. Instead we will use a send and receive model.\n",
+    "The send function and the receive function will run in different processes."
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Define the send and receive functions:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "def send(configured_network, num_frames):\n",
+    "    vstreams_params = InputVStreamParams.make(configured_network)\n",
+    "    configured_network.wait_for_activation(1000)\n",
+    "    with InputVStreams(configured_network, vstreams_params) as vstreams:\n",
+    "        vstream_to_buffer = {vstream: np.ndarray([1] + list(vstream.shape), dtype=vstream.dtype) for vstream in vstreams}\n",
+    "        for _ in range(num_frames):\n",
+    "            for vstream, buff in vstream_to_buffer.items():\n",
+    "                vstream.send(buff)\n",
+    "\n",
+    "def recv(configured_network, vstreams_params, num_frames):\n",
+    "    configured_network.wait_for_activation(1000)\n",
+    "    with OutputVStreams(configured_network, vstreams_params) as vstreams:\n",
+    "        for _ in range(num_frames):\n",
+    "            for vstream in vstreams:\n",
+    "                data = vstream.recv()\n",
+    "\n",
+    "def recv_all(configured_network, num_frames):\n",
+    "    vstreams_params_groups = OutputVStreamParams.make_groups(configured_network)\n",
+    "    recv_procs = []\n",
+    "    for vstreams_params in vstreams_params_groups:\n",
+    "        proc = Process(target=recv, args=(configured_network, vstreams_params, num_frames))\n",
+    "        proc.start()\n",
+    "        recv_procs.append(proc)\n",
+    "    for proc in recv_procs:\n",
+    "        proc.join()"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Define the amount of frames to stream, define the processes, create the target and run processes:\n"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "# Define the amount of frames to stream\n",
+    "num_of_frames = 1000\n",
+    "\n",
+    "send_process = Process(target=send, args=(network_group, num_of_frames))\n",
+    "recv_process = Process(target=recv_all, args=(network_group, num_of_frames))\n",
+    "recv_process.start()\n",
+    "send_process.start()\n",
+    "print('Starting streaming (hef=\\'{}\\', num_of_frames={})'.format(model_name, num_of_frames))\n",
+    "with network_group.activate(network_group_params):\n",
+    "    send_process.join()\n",
+    "    recv_process.join()\n",
+    "print('Done')"
+   ]
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  },
+  "language_info": {
+   "codemirror_mode": {
+    "name": "ipython",
+    "version": 3
+   },
+   "file_extension": ".py",
+   "mimetype": "text/x-python",
+   "name": "python",
+   "nbconvert_exporter": "python",
+   "pygments_lexer": "ipython3",
+   "version": "3.6.9"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 2
+}
diff --git a/hailort/libhailort/bindings/python/platform/tutorials/notebooks/Power Measurement Tutorial.ipynb b/hailort/libhailort/bindings/python/platform/tutorials/notebooks/Power Measurement Tutorial.ipynb
new file mode 100644 (file)
index 0000000..a180861
--- /dev/null
@@ -0,0 +1,185 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "# Python power measurement tutorial\n",
+    "\n",
+    "This tutorial will show how to perform a power measurement on the chip.\n",
+    "\n",
+    "The Hailo chip supports power measurement which is done via the control protocol.\n",
+    "\n",
+    "**Requirements:**\n",
+    "\n",
+    "* Run the notebook inside the Python virtual environment: ```source hailo_virtualenv/bin/activate```\n",
+    "\n",
+    "**Attention:**\n",
+    "\n",
+    "* These examples should run in a different process than the one that performs the actual inference.\n"
+   ]
+  },
+  {
+   "source": [
+    "## Single power measurement"
+   ],
+   "cell_type": "markdown",
+   "metadata": {}
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%matplotlib inline\n",
+    "import time\n",
+    "\n",
+    "from hailo_platform.drivers.hailo_controller.power_measurement import DvmTypes, PowerMeasurementTypes, SamplingPeriod, AveragingFactor # noqa F401\n",
+    "from hailo_platform import PcieDevice"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Initialize the hardware object:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "target = PcieDevice()"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "When using the ``power_measurement()`` function with no parameters, the function tries to detect which board is connected (evaluation board, M.2 or mPCIe) and determine the DVM accordingly (at the moment only the mentioned boards are supported). \n",
+    "\n",
+    "The parameter ``dvm`` (of type ``DvmTypes``) defines which DVM will be measured. The user can choose a specific DVM or choose the default DVM. The meaning of the default DVM changes according to the board or module in use. \n",
+    "\n",
+    "The default for the evaluation board is the sum of three DVMs: ``DvmTypes.VDD_CORE``, ``DvmTypes.MIPI_AVDD`` and ``DvmTypes.AVDD_H``. The sum of these three DVMs approximates of the total power consumption of the chip in PCIe setups. Only power can be measured using this default option, as voltage and current can't be summed up this way. \n",
+    "\n",
+    "The default for platforms supporting current monitoring, such as M.2 and mPCIe modules, is ``DvmTypes.OVERCURRENT_PROTECTION``, which measures the power consumption of the whole module. \n",
+    "\n",
+    "See the API documentation for further details about the supported DVMs and measurement types.\n"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "single_power_measurement = target.control.power_measurement()\n",
+    "print('Power from single measurement: {} W'.format(single_power_measurement))"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## Periodic power measurement\n",
+    "\n",
+    "In the following example, a periodic power measurement is taken.\n",
+    "\n",
+    "A measurement index is selected. It is a number between 0 and 3. The DVM is not given so the default DVM will be used, as explained above.\n"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "index = 0\n",
+    "target.control.set_power_measurement(index)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "The following call to ``start_power_measurement()`` allows to configure the power sampling frequency. In this case we keep the default configuration. The API documentation provides more details. \n"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "target.control.start_power_measurement()"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Clear old samples and statistics (min, max, average) each time the measurement is taken from the chip."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "should_clear = True"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "The power measurement is read every second, for 10 seconds.\n",
+    "\n",
+    "The firmware calculates the min, max, and average of all the values from the sensor. Note that the average calculated by the firmware is the \"average of averages\". This is the average of all values that have already been averaged by the sensor. The host then requests these values from the firmware every second by calling the ``get_power_measurement()`` function. The host can also read the average time interval between new sensor values."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {
+    "scrolled": true
+   },
+   "outputs": [],
+   "source": [
+    "for _ in range(10):\n",
+    "    time.sleep(1)\n",
+    "    # Get saved power measurement values from the firmware.\n",
+    "    measurements = target.control.get_power_measurement(index, should_clear=should_clear)\n",
+    "    print('Average power is {} W. Min power is {} W. Max power is {} W.\\nAverage time between power samples is {} mS\\n'.format(measurements.average_value, measurements.min_value, measurements.max_value, measurements.average_time_value_milliseconds))\n",
+    "    \n",
+    "# Stop performing periodic power measurement\n",
+    "target.control.stop_power_measurement()"
+   ]
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  },
+  "language_info": {
+   "codemirror_mode": {
+    "name": "ipython",
+    "version": 3
+   },
+   "file_extension": ".py",
+   "mimetype": "text/x-python",
+   "name": "python",
+   "nbconvert_exporter": "python",
+   "pygments_lexer": "ipython3",
+   "version": "3.6.9"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 2
+}
\ No newline at end of file
diff --git a/hailort/libhailort/bindings/python/src/CMakeLists.txt b/hailort/libhailort/bindings/python/src/CMakeLists.txt
new file mode 100644 (file)
index 0000000..0df9525
--- /dev/null
@@ -0,0 +1,32 @@
+cmake_minimum_required(VERSION 3.0.0)
+
+if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
+    string(REPLACE "." "" dpython ${PYBIND11_PYTHON_VERSION}) # E.g "3.5" -> "35"
+    if(${dpython} LESS "38")
+        set(m_flag "m")
+    else()
+        set(m_flag "")
+    endif()
+    set(PYTHON_MODULE_EXTENSION ".cpython-${dpython}${m_flag}-${CMAKE_SYSTEM_PROCESSOR}-linux-gnu.so")
+endif()
+
+set(PYHAILORT_DIR ${CMAKE_CURRENT_LIST_DIR})
+add_subdirectory(internal)
+pybind11_add_module(_pyhailort
+    pyhailort.cpp
+    ${HAILORT_COMMON_CPP_SOURCES}
+)
+target_include_directories(_pyhailort
+    PRIVATE
+    $<BUILD_INTERFACE:${HAILORT_INC_DIR}>
+    $<BUILD_INTERFACE:${HAILORT_COMMON_DIR}>
+    $<BUILD_INTERFACE:${HAILORT_SRC_DIR}>
+    $<BUILD_INTERFACE:${COMMON_INC_DIR}>
+)
+
+target_link_libraries(_pyhailort PRIVATE libhailort spdlog::spdlog)
+if(WIN32)
+    target_link_libraries(_pyhailort PRIVATE Ws2_32 Iphlpapi Shlwapi)
+endif()
+target_compile_options(_pyhailort PRIVATE ${HAILORT_COMPILE_OPTIONS})
+exclude_archive_libs_symbols(_pyhailort)
diff --git a/hailort/libhailort/bindings/python/src/__init__.py b/hailort/libhailort/bindings/python/src/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/hailort/libhailort/bindings/python/src/bindings_common.hpp b/hailort/libhailort/bindings/python/src/bindings_common.hpp
new file mode 100644 (file)
index 0000000..ea31187
--- /dev/null
@@ -0,0 +1,70 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file bindings_common.hpp
+ * @brief Common funcs and defs for Python bindings
+ **/
+
+#ifndef _BINDINGS_COMMON_HPP_
+#define _BINDINGS_COMMON_HPP_
+
+#include "hailo/hailort.h"
+#include "hailo/hailort_common.hpp"
+#include "hailo/network_group.hpp"
+#include "utils.hpp"
+#include "common/logger_macros.hpp"
+
+namespace hailort
+{
+
+int convert_format_type_to_int(const hailo_format_type_t& type)
+{
+    switch (type) {
+    case HAILO_FORMAT_TYPE_UINT8:
+        return 1;
+    case HAILO_FORMAT_TYPE_UINT16:
+        return 2;
+    case HAILO_FORMAT_TYPE_FLOAT32:
+        return 4;
+    default:
+        throw HailoRTStatusException("Invalid format type.");
+    }
+}
+
+std::string convert_format_type_to_string(const hailo_format_type_t& type)
+{
+    switch (type) {
+    case HAILO_FORMAT_TYPE_UINT8:
+        return "uint8";
+    case HAILO_FORMAT_TYPE_UINT16:
+        return "uint16";
+    case HAILO_FORMAT_TYPE_FLOAT32:
+        return "float32";
+    default:
+        throw HailoRTStatusException("Invalid format type.");
+    }
+}
+
+std::vector<size_t> get_pybind_shape(const hailo_vstream_info_t& vstream_info, const hailo_format_t &user_format)
+{
+    // We are using user_format instead of hw format inside the vstream_info
+    const auto shape = vstream_info.shape;
+    // TODO: support no transformations (i.e. use stream_info.hw_shape) (SDK-16811)
+    switch (user_format.order)
+    {
+    case HAILO_FORMAT_ORDER_HAILO_NMS:
+        return { HailoRTCommon::get_nms_host_shape_size(vstream_info.nms_shape) };
+    case HAILO_FORMAT_ORDER_NC:
+        return {shape.features};
+    case HAILO_FORMAT_ORDER_NHW:
+        return {shape.height, shape.width};
+    default:
+        return {shape.height, shape.width, shape.features};
+    }
+}
+
+} /* namespace hailort */
+
+#endif /* _BINDINGS_COMMON_HPP_ */
diff --git a/hailort/libhailort/bindings/python/src/hef_api.hpp b/hailort/libhailort/bindings/python/src/hef_api.hpp
new file mode 100644 (file)
index 0000000..a8f7312
--- /dev/null
@@ -0,0 +1,375 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file hef_api.hpp
+ * @brief Defines binding to an HEF class, and network_group usage over Python.
+ *
+ * TODO: doc
+ **/
+
+#ifndef HEF_API_HPP_
+#define HEF_API_HPP_
+
+#include "hailo/hef.hpp"
+#include "hailo/network_rate_calculator.hpp"
+
+#include "bindings_common.hpp"
+#include "vstream_api.hpp"
+#include "utils.hpp"
+#include "common/logger_macros.hpp"
+
+#include <pybind11/pybind11.h>
+#include <pybind11/numpy.h>
+#include <pybind11/detail/common.h>
+#include <pybind11/stl.h>
+#include <pybind11/complex.h>
+#include <pybind11/functional.h>
+
+#include <string>
+
+namespace hailort
+{
+
+class HefWrapper {
+public:
+    HefWrapper(const std::string &hef_path)
+    {
+        auto hef_expected = Hef::create(hef_path);
+        VALIDATE_EXPECTED(hef_expected);
+
+        hef = make_unique_nothrow<Hef>(hef_expected.release());
+        if (nullptr == hef) {
+            THROW_STATUS_ERROR(HAILO_OUT_OF_HOST_MEMORY);
+        }
+    };
+
+    HefWrapper(const MemoryView &hef_buffer)
+    {
+        auto hef_expected = Hef::create(hef_buffer);
+        VALIDATE_EXPECTED(hef_expected);
+
+        hef = make_unique_nothrow<Hef>(hef_expected.release());
+        if (nullptr == hef) {
+            THROW_STATUS_ERROR(HAILO_OUT_OF_HOST_MEMORY);
+        }
+    };
+
+    static HefWrapper create_from_buffer(py::bytes data)
+    {
+        return HefWrapper(MemoryView((uint8_t*)std::string(data).c_str(), std::string(data).size()));
+    }
+
+    static HefWrapper create_from_file(const std::string &hef_path)
+    {
+        return HefWrapper(hef_path);
+    }
+
+    const std::unique_ptr<Hef>& hef_ptr() const
+    {
+        return hef;
+    }
+
+    py::list get_network_group_names()
+    {
+        return py::cast(hef->get_network_groups_names());
+    }
+
+    py::list get_network_groups_infos()
+    {
+        auto network_group_infos = hef->get_network_groups_infos();
+        VALIDATE_EXPECTED(network_group_infos);
+        return py::cast(network_group_infos.release());
+    }
+
+    py::list get_sorted_output_names(std::string net_group_name)
+    {
+        auto names_list = hef->get_sorted_output_names(net_group_name);
+        VALIDATE_EXPECTED(names_list);
+
+        return py::cast(names_list.release());
+    };
+
+    float64_t get_bottleneck_fps(const std::string &net_group_name)
+    {
+        Expected<float64_t> bottleneck_fps = hef->get_bottleneck_fps(net_group_name);
+        VALIDATE_EXPECTED(bottleneck_fps);
+        return bottleneck_fps.release();
+    };
+
+    py::dict get_udp_rates_dict(const std::string &net_group_name, uint32_t fps, uint32_t max_supported_rate_bytes)
+    {
+        auto rate_calculator = NetworkUdpRateCalculator::create(hef.release(), net_group_name);
+        VALIDATE_EXPECTED(rate_calculator);
+        auto rates_per_name = rate_calculator.value().calculate_inputs_bandwith(fps, max_supported_rate_bytes);
+        VALIDATE_EXPECTED(rates_per_name);
+        return py::cast(rates_per_name.release());
+    };
+
+    py::list get_original_names_from_stream_name(const std::string &stream_name, const std::string &net_group_name)
+    {
+        LOGGER__WARNING("'get_original_names_from_stream_name()' is deprecated. One should use get_original_names_from_vstream_name()");
+        auto results = hef->get_vstream_names_from_stream_name(stream_name, net_group_name);
+        VALIDATE_EXPECTED(results);
+        return py::cast(results.release());
+    };
+
+    std::string get_stream_name_from_original_name(const std::string &original_name, const std::string &net_group_name)
+    {
+        LOGGER__WARNING("'get_stream_name_from_original_name()' is deprecated. One should use get_vstream_name_from_original_name()");
+        auto results = hef->get_stream_names_from_vstream_name(original_name, net_group_name);
+        VALIDATE_EXPECTED(results);
+        return results.release()[0];
+    };
+
+    py::list get_original_names_from_vstream_name(const std::string &vstream_name, const std::string &net_group_name)
+    {
+        auto results = hef->get_original_names_from_vstream_name(vstream_name, net_group_name);
+        VALIDATE_EXPECTED(results);
+        return py::cast(results.release());
+    };
+
+    std::string get_vstream_name_from_original_name(const std::string &original_name, const std::string &net_group_name)
+    {
+        auto results = hef->get_vstream_name_from_original_name(original_name, net_group_name);
+        VALIDATE_EXPECTED(results);
+        return results.release();
+    };
+
+    py::list get_stream_names_from_vstream_name(const std::string &vstream_name, const std::string &net_group_name)
+    {
+        auto results = hef->get_stream_names_from_vstream_name(vstream_name, net_group_name);
+        VALIDATE_EXPECTED(results);
+        return py::cast(results.release());
+    };
+
+    py::list get_vstream_names_from_stream_name(const std::string &stream_name, const std::string &net_group_name)
+    {
+        auto results = hef->get_vstream_names_from_stream_name(stream_name, net_group_name);
+        VALIDATE_EXPECTED(results);
+        return py::cast(results.release());
+    };
+
+    py::dict get_input_vstreams_params(const std::string &name, bool quantized, hailo_format_type_t format_type,
+        uint32_t timeout_ms, uint32_t queue_size)
+    {
+        auto result = hef->make_input_vstream_params(name, quantized, format_type, timeout_ms, queue_size);
+        VALIDATE_EXPECTED(result);
+        return py::cast(result.value());
+    };
+
+    py::dict get_output_vstreams_params(const std::string &name, bool quantized, hailo_format_type_t format_type,
+        uint32_t timeout_ms, uint32_t queue_size)
+    {
+        auto result = hef->make_output_vstream_params(name, quantized, format_type, timeout_ms, queue_size);
+        VALIDATE_EXPECTED(result);
+        return py::cast(result.value());
+    };
+
+    py::list get_input_vstream_infos(const std::string &name)
+    {
+        auto result = hef->get_input_vstream_infos(name);
+        VALIDATE_EXPECTED(result);
+        return py::cast(result.value());
+    }
+
+    py::list get_output_vstream_infos(const std::string &name)
+    {
+        auto result = hef->get_output_vstream_infos(name);
+        VALIDATE_EXPECTED(result);
+        return py::cast(result.value());
+    }
+
+    py::list get_all_vstream_infos(const std::string &name)
+    {
+        auto result = hef->get_all_vstream_infos(name);
+        VALIDATE_EXPECTED(result);
+        return py::cast(result.value());
+    }
+
+    py::list get_input_stream_infos(const std::string &name)
+    {
+        auto result = hef->get_input_stream_infos(name);
+        VALIDATE_EXPECTED(result);
+        return py::cast(result.value());
+    }
+
+    py::list get_output_stream_infos(const std::string &name)
+    {
+        auto result = hef->get_output_stream_infos(name);
+        VALIDATE_EXPECTED(result);
+        return py::cast(result.value());
+    }
+
+    py::list get_all_stream_infos(const std::string &name)
+    {
+        auto result = hef->get_all_stream_infos(name);
+        VALIDATE_EXPECTED(result);
+        return py::cast(result.value());
+    }
+
+    py::dict create_configure_params(hailo_stream_interface_t interface)
+    {
+        auto configure_params = hef->create_configure_params(interface);
+        VALIDATE_EXPECTED(configure_params);
+
+        return py::cast(configure_params.release());
+    };
+
+    py::dict create_configure_params_mipi_input(hailo_stream_interface_t output_interface,
+        const hailo_mipi_input_stream_params_t &mipi_params)
+    {
+        auto configure_params = hef->create_configure_params_mipi_input(output_interface, mipi_params);
+        VALIDATE_EXPECTED(configure_params);
+
+        return py::cast(configure_params.release());
+    };
+
+    py::list get_networks_names(const std::string &net_group_name)
+    {
+        auto network_infos = hef->get_network_infos(net_group_name);
+        VALIDATE_EXPECTED(network_infos);
+
+        std::vector<std::string> res;
+        for (const auto &info : network_infos.value()) {
+            res.push_back(info.name);
+        }
+
+        return py::cast(res);
+    };
+
+private:
+    std::unique_ptr<Hef> hef;
+};
+
+class ActivatedAppContextManagerWrapper final
+{
+public:
+    ActivatedAppContextManagerWrapper(ConfiguredNetworkGroup &net_group,
+        const hailo_activate_network_group_params_t &network_group_params)
+        : m_net_group(net_group),
+          m_network_group_params(network_group_params) {}
+
+    const ActivatedNetworkGroup& enter()
+    {
+        auto activated = m_net_group.activate(m_network_group_params);
+        VALIDATE_EXPECTED(activated);
+
+        m_activated_net_group = activated.release();
+
+        return std::ref(*m_activated_net_group);
+    }
+
+    void exit()
+    {
+        m_activated_net_group.reset();
+    }
+
+    static void add_to_python_module(py::module &m)
+    {
+        py::class_<ActivatedAppContextManagerWrapper>(m, "ActivatedApp")
+        .def("__enter__", &ActivatedAppContextManagerWrapper::enter, py::return_value_policy::reference)
+        .def("__exit__",  [&](ActivatedAppContextManagerWrapper &self, py::args) { self.exit(); })
+        ;
+    }
+
+private:
+    std::unique_ptr<ActivatedNetworkGroup> m_activated_net_group;
+    ConfiguredNetworkGroup &m_net_group;
+    hailo_activate_network_group_params_t m_network_group_params;
+};
+
+void HEF_API_initialize_python_module(py::module &m)
+{
+    py::class_<HefWrapper>(m, "Hef")
+        .def("create_from_buffer", &HefWrapper::create_from_buffer)
+        .def("create_from_file", &HefWrapper::create_from_file)
+        .def("get_network_group_names", &HefWrapper::get_network_group_names)
+        .def("get_network_groups_infos", &HefWrapper::get_network_groups_infos)
+        .def("get_sorted_output_names", &HefWrapper::get_sorted_output_names)
+        .def("get_bottleneck_fps", &HefWrapper::get_bottleneck_fps)
+        .def("get_stream_name_from_original_name", &HefWrapper::get_stream_name_from_original_name) // deprecated
+        .def("get_original_names_from_stream_name", &HefWrapper::get_original_names_from_stream_name) // deprecated
+        .def("get_stream_names_from_vstream_name", &HefWrapper::get_stream_names_from_vstream_name)
+        .def("get_vstream_names_from_stream_name", &HefWrapper::get_vstream_names_from_stream_name)
+        .def("get_vstream_name_from_original_name", &HefWrapper::get_vstream_name_from_original_name)
+        .def("get_original_names_from_vstream_name", &HefWrapper::get_original_names_from_vstream_name)
+        .def("get_udp_rates_dict", &HefWrapper::get_udp_rates_dict)
+        .def("create_configure_params", &HefWrapper::create_configure_params)
+        .def("create_configure_params_mipi_input", &HefWrapper::create_configure_params_mipi_input)
+        .def("get_input_vstreams_params", &HefWrapper::get_input_vstreams_params)
+        .def("get_output_vstreams_params", &HefWrapper::get_output_vstreams_params)
+        .def("get_input_vstream_infos", &HefWrapper::get_input_vstream_infos)
+        .def("get_output_vstream_infos", &HefWrapper::get_output_vstream_infos)
+        .def("get_all_vstream_infos", &HefWrapper::get_all_vstream_infos)
+        .def("get_input_stream_infos", &HefWrapper::get_input_stream_infos)
+        .def("get_output_stream_infos", &HefWrapper::get_output_stream_infos)
+        .def("get_all_stream_infos", &HefWrapper::get_all_stream_infos)
+        .def("get_networks_names", &HefWrapper::get_networks_names)
+        ;
+
+    py::class_<ConfiguredNetworkGroup>(m, "ConfiguredNetworkGroup")
+        .def("get_name", [](ConfiguredNetworkGroup& self)
+            {
+                return self.get_network_group_name();
+            })
+        .def("get_default_streams_interface", [](ConfiguredNetworkGroup& self)
+            {
+                auto result = self.get_default_streams_interface();
+                VALIDATE_EXPECTED(result);
+                return result.value();
+            })
+        .def("activate", [](ConfiguredNetworkGroup& self,
+            const hailo_activate_network_group_params_t &network_group_params)
+            {
+                return ActivatedAppContextManagerWrapper(self, network_group_params);
+            })
+        .def("wait_for_activation", [](ConfiguredNetworkGroup& self, uint32_t timeout_ms)
+            {
+                auto status = self.wait_for_activation(std::chrono::milliseconds(timeout_ms));
+                VALIDATE_STATUS(status);
+            })
+        .def("InputVStreams", [](ConfiguredNetworkGroup &self, std::map<std::string, hailo_vstream_params_t> &input_vstreams_params)
+            {
+                return InputVStreamsWrapper::create(self, input_vstreams_params);
+            })
+        .def("OutputVStreams", [](ConfiguredNetworkGroup &self, std::map<std::string, hailo_vstream_params_t> &output_vstreams_params)
+            {
+                return OutputVStreamsWrapper::create(self, output_vstreams_params);
+            })
+        .def("get_udp_rates_dict", [](ConfiguredNetworkGroup& self, uint32_t fps, uint32_t max_supported_rate_bytes)
+        {
+            auto rate_calculator = NetworkUdpRateCalculator::create(self);
+            VALIDATE_EXPECTED(rate_calculator);
+
+            auto udp_input_streams = self.get_input_streams_by_interface(HAILO_STREAM_INTERFACE_ETH);
+            auto results = rate_calculator->get_udp_ports_rates_dict(udp_input_streams,
+                fps, max_supported_rate_bytes);
+            VALIDATE_EXPECTED(results);
+
+            return py::cast(results.value());
+        })
+        ;
+
+    ActivatedAppContextManagerWrapper::add_to_python_module(m);
+
+    py::class_<ActivatedNetworkGroup>(m, "ActivatedNetworkGroup")
+        .def("get_intermediate_buffer", [](ActivatedNetworkGroup& self, uint8_t src_context_index,
+            uint8_t src_stream_index)
+        {
+            auto buff = self.get_intermediate_buffer(std::make_pair(src_context_index, src_stream_index));
+            VALIDATE_EXPECTED(buff);
+
+            return py::bytes(reinterpret_cast<char*>(buff->data()), buff->size());
+        })
+        .def("get_invalid_frames_count", [](ActivatedNetworkGroup& self)
+        {
+            return self.get_invalid_frames_count();
+        })
+        ;
+}
+
+} /* namespace hailort */
+
+#endif /* HEF_API_HPP_ */
diff --git a/hailort/libhailort/bindings/python/src/internal/CMakeLists.txt b/hailort/libhailort/bindings/python/src/internal/CMakeLists.txt
new file mode 100644 (file)
index 0000000..e011653
--- /dev/null
@@ -0,0 +1,25 @@
+cmake_minimum_required(VERSION 3.0.0)
+
+pybind11_add_module(_pyhailort_internal SHARED
+    pyhailort_internal.cpp
+    control_api.cpp
+    ${HAILORT_SRCS_ABS}
+)
+
+target_include_directories(_pyhailort_internal
+    PRIVATE
+    $<BUILD_INTERFACE:${PYHAILORT_DIR}>
+    $<BUILD_INTERFACE:${HAILORT_INC_DIR}>
+    $<BUILD_INTERFACE:${HAILORT_COMMON_DIR}>
+    $<BUILD_INTERFACE:${HAILORT_SRC_DIR}>
+    $<BUILD_INTERFACE:${COMMON_INC_DIR}>
+    $<BUILD_INTERFACE:${DRIVER_INC_DIR}>
+)
+
+target_link_libraries(_pyhailort_internal PRIVATE libhailort hef_proto spdlog::spdlog readerwriterqueue)
+if(WIN32)
+    target_link_libraries(_pyhailort_internal PRIVATE Ws2_32 Iphlpapi Shlwapi)
+endif()
+
+target_compile_options(_pyhailort_internal PRIVATE ${HAILORT_COMPILE_OPTIONS})
+exclude_archive_libs_symbols(_pyhailort_internal)
\ No newline at end of file
diff --git a/hailort/libhailort/bindings/python/src/internal/control_api.cpp b/hailort/libhailort/bindings/python/src/internal/control_api.cpp
new file mode 100644 (file)
index 0000000..75a4f26
--- /dev/null
@@ -0,0 +1,322 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file control_api.cpp
+ * @brief Defines binding to control functions
+ *
+ **/
+
+#include "control_api.hpp"
+#include "utils.hpp"
+#include "hailo/device.hpp"
+#include "common/utils.hpp"
+
+namespace hailort
+{
+
+
+void ControlWrapper::set_clock_freq(uintptr_t device, uint32_t clock_freq)
+{
+    VALIDATE_NOT_NULL(reinterpret_cast<Device*>(device));
+    auto status = Control::set_clock_freq(*reinterpret_cast<Device*>(device), clock_freq);
+    VALIDATE_STATUS(status);
+}
+
+void ControlWrapper::close_all_streams(uintptr_t device)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+
+    VALIDATE_NOT_NULL(reinterpret_cast<Device *>(device));
+    status = Control::close_all_streams(*reinterpret_cast<Device *>(device));
+    VALIDATE_STATUS(status);
+}
+
+void ControlWrapper::config_ahb_to_axi(uintptr_t device, bool use_64bit_data_only)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    CONTROL_PROTOCOL__config_core_top_type_t config_type = CONTROL_PROTOCOL__CONFIG_CORE_TOP_TYPE_AHB_TO_AXI;
+    CONTROL_PROTOCOL__config_core_top_params_t params = {0};
+    params.ahb_to_axi.enable_use_64bit_data_only = use_64bit_data_only;
+
+    VALIDATE_NOT_NULL(reinterpret_cast<Device *>(device));
+    status = Control::config_core_top(*reinterpret_cast<Device *>(device), config_type, &params);
+    VALIDATE_STATUS(status);
+
+    return;
+}
+
+void ControlWrapper::phy_operation(uintptr_t device, CONTROL_PROTOCOL__phy_operation_t operation_type)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+
+    VALIDATE_NOT_NULL(reinterpret_cast<Device *>(device));
+    status = Control::phy_operation(*reinterpret_cast<Device *>(device), operation_type);
+    VALIDATE_STATUS(status);
+
+    return;
+}
+
+uint32_t ControlWrapper::latency_measurement_read(uintptr_t device)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    uint32_t inbound_to_outbound_latency_nsec = 0;
+
+    VALIDATE_NOT_NULL(reinterpret_cast<Device *>(device));
+    status = Control::latency_measurement_read(*reinterpret_cast<Device *>(device), &inbound_to_outbound_latency_nsec);
+    VALIDATE_STATUS(status);
+
+    return inbound_to_outbound_latency_nsec;
+}
+
+void ControlWrapper::latency_measurement_config(uintptr_t device, uint8_t latency_measurement_en,
+    uint32_t inbound_start_buffer_number, uint32_t outbound_stop_buffer_number, uint32_t inbound_stream_index,
+    uint32_t outbound_stream_index)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+
+    VALIDATE_NOT_NULL(reinterpret_cast<Device *>(device));
+    status = Control::latency_measurement_config(*reinterpret_cast<Device *>(device), latency_measurement_en, inbound_start_buffer_number,
+            outbound_stop_buffer_number, inbound_stream_index, outbound_stream_index);
+    VALIDATE_STATUS(status);
+
+    return;
+}
+
+void ControlWrapper::start_firmware_update(uintptr_t device)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+
+    VALIDATE_NOT_NULL(reinterpret_cast<Device *>(device));
+    status = Control::start_firmware_update(*reinterpret_cast<Device *>(device));
+    VALIDATE_STATUS(status);
+
+    return;
+}
+
+void ControlWrapper::finish_firmware_update(uintptr_t device)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+
+    VALIDATE_NOT_NULL(reinterpret_cast<Device *>(device));
+    status = Control::finish_firmware_update(*reinterpret_cast<Device *>(device));
+    VALIDATE_STATUS(status);
+
+    return;
+}
+
+void ControlWrapper::write_firmware_update(uintptr_t device, uint32_t offset, py::bytes data, uint32_t length)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+
+    VALIDATE_NOT_NULL(reinterpret_cast<Device *>(device));
+    status = Control::write_firmware_update(*reinterpret_cast<Device *>(device), offset, (uint8_t*)std::string(data).c_str(), length);
+    VALIDATE_STATUS(status);
+
+    return;
+}
+
+void ControlWrapper::validate_firmware_update(uintptr_t device, py::bytes md5_raw_data, uint32_t firmware_size)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    MD5_SUM_t expected_md5 = {0};
+
+    memcpy(&expected_md5, (uint8_t*)std::string(md5_raw_data).c_str(), sizeof(expected_md5));
+
+    VALIDATE_NOT_NULL(reinterpret_cast<Device *>(device));
+    status = Control::validate_firmware_update(*reinterpret_cast<Device *>(device), &expected_md5, firmware_size);
+    VALIDATE_STATUS(status);
+
+    return;
+}
+
+py::bytes ControlWrapper::sensor_get_config(uintptr_t device, uint32_t section_index, uint32_t offset, uint32_t data_length)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    std::unique_ptr<std::string> response = make_unique_nothrow<std::string>(data_length, '\x00');
+    VALIDATE_NOT_NULL(response);
+    
+    VALIDATE_NOT_NULL(reinterpret_cast<Device *>(device));
+    status = Control::sensor_get_config(*reinterpret_cast<Device *>(device), section_index, offset, data_length,
+        (uint8_t*)(response->data()));
+   
+    VALIDATE_STATUS(status);
+
+     return *response;
+}
+
+void ControlWrapper::idle_time_set_measurement(uintptr_t device, bool measurement_enable)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+
+    VALIDATE_NOT_NULL(reinterpret_cast<Device *>(device));
+    status = Control::idle_time_set_measurement(*reinterpret_cast<Device *>(device), measurement_enable);
+    VALIDATE_STATUS(status);
+}
+
+uint64_t ControlWrapper::idle_time_get_measurement(uintptr_t device)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    uint64_t measurement = 0;
+
+    VALIDATE_NOT_NULL(reinterpret_cast<Device *>(device));
+    status = Control::idle_time_get_measurement(*reinterpret_cast<Device *>(device), &measurement);
+    VALIDATE_STATUS(status);
+
+    return measurement;
+}
+
+void ControlWrapper::d2h_notification_manager_set_host_info(uintptr_t device, uint16_t host_port, uint32_t host_ip_address)
+{
+    VALIDATE_NOT_NULL(reinterpret_cast<Device *>(device));
+    hailo_status status = Control::d2h_notification_manager_set_host_info(*reinterpret_cast<Device *>(device), host_port, host_ip_address);
+    VALIDATE_STATUS(status);
+}
+
+void ControlWrapper::d2h_notification_manager_send_host_info_notification(uintptr_t device, uint8_t notification_priority)
+{
+    VALIDATE_NOT_NULL(reinterpret_cast<Device *>(device));
+    hailo_status status = Control::d2h_notification_manager_send_host_info_notification(*reinterpret_cast<Device *>(device), notification_priority);
+    VALIDATE_STATUS(status);
+}
+
+/* Context switch */
+void ControlWrapper::set_context_switch_breakpoint(uintptr_t device, 
+        uint8_t breakpoint_id,
+        bool break_at_any_network_group_index, uint8_t network_group_index, 
+        bool break_at_any_batch_index, uint16_t batch_index, 
+        bool break_at_any_context_index,uint8_t context_index, 
+        bool break_at_any_action_index, uint16_t action_index) 
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    CONTROL_PROTOCOL__context_switch_breakpoint_control_t breakpoint_control = 
+        CONTROL_PROTOCOL__CONTEXT_SWITCH_BREAKPOINT_CONTROL_SET;
+    CONTROL_PROTOCOL__context_switch_breakpoint_data_t breakpoint_data = {
+        break_at_any_network_group_index,
+        network_group_index,
+        break_at_any_batch_index,
+        batch_index,
+        break_at_any_context_index,
+        context_index,
+        break_at_any_action_index,
+        action_index};
+
+    VALIDATE_NOT_NULL(reinterpret_cast<Device *>(device));
+    status = Control::config_context_switch_breakpoint(*reinterpret_cast<Device *>(device), breakpoint_id,
+            breakpoint_control, &breakpoint_data);
+    VALIDATE_STATUS(status);
+}
+
+void ControlWrapper::continue_context_switch_breakpoint(uintptr_t device, uint8_t breakpoint_id) 
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    CONTROL_PROTOCOL__context_switch_breakpoint_control_t breakpoint_control = 
+        CONTROL_PROTOCOL__CONTEXT_SWITCH_BREAKPOINT_CONTROL_CONTINUE;
+    CONTROL_PROTOCOL__context_switch_breakpoint_data_t breakpoint_data = {false,0,false,0,false,0,false,0};
+
+    VALIDATE_NOT_NULL(reinterpret_cast<Device *>(device));
+    status = Control::config_context_switch_breakpoint(*reinterpret_cast<Device *>(device), breakpoint_id, 
+            breakpoint_control, &breakpoint_data);
+    VALIDATE_STATUS(status);
+}
+
+void ControlWrapper::clear_context_switch_breakpoint(uintptr_t device, uint8_t breakpoint_id) 
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    CONTROL_PROTOCOL__context_switch_breakpoint_control_t breakpoint_control = 
+        CONTROL_PROTOCOL__CONTEXT_SWITCH_BREAKPOINT_CONTROL_CLEAR;
+    CONTROL_PROTOCOL__context_switch_breakpoint_data_t breakpoint_data = {false,0,false,0,false,0,false,0};
+
+    VALIDATE_NOT_NULL(reinterpret_cast<Device *>(device));
+    status = Control::config_context_switch_breakpoint(*reinterpret_cast<Device *>(device), breakpoint_id,
+            breakpoint_control, &breakpoint_data);
+    VALIDATE_STATUS(status);
+}
+
+uint8_t ControlWrapper::get_context_switch_breakpoint_status(uintptr_t device, uint8_t breakpoint_id) 
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    CONTROL_PROTOCOL__context_switch_debug_sys_status_t breakpoint_status = 
+        CONTROL_PROTOCOL__CONTEXT_SWITCH_DEBUG_SYS_STATUS_COUNT;
+
+    VALIDATE_NOT_NULL(reinterpret_cast<Device *>(device));
+    status = Control::get_context_switch_breakpoint_status(*reinterpret_cast<Device *>(device), breakpoint_id,
+            &breakpoint_status);
+    VALIDATE_STATUS(status);
+
+    return static_cast<uint8_t>(breakpoint_status);
+}
+
+void ControlWrapper::config_context_switch_timestamp(uintptr_t device, uint16_t batch_index) 
+{
+    VALIDATE_NOT_NULL(reinterpret_cast<Device *>(device));
+    hailo_status status = Control::config_context_switch_timestamp(*reinterpret_cast<Device *>(device), batch_index, true);
+    VALIDATE_STATUS(status);
+}
+
+void ControlWrapper::remove_context_switch_timestamp_configuration(uintptr_t device) 
+{
+    VALIDATE_NOT_NULL(reinterpret_cast<Device *>(device));
+    hailo_status status = Control::config_context_switch_timestamp(*reinterpret_cast<Device *>(device), 0, false);
+    VALIDATE_STATUS(status);
+}
+
+void ControlWrapper::enable_debugging(uintptr_t device, bool is_rma)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+
+    VALIDATE_NOT_NULL(reinterpret_cast<Device *>(device));
+    status = Control::enable_debugging(*reinterpret_cast<Device *>(device), is_rma);
+    VALIDATE_STATUS(status);
+}
+
+void ControlWrapper::add_to_python_module(py::module &m)
+{
+    m.def("_set_clock_freq", &ControlWrapper::set_clock_freq);
+    m.def("close_all_streams", &ControlWrapper::close_all_streams);
+    m.def("config_ahb_to_axi", &ControlWrapper::config_ahb_to_axi);
+    m.def("phy_operation", &ControlWrapper::phy_operation);
+    m.def("latency_measurement_read", &ControlWrapper::latency_measurement_read);
+    m.def("latency_measurement_config", &ControlWrapper::latency_measurement_config);
+    m.def("start_firmware_update", &ControlWrapper::start_firmware_update);
+    m.def("finish_firmware_update", &ControlWrapper::finish_firmware_update);
+    m.def("write_firmware_update", &ControlWrapper::write_firmware_update);
+    m.def("validate_firmware_update", &ControlWrapper::validate_firmware_update);
+    m.def("sensor_get_config", &ControlWrapper::sensor_get_config);
+    m.def("idle_time_set_measurement", &ControlWrapper::idle_time_set_measurement);
+    m.def("idle_time_get_measurement", &ControlWrapper::idle_time_get_measurement);
+    m.def("d2h_notification_manager_set_host_info", &ControlWrapper::d2h_notification_manager_set_host_info);
+    m.def("d2h_notification_manager_send_host_info_notification", &ControlWrapper::d2h_notification_manager_send_host_info_notification);
+    m.def("set_context_switch_breakpoint", &set_context_switch_breakpoint);
+    m.def("continue_context_switch_breakpoint", &continue_context_switch_breakpoint);
+    m.def("clear_context_switch_breakpoint", &clear_context_switch_breakpoint);
+    m.def("get_context_switch_breakpoint_status", &get_context_switch_breakpoint_status);
+    m.def("config_context_switch_timestamp", &config_context_switch_timestamp);
+    m.def("remove_context_switch_timestamp_configuration", &remove_context_switch_timestamp_configuration);
+    m.def("enable_debugging", &enable_debugging);
+    
+    // TODO: HRT-5764 - Remove 'py::module_local()' when removing _pyhailort_internal from external
+    // py::module_local() is needed because these enums are currently in both _pyhailort and _pyhailort_internal, 
+    // and when trying to import one of them on the python side you will get the error:
+    // ImportError: generic_type: type "enum_name" is already registered!
+    // py::module_local() tells pybind11 to keep the external class/enum binding localized to the module. 
+    py::enum_<CONTROL_PROTOCOL__context_switch_debug_sys_status_t>(m, "ContextSwitchBreakpointStatus", py::module_local())
+        .value("CONTEXT_SWITCH_BREAKPOINT_STATUS_CLEARED",CONTROL_PROTOCOL__CONTEXT_SWITCH_DEBUG_SYS_STATUS_CLEARED)
+        .value("CONTEXT_SWITCH_BREAKPOINT_STATUS_WAITING_FOR_BREAKPOINT",CONTROL_PROTOCOL__CONTEXT_SWITCH_DEBUG_SYS_STATUS_WAITING_FOR_BREAKPOINT)
+        .value("CONTEXT_SWITCH_BREAKPOINT_STATUS_REACHED_BREAKPOINT",CONTROL_PROTOCOL__CONTEXT_SWITCH_DEBUG_SYS_STATUS_REACHED_BREAKPOINT)
+        ;
+
+    py::enum_<CONTROL_PROTOCOL__phy_operation_t>(m, "CONTROL_PROTOCOL__phy_operation_t", py::module_local())
+        .value("PHY_OPERATION_RESET", CONTROL_PROTOCOL__PHY_OPERATION_RESET)
+        ;
+
+    py::enum_<CONTROL_PROTOCOL__mipi_deskew_enable_t>(m, "CONTROL_PROTOCOL__mipi_deskew_enable_t", py::module_local())
+        .value("MIPI__DESKEW_FORCE_DISABLE", CONTROL_PROTOCOL__MIPI_DESKEW__FORCE_DISABLE)
+        .value("MIPI__DESKEW_FORCE_ENABLE", CONTROL_PROTOCOL__MIPI_DESKEW__FORCE_ENABLE)
+        .value("MIPI__DESKEW_DEFAULT", CONTROL_PROTOCOL__MIPI_DESKEW__DEFAULT)
+        ;
+
+}
+
+} /* namespace hailort */
\ No newline at end of file
diff --git a/hailort/libhailort/bindings/python/src/internal/control_api.hpp b/hailort/libhailort/bindings/python/src/internal/control_api.hpp
new file mode 100644 (file)
index 0000000..47dce83
--- /dev/null
@@ -0,0 +1,66 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file control_api.hpp
+ * @brief Defines binding to control functions
+ *
+ **/
+
+#ifndef _CONTROL_API_HPP_
+#define _CONTROL_API_HPP_
+
+#include "control.hpp"
+#include "utils.hpp"
+
+#include <pybind11/pybind11.h>
+#include <pybind11/pybind11.h>
+#include <pybind11/numpy.h>
+#include <pybind11/detail/common.h>
+#include <pybind11/stl.h>
+#include <pybind11/complex.h>
+#include <pybind11/functional.h>
+
+namespace hailort
+{
+
+class ControlWrapper {
+public:
+    static void add_to_python_module(py::module &m);
+
+    static void set_clock_freq(uintptr_t device, uint32_t clock_freq);
+    static void close_all_streams(uintptr_t device);
+    static void config_ahb_to_axi(uintptr_t device, bool use_64bit_data_only);
+    static void phy_operation(uintptr_t device, CONTROL_PROTOCOL__phy_operation_t operation_type);
+    static uint32_t latency_measurement_read(uintptr_t device);
+    static void latency_measurement_config(uintptr_t device, uint8_t latency_measurement_en,
+        uint32_t inbound_start_buffer_number, uint32_t outbound_stop_buffer_number, uint32_t inbound_stream_index,
+        uint32_t outbound_stream_index);
+    static void start_firmware_update(uintptr_t device);
+    static void finish_firmware_update(uintptr_t device);
+    static void write_firmware_update(uintptr_t device, uint32_t offset, py::bytes data, uint32_t length);
+    static void validate_firmware_update(uintptr_t device, py::bytes md5_raw_data, uint32_t firmware_size);
+    static py::bytes sensor_get_config(uintptr_t device, uint32_t section_index, uint32_t offset, uint32_t data_length);
+    static void idle_time_set_measurement(uintptr_t device, bool measurement_enable);
+    static uint64_t idle_time_get_measurement(uintptr_t device);
+    static void d2h_notification_manager_set_host_info(uintptr_t device, uint16_t host_port, uint32_t host_ip_address);
+    static void d2h_notification_manager_send_host_info_notification(uintptr_t device, uint8_t notification_priority);
+    static void enable_debugging(uintptr_t device, bool is_rma);
+
+    /* Context switch */
+    static void set_context_switch_breakpoint(uintptr_t device, uint8_t breakpoint_id,
+        bool break_at_any_network_group_index, uint8_t network_group_index, 
+        bool break_at_any_batch_index, uint16_t batch_index, 
+        bool break_at_any_context_index,uint8_t context_index, 
+        bool break_at_any_action_index, uint16_t action_index);
+    static void continue_context_switch_breakpoint(uintptr_t device, uint8_t breakpoint_id);
+    static void clear_context_switch_breakpoint(uintptr_t device, uint8_t breakpoint_id);
+    static uint8_t get_context_switch_breakpoint_status(uintptr_t device, uint8_t breakpoint_id);
+    static void config_context_switch_timestamp(uintptr_t device, uint16_t batch_index);
+    static void remove_context_switch_timestamp_configuration(uintptr_t device);
+};
+
+} /* namespace hailort */
+
+#endif /* _CONTROL_API_HPP_ */
diff --git a/hailort/libhailort/bindings/python/src/internal/pyhailort_internal.cpp b/hailort/libhailort/bindings/python/src/internal/pyhailort_internal.cpp
new file mode 100644 (file)
index 0000000..0f4c360
--- /dev/null
@@ -0,0 +1,216 @@
+#include <pybind11/pybind11.h>
+#include <pybind11/numpy.h>
+#include <pybind11/detail/common.h>
+#include <pybind11/stl.h>
+#include <pybind11/complex.h>
+#include <pybind11/functional.h>
+#include <vector>
+
+#include "pyhailort_internal.hpp"
+#include "control_api.hpp"
+#include "utils.hpp"
+#include "utils.h"
+
+#include "hailo/hailort.h"
+#include "transform_internal.hpp"
+
+namespace hailort
+{
+
+void PyhailortInternal::demux_output_buffer(
+    py::bytes src, const hailo_format_t &src_format, const hailo_3d_image_shape_t &src_shape,
+    std::map<std::string, py::array> dst_buffers, const LayerInfo &mux_layer_info)
+{
+    const size_t hw_frame_size = HailoRTCommon::get_frame_size(src_shape, src_format);
+    auto expected_output_demuxer = OutputDemuxerBase::create(hw_frame_size, mux_layer_info);
+    VALIDATE_EXPECTED(expected_output_demuxer);
+
+    auto demuxer = expected_output_demuxer.release();
+
+    std::map<std::string, MemoryView> dst_ptrs;
+    for (auto &dst_buffer_pair : dst_buffers) {
+        dst_ptrs.insert(std::make_pair(dst_buffer_pair.first,
+            MemoryView(reinterpret_cast<uint8_t*>(dst_buffer_pair.second.mutable_data()),
+                dst_buffer_pair.second.nbytes())));
+    }
+
+    const auto src_str = static_cast<std::string>(src);
+    auto status = demuxer.transform_demux(
+        MemoryView(const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>(src_str.c_str())), src_str.length()), dst_ptrs);
+    VALIDATE_STATUS(status);
+}
+
+void PyhailortInternal::transform_input_buffer(
+    py::array src, const hailo_format_t &src_format, const hailo_3d_image_shape_t &src_shape,
+    uintptr_t dst, size_t dst_size, const hailo_format_t &dst_format, const hailo_3d_image_shape_t &dst_shape,
+    const hailo_quant_info_t &dst_quant_info)
+{
+    auto transform_context = InputTransformContext::create(src_shape, src_format, dst_shape, dst_format,
+        dst_quant_info);
+    VALIDATE_EXPECTED(transform_context);
+
+    MemoryView dst_buffer(reinterpret_cast<uint8_t*>(dst), dst_size);
+    auto status = transform_context.value()->transform(
+        MemoryView::create_const(const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>(src.data())), src.nbytes()),
+        dst_buffer);
+    VALIDATE_STATUS(status);
+}
+
+void PyhailortInternal::transform_output_buffer(
+    py::bytes src, const hailo_format_t &src_format, const hailo_3d_image_shape_t &src_shape,
+    py::array dst, const hailo_format_t &dst_format, const hailo_3d_image_shape_t &dst_shape,
+    const hailo_quant_info_t &dst_quant_info)
+{
+    auto transform_context = OutputTransformContext::create(src_shape, src_format, dst_shape, dst_format,
+        dst_quant_info, {});
+    VALIDATE_EXPECTED(transform_context);
+
+    const auto src_str = static_cast<std::string>(src);
+    MemoryView dst_buffer(reinterpret_cast<uint8_t*>(dst.mutable_data()), dst.nbytes());
+    auto status = transform_context.value()->transform(MemoryView::create_const(src_str.c_str(),
+        src_str.length()), dst_buffer);
+    VALIDATE_STATUS(status);
+}
+
+void PyhailortInternal::transform_output_buffer_nms(
+    py::bytes src, const hailo_format_t &src_format, const hailo_3d_image_shape_t &src_shape,
+    py::array dst, const hailo_format_t &dst_format, const hailo_3d_image_shape_t &dst_shape,
+    const hailo_quant_info_t &dst_quant_info, const hailo_nms_info_t &nms_info)
+{
+    auto transform_context = OutputTransformContext::create(src_shape, src_format, dst_shape, dst_format,
+        dst_quant_info, nms_info);
+    VALIDATE_EXPECTED(transform_context);
+
+    const auto src_str = static_cast<std::string>(src);
+    MemoryView dst_buffer(reinterpret_cast<uint8_t*>(dst.mutable_data()), dst.nbytes());
+    auto status = transform_context.value()->transform(MemoryView::create_const(src_str.c_str(),
+        src_str.size()), dst_buffer);
+    VALIDATE_STATUS(status);
+}
+
+bool PyhailortInternal::is_input_transformation_required(
+    const hailo_3d_image_shape_t &src_shape, const hailo_format_t &src_format,
+    const hailo_3d_image_shape_t &dst_shape, const hailo_format_t &dst_format,
+    const hailo_quant_info_t &quant_info)
+{
+    return InputTransformContext::is_transformation_required(src_shape, src_format, dst_shape, dst_format,
+        quant_info);
+}
+
+bool PyhailortInternal::is_output_transformation_required(
+    const hailo_3d_image_shape_t &src_shape, const hailo_format_t &src_format,
+    const hailo_3d_image_shape_t &dst_shape, const hailo_format_t &dst_format,
+    const hailo_quant_info_t &quant_info)
+{
+    return OutputTransformContext::is_transformation_required(src_shape, src_format, dst_shape, dst_format,
+        quant_info);
+}
+
+py::list PyhailortInternal::get_all_layers_info(const HefWrapper &hef, const std::string &net_group_name)
+{
+    auto network_gorup_metadata = hef.hef_ptr()->pimpl->get_network_group_metadata(net_group_name);
+    VALIDATE_EXPECTED(network_gorup_metadata);
+
+    auto layers_info = network_gorup_metadata->get_all_layer_infos();
+    VALIDATE_EXPECTED(layers_info);
+
+    return py::cast(layers_info.release());
+}
+
+PYBIND11_MODULE(_pyhailort_internal, m) {
+    ControlWrapper::add_to_python_module(m);
+    m.def("demux_output_buffer", &PyhailortInternal::demux_output_buffer);
+    m.def("transform_input_buffer", &PyhailortInternal::transform_input_buffer);
+    m.def("transform_output_buffer", &PyhailortInternal::transform_output_buffer);
+    m.def("transform_output_buffer_nms", &PyhailortInternal::transform_output_buffer_nms);
+    m.def("is_input_transformation_required", &PyhailortInternal::is_input_transformation_required);
+    m.def("is_output_transformation_required", &PyhailortInternal::is_output_transformation_required);
+    m.def("get_all_layers_info", &PyhailortInternal::get_all_layers_info);
+
+    py::class_<BufferIndices>(m, "BufferIndices", py::module_local())
+        .def_readonly("index", &BufferIndices::index)
+        .def_readonly("cluster_index", &BufferIndices::cluster_index)
+        ;
+
+    py::class_<LayerInfo>(m, "HailoLayerInfo", py::module_local())
+        .def_readonly("is_mux", &LayerInfo::is_mux)
+        .def_readonly("mux_predecessors", &LayerInfo::predecessor)
+        .def_readonly("is_defused_nms", &LayerInfo::is_defused_nms)
+        .def_readonly("fused_nms_layer", &LayerInfo::fused_nms_layer)
+        .def_property_readonly("shape", [](LayerInfo& self)
+        {
+            switch (self.format.order) {
+                case HAILO_FORMAT_ORDER_NC:
+                    return py::make_tuple(self.shape.features);
+                case HAILO_FORMAT_ORDER_NHW:
+                    return py::make_tuple(self.shape.height, self.shape.width);
+                default:
+                    return py::make_tuple(self.shape.height, self.shape.width, self.shape.features);
+            }
+        })
+        .def_property_readonly("height", [](LayerInfo& self)
+        {
+            return self.shape.height;
+        })
+        .def_property_readonly("width", [](LayerInfo& self)
+        {
+            return self.shape.width;
+        })
+        .def_property_readonly("features", [](LayerInfo& self)
+        {
+            return self.shape.features;
+        })
+        .def("hw_shape", [](LayerInfo& self)
+        {
+            return py::make_tuple(self.hw_shape.height, self.hw_shape.width, self.hw_shape.features);
+        })
+        .def_property_readonly("padded_height", [](LayerInfo& self)
+        {
+            return self.hw_shape.height;
+        })
+        .def_property_readonly("padded_width", [](LayerInfo& self)
+        {
+            return self.hw_shape.width;
+        })
+        .def_property_readonly("padded_features", [](LayerInfo& self)
+        {
+            return self.hw_shape.features;
+        })
+        .def_readonly("data_bytes", &LayerInfo::hw_data_bytes)
+        .def_readonly("format", &LayerInfo::format)
+        .def_property_readonly("format_order", [](LayerInfo& self)
+        {
+            return self.format.order;
+        })
+        .def_readonly("direction", &LayerInfo::direction)
+        .def_readonly("sys_index", &LayerInfo::index)
+        .def_readonly("name", &LayerInfo::name)
+        .def_readonly("quant_info", &LayerInfo::quant_info)
+        // For backwards compatibility (accessing qp through layer_info directly)
+        .def_property_readonly("qp_zp", [](LayerInfo& self)
+        {
+            return self.quant_info.qp_zp;
+        })
+        .def_property_readonly("qp_scale", [](LayerInfo& self)
+        {
+            return self.quant_info.qp_scale;
+        })
+        .def_property_readonly("limvals_min", [](LayerInfo& self)
+        {
+            return self.quant_info.limvals_min;
+        })
+        .def_property_readonly("limvals_max", [](LayerInfo& self)
+        {
+            return self.quant_info.limvals_max;
+        })
+        .def_readonly("nms_info", &LayerInfo::nms_info)
+        .def_readonly("height_gcd", &LayerInfo::height_gcd)
+        .def_readonly("height_ratios", &LayerInfo::height_ratios)
+        .def_readonly("buffer_indices", &LayerInfo::buffer_indices)
+        .def_readonly("core_bytes_per_buffer", &LayerInfo::core_bytes_per_buffer)
+        .def_readonly("core_buffers_per_frame", &LayerInfo::core_buffers_per_frame)
+        .def_readonly("network_name", &LayerInfo::partial_network_name)
+        ;
+}
+
+} /* namespace hailort */
\ No newline at end of file
diff --git a/hailort/libhailort/bindings/python/src/internal/pyhailort_internal.hpp b/hailort/libhailort/bindings/python/src/internal/pyhailort_internal.hpp
new file mode 100644 (file)
index 0000000..f763700
--- /dev/null
@@ -0,0 +1,51 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file pyhailort_internal.hpp
+ * @brief Defines binding of internal functions over Python.
+ **/
+
+#ifndef _PYHAILORT_INTERNAL_
+#define _PYHAILORT_INTERNAL_
+
+#include <pybind11/pybind11.h>
+#include <pybind11/numpy.h>
+#include <pybind11/detail/common.h>
+#include <pybind11/stl.h>
+#include <pybind11/complex.h>
+#include <pybind11/functional.h>
+#include <vector>
+
+#include "hef_internal.hpp"
+#include "hef_api.hpp"
+#include "utils.hpp"
+#include "utils.h"
+
+namespace hailort
+{
+
+class PyhailortInternal {
+public:
+    static void demux_output_buffer(py::bytes src, const hailo_format_t &src_format, const hailo_3d_image_shape_t &src_shape,
+        std::map<std::string, py::array> dst_buffers, const LayerInfo &mux_layer_info);
+    static void transform_input_buffer(py::array src, const hailo_format_t &src_format, const hailo_3d_image_shape_t &src_shape,
+        uintptr_t dst, size_t dst_size, const hailo_format_t &dst_format, const hailo_3d_image_shape_t &dst_shape,
+        const hailo_quant_info_t &dst_quant_info);
+    static void transform_output_buffer(py::bytes src, const hailo_format_t &src_format,
+        const hailo_3d_image_shape_t &src_shape, py::array dst, const hailo_format_t &dst_format,
+        const hailo_3d_image_shape_t &dst_shape, const hailo_quant_info_t &dst_quant_info);
+    static void transform_output_buffer_nms(py::bytes src, const hailo_format_t &src_format,
+        const hailo_3d_image_shape_t &src_shape, py::array dst, const hailo_format_t &dst_format,
+        const hailo_3d_image_shape_t &dst_shape, const hailo_quant_info_t &dst_quant_info, const hailo_nms_info_t &nms_info);
+    static bool is_input_transformation_required(const hailo_3d_image_shape_t &src_shape, const hailo_format_t &src_format,
+        const hailo_3d_image_shape_t &dst_shape, const hailo_format_t &dst_format, const hailo_quant_info_t &quant_info);
+    static bool is_output_transformation_required(const hailo_3d_image_shape_t &src_shape, const hailo_format_t &src_format,
+        const hailo_3d_image_shape_t &dst_shape, const hailo_format_t &dst_format, const hailo_quant_info_t &quant_info);
+    static py::list get_all_layers_info(const HefWrapper &hef, const std::string &net_group_name);
+};
+
+} /* namespace hailort */
+
+#endif /* _PYHAILORT_INTERNAL_ */
\ No newline at end of file
diff --git a/hailort/libhailort/bindings/python/src/pyhailort.cpp b/hailort/libhailort/bindings/python/src/pyhailort.cpp
new file mode 100644 (file)
index 0000000..20d15c1
--- /dev/null
@@ -0,0 +1,1869 @@
+#include <pybind11/pybind11.h>
+#include <pybind11/numpy.h>
+#include <pybind11/detail/common.h>
+#include <pybind11/stl.h>
+#include <pybind11/complex.h>
+#include <pybind11/functional.h>
+#include <vector>
+using namespace std;
+
+#include "hailo/hailort.h"
+#include "hailo/device.hpp"
+#include "hailo/transform.hpp"
+#include "hailo/hef.hpp"
+#include "hailo/hailort_common.hpp"
+#include "hailo/quantization.hpp"
+
+#include "hef_api.hpp"
+#include "vstream_api.hpp"
+#include "vdevice_api.hpp"
+#include "utils.hpp"
+#include "utils.h"
+
+#include "common/socket.hpp"
+#include "sensor_config_exports.h"
+#include "hailort_defaults.hpp"
+#if defined(__GNUC__)
+#include "common/os/posix/traffic_control.hpp"
+#endif
+
+namespace hailort
+{
+
+#define MAX_HAILO_PACKET_SIZE (4*1024)
+
+
+class PowerMeasurementData { 
+    public:
+        float32_t m_average_value;
+        float32_t m_average_time_value_milliseconds;
+        float32_t m_min_value;
+        float32_t m_max_value;
+        uint32_t m_total_number_of_samples;
+        PowerMeasurementData(hailo_power_measurement_data_t &&c_power_data);
+        bool equals(const PowerMeasurementData &other);
+        static py::tuple get_state(const PowerMeasurementData &power_measurement_data);
+        static PowerMeasurementData set_state(py::tuple t);
+        const static uint32_t NUM_OF_MEMBERS = 5;
+};
+
+PowerMeasurementData::PowerMeasurementData(hailo_power_measurement_data_t &&c_power_data)
+{
+    m_average_value = c_power_data.average_value;
+    m_average_time_value_milliseconds = c_power_data.average_time_value_milliseconds;
+    m_min_value = c_power_data.min_value;
+    m_max_value = c_power_data.max_value;
+    m_total_number_of_samples = c_power_data.total_number_of_samples;
+}
+
+/* Return a tuple that fully encodes the state of the object */
+py::tuple PowerMeasurementData::get_state(const PowerMeasurementData &power_measurement_data){
+    return py::make_tuple(
+        power_measurement_data.m_average_value,
+        power_measurement_data.m_average_time_value_milliseconds,
+        power_measurement_data.m_min_value,
+        power_measurement_data.m_max_value,
+        power_measurement_data.m_total_number_of_samples);
+}
+
+PowerMeasurementData PowerMeasurementData::set_state(py::tuple t){
+    if (PowerMeasurementData::NUM_OF_MEMBERS != t.size())
+        throw std::runtime_error("Invalid power measurement data state!");
+
+    /* Create a new C++ instance */
+    hailo_power_measurement_data_t data;
+    data.average_value = t[0].cast<float32_t>();
+    data.average_time_value_milliseconds = t[1].cast<float32_t>();
+    data.min_value = t[2].cast<float32_t>();
+    data.max_value = t[3].cast<float32_t>();
+    data.total_number_of_samples = t[4].cast<uint32_t>();
+    return PowerMeasurementData(std::move(data));
+}
+
+bool PowerMeasurementData::equals(const PowerMeasurementData &other) {
+    return ((this->m_average_value == other.m_average_value) &&
+        (this->m_average_time_value_milliseconds == other.m_average_time_value_milliseconds) &&
+        (this->m_min_value == other.m_min_value) &&
+        (this->m_max_value == other.m_max_value) &&
+        (this->m_total_number_of_samples == other.m_total_number_of_samples));
+}
+
+bool temperature_info_equals(hailo_chip_temperature_info_t &first, hailo_chip_temperature_info_t &second){
+    return ((first.ts0_temperature == second.ts0_temperature) &&
+        (first.ts1_temperature == second.ts1_temperature) &&
+        (first.sample_count == second.sample_count));
+}
+
+bool hailo_format_equals(hailo_format_t &first, hailo_format_t &second){
+    return ((first.type == second.type) &&
+        (first.order == second.order) &&
+        (first.flags == second.flags));
+}
+class UdpScan {
+    public:
+        UdpScan();
+        std::list<std::string> scan_devices(char *interface_name, uint32_t timeout_milliseconds);
+    private:
+        static const size_t m_max_number_of_devices = 100;
+        hailo_eth_device_info_t m_eth_device_infos[m_max_number_of_devices] = {};
+};
+
+/* Device */
+// TODO HRT-5285: Change Python bindings of device class to use CPP API and move functionality to a new device module.
+uintptr_t create_eth_device(char *device_address, size_t device_address_length, uint16_t port,
+    uint32_t timeout_milliseconds, uint8_t max_number_of_attempts)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    hailo_device device = NULL;
+    hailo_eth_device_info_t device_info = {};
+
+    /* Validate address length */
+    if (INET_ADDRSTRLEN < device_address_length) {
+        EXIT_WITH_ERROR("device_address_length is invalid")
+    }
+
+    device_info.host_address.sin_family = AF_INET;
+    device_info.host_address.sin_port = HAILO_ETH_PORT_ANY;
+    status = Socket::pton(AF_INET, HAILO_ETH_ADDRESS_ANY, &(device_info.host_address.sin_addr));
+    VALIDATE_STATUS(status);
+
+    device_info.device_address.sin_family = AF_INET;
+    device_info.device_address.sin_port = port;
+    status = Socket::pton(AF_INET, device_address, &(device_info.device_address.sin_addr));
+    VALIDATE_STATUS(status);
+
+    device_info.timeout_millis = timeout_milliseconds;
+    device_info.max_number_of_attempts = max_number_of_attempts;
+    device_info.max_payload_size = HAILO_DEFAULT_ETH_MAX_PAYLOAD_SIZE;
+    
+    status = hailo_create_ethernet_device(&device_info, &device);
+    VALIDATE_STATUS(status);
+
+    return (uintptr_t)device;
+}
+
+std::vector<hailo_pcie_device_info_t> scan_pcie_devices(void)
+{
+    auto scan_result = Device::scan_pcie();
+    VALIDATE_EXPECTED(scan_result);
+
+    return scan_result.release();
+}
+
+uintptr_t create_pcie_device(hailo_pcie_device_info_t *device_info)
+{
+    hailo_device device = NULL;
+    hailo_status status = hailo_create_pcie_device(device_info, &device);
+    VALIDATE_STATUS(status);
+
+    return (uintptr_t)device;
+}
+
+void release_device(uintptr_t device)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+
+    status = hailo_release_device((hailo_device)device);
+    VALIDATE_STATUS(status);
+
+    return;
+}
+
+uintptr_t get_hlpcie_device(uintptr_t hailort_device)
+{
+    return hailort_device;
+}
+
+/* Controls */
+hailo_device_identity_t identify(uintptr_t device)
+{
+    VALIDATE_NOT_NULL(reinterpret_cast<Device *>(device));
+
+    auto board_info = reinterpret_cast<Device *>(device)->identify();
+    VALIDATE_EXPECTED(board_info);
+
+    return board_info.release();
+}
+
+hailo_core_information_t core_identify(uintptr_t device)
+{
+    VALIDATE_NOT_NULL(reinterpret_cast<Device *>(device));
+
+    auto core_info = reinterpret_cast<Device *>(device)->core_identify();
+    VALIDATE_EXPECTED(core_info);
+
+    return core_info.release();
+}
+
+void set_fw_logger(uintptr_t device, hailo_fw_logger_level_t level, uint32_t interface_mask)
+{
+    VALIDATE_NOT_NULL(reinterpret_cast<Device*>(device));
+
+    auto status = reinterpret_cast<Device *>(device)->set_fw_logger(level, interface_mask);
+    VALIDATE_STATUS(status);
+}
+
+void set_throttling_state(uintptr_t device, bool should_activate)
+{
+    VALIDATE_NOT_NULL(reinterpret_cast<Device*>(device));
+
+    auto status = reinterpret_cast<Device *>(device)->set_throttling_state(should_activate);
+    VALIDATE_STATUS(status);
+}
+
+bool get_throttling_state(uintptr_t device)
+{
+    VALIDATE_NOT_NULL(reinterpret_cast<Device*>(device));
+
+    auto is_active_expected = reinterpret_cast<Device *>(device)->get_throttling_state();
+    VALIDATE_EXPECTED(is_active_expected);
+
+    return is_active_expected.release();
+}
+
+void set_overcurrent_state(uintptr_t device, bool should_activate)
+{
+    VALIDATE_NOT_NULL(reinterpret_cast<Device*>(device));
+
+    auto status = reinterpret_cast<Device*>(device)->set_overcurrent_state(should_activate);
+    VALIDATE_STATUS(status);
+}
+
+bool get_overcurrent_state(uintptr_t device)
+{
+    VALIDATE_NOT_NULL(reinterpret_cast<Device*>(device));
+
+    auto is_required_expected = reinterpret_cast<Device*>(device)->get_overcurrent_state();
+    VALIDATE_EXPECTED(is_required_expected);
+
+    return is_required_expected.release();
+}
+
+py::bytes read_memory(uintptr_t device, uint32_t address, uint32_t length)
+{
+    std::unique_ptr<std::string> response = make_unique_nothrow<std::string>(length, '\x00');
+    VALIDATE_NOT_NULL(response);
+    VALIDATE_NOT_NULL(reinterpret_cast<Device*>(device));
+
+    MemoryView data_view(const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>(response->data())), length);
+    auto status = (reinterpret_cast<Device*>(device))->read_memory(address, data_view);
+    VALIDATE_STATUS(status);
+
+    return *response;
+}
+
+void write_memory(uintptr_t device, uint32_t address, py::bytes data, uint32_t length)
+{
+    VALIDATE_NOT_NULL(reinterpret_cast<Device*>(device));
+
+    auto status = (reinterpret_cast<Device*>(device))->write_memory(address, MemoryView(const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>(std::string(data).c_str())), length));
+    VALIDATE_STATUS(status);
+
+    return;
+}
+
+void test_chip_memories(uintptr_t device)
+{
+    VALIDATE_NOT_NULL(reinterpret_cast<Device *>(device));
+
+    hailo_status status = reinterpret_cast<Device *>(device)->test_chip_memories();
+    VALIDATE_STATUS(status);
+}
+
+void i2c_write(uintptr_t device, hailo_i2c_slave_config_t *slave_config, uint32_t register_address, py::bytes data,
+    uint32_t length)
+{
+    VALIDATE_NOT_NULL(reinterpret_cast<Device *>(device));
+    VALIDATE_NOT_NULL(slave_config);
+
+    std::string data_str(data);
+    MemoryView data_view = MemoryView::create_const(data_str.c_str(), length);
+    auto status = reinterpret_cast<Device *>(device)->i2c_write(*slave_config, register_address, data_view);
+    VALIDATE_STATUS(status);
+}
+
+py::bytes i2c_read(uintptr_t device, hailo_i2c_slave_config_t *slave_config, uint32_t register_address, uint32_t length)
+{
+    VALIDATE_NOT_NULL(reinterpret_cast<Device *>(device));
+    VALIDATE_NOT_NULL(slave_config);
+
+    std::unique_ptr<std::string> response = make_unique_nothrow<std::string>(length, '\x00');
+    VALIDATE_NOT_NULL(response);
+
+    MemoryView data_view(const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>(response->data())), length);
+    auto status = reinterpret_cast<Device *>(device)->i2c_read(*slave_config, register_address, data_view);
+    VALIDATE_STATUS(status);
+
+    return *response;
+}
+
+float32_t power_measurement(uintptr_t device, hailo_dvm_options_t dvm,
+    hailo_power_measurement_types_t measurement_type)
+{
+    VALIDATE_NOT_NULL(reinterpret_cast<Device *>(device));
+
+    auto measurement = reinterpret_cast<Device *>(device)->power_measurement(dvm, measurement_type);
+    VALIDATE_EXPECTED(measurement);
+    
+    return measurement.release();
+}
+
+void start_power_measurement(uintptr_t device, uint32_t delay_milliseconds,
+    hailo_averaging_factor_t averaging_factor, hailo_sampling_period_t sampling_period)
+{
+    VALIDATE_NOT_NULL(reinterpret_cast<Device *>(device));
+
+    auto status = reinterpret_cast<Device *>(device)->start_power_measurement(delay_milliseconds, averaging_factor,
+        sampling_period);
+    VALIDATE_STATUS(status);
+
+    return;
+}
+
+void set_power_measurement(uintptr_t device, uint32_t index, hailo_dvm_options_t dvm,
+    hailo_power_measurement_types_t measurement_type)
+{
+    VALIDATE_NOT_NULL(reinterpret_cast<Device *>(device));
+
+    auto status = reinterpret_cast<Device *>(device)->set_power_measurement(index, dvm, measurement_type);
+    VALIDATE_STATUS(status);
+
+    return;
+}
+
+PowerMeasurementData get_power_measurement(uintptr_t device, uint32_t index, bool should_clear)
+{
+    VALIDATE_NOT_NULL(reinterpret_cast<Device *>(device));
+
+    auto measurement_data = reinterpret_cast<Device *>(device)->get_power_measurement(index, should_clear);
+    VALIDATE_EXPECTED(measurement_data);
+
+    return PowerMeasurementData(measurement_data.release());
+}
+
+void stop_power_measurement(uintptr_t device)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+
+    status = hailo_stop_power_measurement((hailo_device)device);
+    VALIDATE_STATUS(status);
+
+    return;
+}
+
+void reset(uintptr_t device, hailo_reset_device_mode_t mode)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+
+    status = hailo_reset_device((hailo_device)device, mode);
+    VALIDATE_STATUS(status);
+
+    return;
+}
+
+hailo_fw_user_config_information_t examine_user_config(uintptr_t device)
+{
+    VALIDATE_NOT_NULL(reinterpret_cast<Device *>(device));
+
+    auto user_config_info = reinterpret_cast<Device *>(device)->examine_user_config();
+    VALIDATE_EXPECTED(user_config_info);
+
+    return user_config_info.release();
+}
+
+py::bytes read_user_config(uintptr_t device)
+{
+    VALIDATE_NOT_NULL(reinterpret_cast<Device *>(device));
+
+    auto config_buffer = reinterpret_cast<Device *>(device)->read_user_config();
+    VALIDATE_EXPECTED(config_buffer);
+
+    std::unique_ptr<std::string> response = make_unique_nothrow<std::string>(
+        const_cast<char*>(reinterpret_cast<const char*>(config_buffer->data())), config_buffer->size());
+    VALIDATE_NOT_NULL(response);
+
+    return *response;
+}
+
+void write_user_config(uintptr_t device, py::bytes data)
+{
+    VALIDATE_NOT_NULL(reinterpret_cast<Device *>(device));
+
+    std::string data_str(data); 
+    MemoryView data_view = MemoryView::create_const(data_str.c_str(), data_str.size());
+    auto status = reinterpret_cast<Device *>(device)->write_user_config(data_view);
+    VALIDATE_STATUS(status);
+
+    return;
+}
+
+void erase_user_config(uintptr_t device)
+{
+    VALIDATE_NOT_NULL(reinterpret_cast<Device *>(device));
+
+    auto status = reinterpret_cast<Device *>(device)->erase_user_config();
+    VALIDATE_STATUS(status);
+
+    return;
+}
+
+py::bytes read_board_config(uintptr_t device)
+{
+    auto config_buffer = reinterpret_cast<Device *>(device)->read_board_config();
+    VALIDATE_EXPECTED(config_buffer);
+
+    std::unique_ptr<std::string> response = make_unique_nothrow<std::string>(
+        const_cast<char*>(reinterpret_cast<const char*>(config_buffer->data())), config_buffer->size());
+    VALIDATE_NOT_NULL(response);
+    
+    return *response;
+}
+
+void write_board_config(uintptr_t device, py::bytes data)
+{
+    VALIDATE_NOT_NULL(reinterpret_cast<Device *>(device));
+
+    std::string data_str(data); 
+    MemoryView data_view = MemoryView::create_const(data_str.c_str(), data_str.size());
+    auto status = reinterpret_cast<Device *>(device)->write_board_config(data_view);
+    VALIDATE_STATUS(status);
+
+    return;
+}
+
+hailo_extended_device_information_t get_extended_device_information(uintptr_t device)
+{
+    VALIDATE_NOT_NULL(reinterpret_cast<Device *>(device));
+
+    auto extended_device_info = reinterpret_cast<Device *>(device)->get_extended_device_information();
+    VALIDATE_EXPECTED(extended_device_info);
+       
+    return extended_device_info.release();
+}
+
+hailo_health_info_t get_health_information(uintptr_t device)
+{
+    VALIDATE_NOT_NULL(reinterpret_cast<Device *>(device));
+    auto health_info = reinterpret_cast<Device *>(device)->get_health_information();
+    VALIDATE_EXPECTED(health_info);
+       
+    return health_info.release();
+}
+
+void sensor_store_config(uintptr_t device, uint32_t section_index, uint32_t reset_data_size, uint32_t sensor_type, const std::string &config_file_path,
+    uint16_t config_height, uint16_t config_width, uint16_t config_fps, const std::string &config_name)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    VALIDATE_NOT_NULL(reinterpret_cast<Device*>(device));
+    status = (reinterpret_cast<Device*>(device))->store_sensor_config(section_index, static_cast<hailo_sensor_types_t>(sensor_type), reset_data_size, config_height, config_width,
+        config_fps, config_file_path, config_name);
+
+    VALIDATE_STATUS(status);
+
+    return;
+}
+
+void store_isp_config(uintptr_t device, uint32_t reset_config_size, uint16_t config_height, uint16_t config_width, uint16_t config_fps,
+    const std::string &isp_static_config_file_path, const std::string &isp_runtime_config_file_path, const std::string &config_name)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    VALIDATE_NOT_NULL(reinterpret_cast<Device*>(device));
+    status = (reinterpret_cast<Device*>(device))->store_isp_config(reset_config_size, config_height, config_width, config_fps,
+        isp_static_config_file_path, isp_runtime_config_file_path, config_name);
+    VALIDATE_STATUS(status);
+
+    return;
+}
+
+py::bytes sensor_get_sections_info(uintptr_t device)
+{
+    VALIDATE_NOT_NULL(reinterpret_cast<Device *>(device));
+
+    auto buffer = (reinterpret_cast<Device*>(device))->sensor_get_sections_info();
+    VALIDATE_EXPECTED(buffer);
+    
+    std::unique_ptr<std::string> response = make_unique_nothrow<std::string>(
+        const_cast<char*>(reinterpret_cast<const char*>(buffer->data())), buffer->size());
+    VALIDATE_NOT_NULL(response);
+
+    return *response;
+}
+
+void sensor_set_i2c_bus_index(uintptr_t device, uint32_t sensor_type, uint32_t bus_index)
+{
+    VALIDATE_NOT_NULL(reinterpret_cast<Device *>(device));
+
+    hailo_status status = (reinterpret_cast<Device*>(device))->sensor_set_i2c_bus_index(
+        static_cast<hailo_sensor_types_t>(sensor_type), bus_index);
+    VALIDATE_STATUS(status);
+
+    return;
+}
+
+void sensor_load_and_start_config(uintptr_t device, uint32_t section_index)
+{
+    VALIDATE_NOT_NULL(reinterpret_cast<Device *>(device));
+
+    auto status = (reinterpret_cast<Device*>(device))->sensor_load_and_start_config(section_index);
+    VALIDATE_STATUS(status);
+
+    return;
+}
+
+void sensor_reset(uintptr_t device, uint32_t section_index)
+{
+    VALIDATE_NOT_NULL(reinterpret_cast<Device *>(device));
+
+    auto status = (reinterpret_cast<Device*>(device))->sensor_reset(section_index);
+    VALIDATE_STATUS(status);
+
+    return;
+}
+
+void sensor_set_generic_i2c_slave(uintptr_t device, uint16_t slave_address,
+    uint8_t register_address_size, uint8_t bus_index, uint8_t should_hold_bus, uint8_t endianness)
+{
+    VALIDATE_NOT_NULL(reinterpret_cast<Device *>(device));
+
+    auto status = (reinterpret_cast<Device*>(device))->sensor_set_generic_i2c_slave(slave_address, register_address_size,
+        bus_index, should_hold_bus, endianness);
+    VALIDATE_STATUS(status);
+
+    return;
+}
+
+void firmware_update(uintptr_t device, py::bytes fw_bin, uint32_t fw_bin_length, bool should_reset)
+{
+    VALIDATE_NOT_NULL(reinterpret_cast<Device *>(device));
+    hailo_status status = reinterpret_cast<Device *>(device)->firmware_update(MemoryView::create_const(std::string(fw_bin).c_str(), fw_bin_length),
+        should_reset);
+    VALIDATE_STATUS(status);
+}
+
+void second_stage_update(uintptr_t device, py::bytes second_stage_bin, uint32_t second_stage_bin_length)
+{
+    VALIDATE_NOT_NULL(reinterpret_cast<Device *>(device));
+    hailo_status status = reinterpret_cast<Device *>(device)->second_stage_update((uint8_t *)std::string(second_stage_bin).c_str(), 
+        second_stage_bin_length);
+    VALIDATE_STATUS(status);
+}
+
+py::list configure_device_from_hef(uintptr_t device, const HefWrapper &hef,
+    const NetworkGroupsParamsMap &configure_params={})
+{
+    if (nullptr == (void*)device) {
+        EXIT_WITH_ERROR("Got NULL in parameter 'device'!");
+    }
+
+    auto network_groups = (reinterpret_cast<Device*>(device))->configure(*hef.hef_ptr(), configure_params);
+    VALIDATE_EXPECTED(network_groups);
+
+    py::list results;
+    for (const auto &network_group : network_groups.value()) {
+        results.append(network_group.get());
+    }
+
+    return results;
+}
+
+// Quantization
+void dequantize_output_buffer_from_uint8(py::array src_buffer, py::array dst_buffer, const hailo_format_type_t &dst_dtype,
+    uint32_t shape_size, const hailo_quant_info_t &quant_info)
+{
+    switch (dst_dtype) {
+        case HAILO_FORMAT_TYPE_UINT8:
+            Quantization::dequantize_output_buffer<uint8_t, uint8_t>(static_cast<uint8_t*>(src_buffer.mutable_data()),
+                static_cast<uint8_t*>(dst_buffer.mutable_data()), shape_size, quant_info);
+            break;
+        case HAILO_FORMAT_TYPE_UINT16:
+            Quantization::dequantize_output_buffer<uint16_t, uint8_t>(static_cast<uint8_t*>(src_buffer.mutable_data()),
+                static_cast<uint16_t*>(dst_buffer.mutable_data()), shape_size, quant_info);
+            break;
+        case HAILO_FORMAT_TYPE_FLOAT32:
+            Quantization::dequantize_output_buffer<float32_t, uint8_t>(static_cast<uint8_t*>(src_buffer.mutable_data()),
+                static_cast<float32_t*>(dst_buffer.mutable_data()), shape_size, quant_info);
+            break;
+        default:
+            LOGGER__ERROR("Output quantization isn't supported from src format type uint8 to dst format type = {}",
+                convert_format_type_to_string(dst_dtype));
+            THROW_STATUS_ERROR(HAILO_INVALID_ARGUMENT);
+            break;
+    }
+}
+
+void dequantize_output_buffer_from_uint16(py::array src_buffer, py::array dst_buffer, const hailo_format_type_t &dst_dtype,
+    uint32_t shape_size, const hailo_quant_info_t &quant_info)
+{
+    switch (dst_dtype) {
+        case HAILO_FORMAT_TYPE_UINT16:
+            Quantization::dequantize_output_buffer<uint16_t, uint16_t>(static_cast<uint16_t*>(src_buffer.mutable_data()),
+                static_cast<uint16_t*>(dst_buffer.mutable_data()), shape_size, quant_info);
+            break;
+        case HAILO_FORMAT_TYPE_FLOAT32:
+            Quantization::dequantize_output_buffer<float32_t, uint16_t>(static_cast<uint16_t*>(src_buffer.mutable_data()),
+                static_cast<float32_t*>(dst_buffer.mutable_data()), shape_size, quant_info);
+            break;
+        default:
+            LOGGER__ERROR("Output quantization isn't supported from src dormat type uint16 to dst format type = {}",
+                convert_format_type_to_string(dst_dtype));
+            THROW_STATUS_ERROR(HAILO_INVALID_ARGUMENT);
+            break;
+    }
+}
+
+void dequantize_output_buffer_from_float32(py::array src_buffer, py::array dst_buffer, const hailo_format_type_t &dst_dtype,
+    uint32_t shape_size, const hailo_quant_info_t &quant_info)
+{
+    switch (dst_dtype) {
+        case HAILO_FORMAT_TYPE_FLOAT32:
+            Quantization::dequantize_output_buffer<float32_t, float32_t>(static_cast<float32_t*>(src_buffer.mutable_data()),
+                static_cast<float32_t*>(dst_buffer.mutable_data()), shape_size, quant_info);
+            break;
+        default:
+            LOGGER__ERROR("Output quantization isn't supported from src format type float32 to dst format type = {}",
+                convert_format_type_to_string(dst_dtype));
+            THROW_STATUS_ERROR(HAILO_INVALID_ARGUMENT);
+            break;
+    }
+}
+
+void dequantize_output_buffer_from_uint8_in_place(py::array dst_buffer, const hailo_format_type_t &dst_dtype,
+    uint32_t shape_size, const hailo_quant_info_t &quant_info)
+{
+    switch (dst_dtype) {
+        case HAILO_FORMAT_TYPE_UINT8:
+            Quantization::dequantize_output_buffer_in_place<uint8_t, uint8_t>(
+                static_cast<uint8_t*>(dst_buffer.mutable_data()), shape_size, quant_info);
+            break;
+        case HAILO_FORMAT_TYPE_UINT16:
+            Quantization::dequantize_output_buffer_in_place<uint16_t, uint8_t>(
+                static_cast<uint16_t*>(dst_buffer.mutable_data()), shape_size, quant_info);
+            break;
+        case HAILO_FORMAT_TYPE_FLOAT32:
+            Quantization::dequantize_output_buffer_in_place<float32_t, uint8_t>(
+                static_cast<float32_t*>(dst_buffer.mutable_data()), shape_size, quant_info);
+            break;
+        default:
+            LOGGER__ERROR("Output quantization isn't supported from src format type uint8 to dst format type = {}",
+                convert_format_type_to_string(dst_dtype));
+            THROW_STATUS_ERROR(HAILO_INVALID_ARGUMENT);
+            break;
+    }
+}
+
+void dequantize_output_buffer_from_uint16_in_place(py::array dst_buffer, const hailo_format_type_t &dst_dtype,
+    uint32_t shape_size, const hailo_quant_info_t &quant_info)
+{
+    switch (dst_dtype) {
+        case HAILO_FORMAT_TYPE_UINT16:
+            Quantization::dequantize_output_buffer_in_place<uint16_t, uint16_t>(
+                static_cast<uint16_t*>(dst_buffer.mutable_data()), shape_size, quant_info);
+            break;
+        case HAILO_FORMAT_TYPE_FLOAT32:
+            Quantization::dequantize_output_buffer_in_place<float32_t, uint16_t>(
+                static_cast<float32_t*>(dst_buffer.mutable_data()), shape_size, quant_info);
+            break;
+        default:
+            LOGGER__ERROR("Output quantization isn't supported from src dormat type uint16 to dst format type = {}",
+                convert_format_type_to_string(dst_dtype));
+            THROW_STATUS_ERROR(HAILO_INVALID_ARGUMENT);
+            break;
+    }
+}
+
+void dequantize_output_buffer_from_float32_in_place(py::array dst_buffer, const hailo_format_type_t &dst_dtype,
+    uint32_t shape_size, const hailo_quant_info_t &quant_info)
+{
+    switch (dst_dtype) {
+        case HAILO_FORMAT_TYPE_FLOAT32:
+            Quantization::dequantize_output_buffer_in_place<float32_t, float32_t>(
+                static_cast<float32_t*>(dst_buffer.mutable_data()), shape_size, quant_info);
+            break;
+        default:
+            LOGGER__ERROR("Output quantization isn't supported from src format type float32 to dst format type = {}",
+                convert_format_type_to_string(dst_dtype));
+            THROW_STATUS_ERROR(HAILO_INVALID_ARGUMENT);
+            break;
+    }
+}
+
+void dequantize_output_buffer_in_place(py::array dst_buffer, const hailo_format_type_t &src_dtype,
+    const hailo_format_type_t &dst_dtype, uint32_t shape_size, const hailo_quant_info_t &quant_info)
+{
+    switch (src_dtype) {
+        case HAILO_FORMAT_TYPE_UINT8:
+            dequantize_output_buffer_from_uint8_in_place(dst_buffer, dst_dtype, shape_size, quant_info);
+            break;
+        case HAILO_FORMAT_TYPE_UINT16:
+            dequantize_output_buffer_from_uint16_in_place(dst_buffer, dst_dtype, shape_size, quant_info);
+            break;
+        case HAILO_FORMAT_TYPE_FLOAT32:
+            dequantize_output_buffer_from_float32_in_place(dst_buffer, dst_dtype, shape_size, quant_info);
+            break;
+        default:
+            LOGGER__ERROR("Unsupported src format type = {}", convert_format_type_to_string(dst_dtype));
+            THROW_STATUS_ERROR(HAILO_INVALID_ARGUMENT);
+            break;
+    }
+}
+
+void dequantize_output_buffer(py::array src_buffer, py::array dst_buffer, const hailo_format_type_t &src_dtype,
+    const hailo_format_type_t &dst_dtype, uint32_t shape_size, const hailo_quant_info_t &quant_info)
+{
+    switch (src_dtype) {
+        case HAILO_FORMAT_TYPE_UINT8:
+            dequantize_output_buffer_from_uint8(src_buffer, dst_buffer, dst_dtype, shape_size, quant_info);
+            break;
+        case HAILO_FORMAT_TYPE_UINT16:
+            dequantize_output_buffer_from_uint16(src_buffer, dst_buffer, dst_dtype, shape_size, quant_info);
+            break;
+        case HAILO_FORMAT_TYPE_FLOAT32:
+            dequantize_output_buffer_from_float32(src_buffer, dst_buffer, dst_dtype, shape_size, quant_info);
+            break;
+        default:
+            LOGGER__ERROR("Unsupported src format type = {}", convert_format_type_to_string(dst_dtype));
+            THROW_STATUS_ERROR(HAILO_INVALID_ARGUMENT);
+            break;
+    }
+}
+
+void quantize_input_buffer_from_uint8(py::array src_buffer, py::array dst_buffer, const hailo_format_type_t &dst_dtype,
+    uint32_t shape_size, const hailo_quant_info_t &quant_info)
+{
+    switch (dst_dtype) {
+        case HAILO_FORMAT_TYPE_UINT8:
+            Quantization::quantize_input_buffer<uint8_t, uint8_t>(static_cast<uint8_t*>(src_buffer.mutable_data()),
+                static_cast<uint8_t*>(dst_buffer.mutable_data()), shape_size, quant_info);
+            break;
+        default:
+            LOGGER__ERROR("Input quantization isn't supported from src format type uint8 to dst format type = {}", convert_format_type_to_string(dst_dtype));
+            THROW_STATUS_ERROR(HAILO_INVALID_ARGUMENT);
+            break;
+    }
+}
+
+void quantize_input_buffer_from_uint16(py::array src_buffer, py::array dst_buffer, const hailo_format_type_t &dst_dtype,
+    uint32_t shape_size, const hailo_quant_info_t &quant_info)
+{
+    switch (dst_dtype) {
+        case HAILO_FORMAT_TYPE_UINT8:
+            Quantization::quantize_input_buffer<uint16_t, uint8_t>(static_cast<uint16_t*>(src_buffer.mutable_data()),
+                static_cast<uint8_t*>(dst_buffer.mutable_data()), shape_size, quant_info);
+            break;
+        case HAILO_FORMAT_TYPE_UINT16:
+            Quantization::quantize_input_buffer<uint16_t, uint16_t>(static_cast<uint16_t*>(src_buffer.mutable_data()),
+                static_cast<uint16_t*>(dst_buffer.mutable_data()), shape_size, quant_info);
+            break;
+        default:
+            LOGGER__ERROR("Input quantization isn't supported from src format type uint16 to dst format type = {}",
+                convert_format_type_to_string(dst_dtype));
+            THROW_STATUS_ERROR(HAILO_INVALID_ARGUMENT);
+            break;
+    }
+}
+
+void quantize_input_buffer_from_float32(py::array src_buffer, py::array dst_buffer, const hailo_format_type_t &dst_dtype,
+    uint32_t shape_size, const hailo_quant_info_t &quant_info)
+{
+    switch (dst_dtype) {
+        case HAILO_FORMAT_TYPE_UINT8:
+            Quantization::quantize_input_buffer<float32_t, uint8_t>(static_cast<float32_t*>(src_buffer.mutable_data()),
+                static_cast<uint8_t*>(dst_buffer.mutable_data()), shape_size, quant_info);
+            break;
+        case HAILO_FORMAT_TYPE_UINT16:
+            Quantization::quantize_input_buffer<float32_t, uint16_t>(static_cast<float32_t*>(src_buffer.mutable_data()),
+                static_cast<uint16_t*>(dst_buffer.mutable_data()), shape_size, quant_info);
+            break;
+        default:
+            LOGGER__ERROR("Input quantization isn't supported from src format type float32 to dst format type = {}",
+                convert_format_type_to_string(dst_dtype));
+            THROW_STATUS_ERROR(HAILO_INVALID_ARGUMENT);
+            break;
+    }
+}
+
+void quantize_input_buffer(py::array src_buffer, py::array dst_buffer, const hailo_format_type_t &src_dtype,
+    const hailo_format_type_t &dst_dtype, uint32_t shape_size, const hailo_quant_info_t &quant_info)
+{
+    switch (src_dtype) {
+        case HAILO_FORMAT_TYPE_UINT8:
+            quantize_input_buffer_from_uint8(src_buffer, dst_buffer, dst_dtype, shape_size, quant_info);
+            break;
+        case HAILO_FORMAT_TYPE_UINT16:
+            quantize_input_buffer_from_uint16(src_buffer, dst_buffer, dst_dtype, shape_size, quant_info);
+            break;
+        case HAILO_FORMAT_TYPE_FLOAT32:
+            quantize_input_buffer_from_float32(src_buffer, dst_buffer, dst_dtype, shape_size, quant_info);
+            break;
+        default:
+            LOGGER__ERROR("Input quantization isn't supported for src format type = {}", convert_format_type_to_string(dst_dtype));
+            THROW_STATUS_ERROR(HAILO_INVALID_ARGUMENT);
+            break;
+    }
+}
+
+void set_pause_frames(uintptr_t device, bool rx_pause_frames_enable)
+{
+    VALIDATE_NOT_NULL(reinterpret_cast<Device *>(device));
+
+    auto status = reinterpret_cast<Device *>(device)->set_pause_frames(rx_pause_frames_enable);
+    VALIDATE_STATUS(status);
+
+    return;
+}
+
+void wd_enable(uintptr_t device, hailo_cpu_id_t cpu_id)
+{
+    VALIDATE_NOT_NULL(reinterpret_cast<Device *>(device));
+
+    hailo_status status = reinterpret_cast<Device *>(device)->wd_enable(cpu_id);
+    VALIDATE_STATUS(status);
+
+    return;
+}
+
+void wd_disable(uintptr_t device, hailo_cpu_id_t cpu_id)
+{
+    VALIDATE_NOT_NULL(reinterpret_cast<Device *>(device));
+
+    hailo_status status = reinterpret_cast<Device *>(device)->wd_disable(cpu_id);
+    VALIDATE_STATUS(status);
+
+    return;
+}
+
+void wd_config(uintptr_t device, hailo_cpu_id_t cpu_id, uint32_t wd_cycles, hailo_watchdog_mode_t wd_mode)
+{
+    VALIDATE_NOT_NULL(reinterpret_cast<Device *>(device));
+    auto status = reinterpret_cast<Device *>(device)->wd_config(cpu_id, wd_cycles, wd_mode);
+    VALIDATE_STATUS(status);
+}
+
+uint32_t previous_system_state(uintptr_t device, hailo_cpu_id_t cpu_id)
+{
+    VALIDATE_NOT_NULL(reinterpret_cast<Device *>(device));
+
+    auto system_state = reinterpret_cast<Device *>(device)->previous_system_state(cpu_id);
+    VALIDATE_EXPECTED(system_state);
+
+    return system_state.release();
+}
+
+hailo_chip_temperature_info_t get_chip_temperature(uintptr_t device)
+{
+    VALIDATE_NOT_NULL(reinterpret_cast<Device *>(device));
+    
+    auto temp_info = reinterpret_cast<Device *>(device)->get_chip_temperature();
+    VALIDATE_EXPECTED(temp_info);
+
+    return temp_info.release();
+}
+
+void set_input_stream_timeout(uintptr_t input_stream, uint32_t timeout_milis)
+{
+    hailo_status status = hailo_set_input_stream_timeout((hailo_input_stream)input_stream, timeout_milis);
+    VALIDATE_STATUS(status);
+}
+
+void set_output_stream_timeout(uintptr_t output_stream, uint32_t timeout_milis)
+{
+    hailo_status status = hailo_set_output_stream_timeout((hailo_output_stream)output_stream, timeout_milis);
+    VALIDATE_STATUS(status);
+}
+
+void set_notification_callback(uintptr_t device, const std::function<void(uintptr_t, const hailo_notification_t&, py::object)> &callback,
+    hailo_notification_id_t notification_id, py::object opaque)
+{
+    // we capture opaque and move it because when opaque goes out of score it will be deleted,
+    // so capturing it ensures that it will not be deleted
+    hailo_status status = ((Device*)device)->set_notification_callback(
+        [callback, op = std::move(opaque)] (Device &device, const hailo_notification_t &notification, void* opaque) {
+            (void)opaque;
+            callback((uintptr_t)&device, notification, op);
+        }, notification_id, nullptr);
+    VALIDATE_STATUS(status);
+}
+
+void remove_notification_callback(uintptr_t device, hailo_notification_id_t notification_id)
+{
+    hailo_status status = hailo_remove_notification_callback(reinterpret_cast<hailo_device>(device), notification_id);
+    VALIDATE_STATUS(status);
+}
+
+UdpScan::UdpScan()
+{
+}
+
+std::list<std::string> UdpScan::scan_devices(char* interface_name, uint32_t timeout_milliseconds)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    size_t number_of_devices = 0;
+    std::list<std::string> device_addresses;
+    char textual_ip_address[INET_ADDRSTRLEN] = {0};
+    const char *inet_ntop_rc = NULL;
+
+    status = hailo_scan_ethernet_devices(interface_name, m_eth_device_infos, 1, &number_of_devices, timeout_milliseconds);
+    VALIDATE_STATUS(status);
+
+    for(size_t i = 0; i<number_of_devices; ++i) {
+        inet_ntop_rc = inet_ntop(AF_INET, &(m_eth_device_infos[i].device_address.sin_addr), textual_ip_address, INET_ADDRSTRLEN);
+        if (NULL == inet_ntop_rc) {
+            EXIT_WITH_ERROR("Could not convert ip address to textual format (inet_ntop has failed)");
+        }
+        device_addresses.push_back(textual_ip_address);
+    }
+
+    return device_addresses;
+}
+
+py::bytes read_log(uintptr_t device, size_t byte_count, hailo_cpu_id_t cpu_id)
+{
+    std::string response;
+
+    response.reserve(byte_count);
+    response.resize(byte_count);
+
+    MemoryView response_view ((&response[0]), byte_count);
+    auto response_size_expected = ((Device*)device)->read_log(response_view, cpu_id);
+    VALIDATE_EXPECTED(response_size_expected);
+
+    response.resize(response_size_expected.release());
+    return py::bytes(response);
+}
+
+void direct_write_memory(uintptr_t device, uint32_t address, py::bytes buffer)
+{
+    const auto buffer_str = static_cast<std::string>(buffer);
+    hailo_status status = ((Device*)device)->direct_write_memory(address, buffer_str.c_str(),
+        (uint32_t) (buffer_str.length()));
+    VALIDATE_STATUS(status);
+}
+
+py::bytes direct_read_memory(uintptr_t device, uint32_t address, uint32_t size)
+{
+    std::string buffer_str;
+
+    buffer_str.reserve(size);
+    buffer_str.resize(size);
+
+    hailo_status status = ((Device*)device)->direct_read_memory(address, (char*)buffer_str.c_str(), size);
+    VALIDATE_STATUS(status);
+
+    buffer_str.resize(size);
+    return py::bytes(buffer_str);
+}
+
+std::string get_status_message(uint32_t status_in)
+{
+    auto status_str = hailo_get_status_message((hailo_status)status_in);
+    if (status_str == nullptr) {
+        // Invalid status
+        return "";
+    }
+    else {
+        return status_str;
+    }
+}
+
+#if defined(__GNUC__)
+
+class TrafficControlUtilWrapper final
+{
+public:
+    static TrafficControlUtilWrapper create(const std::string &ip, uint16_t port, uint32_t rate_bytes_per_sec)
+    {
+        auto tc_expected = TrafficControlUtil::create(ip, port, rate_bytes_per_sec);
+        VALIDATE_STATUS(tc_expected.status());
+
+        auto tc_ptr = make_unique_nothrow<TrafficControlUtil>(tc_expected.release());
+        if (nullptr == tc_ptr) {
+            VALIDATE_STATUS(HAILO_OUT_OF_HOST_MEMORY);
+        }
+        return TrafficControlUtilWrapper(std::move(tc_ptr));
+    }
+
+    void set_rate_limit()
+    {
+        VALIDATE_STATUS(m_tc->set_rate_limit());
+    }
+
+    void reset_rate_limit()
+    {
+        VALIDATE_STATUS(m_tc->reset_rate_limit());
+    }
+
+    static std::string get_interface_name(const std::string &ip)
+    {
+        auto name = TrafficControlUtil::get_interface_name(ip);
+        VALIDATE_STATUS(name.status());
+
+        return name.value();
+    }
+
+    static void add_to_python_module(py::module &m)
+    {
+        py::class_<TrafficControlUtilWrapper>(m, "TrafficControlUtil")
+        .def(py::init(&TrafficControlUtilWrapper::create))
+        .def("set_rate_limit", &TrafficControlUtilWrapper::set_rate_limit)
+        .def("reset_rate_limit", &TrafficControlUtilWrapper::reset_rate_limit)
+        .def_static("get_interface_name", [](const std::string &ip) {
+            return TrafficControlUtilWrapper::get_interface_name(ip);
+        });
+        ;
+        ;
+    }
+
+private:
+    TrafficControlUtilWrapper(std::unique_ptr<TrafficControlUtil> tc) :
+        m_tc(std::move(tc))
+    {}
+    
+    std::unique_ptr<TrafficControlUtil> m_tc;
+};
+
+#endif
+
+// End of temp hack for hlpcie
+
+PYBIND11_MODULE(_pyhailort, m) {
+    m.def("get_status_message", &get_status_message);
+    // Device
+    m.def("create_eth_device", &create_eth_device);
+    m.def("create_pcie_device", &create_pcie_device);
+    m.def("scan_pcie_devices", &scan_pcie_devices);
+    m.def("release_device", &release_device);
+    m.def("get_hlpcie_device", &get_hlpcie_device);
+    // Controls
+    m.def("identify", &identify);
+    m.def("core_identify", &core_identify);
+    m.def("set_fw_logger", &set_fw_logger);
+    m.def("read_memory", &read_memory);
+    m.def("write_memory", &write_memory);
+    m.def("power_measurement", &power_measurement);
+    m.def("start_power_measurement", &start_power_measurement);
+    m.def("stop_power_measurement", &stop_power_measurement);
+    m.def("set_power_measurement", &set_power_measurement);
+    m.def("get_power_measurement", &get_power_measurement);
+    m.def("firmware_update", &firmware_update);
+    m.def("second_stage_update", &second_stage_update);
+    m.def("examine_user_config", &examine_user_config);
+    m.def("read_user_config", &read_user_config);
+    m.def("write_user_config", &write_user_config);
+    m.def("erase_user_config", &erase_user_config);
+    m.def("read_board_config", &read_board_config);
+    m.def("write_board_config", &write_board_config);
+    m.def("i2c_write", &i2c_write);
+    m.def("i2c_read", &i2c_read);
+    m.def("sensor_store_config", &sensor_store_config);
+    m.def("store_isp_config", &store_isp_config);
+    m.def("sensor_set_i2c_bus_index", &sensor_set_i2c_bus_index);
+    m.def("sensor_load_and_start_config", &sensor_load_and_start_config);
+    m.def("sensor_reset", &sensor_reset);
+    m.def("sensor_set_generic_i2c_slave", &sensor_set_generic_i2c_slave);
+    m.def("sensor_get_sections_info", &sensor_get_sections_info);
+    m.def("reset", &reset);
+    m.def("wd_enable", &wd_enable);
+    m.def("wd_disable", &wd_disable);
+    m.def("wd_config", &wd_config);
+    m.def("previous_system_state", &previous_system_state);
+    m.def("get_chip_temperature", &get_chip_temperature);
+    m.def("get_extended_device_information", &get_extended_device_information);
+    m.def("set_pause_frames", &set_pause_frames);
+    m.def("test_chip_memories", &test_chip_memories);
+    m.def("_get_health_information", &get_health_information);
+    m.def("set_throttling_state", &set_throttling_state);
+    m.def("get_throttling_state", &get_throttling_state);
+    m.def("_set_overcurrent_state", &set_overcurrent_state);
+    m.def("_get_overcurrent_state", &get_overcurrent_state);
+    //HEF
+    m.def("configure_device_from_hef", &configure_device_from_hef);
+    //Stream related
+    m.def("set_input_stream_timeout", &set_input_stream_timeout);
+    m.def("set_output_stream_timeout", &set_output_stream_timeout);
+    m.def("set_notification_callback", &set_notification_callback);
+    m.def("remove_notification_callback", &remove_notification_callback);
+    m.def("dequantize_output_buffer_in_place", &dequantize_output_buffer_in_place);
+    m.def("dequantize_output_buffer", &dequantize_output_buffer);
+    m.def("quantize_input_buffer", &quantize_input_buffer);
+
+    py::class_<hailo_pcie_device_info_t>(m, "PcieDeviceInfo")
+        .def(py::init<>())
+        .def_static("_parse", [](const std::string &device_info_str) {
+            auto device_info = Device::parse_pcie_device_info(device_info_str);
+            VALIDATE_EXPECTED(device_info);
+            return device_info.release();
+        })
+        .def_readwrite("domain", &hailo_pcie_device_info_t::domain)
+        .def_readwrite("bus", &hailo_pcie_device_info_t::bus)
+        .def_readwrite("device", &hailo_pcie_device_info_t::device)
+        .def_readwrite("func", &hailo_pcie_device_info_t::func)
+        .def("__str__", [](const hailo_pcie_device_info_t &self) {
+            auto device_info_str = Device::pcie_device_info_to_string(self);
+            VALIDATE_EXPECTED(device_info_str);
+            return device_info_str.release();
+        })
+        ;
+
+    py::class_<UdpScan>(m, "UdpScan")
+        .def(py::init<>())
+        .def("scan_devices", &UdpScan::scan_devices)
+        ;
+
+    py::class_<PowerMeasurementData>(m, "PowerMeasurementData")
+        .def_readonly("average_value", &PowerMeasurementData::m_average_value, "float, The average value of the samples that were sampled")
+        .def_readonly("average_time_value_milliseconds", &PowerMeasurementData::m_average_time_value_milliseconds, "float, Average time in milliseconds between sampels")
+        .def_readonly("min_value", &PowerMeasurementData::m_min_value, "float, The minimum value of the samples that were sampled")
+        .def_readonly("max_value", &PowerMeasurementData::m_max_value, "float, The maximun value of the samples that were sampled")
+        .def_readonly("total_number_of_samples", &PowerMeasurementData::m_total_number_of_samples, "uint, The number of samples that were sampled")
+        .def("equals", &PowerMeasurementData::equals)
+        .def(py::pickle(&PowerMeasurementData::get_state, &PowerMeasurementData::set_state))
+        ;
+
+    py::enum_<hailo_device_architecture_t>(m, "DeviceArchitecture")
+        .value("HAILO8_A0", HAILO_ARCH_HAILO8_A0)
+        .value("HAILO8_B0", HAILO_ARCH_HAILO8_B0)
+        .value("MERCURY_CA", HAILO_ARCH_MERCURY_CA)
+    ;
+
+    /* TODO: SDK-15648 */
+    py::enum_<hailo_dvm_options_t>(m, "DvmTypes", "Enum-like class representing the different DVMs that can be measured.\nThis determines the device that would be measured.")
+        .value("AUTO", HAILO_DVM_OPTIONS_AUTO, "Choose the default value according to the supported features.")
+        .value("VDD_CORE", HAILO_DVM_OPTIONS_VDD_CORE, "Perform measurements over the core. Exists only in Hailo-8 EVB.")
+        .value("VDD_IO", HAILO_DVM_OPTIONS_VDD_IO, "Perform measurements over the IO. Exists only in Hailo-8 EVB.")
+        .value("MIPI_AVDD", HAILO_DVM_OPTIONS_MIPI_AVDD, "Perform measurements over the MIPI avdd. Exists only in Hailo-8 EVB.")
+        .value("MIPI_AVDD_H", HAILO_DVM_OPTIONS_MIPI_AVDD_H, "Perform measurements over the MIPI avdd_h. Exists only in Hailo-8 EVB.")
+        .value("USB_AVDD_IO", HAILO_DVM_OPTIONS_USB_AVDD_IO, "Perform measurements over the IO. Exists only in Hailo-8 EVB.")
+        .value("VDD_TOP", HAILO_DVM_OPTIONS_VDD_TOP, "Perform measurements over the top. Exists only in Hailo-8 EVB.")
+        .value("USB_AVDD_IO_HV", HAILO_DVM_OPTIONS_USB_AVDD_IO_HV, "Perform measurements over the USB_AVDD_IO_HV. Exists only in Hailo-8 EVB.")
+        .value("AVDD_H", HAILO_DVM_OPTIONS_AVDD_H, "Perform measurements over the AVDD_H. Exists only in Hailo-8 EVB.")
+        .value("SDIO_VDD_IO", HAILO_DVM_OPTIONS_SDIO_VDD_IO, "Perform measurements over the SDIO_VDDIO. Exists only in Hailo-8 EVB.")
+        .value("OVERCURRENT_PROTECTION", HAILO_DVM_OPTIONS_OVERCURRENT_PROTECTION, "Perform measurements over the OVERCURRENT_PROTECTION dvm. Exists only for Hailo-8 platforms supporting current monitoring (such as M.2 and mPCIe).")
+        ;
+
+    py::enum_<hailo_power_measurement_types_t>(m, "PowerMeasurementTypes", "Enum-like class representing the different power measurement types. This determines what\nwould be measured on the device.")
+        .value("AUTO", HAILO_POWER_MEASUREMENT_TYPES__AUTO, "Choose the default value according to the supported features.")
+        .value("SHUNT_VOLTAGE", HAILO_POWER_MEASUREMENT_TYPES__SHUNT_VOLTAGE, "Measure the shunt voltage. Unit is mV")
+        .value("BUS_VOLTAGE", HAILO_POWER_MEASUREMENT_TYPES__BUS_VOLTAGE, "Measure the bus voltage. Unit is mV")
+        .value("POWER", HAILO_POWER_MEASUREMENT_TYPES__POWER, "Measure the power. Unit is W")
+        .value("CURRENT", HAILO_POWER_MEASUREMENT_TYPES__CURRENT, "Measure the current. Unit is mA")
+        ;
+
+    py::enum_<hailo_sampling_period_t>(m, "SamplingPeriod", "Enum-like class representing all bit options and related conversion times for each bit\nsetting for Bus Voltage and Shunt Voltage.")
+        .value("PERIOD_140us", HAILO_SAMPLING_PERIOD_140US, "The sensor provides a new sampling every 140us.")
+        .value("PERIOD_204us", HAILO_SAMPLING_PERIOD_204US, "The sensor provides a new sampling every 204us.")
+        .value("PERIOD_332us", HAILO_SAMPLING_PERIOD_332US, "The sensor provides a new sampling every 332us.")
+        .value("PERIOD_588us", HAILO_SAMPLING_PERIOD_588US, "The sensor provides a new sampling every 588us.")
+        .value("PERIOD_1100us", HAILO_SAMPLING_PERIOD_1100US, "The sensor provides a new sampling every 1100us.")
+        .value("PERIOD_2116us", HAILO_SAMPLING_PERIOD_2116US, "The sensor provides a new sampling every 2116us.")
+        .value("PERIOD_4156us", HAILO_SAMPLING_PERIOD_4156US, "The sensor provides a new sampling every 4156us.")
+        .value("PERIOD_8244us", HAILO_SAMPLING_PERIOD_8244US, "The sensor provides a new sampling every 8244us.")
+        ;
+
+    py::enum_<hailo_averaging_factor_t>(m, "AveragingFactor", "Enum-like class representing all the AVG bit settings and related number of averages\nfor each bit setting.")
+        .value("AVERAGE_1", HAILO_AVERAGE_FACTOR_1, "Each sample reflects a value of 1 sub-samples.")
+        .value("AVERAGE_4", HAILO_AVERAGE_FACTOR_4, "Each sample reflects a value of 4 sub-samples.")
+        .value("AVERAGE_16", HAILO_AVERAGE_FACTOR_16, "Each sample reflects a value of 16 sub-samples.")
+        .value("AVERAGE_64", HAILO_AVERAGE_FACTOR_64, "Each sample reflects a value of 64 sub-samples.")
+        .value("AVERAGE_128", HAILO_AVERAGE_FACTOR_128, "Each sample reflects a value of 128 sub-samples.")
+        .value("AVERAGE_256", HAILO_AVERAGE_FACTOR_256, "Each sample reflects a value of 256 sub-samples.")
+        .value("AVERAGE_512", HAILO_AVERAGE_FACTOR_512, "Each sample reflects a value of 512 sub-samples.")
+        .value("AVERAGE_1024", HAILO_AVERAGE_FACTOR_1024, "Each sample reflects a value of 1024 sub-samples.")
+        ;
+
+    py::class_<hailo_notification_t>(m, "Notification")
+        .def_readonly("notification_id", &hailo_notification_t::id)
+        .def_readonly("sequence", &hailo_notification_t::sequence)
+        .def_readonly("body", &hailo_notification_t::body)
+        ;
+
+    py::class_<hailo_notification_message_parameters_t>(m, "NotificationMessageParameters")
+        UNION_PROPERTY(hailo_notification_message_parameters_t, hailo_rx_error_notification_message_t, rx_error_notification)
+        UNION_PROPERTY(hailo_notification_message_parameters_t, hailo_debug_notification_message_t, debug_notification)
+        UNION_PROPERTY(hailo_notification_message_parameters_t, hailo_health_monitor_dataflow_shutdown_notification_message_t, health_monitor_dataflow_shutdown_notification)
+        UNION_PROPERTY(hailo_notification_message_parameters_t, hailo_health_monitor_temperature_alarm_notification_message_t, health_monitor_temperature_alarm_notification)
+        UNION_PROPERTY(hailo_notification_message_parameters_t, hailo_health_monitor_overcurrent_alert_notification_message_t, health_monitor_overcurrent_alert_notification)
+        UNION_PROPERTY(hailo_notification_message_parameters_t, hailo_health_monitor_lcu_ecc_error_notification_message_t, health_monitor_lcu_ecc_error_notification)
+        UNION_PROPERTY(hailo_notification_message_parameters_t, hailo_health_monitor_cpu_ecc_notification_message_t, health_monitor_cpu_ecc_notification)
+        UNION_PROPERTY(hailo_notification_message_parameters_t, hailo_health_monitor_clock_changed_notification_message_t, health_monitor_clock_changed_notification)
+        ;
+
+    py::enum_<hailo_overcurrent_protection_overcurrent_zone_t>(m, "OvercurrentAlertState")
+        .value("OVERCURRENT_ZONE_NONE", HAILO_OVERCURRENT_PROTECTION_OVERCURRENT_ZONE__NONE)
+        .value("OVERCURRENT_ZONE_ORANGE", HAILO_OVERCURRENT_PROTECTION_OVERCURRENT_ZONE__ORANGE)
+        .value("OVERCURRENT_ZONE_RED", HAILO_OVERCURRENT_PROTECTION_OVERCURRENT_ZONE__RED)
+        ;
+    
+    py::enum_<hailo_temperature_protection_temperature_zone_t>(m, "TemperatureZone")
+        .value("TEMPERATURE_ZONE_GREEN", HAILO_TEMPERATURE_PROTECTION_TEMPERATURE_ZONE__GREEN)
+        .value("TEMPERATURE_ZONE_ORANGE", HAILO_TEMPERATURE_PROTECTION_TEMPERATURE_ZONE__ORANGE)
+        .value("TEMPERATURE_ZONE_RED", HAILO_TEMPERATURE_PROTECTION_TEMPERATURE_ZONE__RED)
+        ;
+    
+    py::class_<hailo_rx_error_notification_message_t>(m, "RxErrorNotificationMessage")
+        .def_readonly("error", &hailo_rx_error_notification_message_t::error)
+        .def_readonly("queue_number", &hailo_rx_error_notification_message_t::queue_number)
+        .def_readonly("rx_errors_count", &hailo_rx_error_notification_message_t::rx_errors_count)
+        ;
+
+    py::class_<hailo_debug_notification_message_t>(m, "DebugNotificationMessage")
+        .def_readonly("connection_status", &hailo_debug_notification_message_t::connection_status)
+        .def_readonly("connection_type", &hailo_debug_notification_message_t::connection_type)
+        .def_readonly("pcie_is_active", &hailo_debug_notification_message_t::pcie_is_active)
+        .def_readonly("host_port", &hailo_debug_notification_message_t::host_port)
+        .def_readonly("host_ip_addr", &hailo_debug_notification_message_t::host_ip_addr)
+        ;
+
+    py::class_<hailo_health_monitor_dataflow_shutdown_notification_message_t>(m, "HealthMonitorDataflowShutdownNotificationMessage")
+        .def_readonly("closed_input_streams", &hailo_health_monitor_dataflow_shutdown_notification_message_t::closed_input_streams)
+        .def_readonly("closed_output_streams", &hailo_health_monitor_dataflow_shutdown_notification_message_t::closed_output_streams)
+        .def_readonly("ts0_temperature", &hailo_health_monitor_dataflow_shutdown_notification_message_t::ts0_temperature)
+        .def_readonly("ts1_temperature", &hailo_health_monitor_dataflow_shutdown_notification_message_t::ts1_temperature)
+        ;
+
+    py::class_<hailo_health_monitor_temperature_alarm_notification_message_t>(m, "HealthMonitorTemperatureAlarmNotificationMessage")
+        .def_readonly("temperature_zone", &hailo_health_monitor_temperature_alarm_notification_message_t::temperature_zone)
+        .def_readonly("alarm_ts_id", &hailo_health_monitor_temperature_alarm_notification_message_t::alarm_ts_id)
+        .def_readonly("ts0_temperature", &hailo_health_monitor_temperature_alarm_notification_message_t::ts0_temperature)
+        .def_readonly("ts1_temperature", &hailo_health_monitor_temperature_alarm_notification_message_t::ts1_temperature)
+        ;
+
+    py::class_<hailo_health_monitor_overcurrent_alert_notification_message_t>(m, "HealthMonitorOvercurrentAlertNotificationMessage")
+        .def_readonly("overcurrent_zone", &hailo_health_monitor_overcurrent_alert_notification_message_t::overcurrent_zone)
+        .def_readonly("exceeded_alert_threshold", &hailo_health_monitor_overcurrent_alert_notification_message_t::exceeded_alert_threshold)
+        .def_readonly("sampled_current_during_alert", &hailo_health_monitor_overcurrent_alert_notification_message_t::sampled_current_during_alert)
+        ;
+
+    py::class_<hailo_health_monitor_lcu_ecc_error_notification_message_t>(m, "HealthMonitorLcuEccErrorNotificationMessage")
+        .def_readonly("cluster_error", &hailo_health_monitor_lcu_ecc_error_notification_message_t::cluster_error)
+        ;
+
+    py::class_<hailo_health_monitor_cpu_ecc_notification_message_t>(m, "HealthMonitorCpuEccNotificationMessage")
+        .def_readonly("memory_bitmap", &hailo_health_monitor_cpu_ecc_notification_message_t::memory_bitmap)
+        ;
+
+    py::class_<hailo_health_monitor_clock_changed_notification_message_t>(m, "HealthMonitoClockChangedNotificationMessage")
+        .def_readonly("previous_clock", &hailo_health_monitor_clock_changed_notification_message_t::previous_clock)
+        .def_readonly("current_clock", &hailo_health_monitor_clock_changed_notification_message_t::current_clock)
+        ;
+
+    py::enum_<hailo_notification_id_t>(m, "NotificationId")
+        .value("ETHERNET_RX_ERROR", HAILO_NOTIFICATION_ID_ETHERNET_RX_ERROR)
+        .value("HEALTH_MONITOR_TEMPERATURE_ALARM", HAILO_NOTIFICATION_ID_HEALTH_MONITOR_TEMPERATURE_ALARM)
+        .value("HEALTH_MONITOR_DATAFLOW_SHUTDOWN", HAILO_NOTIFICATION_ID_HEALTH_MONITOR_DATAFLOW_SHUTDOWN)
+        .value("HEALTH_MONITOR_OVERCURRENT_ALARM", HAILO_NOTIFICATION_ID_HEALTH_MONITOR_OVERCURRENT_ALARM)
+        .value("HEALTH_MONITOR_LCU_ECC_CORRECTABLE_ERROR", HAILO_NOTIFICATION_ID_LCU_ECC_CORRECTABLE_ERROR)
+        .value("HEALTH_MONITOR_LCU_ECC_UNCORRECTABLE_ERROR",  HAILO_NOTIFICATION_ID_LCU_ECC_UNCORRECTABLE_ERROR)
+        .value("HEALTH_MONITOR_CPU_ECC_ERROR",  HAILO_NOTIFICATION_ID_CPU_ECC_ERROR)
+        .value("HEALTH_MONITOR_CPU_ECC_FATAL",  HAILO_NOTIFICATION_ID_CPU_ECC_FATAL)
+        .value("DEBUG", HAILO_NOTIFICATION_ID_DEBUG)
+        .value("CONTEXT_SWITCH_BREAKPOINT_REACHED", HAILO_NOTIFICATION_ID_CONTEXT_SWITCH_BREAKPOINT_REACHED)
+        .value("HEALTH_MONITOR_CLOCK_CHANGED_EVENT", HAILO_NOTIFICATION_ID_HEALTH_MONITOR_CLOCK_CHANGED_EVENT)
+        ;
+
+    py::enum_<hailo_watchdog_mode_t>(m, "WatchdogMode")
+        .value("WATCHDOG_MODE_HW_SW", HAILO_WATCHDOG_MODE_HW_SW)
+        .value("WATCHDOG_MODE_HW_ONLY", HAILO_WATCHDOG_MODE_HW_ONLY)
+        ;
+
+    py::class_<hailo_firmware_version_t>(m, "FirmwareVersion")
+        .def_readonly("major", &hailo_firmware_version_t::major)
+        .def_readonly("minor", &hailo_firmware_version_t::minor)
+        .def_readonly("revision", &hailo_firmware_version_t::revision)
+        ;
+
+    py::class_<hailo_device_identity_t>(m, "BoardInformation")
+        .def_readonly("protocol_version", &hailo_device_identity_t::protocol_version)
+        .def_readonly("fw_version", &hailo_device_identity_t::fw_version)
+        .def_readonly("logger_version", &hailo_device_identity_t::logger_version)
+        .def_readonly("board_name_length", &hailo_device_identity_t::board_name_length)
+        .def_readonly("is_release", &hailo_device_identity_t::is_release)
+        .def_readonly("device_architecture", &hailo_device_identity_t::device_architecture)
+        .def_property_readonly("board_name", [](const hailo_device_identity_t& board_info) -> py::str {
+            return py::str(board_info.board_name, board_info.board_name_length);
+        })
+        .def_readonly("serial_number_length", &hailo_device_identity_t::serial_number_length)
+        .def_property_readonly("serial_number", [](const hailo_device_identity_t& board_info) -> py::str {
+            return py::str(board_info.serial_number, board_info.serial_number_length);
+        })
+        .def_readonly("part_number_length", &hailo_device_identity_t::part_number_length)
+        .def_property_readonly("part_number", [](const hailo_device_identity_t& board_info) -> py::str {
+            return py::str(board_info.part_number, board_info.part_number_length);
+        })
+        .def_readonly("product_name_length", &hailo_device_identity_t::product_name_length)
+        .def_property_readonly("product_name", [](const hailo_device_identity_t& board_info) -> py::str {
+            return py::str(board_info.product_name, board_info.product_name_length);
+        })
+        ;
+
+    py::class_<hailo_core_information_t>(m, "CoreInformation")
+        .def_readonly("is_release", &hailo_core_information_t::is_release)
+        .def_readonly("fw_version", &hailo_core_information_t::fw_version)
+        ;
+
+    py::class_<hailo_fw_user_config_information_t>(m, "FirmwareUserConfigInformation")
+        .def_readonly("version", &hailo_fw_user_config_information_t::version)
+        .def_readonly("entry_count", &hailo_fw_user_config_information_t::entry_count)
+        .def_readonly("total_size", &hailo_fw_user_config_information_t::total_size)
+        ;
+
+    py::enum_<hailo_endianness_t>(m, "Endianness")
+        .value("BIG_ENDIAN", HAILO_BIG_ENDIAN)
+        .value("LITTLE_ENDIAN", HAILO_LITTLE_ENDIAN)
+        ;
+
+    py::enum_<hailo_sensor_types_t>(m, "SensorConfigTypes")
+        .value("SENSOR_GENERIC", HAILO_SENSOR_TYPES_GENERIC)
+        .value("ONSEMI_AR0220AT", HAILO_SENSOR_TYPES_ONSEMI_AR0220AT)
+        .value("SENSOR_RASPICAM", HAILO_SENSOR_TYPES_RASPICAM)
+        .value("ONSEMI_AS0149AT", HAILO_SENSOR_TYPES_ONSEMI_AS0149AT)
+        .value("HAILO8_ISP", HAILO_SENSOR_TYPES_HAILO8_ISP)
+        ;
+
+    py::enum_<SENSOR_CONFIG_OPCODES_t>(m, "SensorConfigOpCode")
+        .value("SENSOR_CONFIG_OPCODES_WR", SENSOR_CONFIG_OPCODES_WR)
+        .value("SENSOR_CONFIG_OPCODES_RD", SENSOR_CONFIG_OPCODES_RD)
+        .value("SENSOR_CONFIG_OPCODES_RMW", SENSOR_CONFIG_OPCODES_RMW)
+        .value("SENSOR_CONFIG_OPCODES_DELAY", SENSOR_CONFIG_OPCODES_DELAY)
+        ;
+
+    py::class_<hailo_i2c_slave_config_t>(m, "I2CSlaveConfig")
+        .def(py::init<>())
+        .def_readwrite("endianness", &hailo_i2c_slave_config_t::endianness)
+        .def_readwrite("slave_address", &hailo_i2c_slave_config_t::slave_address)
+        .def_readwrite("register_address_size", &hailo_i2c_slave_config_t::register_address_size)
+        .def_readwrite("bus_index", &hailo_i2c_slave_config_t::bus_index)
+        .def_readwrite("should_hold_bus", &hailo_i2c_slave_config_t::should_hold_bus)
+        ;
+
+    py::enum_<hailo_reset_device_mode_t>(m, "ResetDeviceMode")
+        .value("CHIP", HAILO_RESET_DEVICE_MODE_CHIP)
+        .value("NN_CORE", HAILO_RESET_DEVICE_MODE_NN_CORE)
+        .value("SOFT", HAILO_RESET_DEVICE_MODE_SOFT)
+        .value("FORCED_SOFT", HAILO_RESET_DEVICE_MODE_FORCED_SOFT)
+        ;
+
+    py::enum_<hailo_stream_direction_t>(m, "StreamDirection")
+        .value("H2D", HAILO_H2D_STREAM)
+        .value("D2H", HAILO_D2H_STREAM)
+        ;
+
+    py::class_<hailo_3d_image_shape_t>(m, "ImageShape")
+        .def(py::init<>())
+        .def(py::init<const uint32_t, const uint32_t, const uint32_t>())
+        .def_readwrite("height", &hailo_3d_image_shape_t::height)
+        .def_readwrite("width", &hailo_3d_image_shape_t::width)
+        .def_readwrite("features", &hailo_3d_image_shape_t::features)
+        ;
+
+    py::class_<hailo_nms_shape_t>(m, "NmsShape")
+        .def(py::init<>())
+        .def_readonly("number_of_classes", &hailo_nms_shape_t::number_of_classes)
+        .def_readonly("max_bboxes_per_class", &hailo_nms_shape_t::max_bboxes_per_class)
+        ;
+
+    py::class_<hailo_nms_info_t>(m, "NmsInfo")
+        .def(py::init<>())
+        .def_readwrite("number_of_classes", &hailo_nms_info_t::number_of_classes)
+        .def_readwrite("max_bboxes_per_class", &hailo_nms_info_t::max_bboxes_per_class)
+        .def_readwrite("bbox_size", &hailo_nms_info_t::bbox_size)
+        .def_readwrite("chunks_per_frame", &hailo_nms_info_t::chunks_per_frame)
+        ;
+
+    py::enum_<hailo_format_type_t>(m, "FormatType", "Data formats accepted by HailoRT.")
+        .value("AUTO", HAILO_FORMAT_TYPE_AUTO, "Chosen automatically to match the format expected by the device, usually UINT8.")
+        .value("UINT8", HAILO_FORMAT_TYPE_UINT8)
+        .value("UINT16", HAILO_FORMAT_TYPE_UINT16)
+        .value("FLOAT32", HAILO_FORMAT_TYPE_FLOAT32)
+        ;
+
+    py::enum_<hailo_format_order_t>(m, "FormatOrder")
+        .value("AUTO", HAILO_FORMAT_ORDER_AUTO)
+        .value("NHWC", HAILO_FORMAT_ORDER_NHWC)
+        .value("NHCW", HAILO_FORMAT_ORDER_NHCW)
+        .value("FCR", HAILO_FORMAT_ORDER_FCR)
+        .value("F8CR", HAILO_FORMAT_ORDER_F8CR)
+        .value("NHW", HAILO_FORMAT_ORDER_NHW)
+        .value("NC", HAILO_FORMAT_ORDER_NC)
+        .value("BAYER_RGB", HAILO_FORMAT_ORDER_BAYER_RGB)
+        .value("12_BIT_BAYER_RGB", HAILO_FORMAT_ORDER_12_BIT_BAYER_RGB)
+        .value("HAILO_NMS", HAILO_FORMAT_ORDER_HAILO_NMS)
+        .value("RGB888", HAILO_FORMAT_ORDER_RGB888)
+        .value("NCHW", HAILO_FORMAT_ORDER_NCHW)
+        .value("YUY2", HAILO_FORMAT_ORDER_YUY2)
+        ;
+
+    py::enum_<hailo_format_flags_t>(m, "FormatFlags", py::arithmetic())
+        .value("NONE", HAILO_FORMAT_FLAGS_NONE)
+        .value("QUANTIZED", HAILO_FORMAT_FLAGS_QUANTIZED)
+        .value("TRANSPOSED", HAILO_FORMAT_FLAGS_TRANSPOSED)
+        .value("HOST_ARGMAX", HAILO_FORMAT_FLAGS_HOST_ARGMAX)
+        ;
+
+    py::enum_<hailo_stream_transform_mode_t>(m, "TransformMode")
+        .value("NO_TRANSFORM", HAILO_STREAM_NO_TRANSFORM)
+        .value("TRANSFORM_COPY", HAILO_STREAM_TRANSFORM_COPY)
+        ;
+
+    py::class_<hailo_format_t>(m, "HailoFormat")
+        .def(py::init<>())
+        .def_readwrite("type", &hailo_format_t::type)
+        .def_readwrite("order", &hailo_format_t::order)
+        .def_readwrite("flags", &hailo_format_t::flags)
+        .def("equals", &hailo_format_equals)
+        .def(py::pickle(
+            [](const hailo_format_t &hailo_format) { // __getstate__
+                return py::make_tuple(
+                    hailo_format.type,
+                    hailo_format.order,
+                    hailo_format.flags);
+            },
+            [](py::tuple t) { // __setstate__
+                hailo_format_t hailo_format;
+                hailo_format.type = t[0].cast<hailo_format_type_t>();
+                hailo_format.order = t[1].cast<hailo_format_order_t>();
+                hailo_format.flags = t[2].cast<hailo_format_flags_t>();
+                return hailo_format;
+            }
+        ))
+        ;
+
+    py::class_<hailo_quant_info_t>(m, "QuantInfo")
+        .def(py::init<>())
+        .def(py::init<const float32_t, const float32_t, const float32_t, const float32_t>())
+        .def_readwrite("qp_zp", &hailo_quant_info_t::qp_zp)
+        .def_readwrite("qp_scale", &hailo_quant_info_t::qp_scale)
+        .def_readwrite("limvals_min", &hailo_quant_info_t::limvals_min)
+        .def_readwrite("limvals_max", &hailo_quant_info_t::limvals_max)
+        ;
+
+    py::enum_<hailo_mipi_pixels_per_clock_t>(m, "MipiPixelsPerClock")
+        .value("PIXELS_PER_CLOCK_1", HAILO_MIPI_PIXELS_PER_CLOCK_1)
+        .value("PIXELS_PER_CLOCK_2", HAILO_MIPI_PIXELS_PER_CLOCK_2)
+        .value("PIXELS_PER_CLOCK_4", HAILO_MIPI_PIXELS_PER_CLOCK_4)
+        ;
+
+    py::enum_<hailo_mipi_clock_selection_t>(m, "MipiClockSelection")
+        .value("SELECTION_80_TO_100_MBPS", HAILO_MIPI_CLOCK_SELECTION_80_TO_100_MBPS)
+        .value("SELECTION_100_TO_120_MBPS", HAILO_MIPI_CLOCK_SELECTION_100_TO_120_MBPS)
+        .value("SELECTION_120_TO_160_MBPS", HAILO_MIPI_CLOCK_SELECTION_120_TO_160_MBPS)
+        .value("SELECTION_160_TO_200_MBPS", HAILO_MIPI_CLOCK_SELECTION_160_TO_200_MBPS)
+        .value("SELECTION_200_TO_240_MBPS", HAILO_MIPI_CLOCK_SELECTION_200_TO_240_MBPS)
+        .value("SELECTION_240_TO_280_MBPS", HAILO_MIPI_CLOCK_SELECTION_240_TO_280_MBPS)
+        .value("SELECTION_280_TO_320_MBPS", HAILO_MIPI_CLOCK_SELECTION_280_TO_320_MBPS)
+        .value("SELECTION_320_TO_360_MBPS", HAILO_MIPI_CLOCK_SELECTION_320_TO_360_MBPS)
+        .value("SELECTION_360_TO_400_MBPS", HAILO_MIPI_CLOCK_SELECTION_360_TO_400_MBPS)
+        .value("SELECTION_400_TO_480_MBPS", HAILO_MIPI_CLOCK_SELECTION_400_TO_480_MBPS)
+        .value("SELECTION_480_TO_560_MBPS", HAILO_MIPI_CLOCK_SELECTION_480_TO_560_MBPS)
+        .value("SELECTION_560_TO_640_MBPS", HAILO_MIPI_CLOCK_SELECTION_560_TO_640_MBPS)
+        .value("SELECTION_640_TO_720_MBPS", HAILO_MIPI_CLOCK_SELECTION_640_TO_720_MBPS)
+        .value("SELECTION_720_TO_800_MBPS", HAILO_MIPI_CLOCK_SELECTION_720_TO_800_MBPS)
+        .value("SELECTION_800_TO_880_MBPS", HAILO_MIPI_CLOCK_SELECTION_800_TO_880_MBPS)
+        .value("SELECTION_880_TO_1040_MBPS", HAILO_MIPI_CLOCK_SELECTION_880_TO_1040_MBPS)
+        .value("SELECTION_1040_TO_1200_MBPS", HAILO_MIPI_CLOCK_SELECTION_1040_TO_1200_MBPS)
+        .value("SELECTION_1200_TO_1350_MBPS", HAILO_MIPI_CLOCK_SELECTION_1200_TO_1350_MBPS)
+        .value("SELECTION_1350_TO_1500_MBPS", HAILO_MIPI_CLOCK_SELECTION_1350_TO_1500_MBPS)
+        .value("SELECTION_1500_TO_1750_MBPS", HAILO_MIPI_CLOCK_SELECTION_1500_TO_1750_MBPS)
+        .value("SELECTION_1750_TO_2000_MBPS", HAILO_MIPI_CLOCK_SELECTION_1750_TO_2000_MBPS)
+        .value("SELECTION_2000_TO_2250_MBPS", HAILO_MIPI_CLOCK_SELECTION_2000_TO_2250_MBPS)
+        .value("SELECTION_2250_TO_2500_MBPS", HAILO_MIPI_CLOCK_SELECTION_2250_TO_2500_MBPS)
+        .value("SELECTION_AUTOMATIC", HAILO_MIPI_CLOCK_SELECTION_AUTOMATIC)
+        ;
+
+    py::enum_<hailo_mipi_data_type_rx_t>(m, "MipiDataTypeRx")
+        .value("RGB_444", HAILO_MIPI_RX_TYPE_RGB_444)
+        .value("RGB_555", HAILO_MIPI_RX_TYPE_RGB_555)
+        .value("RGB_565", HAILO_MIPI_RX_TYPE_RGB_565)
+        .value("RGB_666", HAILO_MIPI_RX_TYPE_RGB_666)
+        .value("RGB_888", HAILO_MIPI_RX_TYPE_RGB_888)
+        .value("RAW_6", HAILO_MIPI_RX_TYPE_RAW_6)
+        .value("RAW_7", HAILO_MIPI_RX_TYPE_RAW_7)
+        .value("RAW_8", HAILO_MIPI_RX_TYPE_RAW_8)
+        .value("RAW_10", HAILO_MIPI_RX_TYPE_RAW_10)
+        .value("RAW_12", HAILO_MIPI_RX_TYPE_RAW_12)
+        .value("RAW_14", HAILO_MIPI_RX_TYPE_RAW_14)
+        ;
+
+    py::enum_<hailo_mipi_isp_image_in_order_t>(m, "MipiIspImageInOrder")
+        .value("B_FIRST", HAILO_MIPI_ISP_IMG_IN_ORDER_B_FIRST)
+        .value("GB_FIRST", HAILO_MIPI_ISP_IMG_IN_ORDER_GB_FIRST)
+        .value("GR_FIRST", HAILO_MIPI_ISP_IMG_IN_ORDER_GR_FIRST)
+        .value("R_FIRST", HAILO_MIPI_ISP_IMG_IN_ORDER_R_FIRST)
+        ;
+
+    py::enum_<hailo_mipi_isp_image_out_data_type_t>(m, "MipiIspImageOutDataType")
+        .value("RGB_888", HAILO_MIPI_IMG_OUT_DATA_TYPE_RGB_888)
+        .value("YUV_422", HAILO_MIPI_IMG_OUT_DATA_TYPE_YUV_422)
+        ;
+
+    py::enum_<hailo_mipi_isp_light_frequency_t>(m, "IspLightFrequency")
+        .value("LIGHT_FREQ_60_HZ", HAILO_MIPI_ISP_LIGHT_FREQUENCY_60HZ)
+        .value("LIGHT_FREQ_50_HZ", HAILO_MIPI_ISP_LIGHT_FREQUENCY_50HZ)
+        ;
+    
+    py::class_<hailo_isp_params_t>(m, "MipiIspParams")
+        .def_readwrite("img_in_order", &hailo_isp_params_t::isp_img_in_order)
+        .def_readwrite("img_out_data_type", &hailo_isp_params_t::isp_img_out_data_type)
+        .def_readwrite("crop_enable", &hailo_isp_params_t::isp_crop_enable)
+        .def_readwrite("crop_output_width_pixels", &hailo_isp_params_t::isp_crop_output_width_pixels)
+        .def_readwrite("crop_output_height_pixels", &hailo_isp_params_t::isp_crop_output_height_pixels)
+        .def_readwrite("crop_output_width_start_offset_pixels", &hailo_isp_params_t::isp_crop_output_width_start_offset_pixels)
+        .def_readwrite("crop_output_height_start_offset_pixels", &hailo_isp_params_t::isp_crop_output_height_start_offset_pixels)
+        .def_readwrite("test_pattern_enable", &hailo_isp_params_t::isp_test_pattern_enable)
+        .def_readwrite("configuration_bypass", &hailo_isp_params_t::isp_configuration_bypass)
+        .def_readwrite("run_time_ae_enable", &hailo_isp_params_t::isp_run_time_ae_enable)
+        .def_readwrite("run_time_awb_enable", &hailo_isp_params_t::isp_run_time_awb_enable)
+        .def_readwrite("run_time_adt_enable", &hailo_isp_params_t::isp_run_time_adt_enable)
+        .def_readwrite("run_time_af_enable", &hailo_isp_params_t::isp_run_time_af_enable)
+        .def_readwrite("isp_run_time_calculations_interval_ms", &hailo_isp_params_t::isp_run_time_calculations_interval_ms)
+        .def_readwrite("isp_light_frequency", &hailo_isp_params_t::isp_light_frequency)
+        ;
+
+    py::class_<hailo_mipi_common_params_t>(m, "MipiCommonParams")
+        .def_readwrite("img_width_pixels", &hailo_mipi_common_params_t::img_width_pixels)
+        .def_readwrite("img_height_pixels", &hailo_mipi_common_params_t::img_height_pixels)
+        .def_readwrite("pixels_per_clock", &hailo_mipi_common_params_t::pixels_per_clock)
+        .def_readwrite("number_of_lanes", &hailo_mipi_common_params_t::number_of_lanes)
+        .def_readwrite("clock_selection", &hailo_mipi_common_params_t::clock_selection)
+        .def_readwrite("virtual_channel_index", &hailo_mipi_common_params_t::virtual_channel_index)
+        .def_readwrite("data_rate", &hailo_mipi_common_params_t::data_rate)
+        ;
+
+    py::class_<hailo_transform_params_t>(m, "TransformParams")
+        .def(py::init<>())
+        .def_readwrite("transform_mode", &hailo_transform_params_t::transform_mode)
+        .def_readwrite("user_buffer_format", &hailo_transform_params_t::user_buffer_format)
+        ;
+
+    py::class_<hailo_eth_output_stream_params_t>(m, "EthOutputStreamParams")
+        .def(py::init<>())
+        .def_readwrite("device_port", &hailo_eth_output_stream_params_t::device_port)
+        .def_readwrite("host_address", &hailo_eth_output_stream_params_t::host_address)
+        .def_readwrite("is_sync_enabled", &hailo_eth_output_stream_params_t::is_sync_enabled)
+        .def_readwrite("max_payload_size", &hailo_eth_output_stream_params_t::max_payload_size)
+        .def_readwrite("buffers_threshold", &hailo_eth_output_stream_params_t::buffers_threshold)
+        ;
+
+    py::class_<hailo_eth_input_stream_params_t>(m, "EthInputStreamParams")
+        .def(py::init<>())
+        .def_readwrite("device_port", &hailo_eth_input_stream_params_t::device_port)
+        .def_readwrite("host_address", &hailo_eth_input_stream_params_t::host_address)
+        .def_readwrite("max_payload_size", &hailo_eth_input_stream_params_t::max_payload_size)
+        .def_readwrite("is_sync_enabled", &hailo_eth_input_stream_params_t::is_sync_enabled)
+        .def_readwrite("frames_per_sync", &hailo_eth_input_stream_params_t::frames_per_sync)
+        .def_readwrite("buffers_threshold", &hailo_eth_input_stream_params_t::buffers_threshold)
+        ;
+
+    py::class_<hailo_pcie_output_stream_params_t>(m, "PcieOutputStreamParams")
+        .def(py::init<>())
+        ;
+
+    py::class_<hailo_pcie_input_stream_params_t>(m, "PcieInputStreamParams")
+        .def(py::init<>())
+        ;
+
+    py::class_<hailo_core_input_stream_params_t>(m, "CoreInputStreamParams")
+        .def(py::init<>())
+        ;
+
+    py::class_<hailo_core_output_stream_params_t>(m, "CoreOutputStreamParams")
+        .def(py::init<>())
+        ;
+
+    py::class_<hailo_mipi_input_stream_params_t>(m, "MipiInputStreamParams")
+        .def(py::init<>())
+        .def_readwrite("mipi_common_params", &hailo_mipi_input_stream_params_t::mipi_common_params)
+        .def_readwrite("mipi_rx_id", &hailo_mipi_input_stream_params_t::mipi_rx_id)
+        .def_readwrite("data_type", &hailo_mipi_input_stream_params_t::data_type)
+        .def_readwrite("isp_enable", &hailo_mipi_input_stream_params_t::isp_enable)
+        .def_readwrite("isp_params", &hailo_mipi_input_stream_params_t::isp_params)
+        ;
+
+    py::enum_<hailo_stream_interface_t>(m, "StreamInterface")
+        .value("PCIe", HAILO_STREAM_INTERFACE_PCIE)
+        .value("CORE", HAILO_STREAM_INTERFACE_CORE)
+        .value("ETH", HAILO_STREAM_INTERFACE_ETH)
+        .value("MIPI", HAILO_STREAM_INTERFACE_MIPI)
+        ;
+
+    py::class_<hailo_vstream_params_t>(m, "VStreamParams")
+        .def(py::init<>())
+        .def_readwrite("user_buffer_format", &hailo_vstream_params_t::user_buffer_format)
+        .def_readwrite("timeout_ms", &hailo_vstream_params_t::timeout_ms)
+        .def_readwrite("queue_size", &hailo_vstream_params_t::queue_size)
+        ;
+
+    py::enum_<hailo_latency_measurement_flags_t>(m, "LatencyMeasurementFlags")
+        .value("NONE", HAILO_LATENCY_NONE)
+        .value("CLEAR_AFTER_GET", HAILO_LATENCY_CLEAR_AFTER_GET)
+        .value("MEASURE", HAILO_LATENCY_MEASURE)
+        ;
+
+    py::enum_<hailo_power_mode_t>(m, "PowerMode")
+        .value("ULTRA_PERFORMANCE", HAILO_POWER_MODE_ULTRA_PERFORMANCE)
+        .value("PERFORMANCE", HAILO_POWER_MODE_PERFORMANCE)
+        ;
+
+    py::class_<hailo_activate_network_group_params_t>(m, "ActivateNetworkGroupParams")
+        .def(py::init<>())
+        .def_static("default", []() {
+            return HailoRTDefaults::get_network_group_params();
+        });
+        ;
+
+    py::class_<hailo_vdevice_params_t>(m, "VDeviceParams")
+        .def(py::init<>())
+        .def_readwrite("device_count", &hailo_vdevice_params_t::device_count)
+        .def_static("default", []() {
+            return HailoRTDefaults::get_vdevice_params();
+        });
+        ;
+
+    py::class_<hailo_stream_parameters_t>(m, "StreamParameters")
+        .def_readwrite("stream_interface", &hailo_stream_parameters_t::stream_interface)
+        .def_readonly("direction", &hailo_stream_parameters_t::direction)
+        STREAM_PARAMETERS_UNION_PROPERTY(pcie_input_params, hailo_pcie_input_stream_params_t,
+            HAILO_STREAM_INTERFACE_PCIE, HAILO_H2D_STREAM)
+        STREAM_PARAMETERS_UNION_PROPERTY(core_input_params, hailo_core_input_stream_params_t,
+            HAILO_STREAM_INTERFACE_CORE, HAILO_H2D_STREAM)
+        STREAM_PARAMETERS_UNION_PROPERTY(eth_input_params, hailo_eth_input_stream_params_t,
+            HAILO_STREAM_INTERFACE_ETH, HAILO_H2D_STREAM)
+        STREAM_PARAMETERS_UNION_PROPERTY(mipi_input_params, hailo_mipi_input_stream_params_t,
+            HAILO_STREAM_INTERFACE_MIPI, HAILO_H2D_STREAM)
+        STREAM_PARAMETERS_UNION_PROPERTY(pcie_output_params, hailo_pcie_output_stream_params_t,
+            HAILO_STREAM_INTERFACE_PCIE, HAILO_D2H_STREAM)
+        STREAM_PARAMETERS_UNION_PROPERTY(eth_output_params, hailo_eth_output_stream_params_t,
+            HAILO_STREAM_INTERFACE_ETH, HAILO_D2H_STREAM)
+        STREAM_PARAMETERS_UNION_PROPERTY(core_output_params, hailo_core_output_stream_params_t,
+            HAILO_STREAM_INTERFACE_CORE, HAILO_D2H_STREAM)
+        ;
+
+    py::class_<hailo_network_parameters_t>(m, "NetworkParameters")
+        .def(py::init<>())
+        .def_readwrite("batch_size", &hailo_network_parameters_t::batch_size)
+        ;
+
+
+    py::class_<ConfigureNetworkParams>(m, "ConfigureParams")
+        .def(py::init<>())
+        .def_readwrite("batch_size", &ConfigureNetworkParams::batch_size)
+        .def_readwrite("power_mode", &ConfigureNetworkParams::power_mode)
+        .def_readwrite("stream_params_by_name", &ConfigureNetworkParams::stream_params_by_name)
+        .def_readwrite("network_params_by_name", &ConfigureNetworkParams::network_params_by_name)
+        ;
+
+    py::class_<hailo_chip_temperature_info_t>(m, "TemperatureInfo")
+        .def_readonly("ts0_temperature", &hailo_chip_temperature_info_t::ts0_temperature)
+        .def_readonly("ts1_temperature", &hailo_chip_temperature_info_t::ts1_temperature)
+        .def_readonly("sample_count", &hailo_chip_temperature_info_t::sample_count)
+        .def("equals", &temperature_info_equals)
+        .def(py::pickle(
+            [](const hailo_chip_temperature_info_t &temperature_info) { // __getstate__
+                return py::make_tuple(
+                    temperature_info.ts0_temperature,
+                    temperature_info.ts1_temperature,
+                    temperature_info.sample_count);
+            },
+            [](py::tuple t) { // __setstate__
+                hailo_chip_temperature_info_t temperature_info;
+                temperature_info.ts0_temperature = t[0].cast<float32_t>();
+                temperature_info.ts1_temperature = t[1].cast<float32_t>();
+                temperature_info.sample_count = t[2].cast<uint16_t>();
+                return temperature_info;
+            }
+        ))
+        ;
+
+    py::class_<hailo_throttling_level_t>(m, "ThrottlingLevel", py::module_local())
+        .def_readonly("temperature_threshold", &hailo_throttling_level_t::temperature_threshold)
+        .def_readonly("hysteresis_temperature_threshold", &hailo_throttling_level_t::hysteresis_temperature_threshold)
+        .def_readonly("throttling_nn_clock_freq", &hailo_throttling_level_t::throttling_nn_clock_freq)
+        ;
+
+    py::class_<hailo_health_info_t>(m, "HealthInformation")
+        .def_readonly("overcurrent_protection_active", &hailo_health_info_t::overcurrent_protection_active)
+        .def_readonly("current_overcurrent_zone", &hailo_health_info_t::current_overcurrent_zone)
+        .def_readonly("red_overcurrent_threshold", &hailo_health_info_t::red_overcurrent_threshold)
+        .def_readonly("orange_overcurrent_threshold", &hailo_health_info_t::orange_overcurrent_threshold)
+        .def_readonly("temperature_throttling_active", &hailo_health_info_t::temperature_throttling_active)
+        .def_readonly("current_temperature_zone", &hailo_health_info_t::current_temperature_zone)
+        .def_readonly("current_temperature_throttling_level", &hailo_health_info_t::current_temperature_throttling_level)
+        .def_readonly("temperature_throttling_levels", &hailo_health_info_t::temperature_throttling_levels)
+        .def_property_readonly("temperature_throttling_levels", [](const hailo_health_info_t& info) -> py::list {
+            std::vector<hailo_throttling_level_t> throttling_levels;
+            for (const auto &temperature_throttling_level : info.temperature_throttling_levels) {
+                throttling_levels.push_back(temperature_throttling_level);
+            }
+            return py::cast(throttling_levels);
+        })
+        .def_readonly("orange_temperature_threshold", &hailo_health_info_t::orange_temperature_threshold)
+        .def_readonly("orange_hysteresis_temperature_threshold", &hailo_health_info_t::orange_hysteresis_temperature_threshold)
+        .def_readonly("red_temperature_threshold", &hailo_health_info_t::red_temperature_threshold)
+        .def_readonly("red_hysteresis_temperature_threshold", &hailo_health_info_t::red_hysteresis_temperature_threshold)
+        ;
+
+    py::class_<hailo_extended_device_information_t>(m, "ExtendedDeviceInformation")
+        .def_readonly("neural_network_core_clock_rate", &hailo_extended_device_information_t::neural_network_core_clock_rate)
+        .def_readonly("supported_features", &hailo_extended_device_information_t::supported_features)
+        .def_readonly("boot_source", &hailo_extended_device_information_t::boot_source)
+        .def_readonly("lcs", &hailo_extended_device_information_t::lcs)
+        .def_property_readonly("unit_level_tracking_id", [](const hailo_extended_device_information_t& info) -> py::bytes {
+            return std::string((const char*) info.unit_level_tracking_id, sizeof(info.unit_level_tracking_id));
+        })
+        .def_property_readonly("eth_mac_address", [](const hailo_extended_device_information_t& info) -> py::bytes {
+            return std::string((const char*) info.eth_mac_address, sizeof(info.eth_mac_address));
+        })
+        .def_property_readonly("soc_id", [](const hailo_extended_device_information_t& info) -> py::bytes {
+            return std::string((const char*) info.soc_id, sizeof(info.soc_id));
+        })
+        .def_property_readonly("soc_pm_values", [](const hailo_extended_device_information_t& info) -> py::bytes {
+            return std::string((const char*) info.soc_pm_values, sizeof(info.soc_pm_values));
+        })
+        ;
+
+    py::enum_<hailo_device_boot_source_t>(m, "BootSource")
+        .value("INVALID", HAILO_DEVICE_BOOT_SOURCE_INVALID)
+        .value("PCIE", HAILO_DEVICE_BOOT_SOURCE_PCIE)
+        .value("FLASH", HAILO_DEVICE_BOOT_SOURCE_FLASH)
+        ;
+
+    py::enum_<hailo_fw_logger_interface_t>(m, "FwLoggerInterface", py::arithmetic())
+        .value("PCIE", HAILO_FW_LOGGER_INTERFACE_PCIE)
+        .value("UART", HAILO_FW_LOGGER_INTERFACE_UART)
+        ;
+
+    py::enum_<hailo_fw_logger_level_t>(m, "FwLoggerLevel")
+        .value("TRACE", HAILO_FW_LOGGER_LEVEL_TRACE)
+        .value("DEBUG", HAILO_FW_LOGGER_LEVEL_DEBUG)
+        .value("INFO", HAILO_FW_LOGGER_LEVEL_INFO)
+        .value("WARN", HAILO_FW_LOGGER_LEVEL_WARN)
+        .value("ERROR", HAILO_FW_LOGGER_LEVEL_ERROR)
+        .value("FATAL", HAILO_FW_LOGGER_LEVEL_FATAL)
+        ;
+
+    py::class_<hailo_device_supported_features_t>(m, "SupportedFeatures")
+        .def_readonly("ethernet", &hailo_device_supported_features_t::ethernet)
+        .def_readonly("mipi", &hailo_device_supported_features_t::mipi)
+        .def_readonly("pcie", &hailo_device_supported_features_t::pcie)
+        .def_readonly("current_monitoring", &hailo_device_supported_features_t::current_monitoring)
+        .def_readonly("mdio", &hailo_device_supported_features_t::mdio)
+        ;
+
+    py::class_<sockaddr_in>(m, "sockaddr_in")
+        .def_readwrite("sin_port", &sockaddr_in::sin_port)
+        ;
+
+    py::register_exception<HailoRTException>(m, "HailoRTException");
+    py::register_exception<HailoRTCustomException>(m, "HailoRTCustomException");
+    py::register_exception<HailoRTStatusException>(m, "HailoRTStatusException");
+
+    py::enum_<hailo_cpu_id_t>(m, "CpuId")
+        .value("CPU0", HAILO_CPU_ID_0)
+        .value("CPU1", HAILO_CPU_ID_1)
+        ;
+
+    py::enum_<hailo_bootloader_version_t>(m, "BootloaderVersion")
+        .value("UNSIGNED", BOOTLOADER_VERSION_HAILO8_B0_UNSIGNED)
+        .value("SIGNED", BOOTLOADER_VERSION_HAILO8_B0_SIGNED)
+        ;
+
+    py::class_<uint32_t>(m, "HailoRTDefaults")
+        .def_static("HAILO_INFINITE", []() { return HAILO_INFINITE;} )
+        .def_static("HAILO_DEFAULT_ETH_CONTROL_PORT", []() { return HAILO_DEFAULT_ETH_CONTROL_PORT;} )
+        .def_static("BBOX_PARAMS", []() { return HailoRTCommon::BBOX_PARAMS;} )
+        .def_static("DEVICE_BASE_INPUT_STREAM_PORT", []() { return HailoRTCommon::ETH_INPUT_BASE_PORT;} )
+        .def_static("DEVICE_BASE_OUTPUT_STREAM_PORT", []() { return HailoRTCommon::ETH_OUTPUT_BASE_PORT;} )
+        .def_static("PCIE_ANY_DOMAIN", []() { return HAILO_PCIE_ANY_DOMAIN;} )
+        ;
+
+    py::class_<hailo_network_group_info_t>(m, "NetworkGroupInfo", py::module_local())
+        .def_readonly("name", &hailo_network_group_info_t::name)
+        .def_readonly("is_multi_context", &hailo_network_group_info_t::is_multi_context)
+        ;
+
+    py::class_<hailo_vstream_info_t>(m, "VStreamInfo", py::module_local())
+        .def_property_readonly("shape", [](const hailo_vstream_info_t &self) {
+            switch (self.format.order) {
+                case HAILO_FORMAT_ORDER_NC:
+                    return py::make_tuple(self.shape.features);
+                case HAILO_FORMAT_ORDER_NHW:
+                    return py::make_tuple(self.shape.height, self.shape.width);
+                case HAILO_FORMAT_ORDER_HAILO_NMS:
+                    return py::make_tuple(self.nms_shape.number_of_classes, HailoRTCommon::BBOX_PARAMS, self.nms_shape.max_bboxes_per_class);
+                default:
+                    return py::make_tuple(self.shape.height, self.shape.width, self.shape.features);
+            }
+        })
+        .def_property_readonly("nms_shape", [](const hailo_vstream_info_t &self) {
+            if (HAILO_FORMAT_ORDER_HAILO_NMS != self.format.order) {
+                throw HailoRTCustomException("nms_shape is availale only on nms order vstreams");
+            }
+            return self.nms_shape;
+        })
+        .def_readonly("direction", &hailo_vstream_info_t::direction)
+        .def_readonly("format", &hailo_vstream_info_t::format)
+        .def_readonly("quant_info", &hailo_vstream_info_t::quant_info)
+        .def_readonly("name", &hailo_vstream_info_t::name)
+        .def_readonly("network_name", &hailo_vstream_info_t::network_name)
+        .def("__repr__", [](const hailo_vstream_info_t &self) {
+            return std::string("VStreamInfo(\"") + std::string(self.name) + std::string("\")");
+        })
+        ;
+
+    py::class_<hailo_stream_info_t>(m, "StreamInfo", py::module_local())
+        .def_property_readonly("shape", [](const hailo_stream_info_t &self) {
+            switch (self.format.order) {
+                case HAILO_FORMAT_ORDER_NC:
+                    return py::make_tuple(self.hw_shape.features);
+                case HAILO_FORMAT_ORDER_NHW:
+                    return py::make_tuple(self.hw_shape.height, self.hw_shape.width);
+                case HAILO_FORMAT_ORDER_HAILO_NMS:
+                    return py::make_tuple(HailoRTCommon::get_nms_hw_frame_size(self.nms_info));
+                default:
+                    return py::make_tuple(self.hw_shape.height, self.hw_shape.width, self.hw_shape.features);
+            }
+        })
+        .def_property_readonly("nms_shape", [](const hailo_stream_info_t &self) {
+            if (HAILO_FORMAT_ORDER_HAILO_NMS != self.format.order) {
+                throw HailoRTCustomException("nms_shape is availale only on nms order streams");
+            }
+            return py::make_tuple(HailoRTCommon::get_nms_hw_frame_size(self.nms_info));
+        })
+        .def_readonly("direction", &hailo_stream_info_t::direction)
+        .def_readonly("format", &hailo_stream_info_t::format)
+        .def_readonly("name", &hailo_stream_info_t::name)
+        .def_readonly("sys_index", &hailo_stream_info_t::index)
+        .def_readonly("data_bytes", &hailo_stream_info_t::hw_data_bytes)
+        .def("__repr__", [](const hailo_stream_info_t &self) {
+            return std::string("StreamInfo(\"") + std::string(self.name) + std::string("\")");
+        })
+        ;
+
+    // https://github.com/pybind/pybind11/blob/master/docs/advanced/classes.rst
+    py::class_<uint32_t>(m, "HailoSocketDefs", py::module_local())
+        .def_static("MAX_UDP_PAYLOAD_SIZE", []() { return MAX_UDP_PAYLOAD_SIZE;} )
+        .def_static("MIN_UDP_PAYLOAD_SIZE", []() { return MIN_UDP_PAYLOAD_SIZE;} )
+        .def_static("MAX_UDP_PADDED_PAYLOAD_SIZE", []() { return MAX_UDP_PADDED_PAYLOAD_SIZE;} )
+        .def_static("MIN_UDP_PADDED_PAYLOAD_SIZE", []() { return MIN_UDP_PAYLOAD_SIZE;} )
+        .def_static("MAX_ALIGNED_UDP_PAYLOAD_SIZE_RTP", []() { return 1472;} )
+        ;
+
+    m.def("read_log", &read_log, py::return_value_policy::move);
+    m.def("direct_write_memory", &direct_write_memory);
+    m.def("direct_read_memory", &direct_read_memory);
+    m.def("get_format_data_bytes", &HailoRTCommon::get_format_data_bytes);
+
+    HEF_API_initialize_python_module(m);
+    VStream_api_initialize_python_module(m);
+    VDevice_api_initialize_python_module(m);
+    #if defined(__GNUC__)
+    TrafficControlUtilWrapper::add_to_python_module(m);
+    #endif
+
+#ifdef VERSION_INFO
+    m.attr("__version__") = VERSION_INFO;
+#else
+    m.attr("__version__") = "dev";
+#endif
+}
+
+} /* namespace hailort */
diff --git a/hailort/libhailort/bindings/python/src/utils.hpp b/hailort/libhailort/bindings/python/src/utils.hpp
new file mode 100644 (file)
index 0000000..e0498f5
--- /dev/null
@@ -0,0 +1,91 @@
+// TODO: headers + guard
+
+#pragma once
+
+#include <pybind11/pybind11.h>
+#include <string>
+#include <exception>
+
+namespace py = pybind11;
+
+
+class HailoRTException : public std::runtime_error {
+    public:
+        explicit HailoRTException(const std::string &what) : std::runtime_error(what) {}
+};
+
+class HailoRTCustomException : public HailoRTException {
+    public:
+        explicit HailoRTCustomException(const std::string &what) : HailoRTException(what) {}
+};
+
+class HailoRTStatusException : public HailoRTException {
+    public:
+        explicit HailoRTStatusException(const std::string &what) : HailoRTException(what) {}
+};
+    
+
+#define EXIT_WITH_ERROR(__message)                                                  \
+        throw HailoRTCustomException(std::string(__message));
+
+#define THROW_STATUS_ERROR(__status)                                                \
+    do {                                                                            \
+        throw HailoRTStatusException(std::to_string((__status)));                   \
+    } while (0)
+
+#define VALIDATE_STATUS(__status)                                                   \
+    do {                                                                            \
+        if (HAILO_SUCCESS != (__status)) {                                          \
+            THROW_STATUS_ERROR((__status));                                         \
+        }                                                                           \
+    } while (0)
+
+#define VALIDATE_NOT_NULL(__ptr)                                                    \
+    do {                                                                            \
+        if (nullptr == (__ptr)) {                                                   \
+            throw HailoRTStatusException(std::to_string(HAILO_INVALID_ARGUMENT));   \
+        }                                                                           \
+    } while (0)
+
+#define VALIDATE_EXPECTED(__expected)                                               \
+    do {                                                                            \
+        const auto &expected_object = (__expected);                                 \
+        if (!expected_object) {                                                     \
+            throw HailoRTStatusException(std::to_string(expected_object.status())); \
+        }                                                                           \
+    } while (0)
+
+#define UNION_PROPERTY(__union_type, __field_type, __field_name)                    \
+    .def_property(#__field_name,                                                    \
+        [](__union_type& self) -> const __field_type&                               \
+        {                                                                           \
+            return self.__field_name;                                               \
+        },                                                                          \
+        nullptr)
+
+#define STREAM_PARAMETERS_UNION_PROPERTY(__property_name, __property_type, __interface_value, __direction_value) \
+    .def_property(#__property_name,                                                                               \
+        [](hailo_stream_parameters_t& self) -> const __property_type&                                             \
+        {                                                                                                         \
+            if (__interface_value != self.stream_interface) {                                                     \
+                LOGGER__ERROR("Stream params interface is not {}.", #__interface_value);                          \
+                THROW_STATUS_ERROR(HAILO_INVALID_OPERATION);                                                      \
+            }                                                                                                     \
+            if (__direction_value != self.direction) {                                                            \
+                LOGGER__ERROR("Stream params direction is not {}.", #__direction_value);                          \
+                THROW_STATUS_ERROR(HAILO_INVALID_OPERATION);                                                      \
+            }                                                                                                     \
+            return self.__property_name;                                                                          \
+        },                                                                                                        \
+        [](hailo_stream_parameters_t& self, const __property_type& value)                                         \
+        {                                                                                                         \
+            if (__interface_value != self.stream_interface) {                                                     \
+                LOGGER__ERROR("Stream params interface is not {}.", #__interface_value);                          \
+                THROW_STATUS_ERROR(HAILO_INVALID_OPERATION);                                                      \
+            }                                                                                                     \
+            if (__direction_value != self.direction) {                                                            \
+                LOGGER__ERROR("Stream params direction is not {}.", #__direction_value);                          \
+                THROW_STATUS_ERROR(HAILO_INVALID_OPERATION);                                                      \
+            }                                                                                                     \
+            self.__property_name = value;                                                                         \
+        })
diff --git a/hailort/libhailort/bindings/python/src/vdevice_api.hpp b/hailort/libhailort/bindings/python/src/vdevice_api.hpp
new file mode 100644 (file)
index 0000000..857cfad
--- /dev/null
@@ -0,0 +1,102 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file vdevice_api.hpp
+ * @brief Defines binding to a VDevice class usage over Python.
+ *
+ * TODO: doc
+ **/
+
+#ifndef VDEVICE_API_HPP_
+#define VDEVICE_API_HPP_
+
+#include "hailo/hef.hpp"
+#include "hailo/vdevice.hpp"
+
+#include "bindings_common.hpp"
+#include "utils.hpp"
+#include "common/logger_macros.hpp"
+
+#include <pybind11/pybind11.h>
+#include <pybind11/numpy.h>
+#include <pybind11/detail/common.h>
+#include <pybind11/stl.h>
+#include <pybind11/complex.h>
+#include <pybind11/functional.h>
+
+#include <string>
+
+namespace hailort
+{
+
+class VDeviceWrapper {
+public:
+    static VDeviceWrapper create(const hailo_vdevice_params_t &params)
+    {
+        return VDeviceWrapper(params);
+    };
+
+    static VDeviceWrapper create_from_infos(const std::vector<hailo_pcie_device_info_t> &device_infos)
+    {
+        hailo_vdevice_params_t params = {};
+        params.device_count = static_cast<uint32_t>(device_infos.size());
+        params.device_infos = const_cast<hailo_pcie_device_info_t*>(device_infos.data());
+        return VDeviceWrapper(params);
+    };
+
+    VDeviceWrapper(const hailo_vdevice_params_t &params)
+    {
+        auto vdevice_expected = VDevice::create(params);
+        VALIDATE_EXPECTED(vdevice_expected);
+
+        m_vdevice = vdevice_expected.release();
+    };
+
+    py::list get_physical_devices_infos()
+    {
+        auto phys_devs_infos = m_vdevice->get_physical_devices_infos();
+        VALIDATE_EXPECTED(phys_devs_infos);
+
+        return py::cast(phys_devs_infos.value());
+    };
+
+    py::list configure(const HefWrapper &hef,
+        const NetworkGroupsParamsMap &configure_params={})
+    {
+
+        auto network_groups = m_vdevice->configure(*hef.hef_ptr(), configure_params);
+        VALIDATE_EXPECTED(network_groups);
+
+        py::list results;
+        for (const auto &network_group : network_groups.value()) {
+            results.append(network_group.get());
+        }
+
+        return results;
+    }
+
+    void release()
+    {
+        m_vdevice.reset();
+    }
+
+private:
+    std::unique_ptr<VDevice> m_vdevice;
+};
+
+void VDevice_api_initialize_python_module(py::module &m)
+{
+    py::class_<VDeviceWrapper>(m, "VDevice")
+        .def("create", &VDeviceWrapper::create)
+        .def("create_from_infos", &VDeviceWrapper::create_from_infos)
+        .def("get_physical_devices_infos", &VDeviceWrapper::get_physical_devices_infos)
+        .def("configure", &VDeviceWrapper::configure)
+        .def("release", &VDeviceWrapper::release)
+        ;
+}
+
+} /* namespace hailort */
+
+#endif /* VDEVICE_API_HPP_ */
diff --git a/hailort/libhailort/bindings/python/src/vstream_api.hpp b/hailort/libhailort/bindings/python/src/vstream_api.hpp
new file mode 100644 (file)
index 0000000..1c76256
--- /dev/null
@@ -0,0 +1,377 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file vstream_api.hpp
+ * @brief Defines binding to virtual stream usage over Python.
+ **/
+
+#ifndef _VSTREAM_API_HPP_
+#define _VSTREAM_API_HPP_
+
+#include "common/logger_macros.hpp"
+#include "common/utils.hpp"
+#include "hailo/vstream.hpp"
+#include "hailo/inference_pipeline.hpp"
+#include "bindings_common.hpp"
+#include "utils.hpp"
+
+#include <pybind11/pybind11.h>
+#include <pybind11/numpy.h>
+#include <pybind11/detail/common.h>
+#include <pybind11/stl.h>
+#include <pybind11/functional.h>
+
+namespace hailort
+{
+
+class InputVStreamWrapper final
+{
+public:
+    static void add_to_python_module(py::module &m)
+    {
+        py::class_<InputVStream, std::shared_ptr<InputVStream>>(m, "InputVStream")
+        .def("send", [](InputVStream &self, py::array data)
+        {
+            hailo_status status = self.write(
+                MemoryView(const_cast<void*>(reinterpret_cast<const void*>(data.data())), data.nbytes()));
+            VALIDATE_STATUS(status);
+        })
+        .def("flush", [](InputVStream &self)
+        {
+            hailo_status status = self.flush();
+            VALIDATE_STATUS(status);
+        })
+        .def_property_readonly("info", [](InputVStream &self)
+        {
+            return self.get_info();
+        })
+        .def_property_readonly("dtype", [](InputVStream &self)
+        {
+            const auto format_type = self.get_user_buffer_format().type;
+            return py::dtype(convert_format_type_to_string(format_type));
+        })
+        .def_property_readonly("shape", [](InputVStream &self)
+        {
+            return *py::array::ShapeContainer(get_pybind_shape(self.get_info(), self.get_user_buffer_format()));
+        })
+        ;
+    }
+};
+
+class InputVStreamsWrapper final 
+{
+public:
+
+    static InputVStreamsWrapper create(ConfiguredNetworkGroup &net_group,
+        const std::map<std::string, hailo_vstream_params_t> &input_vstreams_params)
+    {
+        auto input_vstreams_expected = VStreamsBuilder::create_input_vstreams(net_group, input_vstreams_params);
+        VALIDATE_STATUS(input_vstreams_expected.status());
+
+        std::unordered_map<std::string, std::shared_ptr<InputVStream>> input_vstreams;
+        for (auto &input : input_vstreams_expected.value()) {
+            input_vstreams.emplace(input.name(), make_shared_nothrow<InputVStream>(std::move(input)));
+        }
+        return InputVStreamsWrapper(input_vstreams);
+    }
+
+    const InputVStreamsWrapper &enter()
+    {
+        return std::ref(*this);
+    }
+
+    void exit()
+    {
+        m_input_vstreams.clear();
+    }
+
+    std::shared_ptr<InputVStream> get_input_by_name(const std::string &name) 
+    {
+        auto input = m_input_vstreams.find(name);
+        if (m_input_vstreams.end() == input) {
+            LOGGER__ERROR("Input virtual stream for name={} not found", name);
+            THROW_STATUS_ERROR(HAILO_NOT_FOUND);
+        }
+
+        return input->second;
+    }
+
+    py::dict get_all_inputs()
+    {
+        return py::cast(m_input_vstreams);
+    }
+
+    void clear()
+    {
+        std::vector<std::reference_wrapper<InputVStream>> inputs;
+        inputs.reserve(m_input_vstreams.size());
+        for (auto &name_vstream_pair : m_input_vstreams) {
+            inputs.emplace_back(std::ref(*name_vstream_pair.second));
+        }
+        
+        auto status = InputVStream::clear(inputs);
+        VALIDATE_STATUS(status);
+    }
+
+    static void add_to_python_module(py::module &m)
+    {
+        py::class_<InputVStreamsWrapper>(m, "InputVStreams")
+        .def(py::init(&InputVStreamsWrapper::create))
+        .def("get_input_by_name", &InputVStreamsWrapper::get_input_by_name)
+        .def("get_all_inputs", &InputVStreamsWrapper::get_all_inputs)
+        .def("clear", &InputVStreamsWrapper::clear)
+        .def("__enter__", &InputVStreamsWrapper::enter, py::return_value_policy::reference)
+        .def("__exit__",  [&](InputVStreamsWrapper &self, py::args) { self.exit(); })
+        ;
+    }
+
+private:
+    InputVStreamsWrapper(std::unordered_map<std::string, std::shared_ptr<InputVStream>> &input_vstreams) :
+        m_input_vstreams(std::move(input_vstreams))
+    {}
+
+    std::unordered_map<std::string, std::shared_ptr<InputVStream>> m_input_vstreams;
+};
+
+class OutputVStreamWrapper final
+{
+public:
+
+    static py::dtype get_dtype(OutputVStream &self)
+    {
+        const auto format_type = self.get_user_buffer_format().type;
+        return py::dtype(convert_format_type_to_string(format_type));
+    }
+
+    static hailo_format_t get_user_buffer_format(OutputVStream &self)
+    {
+        const auto format = self.get_user_buffer_format();
+        return format;
+    }
+    
+    static auto get_shape(OutputVStream &self)
+    {
+        return *py::array::ShapeContainer(get_pybind_shape(self.get_info(), self.get_user_buffer_format()));
+    }
+
+    static void add_to_python_module(py::module &m)
+    {
+        py::class_<OutputVStream, std::shared_ptr<OutputVStream>>(m, "OutputVStream")
+        .def("recv", [](OutputVStream &self)
+        {
+            auto buffer = Buffer::create(self.get_frame_size());
+            VALIDATE_STATUS(buffer.status());
+
+            hailo_status status = self.read(MemoryView(buffer->data(), buffer->size()));
+            VALIDATE_STATUS(status);
+
+            // Note: The ownership of the buffer is transferred to Python wrapped as a py::array.
+            //       When the py::array isn't referenced anymore in Python and is destructed, the py::capsule's dtor
+            //       is called too (and it deletes the raw buffer)
+            const auto unmanaged_addr = buffer.release().release();
+            return py::array(get_dtype(self), get_shape(self), unmanaged_addr,
+                py::capsule(unmanaged_addr, [](void *p) { delete reinterpret_cast<uint8_t*>(p); }));
+        })
+        .def_property_readonly("info", [](OutputVStream &self)
+        {
+            return self.get_info();
+        })
+        .def_property_readonly("dtype", &OutputVStreamWrapper::get_dtype)
+        .def_property_readonly("shape", &OutputVStreamWrapper::get_shape)
+        .def("get_user_buffer_format", &OutputVStreamWrapper::get_user_buffer_format)
+        ;
+    }
+};
+
+class OutputVStreamsWrapper final
+{
+public:
+
+    static OutputVStreamsWrapper create(ConfiguredNetworkGroup &net_group,
+        const std::map<std::string, hailo_vstream_params_t> &output_vstreams_params)
+    {
+        auto output_vstreams_expected = VStreamsBuilder::create_output_vstreams(net_group, output_vstreams_params);
+        VALIDATE_STATUS(output_vstreams_expected.status());
+
+        std::unordered_map<std::string, std::shared_ptr<OutputVStream>> output_vstreams;
+        for (auto &output : output_vstreams_expected.value()) {
+            output_vstreams.emplace(output.name(), make_shared_nothrow<OutputVStream>(std::move(output)));
+        }
+        return OutputVStreamsWrapper(output_vstreams);
+    }
+
+    std::shared_ptr<OutputVStream> get_output_by_name(const std::string &name)
+    {
+        auto output = m_output_vstreams.find(name);
+        if (m_output_vstreams.end() == output) {
+            LOGGER__ERROR("Output virtual stream for name={} not found", name);
+            THROW_STATUS_ERROR(HAILO_NOT_FOUND);
+        }
+
+        return output->second;
+    }
+
+    const OutputVStreamsWrapper &enter()
+    {
+        return std::ref(*this);
+    }
+
+    void exit()
+    {
+        m_output_vstreams.clear();
+    }
+
+    py::dict get_all_outputs()
+    {
+        return py::cast(m_output_vstreams);
+    }
+
+    void clear()
+    {
+        std::vector<std::reference_wrapper<OutputVStream>> outputs;
+        outputs.reserve(m_output_vstreams.size());
+        for (auto &name_vstream_pair : m_output_vstreams) {
+            outputs.emplace_back(std::ref(*name_vstream_pair.second));
+        }
+        
+        auto status = OutputVStream::clear(outputs);
+        VALIDATE_STATUS(status);
+    }
+
+    static void add_to_python_module(py::module &m)
+    {
+        py::class_<OutputVStreamsWrapper>(m, "OutputVStreams")
+        .def(py::init(&OutputVStreamsWrapper::create))
+        .def("get_output_by_name", &OutputVStreamsWrapper::get_output_by_name)
+        .def("get_all_outputs", &OutputVStreamsWrapper::get_all_outputs)
+        .def("clear", &OutputVStreamsWrapper::clear)
+        .def("__enter__", &OutputVStreamsWrapper::enter, py::return_value_policy::reference)
+        .def("__exit__",  [&](OutputVStreamsWrapper &self, py::args) { self.exit(); })
+        ;
+    }
+
+private:
+    OutputVStreamsWrapper(std::unordered_map<std::string, std::shared_ptr<OutputVStream>> &output_vstreams) :
+        m_output_vstreams(std::move(output_vstreams))
+    {}
+
+    std::unordered_map<std::string, std::shared_ptr<OutputVStream>> m_output_vstreams;
+};
+
+class InferVStreamsWrapper final
+{
+public:
+    static InferVStreamsWrapper create(ConfiguredNetworkGroup &network_group,
+        const std::map<std::string, hailo_vstream_params_t> &input_vstreams_params,
+        const std::map<std::string, hailo_vstream_params_t> &output_vstreams_params)
+    {
+        auto infer_pipeline = InferVStreams::create(network_group, input_vstreams_params, output_vstreams_params);
+        VALIDATE_EXPECTED(infer_pipeline);
+        auto infer_vstream_ptr = make_shared_nothrow<InferVStreams>(std::move(infer_pipeline.value()));
+
+        return InferVStreamsWrapper(infer_vstream_ptr);
+    }
+
+    void infer(std::map<std::string, py::array> input_data, std::map<std::string, py::array> output_data,
+        size_t batch_size)
+    {
+        std::map<std::string, MemoryView> input_data_c;
+        std::map<std::string, MemoryView> output_data_c;
+
+        for (auto& name_pair : input_data) {
+            input_data_c.emplace(name_pair.first, MemoryView(name_pair.second.mutable_data(),
+                static_cast<size_t>(name_pair.second.nbytes())));
+        }
+
+        for (auto& name_pair : output_data) {
+            output_data_c.emplace(name_pair.first, MemoryView(name_pair.second.mutable_data(),
+                static_cast<size_t>(name_pair.second.nbytes())));
+        }
+
+        hailo_status status = m_infer_pipeline->infer(input_data_c, output_data_c, batch_size);
+        VALIDATE_STATUS(status);
+    }
+
+    py::dtype get_host_dtype(const std::string &stream_name)
+    {
+        auto input = m_infer_pipeline->get_input_by_name(stream_name);
+        if (HAILO_SUCCESS == input.status()) {
+            return py::dtype(convert_format_type_to_string(input->get().get_user_buffer_format().type));
+        } else if (HAILO_NOT_FOUND != input.status()) {
+            THROW_STATUS_ERROR(input.status());
+        }
+        auto output = m_infer_pipeline->get_output_by_name(stream_name);
+        VALIDATE_EXPECTED(output);
+
+        return py::dtype(convert_format_type_to_string(output->get().get_user_buffer_format().type));
+    }
+
+    hailo_format_t get_user_buffer_format(const std::string &stream_name)
+    {
+        auto input = m_infer_pipeline->get_input_by_name(stream_name);
+        if (HAILO_SUCCESS == input.status()) {
+            return input->get().get_user_buffer_format();
+        } else if (HAILO_NOT_FOUND != input.status()) {
+            THROW_STATUS_ERROR(input.status());
+        }
+        auto output = m_infer_pipeline->get_output_by_name(stream_name);
+        VALIDATE_EXPECTED(output);
+
+        return output->get().get_user_buffer_format();
+    }
+
+    std::vector<size_t> get_shape(const std::string &stream_name)
+    {
+        auto input = m_infer_pipeline->get_input_by_name(stream_name);
+        if (HAILO_SUCCESS == input.status()) {
+            return get_pybind_shape(input->get().get_info(), input->get().get_user_buffer_format());
+        }
+
+        auto output = m_infer_pipeline->get_output_by_name(stream_name);
+        if (HAILO_SUCCESS == output.status()) {
+            return get_pybind_shape(output->get().get_info(), output->get().get_user_buffer_format());
+        }
+
+        LOGGER__ERROR("Stream {} not found", stream_name);
+        THROW_STATUS_ERROR(HAILO_NOT_FOUND);
+    }
+
+    void release()
+    {
+        m_infer_pipeline.reset();
+    }
+
+    static void add_to_python_module(py::module &m)
+    {
+        py::class_<InferVStreamsWrapper>(m, "InferVStreams")
+        .def(py::init(&InferVStreamsWrapper::create))
+        .def("get_host_dtype", &InferVStreamsWrapper::get_host_dtype)
+        .def("get_shape", &InferVStreamsWrapper::get_shape)
+        .def("get_user_buffer_format", &InferVStreamsWrapper::get_user_buffer_format)
+        .def("infer", &InferVStreamsWrapper::infer)
+        .def("release",  [](InferVStreamsWrapper &self, py::args) { self.release(); })
+        ;
+    }
+
+private:
+    InferVStreamsWrapper(std::shared_ptr<InferVStreams> &infer_pipeline) :
+        m_infer_pipeline(std::move(infer_pipeline))
+    {}
+
+     std::shared_ptr<InferVStreams> m_infer_pipeline;
+};
+
+void VStream_api_initialize_python_module(py::module &m)
+{
+    InputVStreamWrapper::add_to_python_module(m);
+    InputVStreamsWrapper::add_to_python_module(m);
+    OutputVStreamWrapper::add_to_python_module(m);
+    OutputVStreamsWrapper::add_to_python_module(m);
+    InferVStreamsWrapper::add_to_python_module(m);
+}
+
+} /* namespace hailort */
+
+#endif // _VSTREAM_API_HPP_
diff --git a/hailort/libhailort/cmake/common_compiler_options.cmake b/hailort/libhailort/cmake/common_compiler_options.cmake
new file mode 100644 (file)
index 0000000..da48baf
--- /dev/null
@@ -0,0 +1,23 @@
+cmake_minimum_required(VERSION 3.0.0)
+
+FUNCTION(disable_exceptions target)
+    if(WIN32)
+        # TODO: Find the right flag for windows (-fno-exceptions equivalent)
+    elseif(UNIX)
+        target_compile_options(libhailort PRIVATE -fno-exceptions)
+    else()
+        message(FATAL_ERROR "Unexpeced host, stopping build")
+    endif()
+ENDFUNCTION()
+
+FUNCTION(exclude_archive_libs_symbols target)
+    if(WIN32)
+        # TODO: check it
+    elseif(UNIX)
+        get_property(TEMP_LINK_FLAGS TARGET ${target} PROPERTY LINK_FLAGS)
+        set(TEMP_LINK_FLAGS "${TEMP_LINK_FLAGS} -Wl,--exclude-libs=ALL")
+        set_property(TARGET ${target} PROPERTY LINK_FLAGS ${TEMP_LINK_FLAGS})
+    else()
+        message(FATAL_ERROR "Unexpeced host, stopping build")
+    endif()
+ENDFUNCTION()
diff --git a/hailort/libhailort/cmake/execute_cmake.cmake b/hailort/libhailort/cmake/execute_cmake.cmake
new file mode 100644 (file)
index 0000000..29601e2
--- /dev/null
@@ -0,0 +1,50 @@
+cmake_minimum_required(VERSION 3.0.0)
+
+function(execute_process_in_clean_env)
+    cmake_parse_arguments(execute_process_in_clean_env "" "RESULT_VARIABLE" "" ${ARGN})
+    if(CMAKE_HOST_UNIX)
+        string(REPLACE ";" " " cmdline "${execute_process_in_clean_env_UNPARSED_ARGUMENTS}")
+        execute_process(COMMAND env -i HOME=$ENV{HOME} PATH=/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin bash -l -c "${cmdline}" OUTPUT_QUIET RESULT_VARIABLE result)
+    else()
+        # TODO: make it clean env for cross compile
+        set(cmdline ${execute_process_in_clean_env_UNPARSED_ARGUMENTS})
+        execute_process(COMMAND cmd /C ${cmdline} OUTPUT_QUIET RESULT_VARIABLE result)
+    endif()
+    if(DEFINED ${execute_process_in_clean_env_RESULT_VARIABLE})
+        set(${execute_process_in_clean_env_RESULT_VARIABLE} ${result} PARENT_SCOPE)
+    endif()
+endfunction()
+
+function(execute_cmake)
+    set(options PARALLEL_BUILD)
+    set(oneValueArgs SOURCE_DIR BUILD_DIR)
+    set(multiValueArgs CONFIGURE_ARGS BUILD_ARGS)
+    cmake_parse_arguments(execute_cmake "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
+
+    execute_process_in_clean_env(
+        "${CMAKE_COMMAND}"
+        "${execute_cmake_SOURCE_DIR}"
+        "-B${execute_cmake_BUILD_DIR}"
+        "${execute_cmake_CONFIGURE_ARGS}"
+        RESULT_VARIABLE result
+    )
+    if(result)
+        message(FATAL_ERROR "Failed configuring ${execute_cmake_SOURCE_DIR}")
+    endif()
+
+    if(${execute_cmake_PARALLEL_BUILD} AND (CMAKE_GENERATOR MATCHES "Unix Makefiles"))
+        execute_process(COMMAND grep -c ^processor /proc/cpuinfo OUTPUT_VARIABLE cores_count RESULT_VARIABLE result)
+        if(result)
+            message(FATAL_ERROR "Failed getting the amount of cores")
+        endif()
+        set(CMAKE_EXTRA_BUILD_ARGS -- -j${cores_count})
+    endif()
+
+    execute_process_in_clean_env(
+        "${CMAKE_COMMAND}" --build "${execute_cmake_BUILD_DIR}" "${execute_cmake_BUILD_ARGS}" ${CMAKE_EXTRA_BUILD_ARGS}
+        RESULT_VARIABLE result
+    )
+    if(result)
+        message(FATAL_ERROR "Failed building ${execute_cmake_SOURCE_DIR}")
+    endif()
+endfunction()
\ No newline at end of file
diff --git a/hailort/libhailort/cmake/toolchains/linux.aarch64.cmake b/hailort/libhailort/cmake/toolchains/linux.aarch64.cmake
new file mode 100644 (file)
index 0000000..ac9f59a
--- /dev/null
@@ -0,0 +1,6 @@
+set(CMAKE_SYSTEM_NAME Linux)
+set(CMAKE_SYSTEM_PROCESSOR aarch64)
+
+set(CMAKE_C_COMPILER aarch64-linux-gnu-gcc)
+set(CMAKE_CXX_COMPILER aarch64-linux-gnu-g++)
+set(CMAKE_STRIP aarch64-linux-gnu-strip CACHE FILEPATH "Strip")
\ No newline at end of file
diff --git a/hailort/libhailort/cmake/toolchains/linux.android28-arm64-v8a.cmake b/hailort/libhailort/cmake/toolchains/linux.android28-arm64-v8a.cmake
new file mode 100644 (file)
index 0000000..5feb60e
--- /dev/null
@@ -0,0 +1,11 @@
+set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -llog")
+set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -llog")
+
+set(ANDROID_ABI "arm64-v8a")
+set(ANDROID_PLATFORM "android-28")
+
+if(NOT DEFINED ENV{ANDROID_NDK})
+    message(FATAL_ERROR "ANDROID_NDK env variable must be set. Please install android ndk and set this variable.")
+endif()
+
+include("$ENV{ANDROID_NDK}/build/cmake/android.toolchain.cmake")
diff --git a/hailort/libhailort/cmake/toolchains/linux.armv7l.cmake b/hailort/libhailort/cmake/toolchains/linux.armv7l.cmake
new file mode 100644 (file)
index 0000000..207cbe6
--- /dev/null
@@ -0,0 +1,12 @@
+set(CMAKE_SYSTEM_NAME Linux)
+set(CMAKE_SYSTEM_PROCESSOR arm)
+
+set(CMAKE_C_COMPILER arm-linux-gnueabi-gcc)
+set(CMAKE_CXX_COMPILER arm-linux-gnueabi-g++)
+set(CMAKE_STRIP arm-linux-gnueabi--strip CACHE FILEPATH "Strip")
+set(CMAKE_LINKER arm-linux-gnueabi-ld)
+
+add_compile_options(-march=armv7-a)
+
+# pybind is not supported in this platform
+set(HAILO_BUILD_PYBIND 0)
diff --git a/hailort/libhailort/cmake/toolchains/linux.armv7lhf.cmake b/hailort/libhailort/cmake/toolchains/linux.armv7lhf.cmake
new file mode 100644 (file)
index 0000000..6c51638
--- /dev/null
@@ -0,0 +1,12 @@
+set(CMAKE_SYSTEM_NAME Linux)
+set(CMAKE_SYSTEM_PROCESSOR arm)
+
+set(CMAKE_C_COMPILER arm-linux-gnueabihf-gcc)
+set(CMAKE_CXX_COMPILER arm-linux-gnueabihf-g++)
+set(CMAKE_STRIP arm-linux-gnueabihf--strip CACHE FILEPATH "Strip")
+set(CMAKE_LINKER arm-linux-gnueabihf-ld)
+
+add_compile_options(-march=armv7-a)
+
+# pybind is not supported in this platform
+set(HAILO_BUILD_PYBIND 0)
diff --git a/hailort/libhailort/cmake/toolchains/linux.x86_64.cmake b/hailort/libhailort/cmake/toolchains/linux.x86_64.cmake
new file mode 100644 (file)
index 0000000..d5befb4
--- /dev/null
@@ -0,0 +1,6 @@
+set(CMAKE_SYSTEM_NAME Linux)
+set(CMAKE_SYSTEM_PROCESSOR x86_64)
+
+set(CMAKE_C_COMPILER gcc)
+set(CMAKE_CXX_COMPILER g++)
+set(CMAKE_STRIP strip CACHE FILEPATH "Strip")
\ No newline at end of file
diff --git a/hailort/libhailort/cmake/toolchains/qnx.aarch64.cmake b/hailort/libhailort/cmake/toolchains/qnx.aarch64.cmake
new file mode 100644 (file)
index 0000000..c476c20
--- /dev/null
@@ -0,0 +1,24 @@
+set(CMAKE_SYSTEM_NAME QNX)
+set(arch ntoaarch64)
+set(QNX_PROCESSOR aarch64)
+
+set(CMAKE_C_COMPILER $ENV{QNX_HOST}/usr/bin/${arch}-gcc)
+set(CMAKE_C_COMPILER_TARGET ${arch})
+
+set(CMAKE_CXX_COMPILER $ENV{QNX_HOST}/usr/bin/${arch}-g++)
+set(CMAKE_CXX_COMPILER_TARGET ${arch})
+
+add_definitions("-D_QNX_SOURCE")
+
+file(GLOB_RECURSE libgcc_a 
+  "$ENV{QNX_HOST}/usr/lib/gcc/${QNX_PROCESSOR}*/*/pic/libgcc.a")
+
+set(CMAKE_C_STANDARD_LIBRARIES_INIT
+  "${libgcc_a} -lc -lsocket -Bstatic -lcS")
+set(CMAKE_CXX_STANDARD_LIBRARIES_INIT
+  "-lc++ -lstdc++ -lm ${CMAKE_C_STANDARD_LIBRARIES_INIT}")
+
+# pybind is not supported in this platform
+set(HAILO_BUILD_PYBIND 0)
+# GStreamer does not work on QNX currently
+set(HAILO_BUILD_GSTREAMER 0)
\ No newline at end of file
diff --git a/hailort/libhailort/cmake/toolchains/qnx.x86_64.cmake b/hailort/libhailort/cmake/toolchains/qnx.x86_64.cmake
new file mode 100644 (file)
index 0000000..2108b4f
--- /dev/null
@@ -0,0 +1,24 @@
+set(CMAKE_SYSTEM_NAME QNX)
+set(arch ntox86_64)
+set(QNX_PROCESSOR x86_64)
+
+set(CMAKE_C_COMPILER $ENV{QNX_HOST}/usr/bin/${arch}-gcc)
+set(CMAKE_C_COMPILER_TARGET ${arch})
+
+set(CMAKE_CXX_COMPILER $ENV{QNX_HOST}/usr/bin/${arch}-g++)
+set(CMAKE_CXX_COMPILER_TARGET ${arch})
+
+add_definitions("-D_QNX_SOURCE")
+
+file(GLOB_RECURSE libgcc_a 
+  "$ENV{QNX_HOST}/usr/lib/gcc/${QNX_PROCESSOR}*/*/pic/libgcc.a")
+
+set(CMAKE_C_STANDARD_LIBRARIES_INIT
+  "${libgcc_a} -lc -lsocket -Bstatic -lcS")
+set(CMAKE_CXX_STANDARD_LIBRARIES_INIT
+  "-lc++ -lstdc++ -lm ${CMAKE_C_STANDARD_LIBRARIES_INIT}")
+
+# pybind is not supported in this platform
+set(HAILO_BUILD_PYBIND 0)
+# GStreamer does not work on QNX currently
+set(HAILO_BUILD_GSTREAMER 0)
\ No newline at end of file
diff --git a/hailort/libhailort/cmake/toolchains/toolchains.yaml b/hailort/libhailort/cmake/toolchains/toolchains.yaml
new file mode 100644 (file)
index 0000000..460fa13
--- /dev/null
@@ -0,0 +1,44 @@
+-   name: linux.x86_64
+    required_packages:
+    -   gcc
+    -   g++
+    python_versions:
+    -   version: '3.6'
+        installation: deb
+        package_name: python3.6-dev
+    -   version: '3.7'
+        installation: deb
+        package_name: python3.7-dev
+    -   version: '3.8'
+        installation: deb
+        package_name: python3.8-dev
+-   name: linux.aarch64
+    required_packages:
+    -   gcc-aarch64-linux-gnu
+    -   g++-aarch64-linux-gnu
+    python_versions:
+    -   version: '3.6'
+        installation: manual
+        package_name: http://launchpadlibrarian.net/362976109/libpython3.6-dev_3.6.5-3_arm64.deb
+        package_dest: /usr/include/aarch64-linux-gnu
+    -   version: '3.7'
+        installation: manual
+        package_name: http://launchpadlibrarian.net/450463692/libpython3.7-dev_3.7.5-2~18.04_arm64.deb
+        package_dest: /usr/include/aarch64-linux-gnu
+    -   version: '3.8'
+        installation: manual
+        package_name: https://launchpad.net/ubuntu/+source/python3.8/3.8.2-1ubuntu1/+build/18834117/+files/libpython3.8-dev_3.8.2-1ubuntu1_arm64.deb        
+        package_dest: /usr/include/aarch64-linux-gnu
+-   name: linux.armv7l
+    required_packages:
+    -   gcc-arm-linux-gnueabi
+    -   g++-arm-linux-gnueabi
+-   name: linux.armv7lhf
+    required_packages:
+    -   gcc-arm-linux-gnueabihf
+    -   g++-arm-linux-gnueabihf
+-   name: linux.android28-arm64-v8a
+    android_ndk:
+        version_name: "android-ndk-r21d"
+        file: "https://dl.google.com/android/repository/android-ndk-r21d-linux-x86_64.zip"
+-   name: windows.x86_64
diff --git a/hailort/libhailort/cmake/toolchains/windows.x86_64.cmake b/hailort/libhailort/cmake/toolchains/windows.x86_64.cmake
new file mode 100644 (file)
index 0000000..81d29ed
--- /dev/null
@@ -0,0 +1,6 @@
+set(CMAKE_SYSTEM_NAME Windows)
+set(CMAKE_SYSTEM_PROCESSOR x86_64)
+# TODO: msvc
+# set(CMAKE_C_COMPILER gcc)
+# set(CMAKE_CXX_COMPILER g++)
+# set(CMAKE_STRIP strip CACHE FILEPATH "Strip")
diff --git a/hailort/libhailort/doc/CMakeLists.txt b/hailort/libhailort/doc/CMakeLists.txt
new file mode 100644 (file)
index 0000000..c2b13af
--- /dev/null
@@ -0,0 +1,32 @@
+cmake_minimum_required(VERSION 3.0.0)
+
+find_package(Doxygen)
+if(DOXYGEN_FOUND)
+    set(DOXYGEN_OUTPUT_DIR ${CMAKE_CURRENT_BINARY_DIR}/doxygen)
+    set(DOXYGEN_INDEX_FILE ${DOXYGEN_OUTPUT_DIR}/xml/index.xml)
+    set(DOXYFILE_IN ${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile.in)
+    set(DOXYFILE_OUT ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile)
+    get_target_property(HAILORT_PUBLIC_HEADERS libhailort PUBLIC_HEADER)
+    string (REPLACE ";" " " HAILORT_PUBLIC_HEADERS_SPACE_SEPARATED "${HAILORT_PUBLIC_HEADERS}")
+
+    # Replace variables inside @@ with the current values
+    configure_file(${DOXYFILE_IN} ${DOXYFILE_OUT} @ONLY)
+    # Doxygen won't create this for us
+    file(MAKE_DIRECTORY ${DOXYGEN_OUTPUT_DIR})
+
+    # Only regenerate Doxygen when the Doxyfile or public headers change
+    add_custom_command(OUTPUT ${DOXYGEN_INDEX_FILE}
+                      DEPENDS ${HAILORT_PUBLIC_HEADERS}
+                      COMMAND ${DOXYGEN_EXECUTABLE} ${DOXYFILE_OUT}
+                      MAIN_DEPENDENCY ${DOXYFILE_OUT} ${DOXYFILE_IN}
+                      COMMENT "Generating doc")
+
+    add_custom_target(doxygen DEPENDS ${DOXYGEN_INDEX_FILE})
+
+    install(
+        DIRECTORY "${DOXYGEN_OUTPUT_DIR}" DESTINATION "doc/"
+        CONFIGURATIONS Release)
+
+    add_custom_target(doc DEPENDS doxygen)
+
+endif()
\ No newline at end of file
diff --git a/hailort/libhailort/doc/Doxyfile.in b/hailort/libhailort/doc/Doxyfile.in
new file mode 100644 (file)
index 0000000..7442d91
--- /dev/null
@@ -0,0 +1,20 @@
+# Doxyfile 1.8.11
+
+PROJECT_NAME           = HailoRT
+PROJECT_NUMBER         = @HAILORT_MAJOR_VERSION@.@HAILORT_MINOR_VERSION@.@HAILORT_REVISION_VERSION@
+INPUT                  = @HAILORT_PUBLIC_HEADERS_SPACE_SEPARATED@
+OUTPUT_DIRECTORY       = @DOXYGEN_OUTPUT_DIR@
+GENERATE_HTML          = NO
+GENERATE_LATEX         = NO
+GENERATE_XML           = YES
+QUIET                  = YES
+WARNINGS               = YES
+WARN_IF_UNDOCUMENTED   = NO
+WARN_IF_DOC_ERROR      = YES
+WARN_NO_PARAMDOC       = YES
+WARN_AS_ERROR          = YES
+MACRO_EXPANSION        = YES
+ENABLE_PREPROCESSING   = YES
+PREDEFINED             = DEPRECATED(msg)= \
+                        __attribute__(x)=
+EXCLUDE_SYMBOLS        = HAILO_MAX_SUPPORTED_POWER_SAMPLES HAILO_POWER_SAMPLES_ARRAY_DIMENSIONS
diff --git a/hailort/libhailort/examples/CMakeLists.txt b/hailort/libhailort/examples/CMakeLists.txt
new file mode 100644 (file)
index 0000000..401e511
--- /dev/null
@@ -0,0 +1,22 @@
+cmake_minimum_required(VERSION 3.0.0)
+
+project(hailort-examples)
+
+set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_LIST_DIR}/cmake/")
+find_package(HailoRT)
+find_package(Threads REQUIRED)
+set(THREADS_PREFER_PTHREAD_FLAG ON)
+
+add_library(example_base INTERFACE)
+target_link_libraries(example_base INTERFACE HailoRT::libhailort Threads::Threads)
+
+if(WIN32)
+    target_compile_options(example_base INTERFACE /W4 /WX /DWIN32_LEAN_AND_MEAN /DNOMINMAX /wd4201 /wd4251)
+else()
+    target_compile_options(example_base INTERFACE -Werror -Wall -Wextra -Wconversion -O3 -DNDEBUG) # TODO support debug/release builds
+endif()
+
+add_subdirectory(cpp)
+add_subdirectory(c)
+
+add_custom_target(hailort_examples DEPENDS ${EXAMPLES_C_TARGETS} ${EXAMPLES_CPP_TARGETS})
\ No newline at end of file
diff --git a/hailort/libhailort/examples/README.md b/hailort/libhailort/examples/README.md
new file mode 100644 (file)
index 0000000..21f4813
--- /dev/null
@@ -0,0 +1,45 @@
+# Examples
+The following examples are provided, demonstrating the HailoRT API:
+- C examples:
+  - `vstreams_example` - Basic inference of a shortcut network (inputs are sent through the device and right back out, without any changes made to the data):
+    - Configure and activate network group and virtual streams.
+    - The data is sent to the device via input vstreams and received via output vstreams.
+    - The data is transformed before sent and after receiving in a different thread using the virtual stream pipeline.
+  - `raw_streams_example` - Basic inference of a shortcut network using raw stream api.
+    - The data is transformed before sent and after received in the same thread sending/receiving using the transformation api.
+  - `data_quantization_example` - Demonstrates how to set input/output stream params so as to allow for custom quantization:
+    - Input streams may be marked as quantized, so that input data will not to be automatically quantized by the HailoRT library.
+    - Output streams may be marked as quantized, so that output data will remain quantized (as it is after exiting the device by default), and won't be 'de-quantized' by the HailoRT library.
+    - This example uses pcie devices.
+  - `switch_hefs_example` - Demonstrates how to work with multiple HEFs using virtual streams.
+    - This example uses pcie devices.
+  - `switch_single_io_hefs_example` - Demonstrates how to work with multiple single input single output HEFs using virtual streams.
+    - This example uses pcie devices.
+  - `multi_network_vstream_example` - Demonstrates how to work with multiple networks in a network group, using virtual streams.
+    - The example works with an HEF that contains one network group, and two networks in the network group.
+    - Configure the network group and set the batch size for each network.
+    - Get the networks information to create the vstreams for each network.
+    - The data is sent to the device via input vstreams and received via output vstreams.
+    - The data is transformed before sent and after receiving in a different thread using the virtual stream pipeline.
+- C++ examples:
+  - `vstreams_example` - Basic inference of a shortcut network, same as `vstreams_example` C example, uses HailoRT C++ api.
+  - `raw_streams_example` - Basic inference of a shortcut network, same as `raw_streams_example` C example, uses HailoRT C++ api.
+  - `multi_network_vstream_example` - Demonstrates how to work with multiple networks in a network group, same as `multi_network_vstream_example ` C example, uses HailoRT C++ api.
+  - `switch_hefs_example` - Demonstrates how to work with multiple HEFs using virtual streams, same as `switch_hefs_example ` C example, uses HailoRT C++ api.
+  - `switch_hefs_example_threads_reuse` - Same as `switch_hefs_example` CPP example, with performance optimizations for I/O threads re-usage instead of re-creation at each network group activation.
+
+## Compiling with CMake
+Examples are configured and compiled using the following commands:
+```sh
+cmake -H. -Bbuild
+cmake --build build
+```
+> **_NOTE:_** Write permissions are required to compile the examples from their current directory.
+If this is not the case, copy the examples directory to another location with the required permissions.
+
+## Running the examples
+One can run the example using the following commands, from the examples directory:
+
+  ```sh
+  build/<c/cpp>/<example_name> [params..]
+  ```
diff --git a/hailort/libhailort/examples/c/CMakeLists.txt b/hailort/libhailort/examples/c/CMakeLists.txt
new file mode 100644 (file)
index 0000000..e001b9e
--- /dev/null
@@ -0,0 +1,39 @@
+cmake_minimum_required(VERSION 3.0.0)
+
+file(GLOB_RECURSE C_EXAMPLE_SOURCES "*.c")
+SET_SOURCE_FILES_PROPERTIES(${C_EXAMPLE_SOURCES} PROPERTIES LANGUAGE C)
+
+add_executable(c_data_quantization_example data_quantization_example.c)
+target_link_libraries(c_data_quantization_example PRIVATE example_base)
+
+add_executable(c_raw_streams_example raw_streams_example.c)
+target_link_libraries(c_raw_streams_example PRIVATE example_base)
+
+add_executable(c_vstreams_example vstreams_example.c)
+target_link_libraries(c_vstreams_example PRIVATE example_base)
+
+add_executable(c_infer_pipeline_example infer_pipeline_example.c)
+target_link_libraries(c_infer_pipeline_example PRIVATE example_base)
+
+add_executable(c_multi_network_vstream_example multi_network_vstream_example.c)
+target_link_libraries(c_multi_network_vstream_example PRIVATE example_base)
+
+add_executable(c_switch_hefs_example switch_hefs_example.c)
+target_link_libraries(c_switch_hefs_example PRIVATE example_base)
+
+add_executable(c_switch_single_io_hefs_example switch_single_io_hefs_example.c)
+target_link_libraries(c_switch_single_io_hefs_example PRIVATE example_base)
+
+add_executable(multi_device_example_c multi_device_example.c)
+target_link_libraries(multi_device_example_c PRIVATE example_base)
+
+set(EXAMPLES_C_TARGETS
+    c_data_quantization_example
+    c_raw_streams_example
+    c_vstreams_example
+    c_infer_pipeline_example
+    c_multi_network_vstream_example
+    c_switch_hefs_example
+    c_switch_single_io_hefs_example
+    multi_device_example_c
+    PARENT_SCOPE)
diff --git a/hailort/libhailort/examples/c/common.h b/hailort/libhailort/examples/c/common.h
new file mode 100644 (file)
index 0000000..85e6e89
--- /dev/null
@@ -0,0 +1,42 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @ file example_common.h
+ * Common macros and defines used by Hailort Examples
+ **/
+
+#ifndef _EXAMPLE_COMMON_H_
+#define _EXAMPLE_COMMON_H_
+
+#include <stdio.h>
+#include <stdlib.h>
+
+
+#define FREE(var)                           \
+    do {                                    \
+        if (NULL != (var)) {                \
+            free(var);                      \
+            var = NULL;                     \
+        }                                   \
+    } while(0)
+
+#define REQUIRE_ACTION(cond, action, label, ...) \
+    do {                                         \
+        if (!(cond)) {                           \
+            printf(__VA_ARGS__);                 \
+            printf("\n");                        \
+            action;                              \
+            goto label;                          \
+        }                                        \
+    } while(0)
+
+#define REQUIRE_SUCCESS(status, label, ...) REQUIRE_ACTION((HAILO_SUCCESS == (status)), , label, __VA_ARGS__)
+
+#define ARRAY_LENGTH(__array) (sizeof((__array)) / sizeof((__array)[0]))
+
+#define NSEC_IN_SEC (1e+9)
+
+
+#endif /* _EXAMPLE_COMMON_H_ */
diff --git a/hailort/libhailort/examples/c/data_quantization_example.c b/hailort/libhailort/examples/c/data_quantization_example.c
new file mode 100644 (file)
index 0000000..831d3c1
--- /dev/null
@@ -0,0 +1,304 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @ file data_quantization_example.c
+ * This example demonstrates using quantization on an HEF network with multiple inputs and multiple outputs
+ **/
+
+#include "common.h"
+#include "hailo_thread.h"
+#include "hailo/hailort.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <string.h>
+#include <time.h>
+
+#define INFER_FRAME_COUNT (100)
+#define MAX_EDGE_LAYERS (16)
+#define DEVICE_COUNT (1)
+#define IN_ARG "in"
+#define OUT_ARG "out"
+#define BOTH_ARG "both"
+#define NONE_ARG "none"
+#define HEF_FILE ("hefs/shortcut_net.hef")
+
+#define USAGE_ERROR_MSG ("Args parsing error.\nUsage: data_quantization_example [in/out/both/none]\n" \
+    "* in - The input is quantized, hence HailoRT won't quantize the input\n" \
+    "* out - The output is to be left quantized, hence HailoRT won't de-quantize the output\n" \
+    "* both - The input is quantized and the output is to be left quantized, hence HailoRT won't do either\n" \
+    "* none - The input isn't quantized and the output is to be de-quantized, hence HailoRT will do both\n")
+
+#define RAND_FLOAT32 ((float32_t)rand() / (float32_t)RAND_MAX)
+#define RAND_UINT8 ((uint8_t)(rand() % 256))
+
+typedef struct quantization_args_t {
+    bool is_input_quantized;
+    bool is_output_quantized;
+} quantization_args_t;
+
+typedef struct write_thread_args_t {
+    hailo_input_vstream input_stream;
+    quantization_args_t quant_args;
+} write_thread_args_t;
+
+void parse_arguments(int argc, char **argv, quantization_args_t *quant_args)
+{
+    if (2 != argc) {
+        printf(USAGE_ERROR_MSG);
+        exit(1);
+    }
+
+    if (0 == strncmp(IN_ARG, argv[1], ARRAY_LENGTH(IN_ARG))) {
+        quant_args->is_input_quantized  = true;
+        quant_args->is_output_quantized = false;
+    } else if (0 == strncmp(OUT_ARG, argv[1], ARRAY_LENGTH(OUT_ARG))) {
+        quant_args->is_input_quantized  = false;
+        quant_args->is_output_quantized = true;
+    } else if (0 == strncmp(BOTH_ARG, argv[1], ARRAY_LENGTH(BOTH_ARG))) {
+        quant_args->is_input_quantized  = true;
+        quant_args->is_output_quantized = true;
+    } else if (0 == strncmp(NONE_ARG, argv[1], ARRAY_LENGTH(NONE_ARG))) {
+        quant_args->is_input_quantized  = false;
+        quant_args->is_output_quantized = false;
+    } else {
+        printf(USAGE_ERROR_MSG);
+        exit(1);
+    }
+}
+
+uint8_t* get_random_input_data(size_t host_input_frame_size, bool is_input_quantized)
+{
+    uint8_t *input_buffer = (uint8_t*)malloc(host_input_frame_size);
+    if (NULL == input_buffer) {
+        printf("Out of host memory\b");
+        return NULL;
+    }
+
+    if (is_input_quantized) {
+        // Input data is expected to be quantized so we generate a random uint8_t buffer
+        for (size_t i = 0; i < host_input_frame_size; i++) {
+            input_buffer[i] = RAND_UINT8;
+        }
+    } else {
+        // Input data isn't to be quantized in advance, rather it'll be quantized by the HailoRT runtime.
+        // Note that the 'format_type' param passed to 'hailo_make_input_vstream_params()' in 'create_vstreams()'
+        // is HAILO_FORMAT_TYPE_FLOAT32, so we create a buffer of random float32_t that'll be cast to uint8_t*
+        // as expected by the HailoRT API. Internally, the float32_t will be quantized and sent to the device
+        // as uint8_t. Also note that host_input_frame_size is already calculated considering that the 'format_type'
+        // is HAILO_FORMAT_TYPE_FLOAT32.
+        for (size_t i = 0; i < (host_input_frame_size / sizeof(float32_t)); i++) {
+            // RAND_FLOAT32 will give us random values in the interval [0.0,1.0); we arbitrarily chose to expand
+            // the values to the interval [0.0, 1000.0).
+            ((float32_t*)input_buffer)[i] = RAND_FLOAT32 * 1000;
+        }
+    }
+
+    return input_buffer;
+}
+
+
+thread_return_type write_to_device(void *args)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    size_t input_frame_size = 0;
+    void *src_data = NULL;
+    write_thread_args_t *write_args = (write_thread_args_t*)args;
+
+    status = hailo_get_input_vstream_frame_size(write_args->input_stream, &input_frame_size);
+    REQUIRE_SUCCESS(status, l_exit, "Failed getting input virtual stream frame size");
+
+    src_data = get_random_input_data(input_frame_size, write_args->quant_args.is_input_quantized);
+    REQUIRE_ACTION(src_data != NULL, status = HAILO_OUT_OF_HOST_MEMORY, l_exit, "Failed to allocate input buffer");
+
+    for (uint32_t i = 0; i < INFER_FRAME_COUNT; i++) {
+        status = hailo_vstream_write_raw_buffer(write_args->input_stream, src_data, input_frame_size);
+        REQUIRE_SUCCESS(status, l_free_buffer, "Failed sending to vstream");
+    }
+
+    status = HAILO_SUCCESS;
+l_free_buffer:
+    FREE(src_data);
+l_exit:
+    return (thread_return_type)status;
+}
+
+thread_return_type read_from_device(void *vstream_ptr)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    size_t output_frame_size = 0;
+    uint8_t *dst_data = NULL;
+    hailo_output_vstream vstream = *(hailo_output_vstream*)vstream_ptr;
+
+    status = hailo_get_output_vstream_frame_size(vstream, &output_frame_size);
+    REQUIRE_SUCCESS(status, l_exit, "Failed getting output virtual stream frame size");
+
+    dst_data = (uint8_t*)malloc(output_frame_size);
+    REQUIRE_ACTION(dst_data != NULL, status = HAILO_OUT_OF_HOST_MEMORY, l_exit, "Failed to allocate output buffer");
+
+    for (uint32_t i = 0; i < INFER_FRAME_COUNT; i++) {
+        status = hailo_vstream_read_raw_buffer(vstream, dst_data, output_frame_size);
+        REQUIRE_SUCCESS(status, l_free_buffer, "hailo_vstream_recv failed");
+    }
+
+    status = HAILO_SUCCESS;
+l_free_buffer:
+    FREE(dst_data);
+l_exit:
+    return (thread_return_type)status;
+}
+
+hailo_status infer(hailo_input_vstream *input_vstreams, size_t input_vstreams_size,
+    hailo_output_vstream *output_vstreams, size_t output_vstreams_size, quantization_args_t quant_args)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    hailo_thread write_threads [MAX_EDGE_LAYERS] = {0};
+    hailo_thread read_threads [MAX_EDGE_LAYERS] = {0};
+    hailo_status write_thread_status = HAILO_UNINITIALIZED;
+    hailo_status read_thread_status = HAILO_UNINITIALIZED;
+    write_thread_args_t write_args[MAX_EDGE_LAYERS] = {0};
+    size_t input_stream_index = 0;
+    size_t output_stream_index = 0;
+    size_t i = 0;
+
+    // Create reading threads
+    for (output_stream_index = 0; output_stream_index < output_vstreams_size; output_stream_index++) {
+        status = hailo_create_thread(read_from_device, &output_vstreams[output_stream_index],
+            &read_threads[output_stream_index]);
+        REQUIRE_SUCCESS(status, l_cleanup, "Failed creating thread");
+    }
+
+    // Create writing threads
+    for (input_stream_index = 0; input_stream_index < input_vstreams_size; input_stream_index++) {
+        write_args[input_stream_index].input_stream  = input_vstreams[input_stream_index];
+        write_args[input_stream_index].quant_args    = quant_args;
+
+        status = hailo_create_thread(write_to_device, &write_args[input_stream_index],
+            &write_threads[input_stream_index]);
+        REQUIRE_SUCCESS(status, l_cleanup, "Failed creating thread");
+    }
+
+l_cleanup:
+    // Join write threads 
+    for (i = 0; i < input_stream_index; i++) {
+        write_thread_status = hailo_join_thread(&write_threads[i]);
+        if (HAILO_SUCCESS != write_thread_status) {
+            printf("write_thread failed \n");
+            status = write_thread_status; // Override write status
+        }
+    }
+
+    // Join read threads 
+    for (i = 0; i < output_stream_index; i++) {
+        read_thread_status = hailo_join_thread(&read_threads[i]);
+        if (HAILO_SUCCESS != read_thread_status) {
+            printf("read_thread failed \n");
+            status = read_thread_status; // Override read status
+        }
+    }
+
+    return status;
+}
+
+hailo_status create_vstreams(hailo_configured_network_group network_group, hailo_input_vstream *input_vstreams,
+    size_t *input_vstreams_size, hailo_output_vstream *output_vstreams, size_t *output_vstreams_size,
+    quantization_args_t quant_args)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    hailo_input_vstream_params_by_name_t input_vstreams_params[MAX_EDGE_LAYERS] = {0};
+    hailo_output_vstream_params_by_name_t output_vstreams_params[MAX_EDGE_LAYERS] = {0};
+    const hailo_format_type_t input_type = (quant_args.is_input_quantized ? HAILO_FORMAT_TYPE_UINT8 :
+        HAILO_FORMAT_TYPE_FLOAT32);
+    const hailo_format_type_t output_type = HAILO_FORMAT_TYPE_UINT8;
+
+    status = hailo_make_input_vstream_params(network_group, quant_args.is_input_quantized, input_type,
+        input_vstreams_params, input_vstreams_size);
+    REQUIRE_SUCCESS(status, l_exit, "Failed making input virtual stream params");
+
+    status = hailo_make_output_vstream_params(network_group, quant_args.is_output_quantized, output_type,
+        output_vstreams_params, output_vstreams_size);
+    REQUIRE_SUCCESS(status, l_exit, "Failed making output virtual stream params");
+
+    REQUIRE_ACTION((*input_vstreams_size <= MAX_EDGE_LAYERS || *output_vstreams_size <= MAX_EDGE_LAYERS),
+        status = HAILO_INVALID_OPERATION, l_exit, "Trying to infer network with too many input/output virtual streams, "
+        "Maximum amount is %d, (either change HEF or change the definition of MAX_EDGE_LAYERS)\n", MAX_EDGE_LAYERS);
+
+    status = hailo_create_input_vstreams(network_group, input_vstreams_params,
+        *input_vstreams_size, input_vstreams);
+    REQUIRE_SUCCESS(status, l_exit, "Failed creating input virtual streams");
+
+    status = hailo_create_output_vstreams(network_group, output_vstreams_params,
+        *output_vstreams_size, output_vstreams);
+    REQUIRE_SUCCESS(status, l_release_input_vstream, "Failed creating output virtual streams");
+
+    status = HAILO_SUCCESS;
+    goto l_exit;
+l_release_input_vstream:
+    (void) hailo_release_input_vstreams(input_vstreams, *input_vstreams_size);
+l_exit:
+    return status;
+}
+
+int main(int argc, char **argv)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    hailo_vdevice vdevice = NULL;
+    hailo_vdevice_params_t params = {0};
+    hailo_hef hef = NULL;
+    hailo_configure_params_t config_params = {0};
+    hailo_configured_network_group network_group = NULL;
+    size_t network_group_size = 1;
+    hailo_input_vstream input_vstreams[MAX_EDGE_LAYERS] = {NULL};
+    hailo_output_vstream output_vstreams[MAX_EDGE_LAYERS] = {NULL};
+    size_t input_vstreams_size = MAX_EDGE_LAYERS;
+    size_t output_vstreams_size = MAX_EDGE_LAYERS;
+    hailo_activated_network_group activated_network_group = NULL;
+    quantization_args_t quant_args;
+
+    parse_arguments(argc, argv, &quant_args);
+
+    status = hailo_init_vdevice_params(&params);
+    REQUIRE_SUCCESS(status, l_exit, "Failed init vdevice_params");
+
+    params.device_count = DEVICE_COUNT;
+    status = hailo_create_vdevice(&params, &vdevice);
+    REQUIRE_SUCCESS(status, l_exit, "Failed to create vdevice");
+
+    status = hailo_create_hef_file(&hef, HEF_FILE);
+    REQUIRE_SUCCESS(status, l_release_vdevice, "Failed reading hef file");
+
+    status = hailo_init_configure_params(hef, HAILO_STREAM_INTERFACE_PCIE, &config_params);
+    REQUIRE_SUCCESS(status, l_release_hef, "Failed initializing configure parameters");
+
+    status = hailo_configure_vdevice(vdevice, hef, &config_params, &network_group, &network_group_size);
+    REQUIRE_SUCCESS(status, l_release_hef, "Failed configure vdevcie from hef");
+    REQUIRE_ACTION(network_group_size == 1, status = HAILO_INVALID_ARGUMENT, l_release_hef,
+        "Invalid network group size");
+
+    status = create_vstreams(network_group, input_vstreams, &input_vstreams_size, output_vstreams,
+        &output_vstreams_size,quant_args);
+    REQUIRE_SUCCESS(status, l_release_hef, "Failed creating virtual streams");
+
+    status = hailo_activate_network_group(network_group, NULL, &activated_network_group);
+    REQUIRE_SUCCESS(status, l_release_vstreams, "Failed activate network group");
+
+    status = infer(input_vstreams, input_vstreams_size, output_vstreams, output_vstreams_size, quant_args);
+    REQUIRE_SUCCESS(status, l_deactivate_network_group, "Inference failure");
+
+    printf("Inference ran successfully\n");
+    status = HAILO_SUCCESS;
+l_deactivate_network_group:
+    (void)hailo_deactivate_network_group(activated_network_group);
+l_release_vstreams:
+    (void)hailo_release_output_vstreams(output_vstreams, output_vstreams_size);
+    (void)hailo_release_input_vstreams(input_vstreams, input_vstreams_size);
+l_release_hef:
+    (void) hailo_release_hef(hef);
+l_release_vdevice:
+    (void) hailo_release_vdevice(vdevice);
+l_exit:
+    return status;
+}
diff --git a/hailort/libhailort/examples/c/hailo_thread.h b/hailort/libhailort/examples/c/hailo_thread.h
new file mode 100644 (file)
index 0000000..8bd6778
--- /dev/null
@@ -0,0 +1,66 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @ file hailo_thread.h
+ * Common threads related functions, for linux and windows
+ **/
+
+#ifndef _HAILO_THREAD_H_
+#define _HAILO_THREAD_H_
+
+#include "hailo/hailort.h"
+
+#if defined(__unix__) || defined(__QNX__) 
+
+#include <pthread.h>
+typedef pthread_t hailo_thread;
+typedef void* thread_return_type;
+
+hailo_status hailo_create_thread(thread_return_type(*func_ptr)(void*), void* args, hailo_thread *thread_out)
+{
+    int creation_results = pthread_create(thread_out, NULL, func_ptr, args);
+    if (0 != creation_results) {
+        return HAILO_INTERNAL_FAILURE;
+    }
+    return HAILO_SUCCESS;
+}
+
+hailo_status hailo_join_thread(hailo_thread *thread)
+{
+    void *results = NULL;
+    pthread_join(*thread, &results);
+    return (hailo_status)results;
+}
+
+#elif defined _MSC_VER // __unix__ || __QNX__
+
+#include <windows.h>
+typedef HANDLE hailo_thread;
+typedef DWORD thread_return_type;
+
+hailo_status hailo_create_thread(thread_return_type(func_ptr)(void*), void* args, hailo_thread *thread_out)
+{
+    *thread_out = CreateThread(NULL, 0, func_ptr, args, 0, NULL);
+    if (NULL == *thread_out) {
+        return HAILO_INTERNAL_FAILURE;
+    }
+    return HAILO_SUCCESS;
+}
+
+hailo_status hailo_join_thread(hailo_thread *thread)
+{
+    DWORD result;
+
+    WaitForSingleObject(*thread, INFINITE);
+    if (!GetExitCodeThread(*thread, &result)) {
+        return HAILO_INTERNAL_FAILURE;
+    }
+    CloseHandle(*thread);
+    return (hailo_status)result;
+}
+
+#endif
+
+#endif /* _HAILO_THREAD_H_ */
diff --git a/hailort/libhailort/examples/c/infer_pipeline_example.c b/hailort/libhailort/examples/c/infer_pipeline_example.c
new file mode 100644 (file)
index 0000000..fc8f7fa
--- /dev/null
@@ -0,0 +1,161 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @ file infer_pipeline_example.c
+ * This example demonstrates the basic data-path on HailoRT using the high level API - Virtual Stream Pipeline.
+ * The program scans for Hailo-8 devices connected to a provided Ethernet interface, generates a random dataset,
+ * and runs it through the device with virtual streams pipeline.
+ **/
+
+#include "common.h"
+#include "string.h"
+#include "hailo_thread.h"
+#include "hailo/hailort.h"
+
+#define MAX_NUM_OF_DEVICES (5)
+#define SCAN_TIMEOUT_MILLISECONDS (2000)
+#define INFER_FRAME_COUNT (100)
+#define MAX_EDGE_LAYERS (16)
+#define HEF_FILE ("hefs/shortcut_net.hef")
+
+#define USAGE_ERROR_MSG ("Args parsing error.\nUsage: infer_pipeline_example <interface_name>\n")
+
+hailo_status infer(hailo_configured_network_group configured_network_group,
+    hailo_input_vstream_params_by_name_t *input_params, hailo_output_vstream_params_by_name_t *output_params,
+    hailo_vstream_info_t *vstreams_infos, size_t vstreams_infos_size)
+{
+    size_t frames_count = INFER_FRAME_COUNT;
+    hailo_status status = HAILO_UNINITIALIZED;
+    hailo_stream_raw_buffer_by_name_t input_buffer = {0};
+    hailo_stream_raw_buffer_by_name_t output_buffer = {0};
+    uint8_t *src_data = NULL;
+    uint8_t *dst_data = NULL;
+    size_t frame_size = 0;
+
+
+    for (size_t i = 0; i < vstreams_infos_size; i++) {
+        if (HAILO_H2D_STREAM == vstreams_infos[i].direction) {
+            memcpy(input_buffer.name, vstreams_infos[i].name, HAILO_MAX_STREAM_NAME_SIZE);
+            status = hailo_get_vstream_frame_size(&(vstreams_infos[i]), &(vstreams_infos[i].format), &frame_size);
+            REQUIRE_SUCCESS(status, l_free_buffers, "Failed getting input virtual stream frame size");
+            input_buffer.raw_buffer.size = (frame_size * frames_count);
+            src_data = malloc(input_buffer.raw_buffer.size);
+            REQUIRE_ACTION(src_data != NULL, status = HAILO_OUT_OF_HOST_MEMORY, l_free_buffers, "Failed to allocate input buffer");
+            // Prepare src data here
+            for (size_t j = 0; j < frame_size; j++) {
+                src_data[j] = (uint8_t)(rand() % 256);
+            }
+
+            input_buffer.raw_buffer.buffer = src_data;
+        } else {
+            memcpy(output_buffer.name, vstreams_infos[i].name, HAILO_MAX_STREAM_NAME_SIZE);
+            status = hailo_get_vstream_frame_size(&(vstreams_infos[i]), &(vstreams_infos[i].format), &frame_size);
+            REQUIRE_SUCCESS(status, l_free_buffers, "Failed getting output virtual stream frame size");
+            output_buffer.raw_buffer.size = (frame_size * frames_count);
+            dst_data = malloc(output_buffer.raw_buffer.size);
+            REQUIRE_ACTION(dst_data != NULL, status = HAILO_OUT_OF_HOST_MEMORY, l_free_buffers, "Failed to allocate output buffer");
+
+            output_buffer.raw_buffer.buffer = dst_data;
+        }
+    }
+
+    status = hailo_infer(configured_network_group,
+        input_params, &input_buffer, 1,
+        output_params, &output_buffer, 1,
+        frames_count);
+    REQUIRE_SUCCESS(status, l_free_buffers, "Inference failure");
+
+l_free_buffers:
+    FREE(dst_data);
+    FREE(src_data);
+
+    return HAILO_SUCCESS;
+}
+
+void parse_arguments(int argc, char **argv, const char **interface_name)
+{
+    if (2 != argc) {
+        printf(USAGE_ERROR_MSG);
+        exit(1);
+    }
+    *interface_name = argv[1];
+}
+
+int main(int argc, char **argv)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    const char *interface_name = NULL;
+    hailo_eth_device_info_t device_infos[MAX_NUM_OF_DEVICES] = {0};
+    size_t num_of_devices = 0;
+    uint32_t timeout = SCAN_TIMEOUT_MILLISECONDS;
+    hailo_device device = NULL;
+    hailo_hef hef = NULL;
+    hailo_configure_params_t config_params = {0};
+    hailo_configured_network_group network_group = NULL;
+    size_t network_group_size = 1;
+    hailo_input_vstream_params_by_name_t input_vstream_params[MAX_EDGE_LAYERS] = {0};
+    hailo_output_vstream_params_by_name_t output_vstream_params[MAX_EDGE_LAYERS] = {0};
+    size_t input_vstreams_size = MAX_EDGE_LAYERS;
+    size_t output_vstreams_size = MAX_EDGE_LAYERS;
+    hailo_activated_network_group activated_network_group = NULL;
+    size_t vstreams_infos_size = MAX_EDGE_LAYERS;
+    hailo_vstream_info_t vstreams_infos[MAX_EDGE_LAYERS] = {0};
+
+    parse_arguments(argc, argv, &interface_name);
+
+    status = hailo_scan_ethernet_devices(interface_name, device_infos, MAX_NUM_OF_DEVICES, &num_of_devices, timeout);
+    REQUIRE_SUCCESS(status, l_exit, "Failed to scan ethernet devices");
+    REQUIRE_ACTION(num_of_devices > 0, status = HAILO_INVALID_ARGUMENT, l_exit, 
+        "Failed to find ethernet devices");
+
+    status = hailo_create_ethernet_device(&device_infos[0], &device);
+    REQUIRE_SUCCESS(status, l_exit, "Failed to create eth_device");
+
+    status = hailo_create_hef_file(&hef, HEF_FILE);
+    REQUIRE_SUCCESS(status, l_release_vdevice, "Failed reading hef file");
+
+    status = hailo_init_configure_params(hef, HAILO_STREAM_INTERFACE_ETH, &config_params);
+    REQUIRE_SUCCESS(status, l_release_hef, "Failed initializing configure parameters");
+
+    status = hailo_configure_device(device, hef, &config_params, &network_group, &network_group_size);
+    REQUIRE_SUCCESS(status, l_release_hef, "Failed configure devcie from hef");
+    REQUIRE_ACTION(network_group_size == 1, status = HAILO_INVALID_ARGUMENT, l_release_hef, 
+        "Invalid network group size");
+
+    status = hailo_make_input_vstream_params(network_group, true, HAILO_FORMAT_TYPE_AUTO,
+        input_vstream_params, &input_vstreams_size);
+    REQUIRE_SUCCESS(status, l_release_hef, "Failed making input virtual stream params");
+
+    status = hailo_make_output_vstream_params(network_group, true, HAILO_FORMAT_TYPE_AUTO,
+        output_vstream_params, &output_vstreams_size);
+    REQUIRE_SUCCESS(status, l_release_hef, "Failed making output virtual stream params");
+
+    REQUIRE_ACTION((input_vstreams_size <= MAX_EDGE_LAYERS || output_vstreams_size <= MAX_EDGE_LAYERS),
+        status = HAILO_INVALID_OPERATION, l_release_hef, "Trying to infer network with too many input/output virtual "
+        "streams, Maximum amount is %d, (either change HEF or change the definition of MAX_EDGE_LAYERS)\n",
+        MAX_EDGE_LAYERS);
+
+    status = hailo_hef_get_all_vstream_infos(hef, NULL, vstreams_infos, &vstreams_infos_size);
+    REQUIRE_SUCCESS(status, l_release_hef, "Failed getting virtual stream infos");
+    REQUIRE_ACTION(vstreams_infos_size == 2, status = HAILO_INVALID_ARGUMENT, l_release_hef, 
+        "Invalid number of virtual streams size");
+
+    status = hailo_activate_network_group(network_group, NULL, &activated_network_group);
+    REQUIRE_SUCCESS(status, l_release_hef, "Failed activate network group");
+
+    status = infer(network_group, input_vstream_params, output_vstream_params, vstreams_infos, vstreams_infos_size);
+    REQUIRE_SUCCESS(status, l_deactivate_network_group, "Failed running inference");
+
+    printf("Inference ran successfully\n");
+    status = HAILO_SUCCESS;
+l_deactivate_network_group:
+    (void)hailo_deactivate_network_group(activated_network_group);
+l_release_hef:
+    (void) hailo_release_hef(hef);
+l_release_vdevice:
+    (void) hailo_release_device(device);
+l_exit:
+    return status;
+}
diff --git a/hailort/libhailort/examples/c/multi_device_example.c b/hailort/libhailort/examples/c/multi_device_example.c
new file mode 100644 (file)
index 0000000..6dbfa27
--- /dev/null
@@ -0,0 +1,214 @@
+/**
+ * Copyright 2020 (C) Hailo Technologies Ltd.
+ * All rights reserved.
+ *
+ * Hailo Technologies Ltd. ("Hailo") disclaims any warranties, including, but not limited to,
+ * the implied warranties of merchantability and fitness for a particular purpose.
+ * This software is provided on an "AS IS" basis, and Hailo has no obligation to provide maintenance,
+ * support, updates, enhancements, or modifications.
+ *
+ * You may use this software in the development of any project.
+ * You shall not reproduce, modify or distribute this software without prior written permission.
+ **/
+/**
+ * @ file multi_device_example.c
+ * This example demonstrates how to work with multiple devices using virtual device.
+ * The program scans for Hailo-8 devices connected to a provided PCIe interface, generates random dataset,
+ * and runs it through the virtual device with virtual streams.
+ **/
+
+#include "common.h"
+#include "hailo_thread.h"
+#include "hailo/hailort.h"
+
+#define INFER_FRAME_COUNT (100)
+#define MAX_EDGE_LAYERS (16)
+#define MAX_PCIE_DEVICES (16)
+#define HEF_FILE ("hefs/shortcut_net.hef")
+
+
+thread_return_type write_to_vdevice(void *args)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    size_t input_frame_size = 0;
+    uint8_t *src_data = NULL;
+    hailo_input_vstream vstream = *(hailo_input_vstream*)args;
+
+    status = hailo_get_input_vstream_frame_size(vstream, &input_frame_size);
+    REQUIRE_SUCCESS(status, l_exit, "Failed getting input virtual stream frame size");
+
+    src_data = malloc(input_frame_size);
+    REQUIRE_ACTION(src_data != NULL, status = HAILO_OUT_OF_HOST_MEMORY, l_exit, "Failed to allocate input buffer");
+
+    // Prepare src data here
+    for (size_t i = 0; i < input_frame_size; i++) {
+        src_data[i] = (uint8_t)(rand() % 256);
+    }
+
+    for (uint32_t i = 0; i < INFER_FRAME_COUNT; i++) {
+        status = hailo_vstream_write_raw_buffer(vstream, src_data, input_frame_size);
+        REQUIRE_SUCCESS(status, l_free_buffer, "Failed sending to vstream");
+    }
+
+    // Flushing is not mandatory here
+    status = hailo_flush_input_vstream(vstream);
+    REQUIRE_SUCCESS(status, l_free_buffer, "Failed flushing vstream");
+
+    status = HAILO_SUCCESS;
+l_free_buffer:
+    FREE(src_data);
+l_exit:
+    return (thread_return_type)status;
+}
+
+thread_return_type read_from_vdevice(void *args)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    size_t output_frame_size = 0;
+    uint8_t *dst_data = NULL;
+    hailo_output_vstream vstream = *(hailo_output_vstream*)args;
+
+    status = hailo_get_output_vstream_frame_size(vstream, &output_frame_size);
+    REQUIRE_SUCCESS(status, l_exit, "Failed getting output virtual stream frame size");
+
+    dst_data = (uint8_t*)malloc(output_frame_size);
+    REQUIRE_ACTION(dst_data != NULL, status = HAILO_OUT_OF_HOST_MEMORY, l_exit, "Failed to allocate output buffer");
+
+    for (uint32_t i = 0; i < INFER_FRAME_COUNT; i++) {
+        status = hailo_vstream_read_raw_buffer(vstream, dst_data, output_frame_size);
+        REQUIRE_SUCCESS(status, l_free_buffer, "hailo_vstream_recv failed");
+    }
+
+    status = HAILO_SUCCESS;
+l_free_buffer:
+    FREE(dst_data);
+l_exit:
+    return (thread_return_type)status;
+}
+
+hailo_status infer(hailo_input_vstream *input_vstreams, size_t input_vstreams_size,
+    hailo_output_vstream *output_vstreams, size_t output_vstreams_size)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    hailo_thread write_threads[MAX_EDGE_LAYERS] = {0};
+    hailo_thread read_threads[MAX_EDGE_LAYERS] = {0};
+    hailo_status write_thread_status = HAILO_UNINITIALIZED;
+    hailo_status read_thread_status = HAILO_UNINITIALIZED;
+    size_t input_threads_index = 0;
+    size_t output_threads_index = 0;
+    size_t index = 0;
+
+    // Create reading threads
+    for (output_threads_index = 0; output_threads_index < output_vstreams_size; output_threads_index++) {
+        status = hailo_create_thread(read_from_vdevice, &output_vstreams[output_threads_index], &read_threads[output_threads_index]);
+        REQUIRE_SUCCESS(status, l_cleanup, "Failed creating thread");
+    }
+
+    // Create writing threads
+    for (input_threads_index = 0; input_threads_index < input_vstreams_size; input_threads_index++) {
+        status = hailo_create_thread(write_to_vdevice, &input_vstreams[input_threads_index], &write_threads[input_threads_index]);
+        REQUIRE_SUCCESS(status, l_cleanup, "Failed creating thread");
+    }
+
+l_cleanup:
+    // Join writing threads
+    for (index = 0; index < input_threads_index; index++) {
+        write_thread_status = hailo_join_thread(&write_threads[index]);
+        if (HAILO_SUCCESS != write_thread_status) {
+            printf("write_thread failed \n");
+            status = write_thread_status; // Override write status
+        }
+    }
+
+    // Join reading threads
+    for (index = 0; index < output_threads_index; index++) {
+        read_thread_status = hailo_join_thread(&read_threads[index]);
+        if (HAILO_SUCCESS != read_thread_status) {
+            printf("read_thread failed \n");
+            status = read_thread_status; // Override read status
+        }
+    }
+
+    return status;
+}
+
+int main()
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    hailo_vdevice vdevice = NULL;
+    hailo_pcie_device_info_t device_infos[MAX_PCIE_DEVICES];
+    size_t actual_count = 0;
+    hailo_vdevice_params_t params = {0};
+    hailo_hef hef = NULL;
+    hailo_configure_params_t config_params = {0};
+    hailo_configured_network_group network_group = NULL;
+    size_t network_group_size = 1;
+    hailo_input_vstream_params_by_name_t input_vstream_params[MAX_EDGE_LAYERS] = {0};
+    hailo_output_vstream_params_by_name_t output_vstream_params[MAX_EDGE_LAYERS] = {0};
+    size_t input_vstreams_size = MAX_EDGE_LAYERS;
+    size_t output_vstreams_size = MAX_EDGE_LAYERS;
+    hailo_activated_network_group activated_network_group = NULL;
+    hailo_input_vstream input_vstreams[MAX_EDGE_LAYERS] = {NULL};
+    hailo_output_vstream output_vstreams[MAX_EDGE_LAYERS] = {NULL};
+
+    status = hailo_scan_pcie_devices(device_infos, MAX_PCIE_DEVICES, &actual_count);
+    REQUIRE_SUCCESS(status, l_exit, "Failed to scan pcie_device");
+
+    status = hailo_init_vdevice_params(&params);
+    REQUIRE_SUCCESS(status, l_exit, "Failed init vdevice_params");
+
+    params.device_count = (uint32_t)actual_count;
+    status = hailo_create_vdevice(&params, &vdevice);
+    REQUIRE_SUCCESS(status, l_exit, "Failed to create vdevice");
+
+    status = hailo_create_hef_file(&hef, HEF_FILE);
+    REQUIRE_SUCCESS(status, l_release_vdevice, "Failed reading hef file");
+
+    status = hailo_init_configure_params(hef, HAILO_STREAM_INTERFACE_PCIE, &config_params);
+    REQUIRE_SUCCESS(status, l_release_hef, "Failed initializing configure parameters");
+
+    status = hailo_configure_vdevice(vdevice, hef, &config_params, &network_group, &network_group_size);
+    REQUIRE_SUCCESS(status, l_release_hef, "Failed configure vdevcie from hef");
+    REQUIRE_ACTION(network_group_size == 1, status = HAILO_INVALID_ARGUMENT, l_release_hef, 
+        "Invalid network group size");
+
+    status = hailo_make_input_vstream_params(network_group, true, HAILO_FORMAT_TYPE_AUTO,
+        input_vstream_params, &input_vstreams_size);
+    REQUIRE_SUCCESS(status, l_release_hef, "Failed making input virtual stream params");
+
+    status = hailo_make_output_vstream_params(network_group, true, HAILO_FORMAT_TYPE_AUTO,
+        output_vstream_params, &output_vstreams_size);
+    REQUIRE_SUCCESS(status, l_release_hef, "Failed making output virtual stream params");
+
+    REQUIRE_ACTION((input_vstreams_size <= MAX_EDGE_LAYERS || output_vstreams_size <= MAX_EDGE_LAYERS),
+        status = HAILO_INVALID_OPERATION, l_release_hef, "Trying to infer network with too many input/output virtual "
+        "streams, Maximum amount is %d, (either change HEF or change the definition of MAX_EDGE_LAYERS)\n",
+        MAX_EDGE_LAYERS);
+
+    status = hailo_create_input_vstreams(network_group, input_vstream_params, input_vstreams_size, input_vstreams);
+    REQUIRE_SUCCESS(status, l_release_hef, "Failed creating virtual input streams\n");
+
+    status = hailo_create_output_vstreams(network_group, output_vstream_params, output_vstreams_size, output_vstreams);
+    REQUIRE_SUCCESS(status, l_release_input_vstream, "Failed creating output virtual streams\n");
+
+    status = hailo_activate_network_group(network_group, NULL, &activated_network_group);
+    REQUIRE_SUCCESS(status, l_release_output_vstream, "Failed activate network group");
+
+    status = infer(input_vstreams, input_vstreams_size, output_vstreams, output_vstreams_size);
+    REQUIRE_SUCCESS(status, l_deactivate_network_group, "Inference failure");
+
+    printf("Inference ran successfully\n");
+    status = HAILO_SUCCESS;
+l_deactivate_network_group:
+    (void)hailo_deactivate_network_group(activated_network_group);
+l_release_output_vstream:
+    (void)hailo_release_output_vstreams(output_vstreams, output_vstreams_size);
+l_release_input_vstream:
+    (void)hailo_release_input_vstreams(input_vstreams, input_vstreams_size);
+l_release_hef:
+    (void) hailo_release_hef(hef);
+l_release_vdevice:
+    (void) hailo_release_vdevice(vdevice);
+l_exit:
+    return status;
+}
diff --git a/hailort/libhailort/examples/c/multi_network_vstream_example.c b/hailort/libhailort/examples/c/multi_network_vstream_example.c
new file mode 100644 (file)
index 0000000..09d26c9
--- /dev/null
@@ -0,0 +1,255 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file multi_network_vstream_example.c
+ * @brief This example demonstrates how to work with multiple networks in a network group, using virtual streams.
+ **/
+
+#include "common.h"
+#include "hailo_thread.h"
+#include "hailo/hailort.h"
+
+#define INFER_FRAME_COUNT (100)
+#define MAX_EDGE_LAYERS (16)
+#define NET_GROUPS_COUNT (1)
+#define NET_COUNT (2)
+#define FIRST_NET_BATCH_SIZE (1)
+#define SECOND_NET_BATCH_SIZE (2)
+#define DEVICE_COUNT (1)
+#define HEF_FILE ("hefs/multi_network_shortcut_net.hef")
+
+typedef struct {
+    hailo_input_vstream vstream;
+    uint16_t batch_size;
+} write_args_t;
+
+typedef struct {
+    hailo_output_vstream vstream;
+    uint16_t batch_size;
+} read_args_t;
+
+thread_return_type write_to_device(void *args)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    size_t input_frame_size = 0;
+    uint8_t *src_data = NULL;
+    write_args_t write_args = *(write_args_t*)args;
+    hailo_input_vstream vstream = write_args.vstream;
+    size_t batch_size = write_args.batch_size;
+
+    status = hailo_get_input_vstream_frame_size(vstream, &input_frame_size);
+    REQUIRE_SUCCESS(status, l_exit, "Failed getting input virtual stream frame size");
+
+    src_data = malloc(input_frame_size);
+    REQUIRE_ACTION(src_data != NULL, status = HAILO_OUT_OF_HOST_MEMORY, l_exit, "Failed to allocate input buffer");
+
+    // Prepare src data here
+    for (size_t i = 0; i < input_frame_size; i++) {
+        src_data[i] = (uint8_t)(rand() % 256);
+    }
+
+    const size_t network_frames_count = INFER_FRAME_COUNT * batch_size;
+    for (uint32_t i = 0; i < network_frames_count; i++) {
+        status = hailo_vstream_write_raw_buffer(vstream, src_data, input_frame_size);
+        REQUIRE_SUCCESS(status, l_free_buffer, "Failed sending to vstream");
+    }
+
+    status = HAILO_SUCCESS;
+l_free_buffer:
+    FREE(src_data);
+l_exit:
+    return (thread_return_type)status;
+}
+
+thread_return_type read_from_device(void *args)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    size_t output_frame_size = 0;
+    uint8_t *dst_data = NULL;
+    read_args_t read_args = *(read_args_t*)args;
+    hailo_output_vstream vstream = read_args.vstream;
+    size_t batch_size = read_args.batch_size;
+
+    status = hailo_get_output_vstream_frame_size(vstream, &output_frame_size);
+    REQUIRE_SUCCESS(status, l_exit, "Failed getting output virtual stream frame size");
+
+    dst_data = (uint8_t*)malloc(output_frame_size);
+    REQUIRE_ACTION(dst_data != NULL, status = HAILO_OUT_OF_HOST_MEMORY, l_exit, "Failed to allocate output buffer");
+
+    const size_t network_frames_count = INFER_FRAME_COUNT * batch_size;
+    for (uint32_t i = 0; i < network_frames_count; i++) {
+        status = hailo_vstream_read_raw_buffer(vstream, dst_data, output_frame_size);
+        REQUIRE_SUCCESS(status, l_free_buffer, "hailo_vstream_recv failed");
+    }
+
+    status = HAILO_SUCCESS;
+l_free_buffer:
+    FREE(dst_data);
+l_exit:
+    return (thread_return_type)status;
+}
+
+hailo_status infer(hailo_input_vstream input_vstreams[NET_COUNT][MAX_EDGE_LAYERS], size_t *input_vstreams_size,
+    hailo_output_vstream output_vstreams[NET_COUNT][MAX_EDGE_LAYERS], size_t *output_vstreams_size, uint16_t *batch_size)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    hailo_thread write_threads[NET_COUNT][MAX_EDGE_LAYERS] = {0};
+    hailo_thread read_threads[NET_COUNT][MAX_EDGE_LAYERS] = {0};
+    write_args_t write_args[NET_COUNT][MAX_EDGE_LAYERS] = {0};
+    read_args_t read_args[NET_COUNT][MAX_EDGE_LAYERS] = {0};
+    hailo_status write_thread_status = HAILO_UNINITIALIZED;
+    hailo_status read_thread_status = HAILO_UNINITIALIZED;
+
+    for (size_t net_index = 0; net_index < NET_COUNT; net_index++) {
+        // Create reading threads
+        for (size_t vstream_index = 0; vstream_index < output_vstreams_size[net_index]; vstream_index++) {
+            read_args[net_index][vstream_index].vstream = output_vstreams[net_index][vstream_index];
+            read_args[net_index][vstream_index].batch_size = batch_size[net_index];
+
+            status = hailo_create_thread(read_from_device, &read_args[net_index][vstream_index], &read_threads[net_index][vstream_index]);
+            REQUIRE_SUCCESS(status, l_cleanup, "Failed creating thread");
+        }
+
+        // Create writing threads
+        for (size_t vstream_index = 0; vstream_index < input_vstreams_size[net_index]; vstream_index++) {
+            write_args[net_index][vstream_index].vstream = input_vstreams[net_index][vstream_index];
+            write_args[net_index][vstream_index].batch_size = batch_size[net_index];
+
+            status = hailo_create_thread(write_to_device, &write_args[net_index][vstream_index], &write_threads[net_index][vstream_index]);
+            REQUIRE_SUCCESS(status, l_cleanup, "Failed creating thread");
+        }
+    }
+
+l_cleanup:
+    // Join writing threads
+    for (size_t net_index = 0; net_index < NET_COUNT; net_index++) {
+        for (size_t vstream_index = 0; vstream_index < input_vstreams_size[net_index]; vstream_index++) {
+            write_thread_status = hailo_join_thread(&write_threads[net_index][vstream_index]);
+            if (HAILO_SUCCESS != write_thread_status) {
+                printf("write_thread failed \n");
+                status = write_thread_status; // Override write status
+            }
+        }
+    }
+
+    // Join reading threads
+    for (size_t net_index = 0; net_index < NET_COUNT; net_index++) {
+        for (size_t vstream_index = 0; vstream_index < output_vstreams_size[net_index]; vstream_index++) {
+            read_thread_status = hailo_join_thread(&read_threads[net_index][vstream_index]);
+            if (HAILO_SUCCESS != read_thread_status) {
+                printf("read_thread failed \n");
+                status = read_thread_status; // Override read status
+            }
+        }
+    }
+
+    return status;
+}
+
+int main()
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    hailo_vdevice vdevice = NULL;
+    hailo_vdevice_params_t params = {0};
+    hailo_hef hef = NULL;
+    hailo_configure_params_t config_params = {0};
+    size_t network_groups_count = NET_GROUPS_COUNT;
+    size_t networks_count = NET_COUNT;
+    hailo_configured_network_group network_group[NET_GROUPS_COUNT] = {NULL};
+    hailo_network_info_t network_info[NET_COUNT] = {0};
+    hailo_input_vstream_params_by_name_t input_vstream_params[NET_COUNT][MAX_EDGE_LAYERS] = {0};
+    hailo_output_vstream_params_by_name_t output_vstream_params[NET_COUNT][MAX_EDGE_LAYERS] = {0};
+    size_t input_vstreams_size[NET_COUNT] = {MAX_EDGE_LAYERS, MAX_EDGE_LAYERS};
+    size_t output_vstreams_size[NET_COUNT] = {MAX_EDGE_LAYERS, MAX_EDGE_LAYERS};
+    hailo_activated_network_group activated_network_group = NULL;
+    hailo_input_vstream input_vstreams[NET_COUNT][MAX_EDGE_LAYERS];
+    hailo_output_vstream output_vstreams[NET_COUNT][MAX_EDGE_LAYERS];
+    uint16_t batch_size[NET_COUNT] = {FIRST_NET_BATCH_SIZE, SECOND_NET_BATCH_SIZE};
+
+    status = hailo_init_vdevice_params(&params);
+    REQUIRE_SUCCESS(status, l_exit, "Failed init vdevice_params");
+
+    params.device_count = DEVICE_COUNT;
+    status = hailo_create_vdevice(&params, &vdevice);
+    REQUIRE_SUCCESS(status, l_exit, "Failed to create vdevice");
+
+    status = hailo_create_hef_file(&hef, HEF_FILE);
+    REQUIRE_SUCCESS(status, l_release_vdevice, "Failed reading hef file");
+
+    status = hailo_init_configure_params(hef, HAILO_STREAM_INTERFACE_PCIE, &config_params);
+    REQUIRE_SUCCESS(status, l_release_hef, "Failed initializing configure parameters");
+
+    // Modify batch_size for each network
+    for (uint8_t network_index = 0; network_index < NET_COUNT; network_index++) {
+        config_params.network_group_params[0].network_params_by_name[network_index].network_params.batch_size =
+            batch_size[network_index];
+    }
+
+    status = hailo_configure_vdevice(vdevice, hef, &config_params, network_group, &network_groups_count);
+    REQUIRE_SUCCESS(status, l_release_hef, "Failed configure vdevcie from hef");
+    REQUIRE_ACTION(NET_GROUPS_COUNT == network_groups_count, status = HAILO_INVALID_ARGUMENT, l_release_hef, 
+        "Invalid network group count");
+
+    status = hailo_get_network_infos(network_group[0], network_info, &networks_count);
+    REQUIRE_SUCCESS(status, l_release_hef, "Failed to get network groups infos");
+    REQUIRE_ACTION(NET_COUNT == networks_count, status = HAILO_INVALID_ARGUMENT, l_release_hef, 
+        "Invalid networks count");
+
+    /* Build vstream params per network */
+    for (uint8_t network_index = 0; network_index < NET_COUNT; network_index++) {
+        status = hailo_hef_make_input_vstream_params(hef, network_info[network_index].name, true, HAILO_FORMAT_TYPE_AUTO,
+            input_vstream_params[network_index], &input_vstreams_size[network_index]);
+        REQUIRE_SUCCESS(status, l_release_hef, "Failed making input virtual stream params");
+
+        status = hailo_hef_make_output_vstream_params(hef, network_info[network_index].name, true, HAILO_FORMAT_TYPE_AUTO,
+            output_vstream_params[network_index], &output_vstreams_size[network_index]);
+        REQUIRE_SUCCESS(status, l_release_hef, "Failed making output virtual stream params");
+
+        REQUIRE_ACTION((input_vstreams_size[network_index] <= MAX_EDGE_LAYERS ||
+            output_vstreams_size[network_index] <= MAX_EDGE_LAYERS), status = HAILO_INVALID_OPERATION, l_release_hef,
+            "Trying to infer network with too many input/output virtual streams, "
+            "Maximum amount is %d, (either change HEF or change the definition of MAX_EDGE_LAYERS)\n",
+            MAX_EDGE_LAYERS);
+    }
+
+    /* Build vstreams per network */
+    for (uint8_t network_index = 0; network_index < NET_COUNT; network_index++) {
+        status = hailo_create_input_vstreams(network_group[0], input_vstream_params[network_index],
+            input_vstreams_size[network_index], input_vstreams[network_index]);
+        REQUIRE_SUCCESS(status, l_release_vstreams, "Failed creating input virtual streams\n");
+
+        status = hailo_create_output_vstreams(network_group[0], output_vstream_params[network_index],
+            output_vstreams_size[network_index], output_vstreams[network_index]);
+        REQUIRE_SUCCESS(status, l_release_vstreams, "Failed creating output virtual streams\n");
+    }
+
+    status = hailo_activate_network_group(network_group[0], NULL, &activated_network_group);
+    REQUIRE_SUCCESS(status, l_release_vstreams, "Failed activate network group");
+
+    status = infer(input_vstreams, input_vstreams_size, output_vstreams, output_vstreams_size, batch_size);
+    REQUIRE_SUCCESS(status, l_deactivate_network_group, "Inference failure");
+
+    printf("Inference ran successfully\n");
+    status = HAILO_SUCCESS;
+l_deactivate_network_group:
+    (void)hailo_deactivate_network_group(activated_network_group);
+l_release_vstreams:
+    for (size_t net_index = 0; net_index < NET_COUNT; net_index++) {
+        if (NULL != output_vstreams[net_index]) {
+            (void)hailo_release_output_vstreams(output_vstreams[net_index], output_vstreams_size[net_index]);
+        }
+    }
+    for (size_t net_index = 0; net_index < NET_COUNT; net_index++) {
+        if (NULL != input_vstreams[net_index]) {
+            (void)hailo_release_input_vstreams(input_vstreams[net_index], input_vstreams_size[net_index]);
+        }
+    }
+l_release_hef:
+    (void) hailo_release_hef(hef);
+l_release_vdevice:
+    (void) hailo_release_vdevice(vdevice);
+l_exit:
+    return status;
+}
diff --git a/hailort/libhailort/examples/c/raw_streams_example.c b/hailort/libhailort/examples/c/raw_streams_example.c
new file mode 100644 (file)
index 0000000..84f48b6
--- /dev/null
@@ -0,0 +1,235 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @ file raw_streams_example.c
+ * This example demonstrates basic usage of HailoRT streaming api.
+ * It loads an HEF network with multiple inputs and multiple outputs into a Hailo PCIe device and performs a
+ * short inference.
+ **/
+
+#include "common.h"
+#include "hailo_thread.h"
+#include "hailo/hailort.h"
+
+#define HEF_FILE ("hefs/shortcut_net.hef")
+#define INFER_FRAME_COUNT (200)
+#define MAX_EDGE_LAYERS (16)
+
+typedef struct write_thread_args_t {
+    hailo_input_stream *input_stream;
+    hailo_stream_info_t *input_stream_info;
+} write_thread_args_t;
+
+typedef struct read_thread_args_t {
+    hailo_output_stream *output_stream;
+    hailo_stream_info_t *output_stream_info;
+} read_thread_args_t;
+
+thread_return_type write_to_device(void *args)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    hailo_transform_params_t transform_params = HAILO_DEFAULT_TRANSFORM_PARAMS;
+    hailo_input_transform_context transform_context = NULL;
+    uint8_t *hw_data = NULL;
+    uint8_t *host_data = NULL;
+    size_t host_frame_size = 0;
+    write_thread_args_t *write_args = (write_thread_args_t*)args; 
+
+    status = hailo_create_input_transform_context(write_args->input_stream_info, &transform_params, &transform_context);
+    REQUIRE_SUCCESS(status, l_exit, "Failed creating input transform_context");
+
+    hw_data = (uint8_t*)malloc(write_args->input_stream_info->hw_frame_size);
+    host_frame_size = hailo_get_host_frame_size(write_args->input_stream_info, &transform_params);
+    host_data = (uint8_t*)malloc(host_frame_size);
+    REQUIRE_ACTION((NULL != hw_data) && (NULL != host_data), status = HAILO_OUT_OF_HOST_MEMORY,
+        l_cleanup, "Out of memory");
+
+    // Prepare src data here
+    for (size_t i = 0; i < host_frame_size; i++) {
+        host_data[i] = (uint8_t)(rand() % 256);
+    }
+
+    for (uint32_t i = 0; i < INFER_FRAME_COUNT; i++) {
+        // Transform + Write
+        status = hailo_transform_frame_by_input_transform_context(transform_context, host_data, host_frame_size,
+            hw_data, write_args->input_stream_info->hw_frame_size);
+        REQUIRE_SUCCESS(status, l_cleanup, "Failed transforming input frame");
+
+        status = hailo_stream_write_raw_buffer(*(write_args->input_stream), hw_data,
+            write_args->input_stream_info->hw_frame_size);
+        REQUIRE_SUCCESS(status, l_cleanup, "Failed writing input frame to device");
+    }
+
+l_cleanup:
+    FREE(host_data);
+    FREE(hw_data);
+    hailo_release_input_transform_context(transform_context);
+        
+l_exit:
+    return (thread_return_type)status;
+}
+
+thread_return_type read_from_device(void *args)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    hailo_transform_params_t transform_params = HAILO_DEFAULT_TRANSFORM_PARAMS;
+    hailo_output_transform_context transform_context = NULL;
+    uint8_t *hw_data = NULL;
+    uint8_t *host_data = NULL;
+    size_t host_frame_size = 0;
+    read_thread_args_t *read_args = (read_thread_args_t*)args; 
+
+    status = hailo_create_output_transform_context(read_args->output_stream_info, &transform_params, &transform_context);
+    REQUIRE_SUCCESS(status, l_exit, "Failed creating output transform_context");
+
+    hw_data = (uint8_t*)malloc(read_args->output_stream_info->hw_frame_size);
+    host_frame_size = hailo_get_host_frame_size(read_args->output_stream_info, &transform_params);
+    host_data = (uint8_t*)malloc(host_frame_size);
+    REQUIRE_ACTION((NULL != hw_data) && (NULL != host_data), status = HAILO_OUT_OF_HOST_MEMORY,
+        l_cleanup, "Out of memory");
+
+    for (uint32_t i = 0; i < INFER_FRAME_COUNT; i++) {
+        // Read + Transform
+        status = hailo_stream_read_raw_buffer(*(read_args->output_stream), hw_data,
+            read_args->output_stream_info->hw_frame_size);
+        REQUIRE_SUCCESS(status, l_cleanup, "Failed reading output frame from device");
+
+        status = hailo_transform_frame_by_output_transform_context(transform_context, hw_data,
+            read_args->output_stream_info->hw_frame_size, host_data, host_frame_size);
+        REQUIRE_SUCCESS(status, l_cleanup, "Failed transforming output frame");
+
+        // Process data here
+    }
+
+l_cleanup:
+    FREE(host_data);
+    FREE(hw_data);
+    hailo_release_output_transform_context(transform_context);
+l_exit:
+    return (thread_return_type)status;
+}
+
+hailo_status infer(hailo_input_stream *input_streams, hailo_stream_info_t *input_streams_info,
+    size_t number_input_streams,hailo_output_stream *output_streams, hailo_stream_info_t *output_streams_info, 
+    size_t number_output_streams)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    hailo_thread write_threads [MAX_EDGE_LAYERS] = {0};
+    hailo_thread read_threads [MAX_EDGE_LAYERS] = {0};
+    write_thread_args_t write_thread_args [MAX_EDGE_LAYERS] = {0};
+    read_thread_args_t read_thread_args [MAX_EDGE_LAYERS] = {0};
+    hailo_status write_thread_status = HAILO_UNINITIALIZED;
+    hailo_status read_thread_status = HAILO_UNINITIALIZED;
+    size_t input_threads_index = 0;
+    size_t output_threads_index = 0;
+    size_t index = 0;
+
+    // Run read threads
+    for (output_threads_index = 0; output_threads_index < number_output_streams; output_threads_index++) {
+        read_thread_args[output_threads_index].output_stream = &output_streams[output_threads_index];
+        read_thread_args[output_threads_index].output_stream_info = &output_streams_info[output_threads_index];
+
+        status = hailo_create_thread(read_from_device, &read_thread_args[output_threads_index],
+            &read_threads[output_threads_index]);
+        REQUIRE_SUCCESS(status, l_cleanup, "Failed creating thread");
+
+    }
+
+    // Run write threads
+    for (input_threads_index = 0; input_threads_index < number_input_streams; input_threads_index++) {
+        write_thread_args[input_threads_index].input_stream = &input_streams[input_threads_index];
+        write_thread_args[input_threads_index].input_stream_info = &input_streams_info[input_threads_index];
+
+        status = hailo_create_thread(write_to_device, &write_thread_args[input_threads_index],
+            &write_threads[input_threads_index]);
+        REQUIRE_SUCCESS(status, l_cleanup, "Failed creating thread");
+    }
+
+l_cleanup:
+    // Join write threads
+    for (index = 0; index < input_threads_index; index++) {
+        write_thread_status = hailo_join_thread(&write_threads[index]);
+        if (HAILO_SUCCESS != write_thread_status) {
+            status = write_thread_status; // Override write status
+        }
+    }
+
+    // Join read threads
+    for (index = 0; index < output_threads_index; index++) {
+        read_thread_status = hailo_join_thread(&read_threads[index]);
+        if (HAILO_SUCCESS != read_thread_status) {
+            status = read_thread_status; // Override read status
+        }
+    }
+
+    return status;
+}
+
+int main()
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    hailo_device device = NULL;
+    hailo_hef hef = NULL;
+    hailo_configure_params_t configure_params = {0};
+    hailo_configured_network_group network_group = NULL;
+    size_t network_group_size = 1;
+    hailo_activated_network_group activated_network_group = NULL;
+    hailo_stream_info_t input_streams_info [MAX_EDGE_LAYERS] = {0};
+    hailo_stream_info_t output_streams_info [MAX_EDGE_LAYERS] = {0};
+    hailo_input_stream input_streams [MAX_EDGE_LAYERS] = {NULL};
+    hailo_output_stream output_streams [MAX_EDGE_LAYERS] = {NULL};
+    size_t number_input_streams = 0;
+    size_t number_output_streams = 0;
+    size_t index = 0;
+
+    status = hailo_create_pcie_device(NULL, &device);
+    REQUIRE_SUCCESS(status, l_exit, "Failed to create pcie_device");
+
+    status = hailo_create_hef_file(&hef, HEF_FILE);
+    REQUIRE_SUCCESS(status, l_release_device, "Failed creating hef file %s", HEF_FILE);
+
+    status = hailo_init_configure_params(hef, HAILO_STREAM_INTERFACE_PCIE, &configure_params);
+    REQUIRE_SUCCESS(status, l_release_hef, "Failed init configure params");
+
+    status = hailo_configure_device(device, hef, &configure_params, &network_group, &network_group_size);
+    REQUIRE_SUCCESS(status, l_release_hef, "Failed configuring devcie");
+    REQUIRE_ACTION(network_group_size == 1, status = HAILO_INVALID_ARGUMENT, l_release_hef, 
+        "Unexpected network group size");
+
+    status = hailo_network_group_get_input_stream_infos(network_group, input_streams_info, MAX_EDGE_LAYERS,
+        &number_input_streams);
+    REQUIRE_SUCCESS(status, l_release_hef, "Failed getting input streams infos");
+
+    status = hailo_network_group_get_output_stream_infos(network_group, output_streams_info, MAX_EDGE_LAYERS,
+        &number_output_streams);
+    REQUIRE_SUCCESS(status, l_release_hef, "Failed getting output streams infos");
+
+    for (index = 0; index < number_input_streams; index++) {
+        status = hailo_get_input_stream(network_group, input_streams_info[index].name, &input_streams[index]);
+        REQUIRE_SUCCESS(status, l_release_hef, "Failed getting input stream %s", input_streams_info[index].name);
+    }
+
+    for (index = 0; index < number_output_streams; index++) {
+        status = hailo_get_output_stream(network_group, output_streams_info[index].name, &output_streams[index]);
+        REQUIRE_SUCCESS(status, l_release_hef, "Failed getting output stream %s", output_streams_info[index].name);
+    }
+
+    status = hailo_activate_network_group(network_group, NULL, &activated_network_group);
+    REQUIRE_SUCCESS(status, l_release_hef, "Failed activate network group");
+
+    status = infer(input_streams, input_streams_info, number_input_streams, output_streams, output_streams_info,
+        number_output_streams);
+    REQUIRE_SUCCESS(status, l_deactivate_network_group, "Failed performing inference");
+    printf("Inference ran successfully\n");
+
+l_deactivate_network_group:
+    (void)hailo_deactivate_network_group(activated_network_group);
+l_release_hef:
+    (void) hailo_release_hef(hef);
+l_release_device:
+    (void) hailo_release_device(device);
+l_exit:
+    return status;
+}
diff --git a/hailort/libhailort/examples/c/switch_hefs_example.c b/hailort/libhailort/examples/c/switch_hefs_example.c
new file mode 100644 (file)
index 0000000..336d3a3
--- /dev/null
@@ -0,0 +1,298 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @ file switch_hefs_example.c
+ * This example demonstrates basic usage of HailoRT streaming api over multiple network groups, using vstreams.
+ * It loads several HEF networks with single/multiple inputs and single/multiple outputs into a Hailo PCIe VDevice and performs a
+ * short inference on each one. 
+ * After inference is finished, the example switches to the next HEF and start inference again.
+ **/
+
+#include "common.h"
+#include "hailo_thread.h"
+#include "hailo/hailort.h"
+#include <time.h>
+
+#define MAX_HEF_PATH_LEN (255)
+#define MAX_EDGE_LAYERS (16)
+
+#define INFER_FRAME_COUNT (100)
+#define HEF_COUNT (2)
+#define RUN_COUNT (10)
+#define DEVICE_COUNT (1)
+
+typedef struct write_thread_args_t {
+    hailo_input_vstream input_vstream;
+    uint8_t *src_data;
+    size_t src_frame_size;
+} write_thread_args_t;
+
+typedef struct read_thread_args_t {
+    hailo_output_vstream output_vstream;
+    uint8_t *dst_data;
+    size_t dst_frame_size;
+} read_thread_args_t;
+
+thread_return_type write_to_device(void *args)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    write_thread_args_t *write_args = (write_thread_args_t*)args;
+
+    for (uint32_t frame = 0; frame < INFER_FRAME_COUNT; frame++) {
+        // Write data
+        status = hailo_vstream_write_raw_buffer(write_args->input_vstream, write_args->src_data, write_args->src_frame_size);
+        REQUIRE_SUCCESS(status, l_exit, "Failed writing input frame to device");
+    }
+
+    status = HAILO_SUCCESS;
+l_exit:
+    return (thread_return_type)status;
+}
+
+thread_return_type read_from_device(void *args)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    read_thread_args_t *read_args = (read_thread_args_t*)args;
+
+    for (uint32_t i = 0; i < INFER_FRAME_COUNT; i++) {
+        // Read data
+        status = hailo_vstream_read_raw_buffer(read_args->output_vstream, read_args->dst_data, read_args->dst_frame_size);
+        REQUIRE_SUCCESS(status, l_exit, "Failed reading output frame from device");
+
+        // Process data here
+    }
+
+    status = HAILO_SUCCESS;
+l_exit:
+    return (thread_return_type)status;
+}
+
+hailo_status create_input_vstream_thread(hailo_input_vstream input_vstream, uint8_t *src_data, size_t src_frame_size,
+    hailo_thread *input_thread, write_thread_args_t *write_args)
+{
+    write_args->src_data = src_data;
+    write_args->src_frame_size = src_frame_size;
+    write_args->input_vstream = input_vstream;
+
+    // Run write
+    return hailo_create_thread(write_to_device, write_args, input_thread);
+}
+
+hailo_status create_output_vstream_thread(hailo_output_vstream output_vstream, uint8_t *dst_data, size_t dst_frame_size,
+    hailo_thread *output_thread, read_thread_args_t *read_args)
+{
+    read_args->dst_data = dst_data;
+    read_args->dst_frame_size = dst_frame_size;
+    read_args->output_vstream = output_vstream;
+
+    // Run read
+    return hailo_create_thread(read_from_device, read_args, output_thread);
+}
+
+hailo_status build_vstreams(hailo_configured_network_group network_group,
+    hailo_input_vstream *input_vstreams, size_t *input_frame_sizes, uint8_t **src_data,
+    hailo_output_vstream *output_vstreams, size_t *output_frame_sizes, uint8_t **dst_data,
+    size_t *num_input_vstreams, size_t *num_output_vstreams)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    hailo_input_vstream_params_by_name_t input_vstream_params[MAX_EDGE_LAYERS];
+    hailo_output_vstream_params_by_name_t output_vstream_params[MAX_EDGE_LAYERS];
+
+    // Make sure it can hold amount of vstreams for hailo_make_input/output_vstream_params
+    size_t input_vstream_size = MAX_EDGE_LAYERS;
+    size_t output_vstream_size = MAX_EDGE_LAYERS;
+
+    status = hailo_make_input_vstream_params(network_group, true, HAILO_FORMAT_TYPE_AUTO,
+        input_vstream_params, &input_vstream_size);
+    REQUIRE_SUCCESS(status, l_exit, "Failed making input virtual stream params");
+    *num_input_vstreams = input_vstream_size;
+
+    status = hailo_make_output_vstream_params(network_group, true, HAILO_FORMAT_TYPE_AUTO,
+        output_vstream_params, &output_vstream_size);
+    REQUIRE_SUCCESS(status, l_exit, "Failed making output virtual stream params");
+    *num_output_vstreams = output_vstream_size;
+
+    REQUIRE_ACTION((*num_input_vstreams <= MAX_EDGE_LAYERS || *num_output_vstreams <= MAX_EDGE_LAYERS),
+        status = HAILO_INVALID_OPERATION, l_exit, "Trying to infer network with too many input/output virtual streams, "
+        "Maximum amount is %d, (either change HEF or change the definition of MAX_EDGE_LAYERS)\n", MAX_EDGE_LAYERS);
+
+    status = hailo_create_input_vstreams(network_group, input_vstream_params, input_vstream_size, input_vstreams);
+    REQUIRE_SUCCESS(status, l_exit, "Failed creating virtual stream");
+
+    status = hailo_create_output_vstreams(network_group, output_vstream_params, output_vstream_size, output_vstreams);
+    REQUIRE_SUCCESS(status, l_release_input_vstream, "Failed creating virtual stream");
+
+    for (size_t i = 0; i < input_vstream_size; i++) {
+        status = hailo_get_input_vstream_frame_size(input_vstreams[i], &input_frame_sizes[i]);
+        REQUIRE_SUCCESS(status, l_clear_buffers, "Failed getting input virtual stream frame size");
+
+        src_data[i] = (uint8_t*)malloc(input_frame_sizes[i]);
+        REQUIRE_ACTION(NULL != src_data[i], status = HAILO_OUT_OF_HOST_MEMORY, l_clear_buffers, "Out of memory");
+
+        // Prepare data here
+        for (size_t frame_index = 0; frame_index < input_frame_sizes[i]; frame_index++) {
+            src_data[i][frame_index] = (uint8_t)(rand() % 256);
+        }
+    }  
+
+    for (size_t i = 0; i < output_vstream_size; i++) {
+        status = hailo_get_output_vstream_frame_size(output_vstreams[i], &output_frame_sizes[i]);
+        REQUIRE_SUCCESS(status, l_clear_buffers, "Failed getting input virtual stream frame size");
+
+        dst_data[i] = (uint8_t*)malloc(output_frame_sizes[i]);
+        REQUIRE_ACTION(NULL != dst_data[i], status = HAILO_OUT_OF_HOST_MEMORY, l_clear_buffers, "Out of memory");
+    }
+
+    status = HAILO_SUCCESS;
+    goto l_exit;
+
+l_clear_buffers:
+    for (size_t i = 0; i < input_vstream_size; i++) {
+        FREE(src_data[i]);
+    }
+    for (size_t i = 0; i < output_vstream_size; i++) {
+        FREE(dst_data[i]);
+    }
+
+    (void)hailo_release_output_vstreams(output_vstreams, output_vstream_size);
+l_release_input_vstream:
+    (void)hailo_release_input_vstreams(input_vstreams, input_vstream_size);
+l_exit:
+    return status;
+}
+
+int main()
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    hailo_vdevice vdevice = NULL;
+    hailo_vdevice_params_t params = {0};
+    hailo_hef hef[HEF_COUNT] = {NULL};
+    hailo_configure_params_t configure_params = {0};
+    hailo_configured_network_group network_groups[HEF_COUNT] = {NULL};
+    size_t network_groups_size = 1;
+    hailo_activated_network_group activated_network_group = NULL;
+    hailo_input_vstream input_vstreams[HEF_COUNT][MAX_EDGE_LAYERS];
+    hailo_output_vstream output_vstreams[HEF_COUNT][MAX_EDGE_LAYERS];
+    size_t input_frame_size[HEF_COUNT][MAX_EDGE_LAYERS];
+    size_t output_frame_size[HEF_COUNT][MAX_EDGE_LAYERS];
+    // Initialize 2d array to all NULL
+    uint8_t *src_data[HEF_COUNT][MAX_EDGE_LAYERS];
+    uint8_t *dst_data[HEF_COUNT][MAX_EDGE_LAYERS];
+    size_t num_input_vstreams[HEF_COUNT];
+    size_t num_output_vstreams[HEF_COUNT];
+    uint8_t hef_index = 0;
+    uint8_t run_index = 0;
+
+    hailo_thread input_vstream_threads[MAX_EDGE_LAYERS];
+    hailo_thread output_vstream_threads[MAX_EDGE_LAYERS];
+    write_thread_args_t write_args[MAX_EDGE_LAYERS];
+    read_thread_args_t read_args[MAX_EDGE_LAYERS];
+
+    bool break_main_loop = false;
+
+    char HEF_FILES[HEF_COUNT][MAX_HEF_PATH_LEN] = {"hefs/shortcut_net.hef", "hefs/shortcut_net.hef"};
+
+    status = hailo_init_vdevice_params(&params);
+    REQUIRE_SUCCESS(status, l_exit, "Failed init vdevice_params");
+
+    params.device_count = DEVICE_COUNT;
+    status = hailo_create_vdevice(&params, &vdevice);
+    REQUIRE_SUCCESS(status, l_exit, "Failed to create vdevice");
+
+    for (hef_index = 0; hef_index < HEF_COUNT; hef_index++) {
+        /* Select user HEFs here. In this example it's the same HEF for all networks */
+        status = hailo_create_hef_file(&hef[hef_index], HEF_FILES[hef_index]);
+        REQUIRE_SUCCESS(status, l_release_hef, "Failed creating hef file %s", HEF_FILES[hef_index]);
+
+        status = hailo_init_configure_params(hef[hef_index], HAILO_STREAM_INTERFACE_PCIE, &configure_params);
+        REQUIRE_SUCCESS(status, l_release_hef, "Failed init configure params");
+
+        status = hailo_configure_vdevice(vdevice, hef[hef_index], &configure_params, &network_groups[hef_index], &network_groups_size);
+        REQUIRE_SUCCESS(status, l_release_hef, "Failed configuring vdevcie");
+        REQUIRE_ACTION(network_groups_size == 1, status = HAILO_INVALID_ARGUMENT, l_release_hef, 
+            "Unexpected network group size");
+
+        status = build_vstreams(network_groups[hef_index],
+            input_vstreams[hef_index], input_frame_size[hef_index], src_data[hef_index],
+            output_vstreams[hef_index], output_frame_size[hef_index], dst_data[hef_index],
+            &num_input_vstreams[hef_index], &num_output_vstreams[hef_index]);
+        REQUIRE_SUCCESS(status, l_release_vstreams, "Failed building streams");
+    }
+
+    // Inference part
+    for (run_index = 0; run_index < RUN_COUNT; run_index++) {
+        for (hef_index = 0; hef_index < HEF_COUNT; hef_index++) {
+            status = hailo_activate_network_group(network_groups[hef_index], NULL, &activated_network_group);
+            REQUIRE_SUCCESS(status, l_release_vstreams, "Failed activate network group");
+
+            for (size_t i = 0; i < num_input_vstreams[hef_index]; i++) {
+                status = create_input_vstream_thread(input_vstreams[hef_index][i], src_data[hef_index][i],
+                    input_frame_size[hef_index][i], &input_vstream_threads[i], &write_args[i]);
+            }
+
+            for (size_t i = 0; i < num_output_vstreams[hef_index]; i++) {
+                status = create_output_vstream_thread(output_vstreams[hef_index][i], dst_data[hef_index][i],
+                    output_frame_size[hef_index][i], &output_vstream_threads[i], &read_args[i]);
+            }
+
+            for (size_t i = 0; i < num_input_vstreams[hef_index]; i++) {
+                status = hailo_join_thread(&input_vstream_threads[i]);
+                if (HAILO_SUCCESS != status) {
+                    printf("write_thread failed \n");
+                    break_main_loop = true;
+                }
+            }
+
+            for (size_t i = 0; i < num_output_vstreams[hef_index]; i++) {
+                status = hailo_join_thread(&output_vstream_threads[i]);
+                if (HAILO_SUCCESS != status) {
+                    printf("write_thread failed \n");
+                    break_main_loop = true;
+                }
+            }
+
+            status = hailo_deactivate_network_group(activated_network_group);
+            REQUIRE_SUCCESS(status, l_deactivate_network_group, "Failed to de-activate network group");
+
+            if(break_main_loop) {
+                goto l_release_vstreams;
+            }
+        }
+    }
+
+    printf("Inference ran successfully\n");
+    status = HAILO_SUCCESS;
+    goto l_release_vstreams;
+
+l_deactivate_network_group:
+    (void)hailo_deactivate_network_group(activated_network_group);
+l_release_vstreams:
+    for (hef_index = 0; hef_index < HEF_COUNT; hef_index++) {
+        (void)hailo_release_output_vstreams(output_vstreams[hef_index], num_output_vstreams[hef_index]);
+        (void)hailo_release_input_vstreams(input_vstreams[hef_index], num_input_vstreams[hef_index]);
+    }
+
+    for (hef_index = 0; hef_index < HEF_COUNT; hef_index++) {
+        for (size_t i = 0; i < num_input_vstreams[hef_index]; i++) {
+            if (NULL != src_data[hef_index] && NULL != src_data[hef_index][i]) {
+                FREE(src_data[hef_index][i]);
+            }
+        }
+        for (size_t i = 0; i < num_output_vstreams[hef_index]; i++) {
+            if (NULL != dst_data[hef_index] && NULL != dst_data[hef_index][i]) {
+                FREE(dst_data[hef_index][i]);
+            }
+        }
+    }
+l_release_hef:
+    for (hef_index = 0; hef_index < HEF_COUNT; hef_index++) {
+        if (NULL != hef[hef_index]) {
+            (void)hailo_release_hef(hef[hef_index]);            
+        }
+    }
+    (void)hailo_release_vdevice(vdevice);
+l_exit:
+    return status;
+}
\ No newline at end of file
diff --git a/hailort/libhailort/examples/c/switch_single_io_hefs_example.c b/hailort/libhailort/examples/c/switch_single_io_hefs_example.c
new file mode 100644 (file)
index 0000000..bb3c4d2
--- /dev/null
@@ -0,0 +1,257 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @ file switch_single_io_hefs_example.c
+ * This example demonstrates basic usage of HailoRT streaming api over multiple network groups, using vstreams.
+ * It loads several HEF networks with a single input and a single output into a Hailo PCIe VDevice and performs a inference on each one. 
+ * After inference is finished, the example switches to the next HEF and start inference again.
+ **/
+
+#include "common.h"
+#include "hailo_thread.h"
+#include "hailo/hailort.h"
+#include <time.h>
+
+#define MAX_HEF_PATH_LEN (255)
+#define MAX_EDGE_LAYERS (16)
+
+#define INFER_FRAME_COUNT (100)
+#define HEF_COUNT (2)
+#define RUN_COUNT (10)
+#define DEVICE_COUNT (1)
+
+typedef struct input_vstream_thread_args_t {
+    hailo_configured_network_group *configured_networks;
+    hailo_input_vstream_params_by_name_t *input_vstream_params;
+} input_vstream_thread_args_t;
+
+typedef struct output_vstream_thread_args_t {
+    hailo_configured_network_group *configured_networks;
+    hailo_activated_network_group *activated_network_group;
+    hailo_output_vstream_params_by_name_t *output_vstream_params;
+} output_vstream_thread_args_t;
+
+thread_return_type input_vstream_thread_func(void *args)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    hailo_input_vstream input_vstreams[HEF_COUNT];
+    size_t input_frame_size[HEF_COUNT];
+    uint8_t *src_data[HEF_COUNT];
+    input_vstream_thread_args_t *input_vstream_args = (input_vstream_thread_args_t*)args;
+
+    for (size_t hef_index = 0; hef_index < HEF_COUNT; hef_index++) {
+        // Input vstream size has to be one in this example- otherwise would haveve returned error
+        status = hailo_create_input_vstreams(input_vstream_args->configured_networks[hef_index],
+            &input_vstream_args->input_vstream_params[hef_index], 1, &input_vstreams[hef_index]);
+        REQUIRE_SUCCESS(status, l_clear_src, "Failed creating virtual stream");
+
+        status = hailo_get_input_vstream_frame_size(input_vstreams[hef_index], &input_frame_size[hef_index]);
+        REQUIRE_SUCCESS(status, l_clear_src, "Failed getting input virtual stream frame size");
+
+        src_data[hef_index] = (uint8_t*)malloc(input_frame_size[hef_index]);
+        REQUIRE_ACTION(NULL != src_data[hef_index], status = HAILO_OUT_OF_HOST_MEMORY, l_clear_src, "Out of memory");
+    }
+
+    for (size_t run_index = 0; run_index < RUN_COUNT; run_index++) {
+        for (size_t hef_index = 0 ; hef_index < HEF_COUNT; hef_index++) {
+            // Wait for hef to be activated to send data
+            hailo_wait_for_network_group_activation(input_vstream_args->configured_networks[hef_index], HAILO_INFINITE);
+
+            // Send data on relevant Hef
+            for (uint32_t frame = 0; frame < INFER_FRAME_COUNT; frame++) {
+                // Prepare data here
+                for (size_t i = 0; i < input_frame_size[hef_index]; i++) {
+                    src_data[hef_index][i] = (uint8_t)(rand() % 256);
+                }
+
+                status = hailo_vstream_write_raw_buffer(input_vstreams[hef_index], src_data[hef_index], input_frame_size[hef_index]);
+                REQUIRE_SUCCESS(status, l_clear_src, "Failed writing input frame to device");
+            }
+        }
+    }
+
+    status = HAILO_SUCCESS;
+
+l_clear_src:
+    for (size_t hef_index = 0; hef_index < HEF_COUNT; hef_index++) {
+        FREE(src_data[hef_index]);
+    }
+
+    (void)hailo_release_input_vstreams(input_vstreams, HEF_COUNT);
+    return (thread_return_type)status;
+}
+
+thread_return_type output_vstream_thread_func(void *args)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    hailo_output_vstream output_vstreams[HEF_COUNT];
+    size_t output_frame_size[HEF_COUNT];
+    uint8_t *dst_data[HEF_COUNT];
+    output_vstream_thread_args_t *output_vstream_args = (output_vstream_thread_args_t*)args;
+
+    for (size_t hef_index = 0; hef_index < HEF_COUNT; hef_index++) {
+        // Output vstream size has to be one in this example- otherwise would haveve returned error
+        status = hailo_create_output_vstreams(output_vstream_args->configured_networks[hef_index],
+            &output_vstream_args->output_vstream_params[hef_index], 1, &output_vstreams[hef_index]);
+        REQUIRE_SUCCESS(status, l_clear_dst, "Failed creating virtual stream");
+
+        status = hailo_get_output_vstream_frame_size(output_vstreams[hef_index], &output_frame_size[hef_index]);
+        REQUIRE_SUCCESS(status, l_clear_dst, "Failed getting input virtual stream frame size");
+
+        dst_data[hef_index] = (uint8_t*)malloc(output_frame_size[hef_index]);
+        REQUIRE_ACTION(NULL != dst_data[hef_index], status = HAILO_OUT_OF_HOST_MEMORY, l_clear_dst, "Out of memory");
+    }
+
+    for (size_t run_index = 0; run_index < RUN_COUNT; run_index++) {
+        for (size_t hef_index = 0 ; hef_index < HEF_COUNT; hef_index++) {
+            // Wait for hef to be activated to send data
+            hailo_wait_for_network_group_activation(output_vstream_args->configured_networks[hef_index], HAILO_INFINITE);
+    
+            for (uint32_t i = 0; i < INFER_FRAME_COUNT; i++) {
+                // Read data
+                status = hailo_vstream_read_raw_buffer(output_vstreams[hef_index],
+                    dst_data[hef_index], output_frame_size[hef_index]);
+                REQUIRE_SUCCESS(status, l_deactivate_network_group, "Failed reading output frame from device");
+    
+                // Process data here
+            }
+    
+            // Deavticate network after finishing inference
+            status = hailo_deactivate_network_group(*(output_vstream_args->activated_network_group));
+            REQUIRE_SUCCESS(status, l_deactivate_network_group, "Failed Deactivating network");
+    
+            // Dont activate on last iteration
+            if (hef_index < HEF_COUNT - 1) {
+                // Activate next network so input thread can start sending again
+                status = hailo_activate_network_group(output_vstream_args->configured_networks[hef_index + 1],
+                    NULL, output_vstream_args->activated_network_group);
+                REQUIRE_SUCCESS(status, l_clear_dst, "Failed Activating network");
+            }
+            else {
+                // Meaning we finished a run and now need to activate the first network again for the next run
+                if (run_index < RUN_COUNT - 1) {
+                    status = hailo_activate_network_group(output_vstream_args->configured_networks[0],
+                        NULL, output_vstream_args->activated_network_group);
+                    REQUIRE_SUCCESS(status, l_clear_dst, "Failed Activating network");
+                }
+            }
+        }
+    }
+
+    status = HAILO_SUCCESS;
+    goto l_clear_dst;
+
+l_deactivate_network_group:
+    (void)hailo_deactivate_network_group(*(output_vstream_args->activated_network_group));
+l_clear_dst:
+    for (size_t hef_index = 0; hef_index < HEF_COUNT; hef_index++) {
+        FREE(dst_data[hef_index]);
+    }
+
+    (void)hailo_release_output_vstreams(output_vstreams, HEF_COUNT);
+    return (thread_return_type)status;
+}
+
+int main()
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    hailo_vdevice vdevice = NULL;
+    hailo_vdevice_params_t params = {0};
+    hailo_hef hef[HEF_COUNT] = {NULL};
+    hailo_configure_params_t configure_params = {0};
+    hailo_activated_network_group activated_network;
+    hailo_configured_network_group network_groups[HEF_COUNT] = {NULL};
+    size_t network_groups_size = 1;
+    uint8_t hef_index = 0;
+    hailo_input_vstream_params_by_name_t input_vstream_params[HEF_COUNT];
+    hailo_output_vstream_params_by_name_t output_vstream_params[HEF_COUNT];
+    size_t input_vstream_size = 1;
+    size_t output_vstream_size = 1;
+
+    hailo_thread input_vstream_thread = {0};
+    hailo_thread output_vstream_thread = {0};
+    input_vstream_thread_args_t input_args = {0};
+    output_vstream_thread_args_t output_args = {0};
+
+    char HEF_FILES[HEF_COUNT][250] = {"hefs/shortcut_net.hef","hefs/shortcut_net.hef"};
+
+    status = hailo_init_vdevice_params(&params);
+    REQUIRE_SUCCESS(status, l_exit, "Failed init vdevice_params");
+
+    params.device_count = DEVICE_COUNT;
+    status = hailo_create_vdevice(&params, &vdevice);
+    REQUIRE_SUCCESS(status, l_exit, "Failed to create vdevice");
+
+    for (hef_index = 0; hef_index < HEF_COUNT; hef_index++) {
+        /* Select user HEFs here. In this example it's the same HEF for all networks */
+        status = hailo_create_hef_file(&hef[hef_index], HEF_FILES[hef_index]);
+        REQUIRE_SUCCESS(status, l_release_hef, "Failed creating hef file %s", HEF_FILES[hef_index]);
+
+        status = hailo_init_configure_params(hef[hef_index], HAILO_STREAM_INTERFACE_PCIE, &configure_params);
+        REQUIRE_SUCCESS(status, l_release_hef, "Failed init configure params");
+
+        status = hailo_configure_vdevice(vdevice, hef[hef_index], &configure_params, &network_groups[hef_index], &network_groups_size);
+        REQUIRE_SUCCESS(status, l_release_hef, "Failed configuring vdevcie");
+        REQUIRE_ACTION(network_groups_size == 1, status = HAILO_INVALID_ARGUMENT, l_release_hef, 
+            "Unexpected network group size");
+
+        // Mae sure each hef is single input single output
+        status = hailo_make_input_vstream_params(network_groups[hef_index], true, HAILO_FORMAT_TYPE_AUTO,
+            &input_vstream_params[hef_index], &input_vstream_size);
+        REQUIRE_SUCCESS(status, l_release_hef, "Failed making input virtual stream params");
+        REQUIRE_ACTION(input_vstream_size == 1, status = HAILO_INVALID_ARGUMENT, l_release_hef, 
+            "INVALID HEF - Only hefs with single input vstream are allowed");
+
+        status = hailo_make_output_vstream_params(network_groups[hef_index], true, HAILO_FORMAT_TYPE_AUTO,
+            &output_vstream_params[hef_index], &output_vstream_size);
+        REQUIRE_SUCCESS(status, l_release_hef, "Failed making output virtual stream params");
+        REQUIRE_ACTION(output_vstream_size == 1, status = HAILO_INVALID_ARGUMENT, l_release_hef, 
+            "INVALID HEF - Only hefs with single output vstream are allowed");
+    }
+
+    input_args.configured_networks      = network_groups;
+    input_args.input_vstream_params     = input_vstream_params;
+
+    // Open input vstream and output vstream threads
+    status = hailo_create_thread(input_vstream_thread_func, &input_args, &input_vstream_thread);
+    REQUIRE_SUCCESS(status, l_release_hef, "Failed creating thread");
+
+    output_args.configured_networks         = network_groups;
+    output_args.activated_network_group     = &activated_network;
+    output_args.output_vstream_params       = output_vstream_params;
+
+    // Open output vstream and output vstream threads
+    status = hailo_create_thread(output_vstream_thread_func, &output_args, &output_vstream_thread);
+    REQUIRE_SUCCESS(status, l_join_input_thread, "Failed creating thread");
+
+    // Activate first network so input thread can start
+    status = hailo_activate_network_group(network_groups[0], NULL, &activated_network);
+    REQUIRE_SUCCESS(status, l_join_output_thread, "Failed Activating network");
+
+
+    status = hailo_join_thread(&input_vstream_thread);
+    REQUIRE_SUCCESS(status, l_join_output_thread, "Failed witing for input thread");
+
+    status = hailo_join_thread(&output_vstream_thread);
+    REQUIRE_SUCCESS(status, l_join_output_thread, "Failed witing for output thread");
+
+    printf("Inference ran successfully\n");
+    status = HAILO_SUCCESS;
+    goto l_release_hef;
+
+l_join_output_thread:
+    (void)hailo_join_thread(&output_vstream_thread);
+l_join_input_thread:
+    (void)hailo_join_thread(&input_vstream_thread);
+l_release_hef:
+    for (hef_index = 0; hef_index < HEF_COUNT; hef_index++) {
+        if (NULL != hef[hef_index]) {
+            (void)hailo_release_hef(hef[hef_index]);            
+        }
+    }
+    (void)hailo_release_vdevice(vdevice);
+l_exit:
+    return status;
+}
diff --git a/hailort/libhailort/examples/c/vstreams_example.c b/hailort/libhailort/examples/c/vstreams_example.c
new file mode 100644 (file)
index 0000000..31854d4
--- /dev/null
@@ -0,0 +1,195 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @ file vstreams_example.c
+ * This example demonstrates the basic data-path on HailoRT using the high level API - Virtual Stream Pipeline.
+ * The program scans for Hailo-8 devices connected to a provided PCIe interface, generates random dataset,
+ * and runs it through the VDevice with virtual streams.
+ **/
+
+#include "common.h"
+#include "hailo_thread.h"
+#include "hailo/hailort.h"
+
+#define INFER_FRAME_COUNT (100)
+#define MAX_EDGE_LAYERS (16)
+#define HEF_FILE ("hefs/shortcut_net.hef")
+
+
+thread_return_type write_to_device(void *args)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    size_t input_frame_size = 0;
+    uint8_t *src_data = NULL;
+    hailo_input_vstream vstream = *(hailo_input_vstream*)args;
+
+    status = hailo_get_input_vstream_frame_size(vstream, &input_frame_size);
+    REQUIRE_SUCCESS(status, l_exit, "Failed getting input virtual stream frame size");
+
+    src_data = malloc(input_frame_size);
+    REQUIRE_ACTION(src_data != NULL, status = HAILO_OUT_OF_HOST_MEMORY, l_exit, "Failed to allocate input buffer");
+
+    // Prepare src data here
+    for (size_t i = 0; i < input_frame_size; i++) {
+        src_data[i] = (uint8_t)(rand() % 256);
+    }
+
+    for (uint32_t i = 0; i < INFER_FRAME_COUNT; i++) {
+        status = hailo_vstream_write_raw_buffer(vstream, src_data, input_frame_size);
+        REQUIRE_SUCCESS(status, l_free_buffer, "Failed sending to vstream");
+    }
+
+    // Flushing is not mandatory here
+    status = hailo_flush_input_vstream(vstream);
+    REQUIRE_SUCCESS(status, l_free_buffer, "Failed flushing vstream");
+
+    status = HAILO_SUCCESS;
+l_free_buffer:
+    FREE(src_data);
+l_exit:
+    return (thread_return_type)status;
+}
+
+thread_return_type read_from_device(void *args)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    size_t output_frame_size = 0;
+    uint8_t *dst_data = NULL;
+    hailo_output_vstream vstream = *(hailo_output_vstream*)args;
+
+    status = hailo_get_output_vstream_frame_size(vstream, &output_frame_size);
+    REQUIRE_SUCCESS(status, l_exit, "Failed getting output virtual stream frame size");
+
+    dst_data = (uint8_t*)malloc(output_frame_size);
+    REQUIRE_ACTION(dst_data != NULL, status = HAILO_OUT_OF_HOST_MEMORY, l_exit, "Failed to allocate output buffer");
+
+    for (uint32_t i = 0; i < INFER_FRAME_COUNT; i++) {
+        status = hailo_vstream_read_raw_buffer(vstream, dst_data, output_frame_size);
+        REQUIRE_SUCCESS(status, l_free_buffer, "hailo_vstream_recv failed");
+    }
+
+    status = HAILO_SUCCESS;
+l_free_buffer:
+    FREE(dst_data);
+l_exit:
+    return (thread_return_type)status;
+}
+
+hailo_status infer(hailo_input_vstream *input_vstreams, size_t input_vstreams_size,
+    hailo_output_vstream *output_vstreams, size_t output_vstreams_size)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    hailo_thread write_threads[MAX_EDGE_LAYERS] = {0};
+    hailo_thread read_threads[MAX_EDGE_LAYERS] = {0};
+    hailo_status write_thread_status = HAILO_UNINITIALIZED;
+    hailo_status read_thread_status = HAILO_UNINITIALIZED;
+    size_t input_threads_index = 0;
+    size_t output_threads_index = 0;
+    size_t index = 0;
+
+    // Create reading threads
+    for (output_threads_index = 0; output_threads_index < output_vstreams_size; output_threads_index++) {
+        status = hailo_create_thread(read_from_device, &output_vstreams[output_threads_index], &read_threads[output_threads_index]);
+        REQUIRE_SUCCESS(status, l_cleanup, "Failed creating thread");
+    }
+
+    // Create writing threads
+    for (input_threads_index = 0; input_threads_index < input_vstreams_size; input_threads_index++) {
+        status = hailo_create_thread(write_to_device, &input_vstreams[input_threads_index], &write_threads[input_threads_index]);
+        REQUIRE_SUCCESS(status, l_cleanup, "Failed creating thread");
+    }
+
+l_cleanup:
+    // Join writing threads
+    for (index = 0; index < input_threads_index; index++) {
+        write_thread_status = hailo_join_thread(&write_threads[index]);
+        if (HAILO_SUCCESS != write_thread_status) {
+            printf("write_thread failed \n");
+            status = write_thread_status; // Override write status
+        }
+    }
+
+    // Join reading threads
+    for (index = 0; index < output_threads_index; index++) {
+        read_thread_status = hailo_join_thread(&read_threads[index]);
+        if (HAILO_SUCCESS != read_thread_status) {
+            printf("read_thread failed \n");
+            status = read_thread_status; // Override read status
+        }
+    }
+
+    return status;
+}
+
+int main()
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    hailo_vdevice vdevice = NULL;
+    hailo_hef hef = NULL;
+    hailo_configure_params_t config_params = {0};
+    hailo_configured_network_group network_group = NULL;
+    size_t network_group_size = 1;
+    hailo_input_vstream_params_by_name_t input_vstream_params[MAX_EDGE_LAYERS] = {0};
+    hailo_output_vstream_params_by_name_t output_vstream_params[MAX_EDGE_LAYERS] = {0};
+    size_t input_vstreams_size = MAX_EDGE_LAYERS;
+    size_t output_vstreams_size = MAX_EDGE_LAYERS;
+    hailo_activated_network_group activated_network_group = NULL;
+    hailo_input_vstream input_vstreams[MAX_EDGE_LAYERS] = {NULL};
+    hailo_output_vstream output_vstreams[MAX_EDGE_LAYERS] = {NULL};
+
+    status = hailo_create_vdevice(NULL, &vdevice);
+    REQUIRE_SUCCESS(status, l_exit, "Failed to create vdevice");
+
+    status = hailo_create_hef_file(&hef, HEF_FILE);
+    REQUIRE_SUCCESS(status, l_release_vdevice, "Failed reading hef file");
+
+    status = hailo_init_configure_params(hef, HAILO_STREAM_INTERFACE_PCIE, &config_params);
+    REQUIRE_SUCCESS(status, l_release_hef, "Failed initializing configure parameters");
+
+    status = hailo_configure_vdevice(vdevice, hef, &config_params, &network_group, &network_group_size);
+    REQUIRE_SUCCESS(status, l_release_hef, "Failed configure vdevcie from hef");
+    REQUIRE_ACTION(network_group_size == 1, status = HAILO_INVALID_ARGUMENT, l_release_hef, 
+        "Invalid network group size");
+
+    status = hailo_make_input_vstream_params(network_group, true, HAILO_FORMAT_TYPE_AUTO,
+        input_vstream_params, &input_vstreams_size);
+    REQUIRE_SUCCESS(status, l_release_hef, "Failed making input virtual stream params");
+
+    status = hailo_make_output_vstream_params(network_group, true, HAILO_FORMAT_TYPE_AUTO,
+        output_vstream_params, &output_vstreams_size);
+    REQUIRE_SUCCESS(status, l_release_hef, "Failed making output virtual stream params");
+
+    REQUIRE_ACTION((input_vstreams_size <= MAX_EDGE_LAYERS || output_vstreams_size <= MAX_EDGE_LAYERS),
+        status = HAILO_INVALID_OPERATION, l_release_hef, "Trying to infer network with too many input/output virtual "
+        "streams, Maximum amount is %d, (either change HEF or change the definition of MAX_EDGE_LAYERS)\n",
+        MAX_EDGE_LAYERS);
+
+    status = hailo_create_input_vstreams(network_group, input_vstream_params, input_vstreams_size, input_vstreams);
+    REQUIRE_SUCCESS(status, l_release_hef, "Failed creating virtual input streams\n");
+
+    status = hailo_create_output_vstreams(network_group, output_vstream_params, output_vstreams_size, output_vstreams);
+    REQUIRE_SUCCESS(status, l_release_input_vstream, "Failed creating output virtual streams\n");
+
+    status = hailo_activate_network_group(network_group, NULL, &activated_network_group);
+    REQUIRE_SUCCESS(status, l_release_output_vstream, "Failed activate network group");
+
+    status = infer(input_vstreams, input_vstreams_size, output_vstreams, output_vstreams_size);
+    REQUIRE_SUCCESS(status, l_deactivate_network_group, "Inference failure");
+
+    printf("Inference ran successfully\n");
+    status = HAILO_SUCCESS;
+l_deactivate_network_group:
+    (void)hailo_deactivate_network_group(activated_network_group);
+l_release_output_vstream:
+    (void)hailo_release_output_vstreams(output_vstreams, output_vstreams_size);
+l_release_input_vstream:
+    (void)hailo_release_input_vstreams(input_vstreams, input_vstreams_size);
+l_release_hef:
+    (void) hailo_release_hef(hef);
+l_release_vdevice:
+    (void) hailo_release_vdevice(vdevice);
+l_exit:
+    return status;
+}
diff --git a/hailort/libhailort/examples/cmake/FindHailoRT.cmake b/hailort/libhailort/examples/cmake/FindHailoRT.cmake
new file mode 100644 (file)
index 0000000..173726c
--- /dev/null
@@ -0,0 +1,35 @@
+# - Try to find HailoRT
+#   - If libhailort is defined (building as part of the build tree), use it
+#   - Otherwise, find HAILORT_LIB and HAILORT_INCLUDE_DIR, and import libhailort
+
+if (NOT TARGET libhailort)
+    # find_path finds a directory containing the named file
+    if(WIN32)
+        find_library(HAILORT_LIB "libhailort.lib" PATH_SUFFIXES "HailoRT/lib/")
+        find_path(HAILORT_INCLUDE_DIR "hailo/" PATH_SUFFIXES "HailoRT/include/")
+
+    else()
+        find_library(HAILORT_LIB "libhailort.so.4.6.0" PATH_SUFFIXES "lib/")
+        find_path(HAILORT_INCLUDE_DIR "hailo/" PATH_SUFFIXES "include/")
+    endif()
+
+    include(FindPackageHandleStandardArgs)
+    # Handle the QUIETLY and REQUIRED arguments and set HAILORT_FOUND to TRUE
+    # if all listed variables are TRUE
+    find_package_handle_standard_args(
+        HailoRT
+        DEFAULT_MSG
+        HAILORT_LIB
+        HAILORT_INCLUDE_DIR
+    )
+
+    add_library(HailoRT::libhailort SHARED IMPORTED)
+    set_target_properties(HailoRT::libhailort PROPERTIES
+        IMPORTED_LOCATION "${HAILORT_LIB}"
+        IMPORTED_IMPLIB "${HAILORT_LIB}"
+        INTERFACE_INCLUDE_DIRECTORIES "${HAILORT_INCLUDE_DIR}"
+    )
+
+else()
+    add_library(HailoRT::libhailort ALIAS libhailort)
+endif()
diff --git a/hailort/libhailort/examples/cpp/CMakeLists.txt b/hailort/libhailort/examples/cpp/CMakeLists.txt
new file mode 100644 (file)
index 0000000..e3f17b4
--- /dev/null
@@ -0,0 +1,32 @@
+cmake_minimum_required(VERSION 3.0.0)
+
+add_executable(cpp_vstreams_example vstreams_example.cpp)
+target_link_libraries(cpp_vstreams_example PRIVATE example_base)
+
+add_executable(cpp_infer_pipeline_example infer_pipeline_example.cpp)
+target_link_libraries(cpp_infer_pipeline_example PRIVATE example_base)
+
+add_executable(cpp_raw_streams_example raw_streams_example.cpp)
+target_link_libraries(cpp_raw_streams_example PRIVATE example_base)
+
+add_executable(cpp_multi_network_vstream_example multi_network_vstream_example.cpp)
+target_link_libraries(cpp_multi_network_vstream_example PRIVATE example_base)
+
+add_executable(cpp_switch_hefs_example switch_hefs_example.cpp)
+target_link_libraries(cpp_switch_hefs_example PRIVATE example_base)
+
+add_executable(cpp_switch_hefs_example_threads_reuse switch_hefs_example_threads_reuse.cpp)
+target_link_libraries(cpp_switch_hefs_example_threads_reuse PRIVATE example_base)
+
+add_executable(multi_device_example_cpp multi_device_example.cpp)
+target_link_libraries(multi_device_example_cpp PRIVATE example_base)
+
+set(EXAMPLES_CPP_TARGETS
+    cpp_vstreams_example
+    cpp_infer_pipeline_example
+    cpp_raw_streams_example
+    cpp_multi_network_vstream_example
+    cpp_switch_hefs_example
+    cpp_switch_hefs_example_threads_reuse
+    multi_device_example_cpp
+    PARENT_SCOPE)
diff --git a/hailort/libhailort/examples/cpp/infer_pipeline_example.cpp b/hailort/libhailort/examples/cpp/infer_pipeline_example.cpp
new file mode 100644 (file)
index 0000000..10113aa
--- /dev/null
@@ -0,0 +1,149 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @ file vstreams_example
+ * This example demonstrates the basic data-path on HailoRT using the high level API - Virtual Stream Pipeline.
+ * The program creates a device according to the provdied IP address, generates a random dataset,
+ * and runs it through the device with virtual streams pipeline.
+ **/
+
+#include "hailo/hailort.hpp"
+
+#include <iostream>
+
+#define HEF_FILE ("hefs/shortcut_net.hef")
+constexpr size_t FRAMES_COUNT = 100;
+constexpr hailo_format_type_t FORMAT_TYPE = HAILO_FORMAT_TYPE_AUTO;
+
+#define USAGE_ERROR_MSG ("Args parsing error.\nUsage: infer_pipeline_example <ip_address>\n")
+
+
+using hailort::Device;
+using hailort::Hef;
+using hailort::Expected;
+using hailort::make_unexpected;
+using hailort::ConfiguredNetworkGroup;
+using hailort::MemoryView;
+using hailort::InferVStreams;
+
+
+Expected<std::shared_ptr<ConfiguredNetworkGroup>> configure_network_group(Device &device)
+{
+    auto hef = Hef::create(HEF_FILE);
+    if (!hef) {
+        return make_unexpected(hef.status());
+    }
+
+    auto configure_params = hef->create_configure_params(HAILO_STREAM_INTERFACE_ETH);
+    if (!configure_params) {
+        return make_unexpected(configure_params.status());
+    }
+
+    auto network_groups = device.configure(hef.value(), configure_params.value());
+    if (!network_groups) {
+        return make_unexpected(network_groups.status());
+    }
+
+    if (1 != network_groups->size()) {
+        std::cerr << "Invalid amount of network groups" << std::endl;
+        return make_unexpected(HAILO_INTERNAL_FAILURE);
+    }
+
+    return std::move(network_groups->at(0));
+}
+
+hailo_status infer(InferVStreams &pipeline)
+{
+    const size_t frames_count = FRAMES_COUNT;
+
+    auto input_vstreams = pipeline.get_input_vstreams();
+    std::map<std::string, std::vector<uint8_t>> input_data;
+    for (const auto &input_vstream : input_vstreams) {
+        input_data.emplace(input_vstream.get().name(), std::vector<uint8_t>(input_vstream.get().get_frame_size() * frames_count));
+    }
+
+    std::map<std::string, MemoryView> input_data_mem_views;
+    for (const auto &input_vstream : input_vstreams) {
+        auto &input_buffer = input_data[input_vstream.get().name()];
+        input_data_mem_views.emplace(input_vstream.get().name(), MemoryView(input_buffer.data(), input_buffer.size()));
+    }
+
+    auto output_vstreams = pipeline.get_output_vstreams();
+    std::map<std::string, std::vector<uint8_t>> output_data;
+    for (const auto &output_vstream : output_vstreams) {
+        output_data.emplace(output_vstream.get().name(), std::vector<uint8_t>(output_vstream.get().get_frame_size() * frames_count));
+    }
+
+    std::map<std::string, MemoryView> output_data_mem_views;
+    for (const auto &output_vstream : output_vstreams) {
+        auto &output_buffer = output_data[output_vstream.get().name()];
+        output_data_mem_views.emplace(output_vstream.get().name(), MemoryView(output_buffer.data(), output_buffer.size()));
+    }
+
+    hailo_status status = pipeline.infer(input_data_mem_views, output_data_mem_views, frames_count);
+
+    return status;
+}
+
+Expected<std::string> parse_arguments(int argc, char **argv)
+{
+    if (2 != argc) {
+        std::cerr << USAGE_ERROR_MSG << std::endl;
+        return make_unexpected(HAILO_INVALID_ARGUMENT);
+    }
+    return std::string(argv[1]);
+}
+
+int main(int argc, char **argv)
+{
+    auto device_ip = parse_arguments(argc, argv);
+    if (!device_ip) {
+        std::cerr << "Failed parsing arguments " << device_ip.status() << std::endl;
+        return device_ip.status();
+    }
+
+    auto device = Device::create_eth(device_ip.value());
+    if (!device) {
+        std::cerr << "Failed create_eth " << device.status() << std::endl;
+        return device.status();
+    }
+
+    auto network_group = configure_network_group(*device.value());
+    if (!network_group) {
+        std::cerr << "Failed to configure network group " << HEF_FILE << std::endl;
+        return network_group.status();
+    }
+
+    auto input_params = network_group.value()->make_input_vstream_params(true, FORMAT_TYPE, HAILO_DEFAULT_VSTREAM_TIMEOUT_MS, HAILO_DEFAULT_VSTREAM_QUEUE_SIZE);
+    if (!input_params) {
+        std::cerr << "Failed make_input_vstream_params " << input_params.status() << std::endl;
+        return input_params.status();
+    }
+
+    auto output_params = network_group.value()->make_output_vstream_params(true, FORMAT_TYPE, HAILO_DEFAULT_VSTREAM_TIMEOUT_MS, HAILO_DEFAULT_VSTREAM_QUEUE_SIZE);
+    if (!output_params) {
+        std::cerr << "Failed make_output_vstream_params " << output_params.status() << std::endl;
+        return output_params.status();
+    }
+
+    auto activated_network_group = network_group.value()->activate();
+    if (!activated_network_group) {
+        std::cerr << "Failed activated network group "  << activated_network_group.status();
+        return activated_network_group.status();
+    }
+
+    auto pipeline = InferVStreams::create(*network_group.value(), input_params.value(), output_params.value());
+    if (!pipeline) {
+        std::cerr << "Failed to create inference pipeline " << pipeline.status() << std::endl;
+        return pipeline.status();
+    }
+
+    auto status = infer(pipeline.value());
+    if (HAILO_SUCCESS == status) {
+        std::cout << "Inference finished successfully" << std::endl;
+    }
+    
+    return status;
+}
diff --git a/hailort/libhailort/examples/cpp/multi_device_example.cpp b/hailort/libhailort/examples/cpp/multi_device_example.cpp
new file mode 100644 (file)
index 0000000..2d55c21
--- /dev/null
@@ -0,0 +1,198 @@
+/**
+ * Copyright 2020 (C) Hailo Technologies Ltd.
+ * All rights reserved.
+ *
+ * Hailo Technologies Ltd. ("Hailo") disclaims any warranties, including, but not limited to,
+ * the implied warranties of merchantability and fitness for a particular purpose.
+ * This software is provided on an "AS IS" basis, and Hailo has no obligation to provide maintenance,
+ * support, updates, enhancements, or modifications.
+ *
+ * You may use this software in the development of any project.
+ * You shall not reproduce, modify or distribute this software without prior written permission.
+ **/
+/**
+ * @ file multi_device_example.cpp
+ * This example demonstrates how to work with multiple devices using virtual device.
+ **/
+
+#include "hailo/hailort.hpp"
+
+#include <iostream>
+
+#define HEF_FILE ("hefs/shortcut_net.hef")
+constexpr size_t FRAMES_COUNT = 100;
+constexpr bool QUANTIZED = true;
+constexpr hailo_format_type_t FORMAT_TYPE = HAILO_FORMAT_TYPE_AUTO;
+constexpr size_t MAX_LAYER_EDGES = 16;
+
+
+using hailort::VDevice;
+using hailort::Hef;
+using hailort::Expected;
+using hailort::make_unexpected;
+using hailort::ConfiguredNetworkGroup;
+using hailort::VStreamsBuilder;
+using hailort::InputVStream;
+using hailort::OutputVStream;
+using hailort::MemoryView;
+
+
+Expected<std::shared_ptr<ConfiguredNetworkGroup>> configure_network_group(VDevice &vdevice)
+{
+    auto hef = Hef::create(HEF_FILE);
+    if (!hef) {
+        return make_unexpected(hef.status());
+    }
+
+    auto configure_params = hef->create_configure_params(HAILO_STREAM_INTERFACE_PCIE);
+    if (!configure_params) {
+        return make_unexpected(configure_params.status());
+    }
+
+    auto network_groups = vdevice.configure(hef.value(), configure_params.value());
+    if (!network_groups) {
+        return make_unexpected(network_groups.status());
+    }
+
+    if (1 != network_groups->size()) {
+        std::cerr << "Invalid amount of network groups" << std::endl;
+        return make_unexpected(HAILO_INTERNAL_FAILURE);
+    }
+
+    return std::move(network_groups->at(0));
+}
+
+void write_all(InputVStream &input, hailo_status &status)
+{
+    std::vector<uint8_t> data(input.get_frame_size());
+    for (size_t i = 0; i < FRAMES_COUNT; i++) {
+        status = input.write(MemoryView(data.data(), data.size()));
+        if (HAILO_SUCCESS != status) {
+            return;
+        }
+    }
+
+    // Flushing is not mandatory here
+    status = input.flush();
+    if (HAILO_SUCCESS != status) {
+        std::cerr << "Failed flushing input vstream" << std::endl;
+        return;
+    }
+
+    status = HAILO_SUCCESS;
+    return;
+}
+
+void read_all(OutputVStream &output, hailo_status &status)
+{
+    std::vector<uint8_t> data(output.get_frame_size());
+    for (size_t i = 0; i < FRAMES_COUNT; i++) {
+        status = output.read(MemoryView(data.data(), data.size()));
+        if (HAILO_SUCCESS != status) {
+            return;
+        }
+    }
+    status = HAILO_SUCCESS;
+    return;
+}
+
+hailo_status infer(std::vector<InputVStream> &input_streams, std::vector<OutputVStream> &output_streams)
+{
+
+    hailo_status status = HAILO_SUCCESS; // Success oriented
+    hailo_status input_status[MAX_LAYER_EDGES] = {HAILO_UNINITIALIZED};
+    hailo_status output_status[MAX_LAYER_EDGES] = {HAILO_UNINITIALIZED};
+    std::unique_ptr<std::thread> input_threads[MAX_LAYER_EDGES];
+    std::unique_ptr<std::thread> output_threads[MAX_LAYER_EDGES];
+    size_t input_thread_index = 0;
+    size_t output_thread_index = 0;
+
+    // Create read threads
+    for (output_thread_index = 0 ; output_thread_index < output_streams.size(); output_thread_index++) {
+        output_threads[output_thread_index] = std::make_unique<std::thread>(read_all,
+            std::ref(output_streams[output_thread_index]), std::ref(output_status[output_thread_index]));
+    }
+
+    // Create write threads
+    for (input_thread_index = 0 ; input_thread_index < input_streams.size(); input_thread_index++) {
+        input_threads[input_thread_index] = std::make_unique<std::thread>(write_all,
+            std::ref(input_streams[input_thread_index]), std::ref(input_status[input_thread_index]));
+    }
+
+    // Join write threads
+    for (size_t i = 0; i < input_thread_index; i++) {
+        input_threads[i]->join();
+        if (HAILO_SUCCESS != input_status[i]) {
+            status = input_status[i];
+        }
+    }
+
+    // Join read threads
+    for (size_t i = 0; i < output_thread_index; i++) {
+        output_threads[i]->join();
+        if (HAILO_SUCCESS != output_status[i]) {
+            status = output_status[i];
+        }
+    }
+
+    if (HAILO_SUCCESS == status) {
+        std::cout << "Inference finished successfully" << std::endl;
+    }
+
+    return status;
+}
+
+int main()
+{
+    auto scan_res = hailort::Device::scan_pcie();
+    if (!scan_res) {
+        std::cerr << "Failed to scan pcie, status = " << scan_res.status() << std::endl;
+        return scan_res.status();
+    }
+
+    hailo_vdevice_params_t params;
+    auto status = hailo_init_vdevice_params(&params);
+    if (HAILO_SUCCESS != status) {
+        std::cerr << "Failed init vdevice_params, status = " << status << std::endl;
+        return status;
+    }
+
+    params.device_count = static_cast<uint32_t>(scan_res->size());
+    auto vdevice = VDevice::create(params);
+    if (!vdevice) {
+        std::cerr << "Failed create vdevice, status = " << vdevice.status() << std::endl;
+        return vdevice.status();
+    }
+
+    auto network_group = configure_network_group(*vdevice.value());
+    if (!network_group) {
+        std::cerr << "Failed to configure network group " << HEF_FILE << std::endl;
+        return network_group.status();
+    }
+
+    auto vstreams = VStreamsBuilder::create_vstreams(*network_group.value(), QUANTIZED, FORMAT_TYPE);
+    if (!vstreams) {
+        std::cerr << "Failed creating vstreams " << vstreams.status() << std::endl;
+        return vstreams.status();
+    }
+
+    if (vstreams->first.size() > MAX_LAYER_EDGES || vstreams->second.size() > MAX_LAYER_EDGES) {
+        std::cerr << "Trying to infer network with too many input/output virtual streams, Maximum amount is " <<
+        MAX_LAYER_EDGES << " (either change HEF or change the definition of MAX_LAYER_EDGES)"<< std::endl;
+        return HAILO_INVALID_OPERATION;
+    }
+
+    auto activated_network_group = network_group.value()->activate();
+    if (!activated_network_group) {
+        std::cerr << "Failed activated network group "  << activated_network_group.status();
+        return activated_network_group.status();
+    }
+
+    status = infer(vstreams->first, vstreams->second);
+    if (HAILO_SUCCESS != status) {
+        std::cerr << "Inference failed "  << status << std::endl;
+        return status;
+    }
+
+    return HAILO_SUCCESS;
+}
diff --git a/hailort/libhailort/examples/cpp/multi_network_vstream_example.cpp b/hailort/libhailort/examples/cpp/multi_network_vstream_example.cpp
new file mode 100644 (file)
index 0000000..1deed94
--- /dev/null
@@ -0,0 +1,250 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file multi_network_vstream_example.cpp
+ * This example demonstrates multi network with virtual streams over c++
+ **/
+
+#include "hailo/hailort.hpp"
+
+#include <iostream>
+
+#define HEF_FILE ("hefs/multi_network_shortcut_net.hef")
+constexpr size_t INFER_FRAME_COUNT = 100;
+constexpr bool QUANTIZED = true;
+constexpr hailo_format_type_t FORMAT_TYPE = HAILO_FORMAT_TYPE_AUTO;
+constexpr size_t MAX_LAYER_EDGES = 16;
+constexpr size_t NET_GROUPS_COUNT = 1;
+constexpr size_t NET_COUNT = 2;
+constexpr size_t FIRST_NET_BATCH_SIZE = 1;
+constexpr size_t SECOND_NET_BATCH_SIZE = 2;
+constexpr uint32_t DEVICE_COUNT = 1;
+
+using hailort::VDevice;
+using hailort::Hef;
+using hailort::Expected;
+using hailort::make_unexpected;
+using hailort::ConfiguredNetworkGroup;
+using hailort::VStreamsBuilder;
+using hailort::InputVStream;
+using hailort::OutputVStream;
+using hailort::MemoryView;
+using InOutVStreams = std::pair<std::vector<InputVStream>, std::vector<OutputVStream>>;
+
+
+Expected<std::shared_ptr<ConfiguredNetworkGroup>> configure_network_group(VDevice &vdevice, Hef &hef, uint16_t batch_size[NET_COUNT])
+{
+    auto configure_params = hef.create_configure_params(HAILO_STREAM_INTERFACE_PCIE);
+    if (!configure_params) {
+        std::cerr << "Failed to create configure params" << std::endl;
+        return make_unexpected(configure_params.status());
+    }
+    if (NET_GROUPS_COUNT != configure_params->size()) {
+        std::cerr << "Invalid amount of network groups configure params" << std::endl;
+        return make_unexpected(HAILO_INTERNAL_FAILURE);
+    }
+
+    // Modify batch_size for each network
+    size_t net_index = 0;
+    for (auto &net_name_params_pair : configure_params->begin()->second.network_params_by_name) {
+        net_name_params_pair.second.batch_size = batch_size[net_index];
+        net_index++;
+    }
+
+    auto network_groups = vdevice.configure(hef, configure_params.value());
+    if (!network_groups) {
+        std::cerr << "Failed to configure vdevice" << std::endl;
+        return make_unexpected(network_groups.status());
+    }
+
+    if (NET_GROUPS_COUNT != network_groups->size()) {
+        std::cerr << "Invalid amount of network groups" << std::endl;
+        return make_unexpected(HAILO_INTERNAL_FAILURE);
+    }
+
+    return std::move(network_groups->at(0));
+}
+
+Expected<std::vector<hailo_network_info_t>> get_network_infos(ConfiguredNetworkGroup &net_group)
+{
+    auto networks_infos = net_group.get_network_infos();
+    if (!networks_infos) {
+        std::cerr << "Failed to get networks infos of network group " << net_group.get_network_group_name() << std::endl;
+        return make_unexpected(networks_infos.status());
+    }
+    if (NET_COUNT != networks_infos->size()) {
+        std::cerr << "Invalid amount of networks in group " << net_group.get_network_group_name() << std::endl;
+        return make_unexpected(networks_infos.status());
+    }
+
+    return networks_infos.release();
+}
+
+Expected<std::map<std::string, InOutVStreams>> create_vstreams_per_network(ConfiguredNetworkGroup &net_group,
+    std::vector<hailo_network_info_t> &networks_infos)
+{
+    // Create vstreams for each network
+    std::map<std::string, InOutVStreams> networks_vstreams;
+    for (auto &network_info : networks_infos) {
+        auto vstreams = VStreamsBuilder::create_vstreams(net_group, QUANTIZED, FORMAT_TYPE, network_info.name);
+        if (!vstreams) {
+            std::cerr << "Failed to create vstreams for network " << network_info.name << std::endl;
+            return make_unexpected(vstreams.status());
+        }
+
+        if (vstreams->first.size() > MAX_LAYER_EDGES || vstreams->second.size() > MAX_LAYER_EDGES) {
+            std::cerr << "Trying to infer network with too many input/output virtual streams, Maximum amount is " <<
+            MAX_LAYER_EDGES << " (either change HEF or change the definition of MAX_LAYER_EDGES)"<< std::endl;
+            return make_unexpected(HAILO_INVALID_OPERATION);
+        }
+
+        networks_vstreams.emplace(network_info.name, vstreams.release());
+    }
+
+    return networks_vstreams;
+}
+
+void write_all(InputVStream &input, hailo_status &status, uint16_t batch_size)
+{
+    std::vector<uint8_t> data(input.get_frame_size());
+    const size_t network_frames_count = INFER_FRAME_COUNT * batch_size;
+    for (size_t i = 0; i < network_frames_count; i++) {
+        status = input.write(MemoryView(data.data(), data.size()));
+        if (HAILO_SUCCESS != status) {
+            return;
+        }
+    }
+    status = HAILO_SUCCESS;
+    return;
+}
+
+void read_all(OutputVStream &output, hailo_status &status, uint16_t batch_size)
+{
+    std::vector<uint8_t> data(output.get_frame_size());
+    const size_t network_frames_count = INFER_FRAME_COUNT * batch_size;
+    for (size_t i = 0; i < network_frames_count; i++) {
+        status = output.read(MemoryView(data.data(), data.size()));
+        if (HAILO_SUCCESS != status) {
+            return;
+        }
+    }
+    status = HAILO_SUCCESS;
+    return;
+}
+
+hailo_status infer(std::map<std::string, InOutVStreams> &network_vstreams_pairs, uint16_t batch_size[NET_COUNT])
+{
+    hailo_status status = HAILO_SUCCESS; // Success oriented
+    hailo_status input_status[NET_COUNT][MAX_LAYER_EDGES];
+    hailo_status output_status[NET_COUNT][MAX_LAYER_EDGES];
+    std::unique_ptr<std::thread> input_threads[NET_COUNT][MAX_LAYER_EDGES];
+    std::unique_ptr<std::thread> output_threads[NET_COUNT][MAX_LAYER_EDGES];
+    size_t input_threads_count[NET_COUNT] = {0};
+    size_t output_threads_count[NET_COUNT] = {0};
+
+    size_t net_index = 0;
+    for (auto &network_vstream_pair : network_vstreams_pairs) {
+        auto &input_vstreams = network_vstream_pair.second.first;
+        auto &output_vstreams = network_vstream_pair.second.second;
+
+        // Create read threads
+        for (size_t i = 0 ; i < output_vstreams.size(); i++) {
+            output_threads[net_index][i] = std::make_unique<std::thread>(read_all,
+                std::ref(output_vstreams[i]), std::ref(output_status[net_index][i]), batch_size[net_index]);
+        }
+
+        // Create write threads
+        for (size_t i = 0 ; i < input_vstreams.size(); i++) {
+            input_threads[net_index][i] = std::make_unique<std::thread>(write_all,
+                std::ref(input_vstreams[i]), std::ref(input_status[net_index][i]), batch_size[net_index]);
+        }
+
+        input_threads_count[net_index] = input_vstreams.size();
+        output_threads_count[net_index] = output_vstreams.size();
+        net_index++;
+    }
+
+    // Join write threads
+    for (net_index = 0; net_index < NET_COUNT; net_index++) {
+        for (size_t thread_index = 0; thread_index < input_threads_count[net_index]; thread_index++) {
+            input_threads[net_index][thread_index]->join();
+            if (HAILO_SUCCESS != input_status[net_index][thread_index]) {
+                status = input_status[net_index][thread_index];
+            }
+        }
+    }
+    // Join read threads
+    for (net_index = 0; net_index < NET_COUNT; net_index++) {
+        for (size_t thread_index = 0; thread_index < output_threads_count[net_index]; thread_index++) {
+            output_threads[net_index][thread_index]->join();
+            if (HAILO_SUCCESS != output_status[net_index][thread_index]) {
+                status = output_status[net_index][thread_index];
+            }
+        }
+    }
+
+    if (HAILO_SUCCESS == status) {
+        std::cout << "Inference finished successfully" << std::endl;
+    }
+
+    return status;
+}
+
+int main()
+{
+    uint16_t batch_size[NET_COUNT] = {FIRST_NET_BATCH_SIZE, SECOND_NET_BATCH_SIZE};
+
+    hailo_vdevice_params_t params;
+    auto status = hailo_init_vdevice_params(&params);
+    if (HAILO_SUCCESS != status) {
+        std::cerr << "Failed init vdevice_params, status = " << status << std::endl;
+        return status;
+    }
+
+    params.device_count = DEVICE_COUNT;
+    auto vdevice = VDevice::create(params);
+    if (!vdevice) {
+        std::cerr << "Failed create vdevice, status = " << vdevice.status() << std::endl;
+        return vdevice.status();
+    }
+
+    auto hef = Hef::create(HEF_FILE);
+    if (!hef) {
+        std::cerr << "Failed to create hef: " << HEF_FILE  << ", status = " << hef.status() << std::endl;
+        return hef.status();
+    }
+
+    auto network_group = configure_network_group(*vdevice.value(), hef.value(), batch_size);
+    if (!network_group) {
+        std::cerr << "Failed to configure network group, status = " << network_group.status() << std::endl;
+        return network_group.status();
+    }
+
+    auto network_infos = get_network_infos(*network_group.value());
+    if (!network_infos) {
+        std::cerr << "Failed to get network infos, status = " << network_infos.status() << std::endl;
+        return network_group.status();
+    }
+
+    auto vstreams = create_vstreams_per_network(*network_group.value(), network_infos.value());
+    if (!vstreams) {
+        std::cerr << "Failed creating vstreams, status = " << vstreams.status() << std::endl;
+        return vstreams.status();
+    }
+
+    auto activated_network_group = network_group.value()->activate();
+    if (!activated_network_group) {
+        std::cerr << "Failed activated network group, status = "  << activated_network_group.status();
+        return activated_network_group.status();
+    }
+
+    status = infer(vstreams.value(), batch_size);
+    if (HAILO_SUCCESS != status) {
+        std::cerr << "Inference failed, status = "  << status << std::endl;
+        return status;
+    }
+
+    return HAILO_SUCCESS;
+}
diff --git a/hailort/libhailort/examples/cpp/raw_streams_example.cpp b/hailort/libhailort/examples/cpp/raw_streams_example.cpp
new file mode 100644 (file)
index 0000000..c336bf3
--- /dev/null
@@ -0,0 +1,187 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @ file raw_streams_example
+ * This example demonstrates using low level streams over c++
+ **/
+
+#include "hailo/hailort.hpp"
+
+#include <iostream>
+
+#define HEF_FILE ("hefs/shortcut_net.hef")
+constexpr size_t FRAMES_COUNT = 100;
+constexpr bool QUANTIZED = true;
+constexpr hailo_format_type_t FORMAT_TYPE = HAILO_FORMAT_TYPE_AUTO;
+constexpr size_t MAX_LAYER_EDGES = 16;
+
+using hailort::Device;
+using hailort::Hef;
+using hailort::Expected;
+using hailort::make_unexpected;
+using hailort::ConfiguredNetworkGroup;
+using hailort::VStreamsBuilder;
+using hailort::InputStream;
+using hailort::InputTransformContext;
+using hailort::OutputTransformContext;
+using hailort::OutputStream;
+using hailort::MemoryView;
+using hailort::InputStreamRefVector;
+using hailort::OutputStreamRefVector;
+
+
+Expected<std::shared_ptr<ConfiguredNetworkGroup>> configure_network_group(Device &device)
+{
+    auto hef = Hef::create(HEF_FILE);
+    if (!hef) {
+        return make_unexpected(hef.status());
+    }
+
+    auto configure_params = hef->create_configure_params(HAILO_STREAM_INTERFACE_PCIE);
+    if (!configure_params) {
+        return make_unexpected(configure_params.status());
+    }
+
+    auto network_groups = device.configure(hef.value(), configure_params.value());
+    if (!network_groups) {
+        return make_unexpected(network_groups.status());
+    }
+
+    if (1 != network_groups->size()) {
+        std::cerr << "Invalid amount of network groups" << std::endl;
+        return make_unexpected(HAILO_INTERNAL_FAILURE);
+    }
+
+    return std::move(network_groups->at(0));
+}
+
+void write_all(InputStream &input, hailo_status &status)
+{
+    auto transform_context = InputTransformContext::create(input.get_info(), QUANTIZED, FORMAT_TYPE);
+    if (!transform_context) {
+        status = transform_context.status();
+        return;
+    }
+
+    std::vector<uint8_t> host_data(transform_context.value()->get_src_frame_size());
+    std::vector<uint8_t> hw_data(input.get_frame_size());
+
+    for (size_t i = 0; i < FRAMES_COUNT; i++) {
+        status = transform_context.value()->transform(MemoryView(host_data.data(), host_data.size()),
+            MemoryView(hw_data.data(), hw_data.size()));
+        if (HAILO_SUCCESS != status) {
+            return;
+        }
+
+        status = input.write(MemoryView(hw_data.data(), hw_data.size()));
+        if (HAILO_SUCCESS != status) {
+            return ;
+        }
+    }
+    return;
+}
+
+void read_all(OutputStream &output, hailo_status &status)
+{
+    auto transform_context = OutputTransformContext::create(output.get_info(), QUANTIZED, FORMAT_TYPE);
+    if (!transform_context) {
+        status = transform_context.status();
+        return;
+    }
+
+    std::vector<uint8_t> hw_data(output.get_frame_size());
+    std::vector<uint8_t> host_data(transform_context.value()->get_dst_frame_size());
+
+    for (size_t i = 0; i < FRAMES_COUNT; i++) {
+        status = output.read(MemoryView(hw_data.data(), hw_data.size()));
+        if (HAILO_SUCCESS != status) {
+            return;
+        }
+
+        status = transform_context.value()->transform(MemoryView(hw_data.data(), hw_data.size()),
+            MemoryView(host_data.data(), host_data.size()));
+        if (HAILO_SUCCESS != status) {
+            return;
+        }
+    }
+    return;
+}
+
+hailo_status infer(InputStreamRefVector &input_streams, OutputStreamRefVector &output_streams)
+{
+    hailo_status status = HAILO_SUCCESS; // Success oriented
+    hailo_status input_status[MAX_LAYER_EDGES] = {HAILO_UNINITIALIZED};
+    hailo_status output_status[MAX_LAYER_EDGES] = {HAILO_UNINITIALIZED};
+    std::unique_ptr<std::thread> input_threads[MAX_LAYER_EDGES];
+    std::unique_ptr<std::thread> output_threads[MAX_LAYER_EDGES];
+    size_t input_thread_index = 0;
+    size_t output_thread_index = 0;
+
+    // Create read threads
+    for (output_thread_index = 0 ; output_thread_index < output_streams.size(); output_thread_index++) {
+        output_threads[output_thread_index] = std::make_unique<std::thread>(read_all,
+            output_streams[output_thread_index], std::ref(output_status[output_thread_index]));
+    }
+
+    // Create write threads
+    for (input_thread_index = 0 ; input_thread_index < input_streams.size(); input_thread_index++) {
+        input_threads[input_thread_index] = std::make_unique<std::thread>(write_all, input_streams[input_thread_index],
+        std::ref(input_status[input_thread_index]));
+    }
+    
+    // Join write threads
+    for (size_t i = 0; i < input_thread_index; i++) {
+        input_threads[i]->join();
+        if (HAILO_SUCCESS != input_status[i]) {
+            status = input_status[i];
+        }
+    }
+
+    // Join read threads
+    for (size_t i = 0; i < output_thread_index; i++) {
+        output_threads[i]->join();
+        if (HAILO_SUCCESS != output_status[i]) {
+            status = output_status[i];
+        }
+    }
+
+    if (HAILO_SUCCESS == status) {
+        std::cout << "Inference finished successfully" << std::endl;
+    }
+
+    return status;
+}
+
+int main()
+{
+    auto device = Device::create_pcie();
+    if (!device) {
+        std::cerr << "Failed create_pcie " << device.status() << std::endl;
+        return device.status();
+    }
+
+    auto network_group = configure_network_group(*device.value());
+    if (!network_group) {
+        std::cerr << "Failed to configure network group " << HEF_FILE << std::endl;
+        return network_group.status();
+    }
+
+    auto inputs = network_group->get()->get_input_streams();
+    auto outputs = network_group->get()->get_output_streams();
+
+    auto activated_network_group = network_group.value()->activate();
+    if (!activated_network_group) {
+        std::cerr << "Failed activated network group "  << activated_network_group.status();
+        return activated_network_group.status();
+    }
+
+    auto status = infer(inputs, outputs);
+    if (HAILO_SUCCESS != status) {
+        std::cerr << "Inference failed "  << status << std::endl;
+        return status;
+    }
+
+    return HAILO_SUCCESS;
+}
diff --git a/hailort/libhailort/examples/cpp/switch_hefs_example.cpp b/hailort/libhailort/examples/cpp/switch_hefs_example.cpp
new file mode 100644 (file)
index 0000000..aab82c8
--- /dev/null
@@ -0,0 +1,196 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @ file switch_hefs_example.cpp
+ * This example demonstrates basic usage of HailoRT streaming api over multiple network groups, using vstreams.
+ * It loads several HEF networks with single/multiple inputs and single/multiple outputs into a Hailo PCIe VDevice and performs a
+ * short inference on each one. 
+ * After inference is finished, the example switches to the next HEF and start inference again.
+ **/
+
+#include "hailo/hailort.hpp"
+
+#include <iostream>
+#include <chrono>
+
+constexpr bool QUANTIZED = true;
+constexpr hailo_format_type_t FORMAT_TYPE = HAILO_FORMAT_TYPE_AUTO;
+constexpr size_t INFER_FRAME_COUNT = 100;
+constexpr size_t RUN_COUNT = 10;
+constexpr uint32_t DEVICE_COUNT = 1;
+
+
+using hailort::VDevice;
+using hailort::Hef;
+using hailort::Expected;
+using hailort::make_unexpected;
+using hailort::ConfiguredNetworkGroup;
+using hailort::VStreamsBuilder;
+using hailort::InputVStream;
+using hailort::OutputVStream;
+using hailort::MemoryView;
+
+
+void write_all(InputVStream &input_vstream, hailo_status &status_out)
+{
+    std::vector<uint8_t> buff(input_vstream.get_frame_size());
+
+    for (size_t i = 0; i < INFER_FRAME_COUNT; i++) {
+        auto status = input_vstream.write(MemoryView(buff.data(), buff.size()));
+        if (HAILO_SUCCESS != status) {
+            status_out = status;
+            return;
+        }
+    }
+    return;
+}
+
+void read_all(OutputVStream &output_vstream, hailo_status &status_out)
+{
+    std::vector<uint8_t> buff(output_vstream.get_frame_size());
+
+    for (size_t i = 0; i < INFER_FRAME_COUNT; i++) {
+        auto status = output_vstream.read(MemoryView(buff.data(), buff.size()));
+        if (HAILO_SUCCESS != status) {
+            status_out = status;
+            return;
+        }
+    }
+    return;
+}
+
+Expected<std::vector<std::pair<std::vector<InputVStream>, std::vector<OutputVStream>>>> build_vstreams(
+    const std::vector<std::shared_ptr<ConfiguredNetworkGroup>> &configured_network_groups)
+{
+    std::vector<std::pair<std::vector<InputVStream>, std::vector<OutputVStream>>> vstreams_per_network_group;
+
+    for (auto &network_group : configured_network_groups) {
+        auto vstreams_exp = VStreamsBuilder::create_vstreams(*network_group, QUANTIZED, FORMAT_TYPE);
+        if (!vstreams_exp) {
+            return make_unexpected(vstreams_exp.status());
+        }
+        vstreams_per_network_group.emplace_back(vstreams_exp.release());
+    }
+    return vstreams_per_network_group;
+}
+
+std::vector<std::unique_ptr<std::thread>> create_read_threads(std::vector<OutputVStream> &vstreams,
+    std::vector<hailo_status> &read_results)
+{
+    std::vector<std::unique_ptr<std::thread>> read_threads;
+
+    read_results.reserve(vstreams.size());
+    for (auto &vstream : vstreams) {
+        read_results.push_back(HAILO_SUCCESS); // Success oriented
+        read_threads.emplace_back(std::make_unique<std::thread>(read_all,
+            std::ref(vstream), std::ref(read_results.back())));
+    }
+    return read_threads;
+}
+
+std::vector<std::unique_ptr<std::thread>> create_write_threads(std::vector<InputVStream> &vstreams,
+    std::vector<hailo_status> &write_results)
+{
+    std::vector<std::unique_ptr<std::thread>> write_threads;
+
+    write_results.reserve(vstreams.size());
+    for (auto &vstream : vstreams) {
+        write_results.push_back(HAILO_SUCCESS); // Success oriented
+        write_threads.emplace_back(std::make_unique<std::thread>(write_all,
+            std::ref(vstream), std::ref(write_results.back())));
+    }
+    return write_threads;
+}
+
+int main()
+{
+    hailo_vdevice_params_t params;
+    auto status = hailo_init_vdevice_params(&params);
+    if (HAILO_SUCCESS != status) {
+        std::cerr << "Failed init vdevice_params, status = " << status << std::endl;
+        return status;
+    }
+
+    params.device_count = DEVICE_COUNT;
+    auto vdevice_exp = VDevice::create(params);
+    if (!vdevice_exp) {
+        std::cerr << "Failed create vdevice, status = " << vdevice_exp.status() << std::endl;
+        return vdevice_exp.status();
+    }
+    auto vdevice = vdevice_exp.release();
+
+    std::vector<std::string> hef_paths = {"hefs/shortcut_net.hef", "hefs/shortcut_net.hef"};
+    std::vector<std::shared_ptr<ConfiguredNetworkGroup>> configured_network_groups;
+
+    for (const auto &path : hef_paths) {
+        auto hef_exp = Hef::create(path);
+        if (!hef_exp) {
+            std::cerr << "Failed to create hef: " << path  << ", status = " << hef_exp.status() << std::endl;
+            return hef_exp.status();
+        }
+        auto hef = hef_exp.release();
+
+        auto added_network_groups = vdevice->configure(hef);
+        if (!added_network_groups) {
+            std::cerr << "Failed to configure vdevice, status = " << added_network_groups.status() << std::endl;
+            return added_network_groups.status();
+        }
+        configured_network_groups.insert(configured_network_groups.end(), added_network_groups->begin(),
+            added_network_groups->end());
+    }
+
+    auto vstreams_per_network_group_exp = build_vstreams(configured_network_groups);
+    if (!vstreams_per_network_group_exp) {
+        std::cerr << "Failed to create vstreams, status = " << vstreams_per_network_group_exp.status() << std::endl;
+        return vstreams_per_network_group_exp.status();
+    }
+    auto vstreams_per_network_group = vstreams_per_network_group_exp.release();
+
+    for (size_t i = 0; i < RUN_COUNT; i++) {
+        for (size_t network_group_idx = 0; network_group_idx < configured_network_groups.size(); network_group_idx++) {
+            auto activated_network_group_exp = configured_network_groups[network_group_idx]->activate();
+
+            if (!activated_network_group_exp) {
+                std::cerr << "Failed to activate network group, status = "  << activated_network_group_exp.status() << std::endl;
+                return activated_network_group_exp.status();
+            }
+
+            // Create send/recv threads
+            std::vector<hailo_status> read_results;
+            auto read_threads = create_read_threads(vstreams_per_network_group[network_group_idx].second, read_results);
+
+            std::vector<hailo_status> write_results;
+            auto write_threads = create_write_threads(vstreams_per_network_group[network_group_idx].first, write_results);
+
+            // Join threads and validate results
+            for (auto &th : write_threads) {
+                if (th->joinable()) {
+                    th->join();
+                }
+            }
+            for (auto &th : read_threads) {
+                if (th->joinable()) {
+                    th->join();
+                }
+            }
+
+            for (auto &thread_status : write_results) {
+                if (HAILO_SUCCESS != thread_status) {
+                    std::cerr << "Inference failed, status = "  << thread_status << std::endl;
+                    return thread_status;
+                }
+            }
+            for (auto &thread_status : read_results) {
+                if (HAILO_SUCCESS != thread_status) {
+                    std::cerr << "Inference failed, status = "  << thread_status << std::endl;
+                    return thread_status;
+                }
+            }
+        }
+    }
+
+    std::cout << "Inference finished successfully" << std::endl;
+    return HAILO_SUCCESS;
+}
\ No newline at end of file
diff --git a/hailort/libhailort/examples/cpp/switch_hefs_example_threads_reuse.cpp b/hailort/libhailort/examples/cpp/switch_hefs_example_threads_reuse.cpp
new file mode 100644 (file)
index 0000000..735a5f8
--- /dev/null
@@ -0,0 +1,301 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**git checkout HRT-4862-change-switch_hefs_example-to-r
+ * @ file switch_hefs_example_threads_reuse.cpp
+ * This example demonstrates basic usage of HailoRT streaming api over multiple networks, using vstreams.
+ * It loads several HEF networks with single/multiple inputs and single/multiple outputs into a Hailo PCIe VDevice and performs a
+ * short inference on each one. 
+ * After inference is finished, the example switches to the next HEF and start inference again.
+ **/
+
+#include "hailo/hailort.hpp"
+
+#include <iostream>
+#include <chrono>
+
+constexpr bool QUANTIZED = true;
+constexpr hailo_format_type_t FORMAT_TYPE = HAILO_FORMAT_TYPE_AUTO;
+
+constexpr size_t INFER_FRAME_COUNT = 100;
+constexpr size_t RUN_COUNT = 10;
+constexpr std::chrono::milliseconds WAIT_FOR_ACTIVATION_TIMEOUT_MS(10);
+constexpr uint32_t DEVICE_COUNT = 1;
+
+using hailort::VDevice;
+using hailort::Hef;
+using hailort::ConfiguredNetworkGroup;
+using hailort::ActivatedNetworkGroup;
+using hailort::VStreamsBuilder;
+using hailort::InputVStream;
+using hailort::OutputVStream;
+using hailort::MemoryView;
+
+
+#include <mutex>
+#include <condition_variable>
+
+class SyncObject final {
+/* Synchronization class used to make sure I/O threads are blocking while their network_group is not activated  */
+public:
+    explicit SyncObject(size_t count) : m_original_count(count), m_count(count), m_all_arrived(false), m_mutex(), m_cv(), m_is_active(true)
+        {};
+
+    /* In main thread we wait until I/O threads are done (0 == m_count),
+       signaling the I/O threads only after deactivating their network_group and resetting m_count to m_original_count */
+    void wait_all(std::unique_ptr<ActivatedNetworkGroup> &&activated_network_group)
+    {
+        if (!m_is_active.load()) {
+            return;
+        }
+        std::unique_lock<std::mutex> lock(m_mutex);
+        m_cv.wait(lock, [this] { return ((0 == m_count) || !m_is_active); });
+        activated_network_group.reset();
+        m_count = m_original_count;
+        m_all_arrived = true;
+        m_cv.notify_all();
+    }
+
+    /* In I/O threads we wait until signaled by main thread (true == m_all_arrived),
+       resetting m_all_arrived to false to make sure it was setted by 'wait_all' call */
+    void notify_and_wait()
+    {
+        if (!m_is_active.load()) {
+            return;
+        }
+        std::unique_lock<std::mutex> lock(m_mutex);
+        m_all_arrived = false;
+        --m_count;
+        m_cv.notify_all();
+        m_cv.wait(lock, [this] { return ((m_all_arrived) || !m_is_active); });
+    }
+
+    void terminate()
+    {
+        {
+            std::unique_lock<std::mutex> lock(m_mutex);
+            m_is_active.store(false);
+        }
+        m_cv.notify_all();
+    }
+
+private:
+    const size_t m_original_count;
+    std::atomic_size_t m_count;
+    std::atomic_bool m_all_arrived;
+
+    std::mutex m_mutex;
+    std::condition_variable m_cv;
+    std::atomic_bool m_is_active;
+};
+
+
+void write_all(std::shared_ptr<ConfiguredNetworkGroup> network_group, InputVStream &input_vstream,
+    std::shared_ptr<SyncObject> sync_object, std::shared_ptr<std::atomic_bool> should_threads_run, hailo_status &status_out)
+{
+    std::vector<uint8_t> buff(input_vstream.get_frame_size());
+
+    auto status = HAILO_UNINITIALIZED;
+    while (true) {
+        if (!(*should_threads_run)) {
+            break;
+        }
+        status = network_group->wait_for_activation(WAIT_FOR_ACTIVATION_TIMEOUT_MS);
+        if (HAILO_TIMEOUT == status) {
+            continue;
+        } else if (HAILO_SUCCESS != status) {
+            std::cerr << "Wait for network group activation failed. status = " << status << std::endl;
+            status_out = status;
+            return;
+        }
+
+        for (size_t i = 0; i < INFER_FRAME_COUNT; i++) {
+            status = input_vstream.write(MemoryView(buff.data(), buff.size()));
+            if (HAILO_SUCCESS != status) {
+                status_out = status;
+                return;
+            }
+        }
+        sync_object->notify_and_wait();
+    }
+    return;
+}
+
+void read_all(std::shared_ptr<ConfiguredNetworkGroup> network_group, OutputVStream &output_vstream,
+    std::shared_ptr<SyncObject> sync_object, std::shared_ptr<std::atomic_bool> should_threads_run, hailo_status &status_out)
+{
+    std::vector<uint8_t> buff(output_vstream.get_frame_size());
+
+    auto status = HAILO_UNINITIALIZED;
+    while (true) {
+        if (!(*should_threads_run)) {
+            break;
+        }
+        status = network_group->wait_for_activation(WAIT_FOR_ACTIVATION_TIMEOUT_MS);
+        if (HAILO_TIMEOUT == status) {
+            continue;
+        } else if (HAILO_SUCCESS != status) {
+            std::cerr << "Wait for network group activation failed. status = " << status << std::endl;
+            status_out = status;
+            return;
+        }
+
+        for (size_t i = 0; i < INFER_FRAME_COUNT; i++) {
+            status = output_vstream.read(MemoryView(buff.data(), buff.size()));
+            if (HAILO_SUCCESS != status) {
+                status_out = status;
+                return;
+            }
+        }
+        sync_object->notify_and_wait();
+    }
+    return;
+}
+
+void network_group_thread_main(std::shared_ptr<ConfiguredNetworkGroup> network_group, std::shared_ptr<SyncObject> sync_object,
+    std::shared_ptr<std::atomic_bool> should_threads_run, hailo_status &status_out)
+{
+    // Create VStreams
+    auto vstreams_exp = VStreamsBuilder::create_vstreams(*network_group, QUANTIZED, FORMAT_TYPE);
+    if (!vstreams_exp) {
+        std::cerr << "Failed to create vstreams, status = " << vstreams_exp.status() << std::endl;
+        status_out = vstreams_exp.status();
+        return;
+    }
+
+    // Create send/recv loops
+    std::vector<std::unique_ptr<std::thread>> recv_ths;
+    std::vector<hailo_status> read_results;
+    read_results.reserve(vstreams_exp->second.size());
+    for (auto &vstream : vstreams_exp->second) {
+        read_results.push_back(HAILO_SUCCESS); // Success oriented
+        recv_ths.emplace_back(std::make_unique<std::thread>(read_all,
+            network_group, std::ref(vstream), sync_object, should_threads_run, std::ref(read_results.back())));
+    }
+
+    std::vector<std::unique_ptr<std::thread>> send_ths;
+    std::vector<hailo_status> write_results;
+    write_results.reserve(vstreams_exp->first.size());
+    for (auto &vstream : vstreams_exp->first) {
+        write_results.push_back(HAILO_SUCCESS); // Success oriented
+        send_ths.emplace_back(std::make_unique<std::thread>(write_all,
+            network_group, std::ref(vstream), std::ref(sync_object), should_threads_run, std::ref(write_results.back())));
+    }
+
+    for (auto &send_th : send_ths) {
+        if (send_th->joinable()) {
+            send_th->join();
+        }
+    }
+    for (auto &recv_th : recv_ths) {
+        if (recv_th->joinable()) {
+            recv_th->join();
+        }
+    }
+
+    for (auto &status : read_results) {
+        if (HAILO_SUCCESS != status) {
+            status_out = status;
+            return;
+        }
+    }
+    for (auto &status : write_results) {
+        if (HAILO_SUCCESS != status) {
+            status_out = status;
+            return;
+        }
+    }
+    status_out = HAILO_SUCCESS;
+    return;
+}
+
+int main()
+{
+    hailo_vdevice_params_t params;
+    auto status = hailo_init_vdevice_params(&params);
+    if (HAILO_SUCCESS != status) {
+        std::cerr << "Failed init vdevice_params, status = " << status << std::endl;
+        return status;
+    }
+
+    params.device_count = DEVICE_COUNT;
+    auto vdevice_exp = VDevice::create(params);
+    if (!vdevice_exp) {
+        std::cerr << "Failed create vdevice, status = " << vdevice_exp.status() << std::endl;
+        return vdevice_exp.status();
+    }
+    auto vdevice = vdevice_exp.release();
+
+    std::vector<std::string> hef_paths = {"hefs/shortcut_net.hef", "hefs/shortcut_net.hef"};
+    std::vector<std::shared_ptr<ConfiguredNetworkGroup>> configured_network_groups;
+
+    for (const auto &path : hef_paths) {
+        auto hef_exp = Hef::create(path);
+        if (!hef_exp) {
+            std::cerr << "Failed to create hef: " << path  << ", status = " << hef_exp.status() << std::endl;
+            return hef_exp.status();
+        }
+        auto hef = hef_exp.release();
+
+        auto added_network_groups = vdevice->configure(hef);
+        if (!added_network_groups) {
+            std::cerr << "Failed to configure vdevice, status = " << added_network_groups.status() << std::endl;
+            return added_network_groups.status();
+        }
+        configured_network_groups.insert(configured_network_groups.end(), added_network_groups->begin(), added_network_groups->end());
+    }
+
+    auto should_threads_run = std::make_shared<std::atomic_bool>(true);
+
+    std::vector<std::shared_ptr<SyncObject>> sync_objects;
+    sync_objects.reserve(configured_network_groups.size());
+    std::vector<hailo_status> threads_results;
+    threads_results.reserve(configured_network_groups.size());
+    std::vector<std::unique_ptr<std::thread>> network_group_threads;
+    network_group_threads.reserve(configured_network_groups.size());
+
+    for (auto network_group : configured_network_groups) {
+        threads_results.push_back(HAILO_UNINITIALIZED);
+        auto vstream_infos = network_group->get_all_vstream_infos();
+        if (!vstream_infos) {
+            std::cerr << "Failed to get vstream infos, status = " << vstream_infos.status() << std::endl;
+            return vstream_infos.status();
+        }
+        sync_objects.emplace_back((std::make_shared<SyncObject>(vstream_infos->size())));
+        network_group_threads.emplace_back(std::make_unique<std::thread>(network_group_thread_main,
+            network_group, sync_objects.back(), should_threads_run, std::ref(threads_results.back())));
+    }
+
+    for (size_t i = 0; i < RUN_COUNT; i++) {
+        for (size_t network_group_idx = 0; network_group_idx < configured_network_groups.size(); network_group_idx++) {
+            auto activated_network_group_exp = configured_network_groups[network_group_idx]->activate();
+            if (!activated_network_group_exp) {
+                std::cerr << "Failed to activate network group, status = "  << activated_network_group_exp.status() << std::endl;
+                return activated_network_group_exp.status();
+            }
+            sync_objects[network_group_idx]->wait_all(activated_network_group_exp.release());
+        }
+    }
+
+    *should_threads_run = false;
+    for (auto &sync_object : sync_objects) {
+        sync_object->terminate();
+    }
+
+    for (auto &th : network_group_threads) {
+        if (th->joinable()) {
+            th->join();
+        }
+    }
+
+    for (auto &thread_status : threads_results) {
+        if (HAILO_SUCCESS != thread_status) {
+            std::cerr << "Inference failed, status = "  << thread_status << std::endl;
+            return thread_status;
+        }
+    }
+
+    std::cout << "Inference finished successfully" << std::endl;
+    return HAILO_SUCCESS;
+}
\ No newline at end of file
diff --git a/hailort/libhailort/examples/cpp/vstreams_example.cpp b/hailort/libhailort/examples/cpp/vstreams_example.cpp
new file mode 100644 (file)
index 0000000..d87f800
--- /dev/null
@@ -0,0 +1,176 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @ file vstreams_example
+ * This example demonstrates using virtual streams over c++
+ **/
+
+#include "hailo/hailort.hpp"
+
+#include <iostream>
+
+#define HEF_FILE ("hefs/shortcut_net.hef")
+constexpr size_t FRAMES_COUNT = 100;
+constexpr bool QUANTIZED = true;
+constexpr hailo_format_type_t FORMAT_TYPE = HAILO_FORMAT_TYPE_AUTO;
+constexpr size_t MAX_LAYER_EDGES = 16;
+
+
+using hailort::VDevice;
+using hailort::Hef;
+using hailort::Expected;
+using hailort::make_unexpected;
+using hailort::ConfiguredNetworkGroup;
+using hailort::VStreamsBuilder;
+using hailort::InputVStream;
+using hailort::OutputVStream;
+using hailort::MemoryView;
+
+
+Expected<std::shared_ptr<ConfiguredNetworkGroup>> configure_network_group(VDevice &vdevice)
+{
+    auto hef = Hef::create(HEF_FILE);
+    if (!hef) {
+        return make_unexpected(hef.status());
+    }
+
+    auto configure_params = hef->create_configure_params(HAILO_STREAM_INTERFACE_PCIE);
+    if (!configure_params) {
+        return make_unexpected(configure_params.status());
+    }
+
+    auto network_groups = vdevice.configure(hef.value(), configure_params.value());
+    if (!network_groups) {
+        return make_unexpected(network_groups.status());
+    }
+
+    if (1 != network_groups->size()) {
+        std::cerr << "Invalid amount of network groups" << std::endl;
+        return make_unexpected(HAILO_INTERNAL_FAILURE);
+    }
+
+    return std::move(network_groups->at(0));
+}
+
+void write_all(InputVStream &input, hailo_status &status)
+{
+    std::vector<uint8_t> data(input.get_frame_size());
+    for (size_t i = 0; i < FRAMES_COUNT; i++) {
+        status = input.write(MemoryView(data.data(), data.size()));
+        if (HAILO_SUCCESS != status) {
+            return;
+        }
+    }
+
+    // Flushing is not mandatory here
+    status = input.flush();
+    if (HAILO_SUCCESS != status) {
+        std::cerr << "Failed flushing input vstream" << std::endl;
+        return;
+    }
+
+    status = HAILO_SUCCESS;
+    return;
+}
+
+void read_all(OutputVStream &output, hailo_status &status)
+{
+    std::vector<uint8_t> data(output.get_frame_size());
+    for (size_t i = 0; i < FRAMES_COUNT; i++) {
+        status = output.read(MemoryView(data.data(), data.size()));
+        if (HAILO_SUCCESS != status) {
+            return;
+        }
+    }
+    status = HAILO_SUCCESS;
+    return;
+}
+
+hailo_status infer(std::vector<InputVStream> &input_streams, std::vector<OutputVStream> &output_streams)
+{
+
+    hailo_status status = HAILO_SUCCESS; // Success oriented
+    hailo_status input_status[MAX_LAYER_EDGES] = {HAILO_UNINITIALIZED};
+    hailo_status output_status[MAX_LAYER_EDGES] = {HAILO_UNINITIALIZED};
+    std::unique_ptr<std::thread> input_threads[MAX_LAYER_EDGES];
+    std::unique_ptr<std::thread> output_threads[MAX_LAYER_EDGES];
+    size_t input_thread_index = 0;
+    size_t output_thread_index = 0;
+
+    // Create read threads
+    for (output_thread_index = 0 ; output_thread_index < output_streams.size(); output_thread_index++) {
+        output_threads[output_thread_index] = std::make_unique<std::thread>(read_all,
+            std::ref(output_streams[output_thread_index]), std::ref(output_status[output_thread_index]));
+    }
+
+    // Create write threads
+    for (input_thread_index = 0 ; input_thread_index < input_streams.size(); input_thread_index++) {
+        input_threads[input_thread_index] = std::make_unique<std::thread>(write_all,
+            std::ref(input_streams[input_thread_index]), std::ref(input_status[input_thread_index]));
+    }
+
+    // Join write threads
+    for (size_t i = 0; i < input_thread_index; i++) {
+        input_threads[i]->join();
+        if (HAILO_SUCCESS != input_status[i]) {
+            status = input_status[i];
+        }
+    }
+
+    // Join read threads
+    for (size_t i = 0; i < output_thread_index; i++) {
+        output_threads[i]->join();
+        if (HAILO_SUCCESS != output_status[i]) {
+            status = output_status[i];
+        }
+    }
+
+    if (HAILO_SUCCESS == status) {
+        std::cout << "Inference finished successfully" << std::endl;
+    }
+
+    return status;
+}
+
+int main()
+{
+    auto vdevice = VDevice::create();
+    if (!vdevice) {
+        std::cerr << "Failed create vdevice, status = " << vdevice.status() << std::endl;
+        return vdevice.status();
+    }
+
+    auto network_group = configure_network_group(*vdevice.value());
+    if (!network_group) {
+        std::cerr << "Failed to configure network group " << HEF_FILE << std::endl;
+        return network_group.status();
+    }
+
+    auto vstreams = VStreamsBuilder::create_vstreams(*network_group.value(), QUANTIZED, FORMAT_TYPE);
+    if (!vstreams) {
+        std::cerr << "Failed creating vstreams " << vstreams.status() << std::endl;
+        return vstreams.status();
+    }
+
+    if (vstreams->first.size() > MAX_LAYER_EDGES || vstreams->second.size() > MAX_LAYER_EDGES) {
+        std::cerr << "Trying to infer network with too many input/output virtual streams, Maximum amount is " <<
+        MAX_LAYER_EDGES << " (either change HEF or change the definition of MAX_LAYER_EDGES)"<< std::endl;
+        return HAILO_INVALID_OPERATION;
+    }
+
+    auto activated_network_group = network_group.value()->activate();
+    if (!activated_network_group) {
+        std::cerr << "Failed activated network group "  << activated_network_group.status();
+        return activated_network_group.status();
+    }
+
+    auto status = infer(vstreams->first, vstreams->second);
+    if (HAILO_SUCCESS != status) {
+        std::cerr << "Inference failed "  << status << std::endl;
+        return status;
+    }
+
+    return HAILO_SUCCESS;
+}
diff --git a/hailort/libhailort/hef.proto b/hailort/libhailort/hef.proto
new file mode 100644 (file)
index 0000000..d8d5cdf
--- /dev/null
@@ -0,0 +1,512 @@
+syntax = "proto3";
+
+message ProtoHEFHef {
+    ProtoHEFHeader header = 1;
+    repeated ProtoHEFNetworkGroup network_groups = 2;
+    bytes mapping = 3;
+    ProtoHEFIncludedFeatures included_features = 4;
+    repeated ProtoHEFExtension extensions = 5;
+    repeated ProtoHEFOptionalExtension optional_extensions = 6;
+}
+
+message ProtoHEFIncludedFeatures {
+    bool abbale = 1;
+    bool posted_writes = 2;
+    bool ddr = 3;
+    uint32 number_of_contexts = 4;
+    bool compressed_params = 5;
+    bool transpose_component = 6;
+    bool padded_ddr_buffers = 7;
+    bool sequencer = 8;
+}
+
+enum ProtoHEFExtensionType {
+    ABBALE = 0;
+    POSTED_WRITES = 1;
+    DDR = 2;
+    PADDED_DDR_BUFFERS = 3;
+    IS_MULTI_CONTEXTS = 4;
+    COMPRESSED_PARAMS = 5;
+    TRANSPOSE_COMPONENT = 6;
+    TEST_ONE = 7;
+    MULTI_NETWORK_VARIABLE_BATCH_SIZE = 8;
+    IS_NMS_MULTI_CONTEXT = 9;
+    OFFLOAD_ARGMAX = 10;
+    UNUSED = 0XFFFF;
+}
+
+message ProtoHEFExtension {
+    uint32 type_index = 1;
+    string name = 2;
+}
+
+message ProtoHEFOptionalExtension  {
+    uint32 type_index = 1;
+    string name = 2;
+}
+
+message ProtoHEFHeader {
+    // The target hw_arch the HEF is compiled to
+    ProtoHEFHwArch hw_arch = 1;
+
+    // Timestamp of the time the HEF was created
+    uint64 timestamp = 2;
+
+    // The version of the SDK the HEF has been created with
+    string sdk_version = 3;
+
+    // The format version of the hef file 
+    uint64 version = 4;
+}
+
+// Enum describing the different possible hw_archs
+enum ProtoHEFHwArch {
+    PROTO__HW_ARCH__HAILO8 = 0;
+    PROTO__HW_ARCH__HAILO8P = 1;
+    PROTO__HW_ARCH__HAILO8R = 2;
+
+    // Reserving low numbers to public hw archs
+    PROTO__HW_ARCH__SAGE_A0 = 100;
+    PROTO__HW_ARCH__SAGE_B0 = 101;
+    PROTO__HW_ARCH__PAPRIKA_B0 = 102;
+    PROTO__HW_ARCH__MERCURY = 103;
+    PROTO__HW_ARCH__GINGER = 104;
+    PROTO__HW_ARCH__LAVENDER = 105;
+}
+
+message ProtoHEFNetworkGroup {
+    // Metadata describing the network_group
+    ProtoHEFNetworkGroupMetadata network_group_metadata = 1;
+
+    // The preliminary configuration of the network_group
+    ProtoHEFPreliminaryConfig preliminary_config = 2;
+
+    // The contexts of the network_group
+    repeated ProtoHEFContext contexts = 3;
+
+    // List of sorted output names according to their order
+    repeated string sorted_outputs_order = 4;
+
+    // Metadata for fused layers
+    ProtoHEFFusedLayersMetadata fused_layers_metadata = 5;
+
+    // Connected component names ordered by index
+    repeated string networks_names = 6;
+}
+
+message ProtoHEFNetworkGroupMetadata {
+    // The name of the network_group
+    string network_group_name = 1;
+
+    // The index of the network_group (execution order)
+    uint32 network_group_index = 2;
+
+    // Indicates whether the network is transposed
+    bool transposed_net = 3;
+
+    // The bottleneck post placement fps
+    double bottleneck_fps = 4;
+
+    // Number of pcie channels being used for config
+    uint32 cfg_channels_count = 5;
+}
+
+message ProtoHEFFusedLayersMetadata {
+    bool network_has_fused_layers = 1;
+    repeated string updated_sorted_output_names = 2;
+    repeated ProtoHEFEdgeLayerFused fused_layers = 3;
+}
+
+message ProtoHEFEdgeLayerFused {
+    ProtoHEFEdgeLayerInfo layer_info = 1;
+    repeated string defused_layers_names = 2;
+    ProtoHEFNmsInfo nms_info = 3;
+}
+
+message ProtoHEFContext {
+    // The index of the context
+    uint32 context_index = 1;
+
+    // Triggers and their actions
+    repeated ProtoHEFOperation operations = 2;
+
+    // Metadata descibing the context
+    ProtoHEFContextMetadata metadata = 3;
+}
+
+message ProtoHEFOperation {
+    // The trigger for the operation
+    ProtoHEFTrigger trigger = 1;
+
+    // The actions that would be performed as part of the operation
+    repeated ProtoHEFAction actions = 2;
+}
+
+// An object describing a trigger
+message ProtoHEFTrigger {
+    uint32 unique_id = 1;
+    oneof trigger {
+        ProtoHEFTriggerLcu trigger_lcu = 2;
+        ProtoHEFTriggerNone trigger_none = 3;
+        ProtoHEFTriggerAllDataWasSentToHostPCIe trigger_all_data_was_sent = 4;
+        ProtoHEFTriggerAllDataWasReceivedFromHostPCIe trigger_all_data_was_received = 5;
+        ProtoHEFTriggerNms trigger_nms = 6;
+        ProtoHEFWaitDmaIdleTrigger trigger_dma_idle = 7;
+    }
+}
+
+// Trigger raised by LCU
+message ProtoHEFTriggerLcu {
+    // The cluster index of the lcu
+    uint32 cluster_index = 1;
+
+    // The lcu index of the lcu
+    uint32 lcu_index = 2;
+}
+
+// Trigger raised by NMS
+message ProtoHEFTriggerNms {
+    // NOTE: reshape-NMS interface is hard-coded 0.
+    uint32 aggregator_index = 1;
+    uint32 pred_cluster_ob_index = 2;
+    uint32 pred_cluster_ob_cluster_index = 3;
+    uint32 pred_cluster_ob_interface = 4;
+    uint32 succ_prepost_ob_index = 5;
+    uint32 succ_prepost_ob_interface = 6;
+}
+
+// Trigger type of None, Can be performed right away
+message ProtoHEFTriggerNone {
+}
+
+// Trigger indicating all of the data was sent to the host
+message ProtoHEFTriggerAllDataWasSentToHostPCIe {
+    uint32 shmifo_index = 1;
+}
+
+// Trigger indicating all of the data was received
+message ProtoHEFTriggerAllDataWasReceivedFromHostPCIe {
+    uint32 shmifo_index = 1;
+}
+
+// Trigger indicating all data was sent to host, when variable
+// amount of output data.
+message ProtoHEFWaitDmaIdleTrigger {
+    uint32 shmifo_index = 1;
+}
+
+// An object describing possible actions
+message ProtoHEFAction {
+    uint32 unique_id = 1;
+    oneof action {
+        ProtoHEFActionWriteData write_data = 2;
+        ProtoHEFActionWriteDataCcw write_data_ccw = 3;
+        ProtoHEFActionWriteCompressedData write_compressed_data = 4;
+        ProtoHEFActionEnableSequencer enable_sequencer = 5;
+        ProtoHEFActionWaitForSequencer wait_for_seqeuncer = 6;
+        ProtoHEFActionDisableLcu disable_lcu = 7;
+        ProtoHEFActionEnableLcu enable_lcu = 8;
+        ProtoHEFActionNone none = 9;
+        ProtoHEFActionAllowInputDataflow allow_input_dataflow = 10;
+        ProtoHEFActionWaitForModuleConfigDone wait_for_module_config_done = 11;
+    }
+}
+
+message ProtoHEFActionWriteData {
+    // The address to write the data to
+    uint64 address = 1;
+
+    // The data that would be written
+    bytes data = 2;
+}
+
+message ProtoHEFActionWriteDataCcw {
+    // The data that would be written
+    bytes data = 1;
+    uint32 cfg_channel_index = 2;
+}
+
+message ProtoHEFActionWriteCompressedData {
+    // The address to write the data to
+    uint64 address = 1;
+
+    // The data that would be written
+    bytes data = 2;
+}
+
+message InitialL3 {
+    // L3 cut index sequencer should start from
+    uint32 initial_l3_index = 1;
+    // Offset in the L3 (in bytes)
+    uint32 initial_l3_offset = 2;
+    bool includes_initial_l3_info = 3;
+}
+
+message ProtoHEFActionEnableSequencer {
+    // Index of the cluster of the sequencer
+    uint32 cluster_index = 1;
+    // Initial L3 sequencer uses
+    uint32 initial_l3_legacy = 2; // backwards compatability for old hailort 4.0.0
+    InitialL3 initial_l3_info = 11;
+    // Bitmap of configured APUs
+    uint32 active_apu_bitmap = 3;
+    // Bitmap of configured subclusters
+    uint64 active_sc_bitmap = 4;
+    // Bitmap of configured l2 memories
+    uint64 active_l2_bitmap = 5;
+    // Bitmap of configured input_aligners
+    uint32 active_ia_bitmap = 6;
+
+    // L2 write starting offset of 16 subclusters; to be used in L2 interleaved writing mode
+    uint32 l2_write_0 = 7;
+    uint32 l2_write_1 = 8;
+    uint32 l2_write_2 = 9;
+    uint32 l2_write_3 = 10;
+}
+
+message ProtoHEFActionWaitForSequencer {
+    // Index of the cluster of the sequencer
+    uint32 cluster_index = 1;
+}
+
+message ProtoHEFActionWaitForModuleConfigDone {
+    // Index indicating which module to wait for
+    uint32 index = 1;
+}
+
+message ProtoHEFActionDisableLcu {
+    // Index of the lcu
+    uint32 lcu_index = 1;
+
+    // Index of the cluster of the lcu
+    uint32 cluster_index = 2;
+
+    // Address to indicate where the FW should write to
+    uint32 lcu_enable_address = 5;
+}
+
+message ProtoHEFActionEnableLcu {
+    // Index of the lcu
+    uint32 lcu_index = 1;
+
+    // Index of the cluster of the lcu
+    uint32 cluster_index = 2;
+
+    // Address at lcu to mark as complete apon reach (after lcu_kernel_done_count times)
+    uint32 lcu_kernel_done_address = 3;
+
+    // Amount of times lcu_kernel_done_addess should be reached before marking is done
+    uint32 lcu_kernel_done_count = 4;
+
+    // Address to indicate where the FW should write to
+    uint32 lcu_enable_address = 5;
+
+    // network index index- name given by networks_names 
+    // in ProtoHEFNetworkGroup
+    uint32 network_index = 6;
+}
+
+// None action - Do not do anything
+message ProtoHEFActionNone {
+}
+
+// Allow sending data to input dataflow
+message ProtoHEFActionAllowInputDataflow {
+    uint32 sys_index = 1;
+    ProtoHEFEdgeConnectionType connection_type = 2;
+}
+
+// Indicates that after this data is written, it should mark the DMA write as completed
+message ProtoHEFActionSetDmaCompelte {
+}
+
+// Wait until indication that DMA was complete 
+message ProtoHEFActionWaitForDmaCompelte {
+}
+
+// Preliminary config that will be written at the beginning of the network_group
+message ProtoHEFPreliminaryConfig {
+    repeated ProtoHEFOperation operation = 1;
+}
+
+// Metadata describing the context
+message ProtoHEFContextMetadata {
+    // Context name
+    string name = 1;
+
+    // Information related to the edge layers
+    repeated ProtoHEFEdgeLayer edge_layers = 2;
+
+    // List of triggers which guarantee the end of each cluster
+    repeated ProtoHEFClusterEndGuarantee cluster_end_guarantee = 3;
+
+    // List of triggers by their estimated execution end
+    repeated ProtoHEFOrderedTrigger trigger_order = 4;
+
+    // List of sorted output names according to their order
+    repeated string sorted_outputs_order = 5;
+
+    // information about the HW package
+    ProtoHEFHwPackageInfo hw_package_info = 6;
+
+    // Information about the shmiglue
+    ProtoHEFShmiglueInfo shmiglue_info = 7; // DEPRACATED
+}
+
+// A dummy shmifo the glue logic of "closed" channels will point to
+message ProtoHEFShmiglueInfo {
+    bool should_use_shmiglue = 1; // DEPRACATED
+    uint32 shmiglue_index = 2; // DEPRACATED
+}
+
+message ProtoHEFEdgeLayer {
+    ProtoHEFEdgeLayerDirection direction = 1;
+    ProtoHEFEdgeLayerType edge_layer_type = 2;
+    oneof edge {
+        ProtoHEFEdgeLayerInfo layer_info = 3;
+        ProtoHEFEdgeLayerMux layer_mux = 4;
+    };
+    ProtoHEFContextSwitchInformation context_switch_info = 5;
+    uint32 network_index = 6;
+}
+
+// Enum indicating the direction of the edge layer
+enum ProtoHEFEdgeLayerDirection {
+    // Edge layer is host to device (input to the chip)
+    PROTO__EDGE_LAYER_DIRECTION__HOST_TO_DEVICE = 0;
+
+    // Edge layer is device to host (output from the chip)
+    PROTO__EDGE_LAYER_DIRECTION__DEVICE_TO_HOST = 1;
+}
+
+enum ProtoHEFEdgeLayerType {
+    PROTO__EDGE_LAYER_TYPE__INFO = 0;
+    PROTO__EDGE_LAYER_TYPE__MUX = 1;
+}
+
+message ProtoHEFEdgeLayerInfo {
+    string name = 1;
+    string hn_output_layer_name = 4;
+    ProtoHEFEdgeLayerBase edge_layer_base = 2;
+    ProtoHEFEdgeLayerNumericInfo numeric_info = 3;
+    repeated string original_names = 5;
+    bool transposed = 6;
+}
+
+message ProtoHefEdge {
+    oneof edge {
+        ProtoHEFEdgeLayerInfo layer_info = 1;
+        ProtoHEFEdgeLayerMux layer_mux = 2;
+    };
+}
+
+message ProtoHEFEdgeLayerMux {
+    string name = 1;
+    string hn_output_layer_name = 4;
+    ProtoHEFEdgeLayerBase edge_layer_base = 2;
+    ProtoHEFEdgeLayerMuxData mux_data = 3;
+    repeated ProtoHefEdge predecessors = 5;
+    repeated string original_names = 6;
+}
+
+message ProtoHEFEdgeLayerMuxData {
+    uint32 number_of_predecessors = 1;
+    uint32 height_gcd = 2;
+    uint32 height_ratios_list_len = 3;
+    repeated uint32 height_ratios_list = 4;
+}
+
+enum ProtoHEFEdgeConnectionType {
+    PROTO__EDGE_CONNECTION_TYPE__BOUNDARY = 0;
+    PROTO__EDGE_CONNECTION_TYPE__INTERMEDIATE = 1;
+    PROTO__EDGE_CONNECTION_TYPE__DDR = 2;
+}
+
+message ProtoHEFContextSwitchInformation {
+    uint32 context_index = 1;
+    string context_name = 2;
+    ProtoHEFEdgeConnectionType edge_connection_type = 3;
+    uint32 connected_context_index = 4;
+    string connected_context_name = 5;
+    uint32 buffers = 6;
+    uint32 connected_sys_index = 7;
+    repeated ProtoHEFConnectedContextInfo connected_contexts = 8;
+}
+
+message ProtoHEFConnectedContextInfo {
+    uint32 index = 1;
+    string name = 2;
+    uint32 sys_index = 3;
+}
+
+message ProtoHEFResourceIndices {
+    uint32 index = 1;
+    uint32 cluster_index = 2;
+}
+
+message ProtoHEFEdgeLayerBase {
+    uint32 height = 1;
+    uint32 padded_height = 2;
+    uint32 width = 3;
+    uint32 padded_width = 4;
+    uint32 features = 5;
+    uint32 padded_features = 6;
+    // TODO: Should change to an enum?
+    uint32 format = 7;
+    uint32 sys_index = 8;
+    uint32 core_bytes_per_buffer = 9;
+    uint32 core_buffers_per_frame = 10;
+    ProtoHEFAdditionalInfo additional_info = 11;
+    uint32 data_bytes = 12;
+
+    repeated ProtoHEFResourceIndices buffer_indices = 13;
+    bool host_argmax = 14;
+}
+
+// Additional information for specific layer types
+message ProtoHEFAdditionalInfo {
+    ProtoHEFNmsInfo nms_info = 1;
+}
+
+// NMS specific parameters
+message ProtoHEFNmsInfo {
+    uint32 type_index = 1;
+    uint64 number_of_classes = 2;
+    uint64 max_output_size = 3;
+    uint64 bbox_size = 4;
+    bool is_defused = 5;
+    ProtoHEFNmsDefuseInfo defuse_info = 6;
+    uint64 input_division_factor = 7;
+}
+
+message ProtoHEFNmsDefuseInfo {
+    uint64 class_group_index = 1;
+    string original_name = 3;
+}
+
+message ProtoHEFEdgeLayerNumericInfo {
+    float qp_zp = 1;
+    float qp_scale = 2;
+    float limvals_min = 3;
+    float limvals_max = 4;
+}
+
+// An object that can be repeated in order to provide the order of the triggers.
+// Contain estimated execution time from previous trigger.
+message ProtoHEFOrderedTrigger {
+    ProtoHEFTrigger triggers = 1;
+    uint64 estimated_time_from_previous_ns = 2;
+}
+
+// Object which maps between a cluster index and a trigger which Guarantees the cluster is no longer in use
+message ProtoHEFClusterEndGuarantee {
+    uint32 cluster_index = 1;
+    ProtoHEFTrigger trigger = 2;
+}
+
+// HW package information - Copied form JLF
+message ProtoHEFHwPackageInfo {
+    uint32 dense_alignment_size = 1;
+    uint32 axi_width = 2;
+    uint32 memory_width = 3;
+}
diff --git a/hailort/libhailort/include/hailo/buffer.hpp b/hailort/libhailort/include/hailo/buffer.hpp
new file mode 100644 (file)
index 0000000..9bacefb
--- /dev/null
@@ -0,0 +1,186 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file buffer.hpp
+ * @brief Buffer helper class
+ **/
+
+#ifndef _HAILO_BUFFER_HPP_
+#define _HAILO_BUFFER_HPP_
+
+#include "hailo/expected.hpp"
+
+#include <memory>
+#include <cstdint>
+#include <type_traits>
+#include <string>
+#include <cassert>
+
+namespace hailort
+{
+
+class HAILORTAPI Buffer final
+{
+public:
+    // Based on https://en.cppreference.com/w/cpp/iterator/iterator
+    class iterator: public std::iterator<std::input_iterator_tag,   // iterator_category
+                                         uint8_t,                   // value_type
+                                         uint8_t,                   // difference_type
+                                         uint8_t*,                  // pointer
+                                         uint8_t&>                  // reference
+    {
+    public:
+        explicit iterator(pointer index = 0) : m_index(index) {}
+        iterator& operator++() { m_index++; return *this; }
+        iterator operator++(int) { iterator retval = *this; ++(*this); return retval; }
+        bool operator==(iterator other) const { return m_index == other.m_index; }
+        bool operator!=(iterator other) const { return !(*this == other); }
+        reference operator*() const { return *m_index; }
+    private:
+        pointer m_index;
+    };
+
+    // Empty buffer (points to null, size is zero)
+    Buffer();
+    ~Buffer() = default;
+
+    Buffer(const Buffer& other) = delete;
+    Buffer& operator=(const Buffer& other) = delete;
+
+    // Moves the data pointed to by other into the newly created buffer; other is invalidated
+    Buffer(Buffer&& other);
+
+    /**
+     * Create functions, may fail be due to out of memory
+     */
+    // Crates a buffer size bytes long, without setting the memory
+    static Expected<Buffer> create(size_t size);
+    // Crates a buffer size bytes long, setting the memory to default_value
+    static Expected<Buffer> create(size_t size, uint8_t default_value);
+    // Creates a copy of the data pointed to by src, size bytes long
+    static Expected<Buffer> create(const uint8_t *src, size_t size);
+    // Creates a new buffer with the contents of the initializer_list
+    static Expected<Buffer> create(std::initializer_list<uint8_t> init);
+    // Moves the data pointed to by other into the lvalue:
+    // * other is invalidated.
+    // * The previous data pointed to by the lvalue is freed
+    Buffer& operator=(Buffer&& other);
+
+    Expected<Buffer> copy() const;
+
+    // Byte-wise comparison
+    bool operator==(const Buffer& rhs) const;
+    bool operator!=(const Buffer& rhs) const;
+
+    // Note: No bounds checking
+    uint8_t& operator[](size_t pos);
+    const uint8_t& operator[](size_t pos) const;
+
+    // Iterator funcs
+    iterator begin();
+    iterator end();
+
+    // Returns a pointer to the start of the buffer
+    uint8_t* data() noexcept;
+    const uint8_t* data() const noexcept;
+
+    // Returns the size of the buffer
+    size_t size() const noexcept;
+    
+    // Returns a pointer to the start of the buffer and releases the ownership
+    // Free the returned pointer with `delete`
+    uint8_t* release() noexcept;
+
+    // Casts the buffer to a string of length size().
+    // If there's a null char in the buffer, the string will terminate at the null char
+    std::string to_string() const;
+
+    // Stream operator overload
+    friend std::ostream& operator<<(std::ostream&, const Buffer&);
+
+    // Returns a pointer to the start of the buffer, cast to T*
+    // Note: If this->size() is less than sizeof(T), then part of the data pointed to by the returned pointer
+    //       will be outside of the buffer's bounds.
+    template<typename T, std::enable_if_t<std::is_pod<T>::value, int> = 0>
+    T* as_pointer()
+    {
+        assert(m_size >= sizeof(T));
+        return reinterpret_cast<T*>(m_data.get());
+    }
+
+    // Returns a copy of the data at the start of the buffer, cast to T
+    // Note: If this->size() is less than sizeof(T), then the copy will hold data that isn't from the buffer!
+    template<typename T, std::enable_if_t<std::is_pod<T>::value, int> = 0>
+    T as_type() const
+    {
+        assert(m_size >= sizeof(T));
+        return *(reinterpret_cast<const T*>(m_data.get()));
+    }
+
+    // The following functions return a copy of the data at the start of the buffer, cast to uint16/32/64_t
+    // Note: If this->size() is less than the size of the ineger type, then the copy will hold data
+    //       that isn't from the buffer!
+    uint16_t as_uint16() const;
+    uint32_t as_uint32() const;
+    uint64_t as_uint64() const;
+
+    // Returns a reference to the data at the start of the buffer, cast to T
+    // Note: If this->size() is less than sizeof(T), then the reference will hold data that isn't from the buffer!
+    template<typename T, std::enable_if_t<std::is_pod<T>::value, int> = 0>
+    T& as_type()
+    {
+        assert(m_size >= sizeof(T));
+        return reinterpret_cast<T&>(m_data[0]);
+    }
+
+    // The following functions return references of the data at the start of the buffer, cast to uint16/32/64_t
+    // Note: If this->size() is less than the size of the ineger type, then the copy will hold data
+    //       that isn't from the buffer!
+    uint16_t& as_uint16();
+    uint32_t& as_uint32();
+    uint64_t& as_uint64();
+
+private:
+    Buffer(std::unique_ptr<uint8_t[]> data, size_t size);
+
+    std::unique_ptr<uint8_t[]> m_data;
+    size_t m_size;
+};
+
+/**
+ * Object that can refer to a contiguous sequence of bytes.
+ * This object does not own the memory and therefore it assumes that the memory exists and valid.
+ */
+class HAILORTAPI MemoryView final
+{
+public:
+    MemoryView();
+    explicit MemoryView(Buffer &buffer);
+    MemoryView(void *data, size_t size);
+    ~MemoryView() = default;
+    
+    MemoryView& operator=(MemoryView&& other) = default;
+    MemoryView(const MemoryView &) = default;
+    MemoryView& operator=(MemoryView &) = default;
+
+    static const MemoryView create_const(const void *data, size_t size);
+
+    uint8_t* data() noexcept;
+    const uint8_t* data() const noexcept;
+    size_t size() const noexcept;
+    bool empty() const noexcept;
+
+    // Stream operator overload
+    friend std::ostream& operator<<(std::ostream&, const MemoryView&);
+
+private:
+    void *m_data;
+    size_t m_size;
+};
+
+} /* namespace hailort */
+
+#endif /* _HAILO_BUFFER_HPP_ */
diff --git a/hailort/libhailort/include/hailo/device.hpp b/hailort/libhailort/include/hailo/device.hpp
new file mode 100644 (file)
index 0000000..9527df3
--- /dev/null
@@ -0,0 +1,650 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file device.hpp
+ * @brief Hailo device representation
+ **/
+
+#ifndef _HAILO_DEVICE_HPP_
+#define _HAILO_DEVICE_HPP_
+
+#include "hailo/hailort.h"
+#include "hailo/expected.hpp"
+#include "hailo/hef.hpp"
+#include "hailo/network_group.hpp"
+
+#include <functional>
+#include <vector>
+#include <map>
+#include <memory>
+#include <chrono>
+
+namespace hailort
+{
+
+typedef enum {
+    BOOTLOADER_VERSION_HAILO8_B0_UNSIGNED = 0,
+    BOOTLOADER_VERSION_HAILO8_B0_SIGNED
+} hailo_bootloader_version_t;
+
+/** @defgroup group_type_definitions HailoRT CPP API definitions
+ *  @{
+ */
+
+class Device;
+using NotificationCallback = std::function<void(Device &device, const hailo_notification_t &notification, void *opaque)>;
+
+/** @} */ // end of group_type_definitions
+
+/*! Represents the Hailo device (chip). */
+class HAILORTAPI Device
+{
+public:
+
+    /** The device type */
+    enum class Type {
+        PCIE = 0,
+        ETH,
+        CORE
+    };
+
+    /**
+     * Returns information on all available pcie devices in the system.
+     * 
+     * @return Upon success, returns Expected of a vector of ::hailo_pcie_device_info_t containing the information.
+     *         Otherwise, returns Unexpected of ::hailo_status error.
+     */
+    static Expected<std::vector<hailo_pcie_device_info_t>> scan_pcie();
+
+    /**
+     * Returns information on all available ethernet devices in the system.
+     * 
+     * @param[in] interface_name            The name of the network interface to scan.
+     * @param[in] timeout                   The time in milliseconds to scan devices.
+     * @return Upon success, returns Expected of a vector of ::hailo_eth_device_info_t containing the information.
+     *         Otherwise, returns Unexpected of ::hailo_status error.
+     */
+    static Expected<std::vector<hailo_eth_device_info_t>> scan_eth(const std::string &interface_name,
+        std::chrono::milliseconds timeout);
+
+    /**
+     * Scans ethernet device by host address.
+     * 
+     * @param[in] host_address              The IP address of the network interface to scan.
+     * @param[in] timeout                   The time in milliseconds to scan devices.
+     * @return Upon success, returns Expected of a vector of ::hailo_eth_device_info_t containing the information.
+     *         Otherwise, returns Unexpected of ::hailo_status error.
+     */
+    static Expected<std::vector<hailo_eth_device_info_t>> scan_eth_by_host_address(const std::string &host_address,
+        std::chrono::milliseconds timeout);
+
+    /**
+     * Creates pcie device if there is only one pcie device connected
+     * 
+     * @return Upon success, returns Expected of a unique_ptr to Device object.
+     *         Otherwise, returns Unexpected of ::hailo_status error.
+     */
+    static Expected<std::unique_ptr<Device>> create_pcie();
+
+    /**
+     * Creates a PCIe device by the given info.
+     * 
+     * @param[in] device_info    Information about the device to open.
+     * @return Upon success, returns Expected of a unique_ptr to Device object.
+     *         Otherwise, returns Unexpected of ::hailo_status error.
+     */
+    static Expected<std::unique_ptr<Device>> create_pcie(const hailo_pcie_device_info_t &device_info);
+
+    /**
+     * Creates an ethernet device by the given info.
+     * 
+     * @param[in] device_info   Information about the device to open.
+     * @return Upon success, returns Expected of a unique_ptr to Device object.
+     *         Otherwise, returns Unexpected of ::hailo_status error.
+     */
+    static Expected<std::unique_ptr<Device>> create_eth(const hailo_eth_device_info_t &device_info);
+
+    /**
+     * Creates an ethernet device by IP address.
+     * 
+     * @param[in] ip_addr      The device IP address.
+     * @return Upon success, returns Expected of a unique_ptr to Device object.
+     *         Otherwise, returns Unexpected of ::hailo_status error.
+     */
+    static Expected<std::unique_ptr<Device>> create_eth(const std::string &ip_addr);
+
+    /**
+     * Parse PCIe device BDF string into hailo device info structure.
+     * 
+     * @param[in] device_info_str   BDF device info, format [\<domain\>].\<bus\>.\<device\>.\<func\>, same format as in lspci.
+     * @return Upon success, returns Expected of ::hailo_pcie_device_info_t containing the information.
+     *         Otherwise, returns Unexpected of ::hailo_status error.
+     */
+    static Expected<hailo_pcie_device_info_t> parse_pcie_device_info(const std::string &device_info_str);
+
+    /**
+     * Returns a string of pcie device info.
+     * 
+     * @param[in] device_info       A ::hailo_pcie_device_info_t containing the pcie device information.
+     * @return Upon success, returns Expected of a string containing the information.
+     *         Otherwise, returns Unexpected of ::hailo_status error.
+     */
+    static Expected<std::string> pcie_device_info_to_string(const hailo_pcie_device_info_t &device_info);
+
+    // For internal use only
+    static bool is_core_driver_loaded();
+    static Expected<std::unique_ptr<Device>> create_core_device();
+
+    /**
+     * Configure the device from an hef.
+     *
+     * @param[in] hef                         A reference to an Hef object to configure the device by.
+     * @param[in] configure_params            A map of configured network group name and parameters.
+     * @return Upon success, returns Expected of a vector of configured network groups.
+     *         Otherwise, returns Unexpected of ::hailo_status error.
+     */
+    virtual Expected<ConfiguredNetworkGroupVector> configure(Hef &hef,
+        const NetworkGroupsParamsMap &configure_params={}) = 0;
+
+    /**
+     * Read data from the debug log buffer.
+     *
+     * @param[in] buffer            A buffer that would receive the debug log data.
+     * @param[in] cpu_id            The cpu source of the debug log.
+     * @return Upon success, returns Expected of the number of bytes that were read.
+     *         Otherwise, returns Unexpected of ::hailo_status error.
+     */
+    virtual Expected<size_t> read_log(MemoryView &buffer, hailo_cpu_id_t cpu_id) = 0;
+
+    /**
+     * Sends identify control to a Hailo device.
+     *
+     * @return Upon success, returns Expected of ::hailo_device_identity_t.
+     *         Otherwise, returns Unexpected of ::hailo_status error.
+     */
+    Expected<hailo_device_identity_t> identify();
+
+    /**
+     * Receive information about the core cpu.
+     * 
+     * @return Upon success, returns Expected of ::hailo_core_information_t.
+     *         Otherwise, returns Unexpected of ::hailo_status error.
+     */
+    Expected<hailo_core_information_t> core_identify();
+
+    /**
+     * Get extended device information about the Hailo device.
+     *
+     * @return Upon success, returns Expected of ::hailo_extended_device_information_t containing the extended information about the device.
+     *         Otherwise, returns Unexpected of ::hailo_status error.
+     */
+    Expected<hailo_extended_device_information_t> get_extended_device_information();
+
+    /**
+     * Configure fw logger level and interface of sending.
+     *
+     * @param[in] level             The minimum logger level.
+     * @param[in] interface_mask    Output interfaces (mix of ::hailo_fw_logger_interface_t).
+     * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns an ::hailo_status error.
+     */
+    hailo_status set_fw_logger(hailo_fw_logger_level_t level, uint32_t interface_mask);
+
+    /**
+     * Change throttling state of temperature protection component.
+     *
+     * @param[in] should_activate   Should be true to enable or false to disable.
+     * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns an ::hailo_status error.
+     */
+    hailo_status set_throttling_state(bool should_activate);
+
+    /**
+     * Writes data to device memory.
+     * 
+     * @param[in] address   The address data would be written to.
+     * @param[in] data      A buffer that contains the data to be written to the memory.
+     * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns a ::hailo_status error.
+     */
+    hailo_status write_memory(uint32_t address, const MemoryView &data);
+
+    /**
+     * Reads data from device memory.
+     * 
+     * @param[in] address   The address data would be read from.
+     * @param[in] data      A buffer that receives the data read from memory.
+     * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns a ::hailo_status error.
+     */    
+    hailo_status read_memory(uint32_t address, MemoryView &data);
+
+    /**
+     * Get current throttling state of temperature protection component.
+     *
+     * @return Upon success, returns Expected of @a bool, indicates weather the throttling state is active or not.
+     *         Otherwise, returns Unexpected of ::hailo_status error.
+     */
+    Expected<bool> get_throttling_state();
+
+    /**
+     * Enable firmware watchdog.
+     *
+     * @param[in] cpu_id   A @a hailo_cpu_id_t indicating which CPU WD to enable.
+     * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns an ::hailo_status error.
+     * @note Advanced API. Please use with care.
+     */
+    hailo_status wd_enable(hailo_cpu_id_t cpu_id);
+
+    /**
+     * Disable firmware watchdog.
+     *
+     * @param[in] cpu_id   A @a hailo_cpu_id_t indicating which CPU WD to disable.
+     * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns an ::hailo_status error.
+     * @note Advanced API. Please use with care.
+     */
+    hailo_status wd_disable(hailo_cpu_id_t cpu_id);
+
+    /**
+     * Configure firmware watchdog.
+     *
+     * @param[in] cpu_id    A @a hailo_cpu_id_t indicating which CPU WD to configure.
+     * @param[in] wd_cycles Number of cycles until watchdog is triggered.
+     * @param[in] wd_mode   A @a hailo_watchdog_mode_t indicating which WD mode to configure.
+     * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns an ::hailo_status error.
+     * @note Advanced API. Please use with care.
+     */
+    hailo_status wd_config(hailo_cpu_id_t cpu_id, uint32_t wd_cycles, hailo_watchdog_mode_t wd_mode);
+
+    /**
+     * Read the FW previous system state.
+     *
+     * @param[in] cpu_id    A @a hailo_cpu_id_t indicating which CPU to state to read.
+     * @return Upon success, returns Expected of @a uint32_t indicating the previous system state.
+     *         0 indicating external reset, 1 indicating WD HW reset,
+     *         2 indicating WD SW reset, 3 indicating SW control reset.
+     *         Otherwise, returns an ::hailo_status error.
+     * @note Advanced API. Please use with care.
+     */
+    Expected<uint32_t> previous_system_state(hailo_cpu_id_t cpu_id);
+
+    /**
+     * Enable/Disable Pause frames.
+     *
+     * @param[in] rx_pause_frames_enable  Indicating weather to enable or disable pause frames.
+     * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns an ::hailo_status error.
+     */
+    hailo_status set_pause_frames(bool rx_pause_frames_enable);
+
+    /**
+     *  Read data from an I2C slave.
+     *
+     * @param[in] slave_config          The ::hailo_i2c_slave_config_t configuration of the slave.
+     * @param[in] register_address      The address of the register from which the data will be read.
+     * @param[in] data                  A buffer that would store the read data.
+     * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns an ::hailo_status error.
+     */
+    hailo_status i2c_read(const hailo_i2c_slave_config_t &slave_config, uint32_t register_address, MemoryView &data);
+
+    /**
+     *  Write data to an I2C slave.
+     *
+     * @param[in] slave_config          The ::hailo_i2c_slave_config_t configuration of the slave.
+     * @param[in] register_address      The address of the register to which the data will be written.
+     * @param[in] data                  A buffer that contains the data to be written to the slave.
+     * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns an ::hailo_status error.
+     */
+    hailo_status i2c_write(const hailo_i2c_slave_config_t &slave_config, uint32_t register_address, const MemoryView &data);
+
+    /**
+     * Perform a single power measurement.
+     * 
+     * @param[in]   dvm                Which DVM will be measured. Default (::HAILO_DVM_OPTIONS_AUTO) will be different according to the board: <br>
+     *                                 - Default (::HAILO_DVM_OPTIONS_AUTO) for EVB is an approximation to the total power consumption of the chip in PCIe setups.
+     *                                 It sums ::HAILO_DVM_OPTIONS_VDD_CORE, ::HAILO_DVM_OPTIONS_MIPI_AVDD and ::HAILO_DVM_OPTIONS_AVDD_H.
+     *                                 Only ::HAILO_POWER_MEASUREMENT_TYPES__POWER can measured with this option.
+     *                                 - Default (::HAILO_DVM_OPTIONS_AUTO) for platforms supporting current monitoring (such as M.2 and mPCIe): OVERCURRENT_PROTECTION.
+     * @param[in]   measurement_type   The type of the measurement. Choosing ::HAILO_POWER_MEASUREMENT_TYPES__AUTO
+     *                                 will select the default value according to the supported features.
+     * @return Upon success, returns @a uint32_t mesuremenet. Measured units are determined due to ::hailo_power_measurement_types_t.
+     *         Otherwise, returns a ::hailo_status error.
+     */
+    Expected<float32_t> power_measurement(hailo_dvm_options_t dvm, hailo_power_measurement_types_t measurement_type);
+
+    /**
+     * Start performing a long power measurement.
+     * 
+     * @param[in]   delay_milliseconds   Amount of time between each measurement interval.
+     *                                   This time period is sleep time of the core.
+     * @param[in]   averaging_factor     Number of samples per time period, sensor configuration value.
+     * @param[in]   sampling_period      Related conversion time, sensor configuration value.
+     *                                   The sensor samples the power every sampling_period {ms} and averages every
+     *                                   averaging_factor samples. The sensor provides a new value every: 2 * sampling_period * averaging_factor {ms}.
+     *                                   The firmware wakes up every interval_milliseconds {ms} and checks the sensor.
+     *                                   If there is a new value to read from the sensor, the firmware reads it.
+     *                                   Note that the average calculated by the firmware is “average of averages”,
+     *                                   because it averages values that have already been averaged by the sensor.
+     * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns a ::hailo_status error.
+     */
+    hailo_status start_power_measurement(uint32_t delay_milliseconds, hailo_averaging_factor_t averaging_factor, hailo_sampling_period_t sampling_period);
+
+    /**
+     * Set parameters for long power measurement.
+     * 
+     * @param[in]   index              Index of the buffer on the firmware the data would be saved at.
+     * @param[in]   dvm                Which DVM will be measured. Default (::HAILO_DVM_OPTIONS_AUTO) will be different according to the board: <br>
+     *                                 - Default (::HAILO_DVM_OPTIONS_AUTO) for EVB is an approximation to the total power consumption of the chip in PCIe setups.
+     *                                 It sums ::HAILO_DVM_OPTIONS_VDD_CORE, ::HAILO_DVM_OPTIONS_MIPI_AVDD and ::HAILO_DVM_OPTIONS_AVDD_H.
+     *                                 Only ::HAILO_POWER_MEASUREMENT_TYPES__POWER can measured with this option.
+     *                                 - Default (::HAILO_DVM_OPTIONS_AUTO) for platforms supporting current monitoring (such as M.2 and mPCIe): OVERCURRENT_PROTECTION.
+     * @param[in]   measurement_type   The type of the measurement. Choosing ::HAILO_POWER_MEASUREMENT_TYPES__AUTO
+     *                                 will select the default value according to the supported features.
+     * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns a ::hailo_status error.
+     */
+    hailo_status set_power_measurement(uint32_t index, hailo_dvm_options_t dvm, hailo_power_measurement_types_t measurement_type);
+
+    /**
+     * Read measured power from a long power measurement
+     * 
+     * @param[in]   index                 Index of the buffer on the firmware the data would be saved at.
+     * @param[in]   should_clear          Flag indicating if the results saved at the firmware will be deleted after reading.
+     * @return Upon success, returns @a hailo_power_measurement_data_t. Measured units are determined due to ::hailo_power_measurement_types_t
+     *         passed to ::hailo_set_power_measurement. Otherwise, returns a ::hailo_status error.
+     */
+    Expected<hailo_power_measurement_data_t> get_power_measurement(uint32_t index, bool should_clear);
+
+    /**
+     * Stop performing a long power measurement.
+     * 
+     * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns a ::hailo_status error.
+     */
+    hailo_status stop_power_measurement();
+
+    /**
+     * Get temperature information on the device
+     *
+     * @return Upon success, returns @a hailo_chip_temperature_info_t, containing temperature information on the device.
+     *         Otherwise, returns a ::hailo_status error.
+     * @note Temperature in Celsius of the 2 internal temperature sensors (TS).
+     */
+    Expected<hailo_chip_temperature_info_t> get_chip_temperature();
+
+    /**
+     * Reset device.
+     * 
+     * @param[in] mode      The mode of the reset.
+     * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns a ::hailo_status error.
+     * @note Calling this function while running other operations on the device (including inference) will
+     * lead to unexpected results!
+     * @note The object used to call this function is not to be used after calling this function!
+     * A new instance should be created.
+     */
+    virtual hailo_status reset(hailo_reset_device_mode_t mode) = 0;
+
+    /**
+     * Sets a callback to be called when a notification with ID @a notification_id will be received.
+     *
+     * @param[in] func                  The callback function to be called.
+     * @param[in] notification_id       The ID of the notification.
+     * @param[in] opaque                User specific data.
+     * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns a ::hailo_status error.
+     */
+    virtual hailo_status set_notification_callback(NotificationCallback func, hailo_notification_id_t notification_id,
+        void *opaque) = 0;
+
+    /**
+     * Removes a previously set callback with ID @a notification_id.
+     *
+     * @param[in] notification_id       The ID of the notification to remove.
+     * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns a ::hailo_status error.
+     */
+    virtual hailo_status remove_notification_callback(hailo_notification_id_t notification_id) = 0;
+
+    /**
+     *  Test chip memories using BIST.
+     *
+     * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns a ::hailo_status error.
+     * @note Cannot be called during inference!
+     */
+    hailo_status test_chip_memories();
+
+    /**
+     *  Update the firmware of a Hailo device.
+     * 
+     * @param[in] firmware_binary       The firmware code to be updated to the device.
+     * @param[in] should_reset          Bool indicating weather to reset the device after updating.
+     * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns a ::hailo_status error.
+     * @note Calling this function while running other operations on the device (including inference) will
+     * lead to unexpected results!
+     * @note The object used to call this function is not to be used after calling this function!
+     * A new instance should be created.
+     */
+    virtual hailo_status firmware_update(const MemoryView &firmware_binary, bool should_reset) = 0;
+
+    /**
+     *  Update the second stage binary.
+     * 
+     * @param[in] second_stage_binary           The SSB code to be updated to the device.
+     * @param[in] second_stage_binary_length    The length of the SSB to be updated.
+     * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns a ::hailo_status error.
+     * @note Calling this function while running other operations on the device (including inference) will
+     * lead to unexpected results!
+     * @note The object used to call this function is not to be used after calling this function!
+     * A new instance should be created.
+     */
+    virtual hailo_status second_stage_update(uint8_t *second_stage_binary, uint32_t second_stage_binary_length) = 0;
+
+    /**
+     * Store sensor configuration to Hailo chip flash memory.
+     *
+     * @param[in] section_index         Flash section index to write to. [0-6]
+     * @param[in] sensor_type           Sensor type.
+     * @param[in] reset_config_size     Size of reset configuration.
+     * @param[in] config_height         Configuration resolution height.
+     * @param[in] config_width          Configuration resolution width.
+     * @param[in] config_fps            Configuration FPS.
+     * @param[in] config_file_path      Sensor configuration file path.
+     * @param[in] config_name           Sensor configuration name.
+     * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns a ::hailo_status error.
+     */
+    virtual hailo_status store_sensor_config(uint32_t section_index, hailo_sensor_types_t sensor_type,
+        uint32_t reset_config_size, uint16_t config_height, uint16_t config_width, uint16_t config_fps,
+        const std::string &config_file_path, const std::string &config_name) = 0;
+    
+    /**
+     * Store sensor ISP configuration to Hailo chip flash memory.
+     *
+     * @param[in] reset_config_size                 Size of reset configuration.
+     * @param[in] config_height                     Configuration resolution height.
+     * @param[in] config_width                      Configuration resolution width.
+     * @param[in] config_fps                        Configuration FPS.
+     * @param[in] isp_static_config_file_path       ISP static configuration file path.
+     * @param[in] isp_runtime_config_file_path      ISP runtime configuration file path.
+     * @param[in] config_name                       Sensor configuration name.
+     * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns a ::hailo_status error.
+     */
+    virtual hailo_status store_isp_config(uint32_t reset_config_size, uint16_t config_height, uint16_t config_width, uint16_t config_fps,
+        const std::string &isp_static_config_file_path, const std::string &isp_runtime_config_file_path, const std::string &config_name) = 0;
+    
+    /**
+     * Gets the sections information of the sensor.
+     *
+     * @return Upon success, returns Expected of a buffer containing the sensor's sections information.
+     *         Otherwise, returns Unexpected of ::hailo_status error.
+     */
+    virtual Expected<Buffer> sensor_get_sections_info() = 0;
+
+    /**
+     * Dump config of given section index into a csv file.
+     *
+     * @param[in] section_index         Flash section index to load config from. [0-7]
+     * @param[in] config_file_path      File path to dump section configuration into.
+     * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns a ::hailo_status error.
+     */
+    virtual hailo_status sensor_dump_config(uint32_t section_index, const std::string &config_file_path) = 0;
+
+    /**
+     * Set the I2C bus to which the sensor of the specified type is connected.
+     *
+     * @param[in] sensor_type           The sensor type.
+     * @param[in] bus_index             The I2C bus index of the sensor.
+     * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns a ::hailo_status error.
+     */
+    virtual hailo_status sensor_set_i2c_bus_index(hailo_sensor_types_t sensor_type, uint32_t bus_index) = 0;
+
+    /**
+     * Load the configuration with I2C in the section index.
+     *
+     * @param[in] section_index         Flash section index to load config from. [0-6]
+     * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns a ::hailo_status error.
+     */
+    virtual hailo_status sensor_load_and_start_config(uint32_t section_index) = 0;
+
+    /**
+     * Reset the sensor that is related to the section index config.
+     *
+     * @param[in] section_index         Flash section index to load config from. [0-6]
+     * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns a ::hailo_status error.
+     */
+    virtual hailo_status sensor_reset(uint32_t section_index) = 0;
+
+    /**
+     * Set a generic I2C slave for sensor usage.
+     *
+     * @param[in] slave_address         The address of the i2c slave.
+     * @param[in] offset_size           Slave offset size (in bytes).
+     * @param[in] bus_index             The bus number the i2c slave is connected to.
+     * @param[in] should_hold_bus       Should hold the bus during the read.
+     * @param[in] slave_endianness      BIG_ENDIAN or LITTEL_ENDIAN.
+     * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns a ::hailo_status error.
+     */
+    virtual hailo_status sensor_set_generic_i2c_slave(uint16_t slave_address, uint8_t offset_size, uint8_t bus_index,
+        uint8_t should_hold_bus, uint8_t slave_endianness) = 0;
+
+    /**
+     * Reads board configuration from device.
+     * 
+     * @return Upon success, returns Expected of a buffer containing the data read.
+     *         Otherwise, returns Unexpected of ::hailo_status error.
+     */  
+    virtual Expected<Buffer> read_board_config() = 0;
+
+    /**
+     * Write board configuration to device
+     * 
+     * @param[in] buffer        A buffer that contains the data to be written .
+     * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns a ::hailo_status error.
+     */  
+    virtual hailo_status write_board_config(const MemoryView &buffer) = 0;
+
+    /**
+     * Gets firmware user configuration information from device.
+     * 
+     * @return Upon success, returns Expected of ::hailo_fw_user_config_information_t.
+     *         Otherwise, returns Unexpected of ::hailo_status error.
+     */  
+    virtual Expected<hailo_fw_user_config_information_t> examine_user_config() = 0;
+
+    /**
+     * Reads firmware user configuration from device.
+     * 
+     * @return Upon success, returns Expected of a buffer containing the data read.
+     *         Otherwise, returns Unexpected of ::hailo_status error.
+     */
+    virtual Expected<Buffer> read_user_config() = 0;
+
+    /**
+     * Write firmware user configuration to device.
+     * 
+     * @param[in] buffer        A buffer that contains the data to be written .
+     * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns a ::hailo_status error.
+     */  
+    virtual hailo_status write_user_config(const MemoryView &buffer) = 0;
+
+    /**
+     * Erase firmware user configuration from the device.
+     * 
+     * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns a ::hailo_status error.
+     */
+    virtual hailo_status erase_user_config() = 0;
+
+    /**
+     * Gets the device architecture.
+     * 
+     * @return Upon success, returns Expected of ::hailo_device_architecture_t.
+     *         Otherwise, returns Unexpected of ::hailo_status error.
+     */
+    virtual Expected<hailo_device_architecture_t> get_architecture() const = 0;
+
+    /**
+     * Gets the device type.
+     * 
+     * @return Upon success, returns Type. Otherwise, returns a ::hailo_status error.
+     */
+    Type get_type() const;
+
+    /**
+     * Gets the device id.
+     * 
+     * @return An identification string of the device.
+     *      For Pcie device, returns the BDF.
+     *      For Ethernet device, returns the IP address.
+     *      For Core device, returns "Core".
+     */
+    virtual const char* get_dev_id() const = 0;
+
+    /**
+     * Gets the stream's default interface.
+     * 
+     * @return Upon success, returns Expected of ::hailo_stream_interface_t.
+     *         Otherwise, returns Unexpected of ::hailo_status error.
+     */
+    Expected<hailo_stream_interface_t> get_default_streams_interface() const;
+
+    /**
+     * @return true if the stream's interface is supported, false otherwise.
+     */
+    virtual bool is_stream_interface_supported(const hailo_stream_interface_t &stream_interface) const = 0;
+
+    virtual hailo_status direct_write_memory(uint32_t address, const void *buffer, uint32_t size);
+    virtual hailo_status direct_read_memory(uint32_t address, void *buffer, uint32_t size);
+    hailo_status set_overcurrent_state(bool should_activate);
+    Expected<bool> get_overcurrent_state();
+    Expected<hailo_health_info_t> get_health_information();
+    // Returns a vector of the number of contexts per network group (preliminary + dynamic)
+    // The sum of the number of contexts will fit in uint8_t
+    Expected<std::vector<uint8_t>> get_number_of_contexts_per_network_group();
+    Expected<Buffer> download_context_action_list(uint8_t context_index, uint32_t *base_address,
+        uint32_t *batch_counter, uint16_t max_size = 10000);
+
+    virtual ~Device() = default;
+    Device(const Device &) = delete;
+    Device &operator=(const Device &) = delete;
+    Device(Device &&) = delete;
+    Device &operator=(Device &&other) = delete;
+
+protected:
+    Device(Type type);
+
+    virtual hailo_status wait_for_wakeup() = 0;
+    virtual void increment_control_sequence() = 0;
+    hailo_status fw_interact(uint8_t *request_buffer, size_t request_size, uint8_t *response_buffer, size_t *response_size);
+    virtual hailo_status fw_interact_impl(uint8_t *request_buffer, size_t request_size, uint8_t *response_buffer, 
+                                          size_t *response_size, hailo_cpu_id_t cpu_id) = 0;
+    
+    // Update the state of the fw, as seen by this device
+    hailo_status update_fw_state();
+
+    Type m_type;
+    uint32_t m_control_sequence;
+    bool m_is_control_version_supported;
+    hailo_device_architecture_t m_device_architecture;
+
+private:
+    uint32_t get_control_sequence();
+    bool is_control_version_supported();
+
+    friend class Control;
+};
+
+} /* namespace hailort */
+
+#endif /* _HAILO_DEVICE_HPP_ */
diff --git a/hailort/libhailort/include/hailo/event.hpp b/hailort/libhailort/include/hailo/event.hpp
new file mode 100644 (file)
index 0000000..d3da128
--- /dev/null
@@ -0,0 +1,116 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file event.hpp
+ * @brief Event and Semaphore wrapper objects used for multithreading
+ **/
+
+#ifndef _HAILO_EVENT_HPP_
+#define _HAILO_EVENT_HPP_
+
+#include "hailo/hailort.h"
+#include "hailo/expected.hpp"
+
+#include <memory>
+#include <vector>
+#include <array>
+#include <chrono>
+#if defined(__GNUC__)
+#include <poll.h>
+#endif
+
+namespace hailort
+{
+
+class Waitable;
+using WaitablePtr = std::shared_ptr<Waitable>;
+using WaitablePtrList = std::vector<WaitablePtr>;
+
+class HAILORTAPI Waitable
+{
+public:    
+    explicit Waitable(underlying_handle_t handle);
+    virtual ~Waitable();
+    Waitable(Waitable&& other);
+
+    Waitable(const Waitable&) = delete;
+    Waitable& operator=(const Waitable&) = delete;
+    Waitable& operator=(Waitable&&) = delete;
+
+    // Blocks the current thread until the waitable is signaled
+    // * If this->is_auto_reset(), then the Waitable is reset after wait returns with HAILO_SUCCESS 
+    // * Otherwise, the Waitable is not reset
+    virtual hailo_status wait(std::chrono::milliseconds timeout) = 0;
+    virtual hailo_status signal() = 0;
+    virtual bool is_auto_reset() = 0;
+    underlying_handle_t get_underlying_handle();
+
+    static constexpr auto INIFINITE_TIMEOUT() { return std::chrono::milliseconds(HAILO_INFINITE); }
+
+protected:
+    #if defined(_MSC_VER)
+    static hailo_status wait_for_single_object(underlying_handle_t handle, std::chrono::milliseconds timeout);
+    #else
+    // Waits on the fd until the waitable is signaled
+    static hailo_status eventfd_poll(underlying_handle_t fd, std::chrono::milliseconds timeout);
+    // Expected to be called after eventfd_poll returns HAILO_SUCCESS
+    static hailo_status eventfd_read(underlying_handle_t fd);
+    static hailo_status eventfd_write(underlying_handle_t fd);
+    #endif
+
+    underlying_handle_t m_handle;
+};
+
+class Event;
+using EventPtr = std::shared_ptr<Event>;
+using EventPtrList = std::vector<EventPtr>;
+
+// Manual reset event
+class HAILORTAPI Event : public Waitable
+{
+public:
+    enum class State
+    {
+        signalled,
+        not_signalled
+    };
+
+    using Waitable::Waitable;
+
+    static Expected<Event> create(const State& initial_state);
+    static EventPtr create_shared(const State& initial_state);
+
+    virtual hailo_status wait(std::chrono::milliseconds timeout) override;
+    virtual hailo_status signal() override;
+    virtual bool is_auto_reset() override;
+    hailo_status reset();
+
+private:
+    static underlying_handle_t open_event_handle(const State& initial_state);
+};
+
+class Semaphore;
+using SemaphorePtr = std::shared_ptr<Semaphore>;
+using SemaphorePtrList = std::vector<SemaphorePtr>;
+
+class HAILORTAPI Semaphore : public Waitable
+{
+public:
+    using Waitable::Waitable;
+
+    static Expected<Semaphore> create(uint32_t initial_count);
+    static SemaphorePtr create_shared(uint32_t initial_count);
+
+    virtual hailo_status wait(std::chrono::milliseconds timeout) override;
+    virtual hailo_status signal() override;
+    virtual bool is_auto_reset() override;
+
+private:
+    static underlying_handle_t open_semaphore_handle(uint32_t initial_count);
+};
+
+} /* namespace hailort */
+
+#endif /* _HAILO_EVENT_HPP_ */
diff --git a/hailort/libhailort/include/hailo/expected.hpp b/hailort/libhailort/include/hailo/expected.hpp
new file mode 100644 (file)
index 0000000..d6b8c3c
--- /dev/null
@@ -0,0 +1,462 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file expected.hpp
+ * @brief Expected<T> is either a T or the ::hailo_status preventing T to be created.
+ *
+ * Examples:
+ * 
+ * *** Example #1 - Construct a new object ***
+ * 
+ * 
+ * class Widget {
+ * private:
+ *     Widget(float f, hailo_status &status)
+ *     {
+ *         status = (f == 3.14f) ? HAILO_SUCCESS : HAILO_INVALID_ARGUMENT;
+ *     }
+ * 
+ * public:
+ *     static Expected<Widget> create(float f)
+ *     {
+ *         hailo_status status = HAILO_UNINITIALIZED;
+ *         Widget object(f, status);
+ *         if (HAILO_SUCCESS != status) {
+ *             LOGGER__ERROR("Failed creating Widget");
+ *             return make_unexpected(status);
+ *         }
+ *         return std::move(object);
+ *     }
+ * };
+ * 
+ * Note that the constructor must be private since we are using Widget::Create to construct a new object safely.
+ * One can construct and use Widget using the following code:
+ * 
+ * static hailo_status construct_widget()
+ * {
+ *     auto widget = Widget::create(3.14f);
+ *     if (!widget) {
+ *         return widget.status();
+ *     }
+ *     // Use widget ...
+ *     return HAILO_SUCCESS;
+ * }
+ * 
+ * 
+ * *** Example #2 - Construct a new object with copy elision  ***
+ * 
+ * If you have concrete evidence that the move constructor causes performance issues, you can use the following code
+ * to construct a new object with copy elision (if supported by the compiler):
+ * 
+ * 
+ * class Widget {
+ * public:
+ *     Widget(ExpectedKey, float f, hailo_status &status)
+ *     {
+ *         status = (f == 3.14f) ? HAILO_SUCCESS : HAILO_INVALID_ARGUMENT;
+ *     }
+ * 
+ *     static Expected<Widget> create(float f)
+ *     {
+ *         hailo_status status = HAILO_UNINITIALIZED;
+ *         Expected<Widget> expected_object(f, status);
+ *         if (HAILO_SUCCESS != status) {
+ *             LOGGER__ERROR("Failed creating Widget");
+ *             expected_object.make_unexpected(status);
+ *         }
+ *         return expected_object;
+ *     }
+ * };
+ * 
+ * Note that the constructor without ExpectedKey must be private and the constructor with ExpectedKey must be public
+ * since we are using Widget::Create to construct a new object safely.
+ * ExpectedKey is a secret key that can only be constructed by Expected<T>, thus this constructor can only be called
+ * from Expected<T>.
+ * 
+ * One can construct and use Widget using the following code:
+ * 
+ * static hailo_status construct_widget()
+ * {
+ *     auto widget = Widget::create(3.14f);
+ *     if (!widget) {
+ *         return widget.status();
+ *     }
+ *     // Use widget ...
+ *     return HAILO_SUCCESS;
+ * }
+ * 
+ * 
+ * *** Example #3 - Object composition  ***
+ * See example below for object composition over Expected<T>:
+ * 
+ * class WidgetA {
+ * private:
+ *     WidgetA(float f, hailo_status &status)
+ *     {
+ *         status = (f == 3.14f) ? HAILO_SUCCESS : HAILO_INVALID_ARGUMENT;
+ *     }
+ * 
+ * public:
+ *     static Expected<WidgetA> create(float f)
+ *     {
+ *         hailo_status status = HAILO_UNINITIALIZED;
+ *         WidgetA object(f, status);
+ *         if (HAILO_SUCCESS != status) {
+ *             LOGGER__ERROR("Failed creating WidgetA");
+ *             return make_unexpected(status);
+ *         }
+ *         return std::move(object);
+ *     }
+ * };
+ * 
+ * class WidgetB {
+ * private:
+ *     WidgetA m_a;
+ * 
+ *     WidgetB(float f, WidgetA &&a, hailo_status &status) :
+ *         m_a(std::move(a))
+ *     {
+ *         status = (f == 2.71f) ? HAILO_SUCCESS : HAILO_INVALID_ARGUMENT;
+ *     }
+ * 
+ * public:
+ *     static Expected<WidgetB> create(float af, float bf)
+ *     {
+ *         hailo_status status = HAILO_UNINITIALIZED;
+ * 
+ *         auto a = WidgetA::create(af);
+ *         if (!a) {
+ *             return make_unexpected(a.status());
+ *         }
+ * 
+ *         WidgetB object(bf, std::move(a.release()), status);
+ *         if (HAILO_SUCCESS != status) {
+ *             LOGGER__ERROR("Failed creating WidgetB");
+ *             return make_unexpected(status);
+ *         }
+ *         return std::move(object);
+ *     }
+ * };
+ * 
+ * 
+ * *** Example #4 - Divide two numbers  ***
+ * 
+ * A divide function implementation:
+ * 
+ * static Expected<int> divide(int numerator, int denominator)
+ * {
+ *     if (denominator == 0) {
+ *         LOGGER__ERROR("Cannot divide by 0");
+ *         return make_unexpected(HAILO_INVALID_ARGUMENT);
+ *     }
+ *     
+ *     return (numerator/denominator);
+ * }
+ * 
+ * 
+ **/
+
+#ifndef _HAILO_EXPECTED_HPP_
+#define _HAILO_EXPECTED_HPP_
+
+#include "hailo/hailort.h"
+#include <assert.h>
+#include <utility>
+#include <type_traits>
+
+namespace hailort
+{
+
+// TODO(oro): constexpr
+// TODO(oro): noexcept
+// TODO(oro): std::is_default_constructible
+// TODO(oro) Add an implicit variadic ctor to support T implicitly. Note that only implicit variadic constructor
+//           will call Ts explicits ctors implicitly! so we must have both with some kind of std::enable_if
+
+/*! Unexpected is an object containing ::hailo_status error, used when an unexpected outcome occurred. */
+class Unexpected final
+{
+public:
+    explicit Unexpected(hailo_status status) :
+        m_status(status)
+    {}
+
+    hailo_status m_status;
+};
+
+inline Unexpected make_unexpected(hailo_status status)
+{
+    return Unexpected(status);
+}
+
+template<typename T>
+class Expected;
+
+/**
+ * A secret key (passkey idiom) used to call public constructors only from Expected<T>.
+ */
+class ExpectedKey {
+private:
+    template<typename> friend class Expected;
+    constexpr explicit ExpectedKey() = default;
+};
+
+/*! Expected<T> is either a T or the ::hailo_status preventing T to be created.*/
+template<typename T>
+class Expected final
+{
+public:
+    /**
+     * Expected<T> can access Expected<U>'s private members (needed for implicit upcasting)
+     */
+    template<class U>
+    friend class Expected;
+
+    /**
+     * Default constructor
+     * 
+     * Construct a new Expected<T> where:
+     *  - m_value is set to default T()
+     *  - m_status is set to HAILO_SUCCESS
+     * 
+     * NOTE: Commented out because we can use the variadic constructor with T's copy constructor.
+     */
+    // Expected() :
+    //     m_value(), m_status(HAILO_SUCCESS)
+    // {}
+    
+    /**
+     * Copy constructor
+     */
+    explicit Expected(const Expected<T> &other) :
+        m_value(other.m_value),
+        m_status(other.m_status)
+    {}
+
+    /**
+     * Copy constructor for implicit upcasting
+     */
+    template <typename U>
+    Expected(const Expected<U>& other) :
+        m_value(other.m_value),
+        m_status(other.m_status)
+    {}
+
+    /**
+     * Move constructor
+     * 
+     * Construct a new Expected<T> where:
+     *  - other.m_value moved to this.m_value.
+     *  - other.m_status moved to this.m_status, and other.m_status is set to HAILO_UNINITIALIZED.
+     */
+    Expected(Expected<T> &&other) :
+        m_value(std::move(other.m_value)),
+        m_status(std::exchange(other.m_status, HAILO_UNINITIALIZED))
+    {}
+
+    /**
+     * Construct a new Expected<T> from T& where:
+     *  - m_value is set to value.
+     *  - m_status is set to HAILO_SUCCESS.
+     * 
+     * NOTE: Commented out because we can use the variadic constructor with T's copy constructor.
+     */
+    // Expected(const T &value)
+    //     : m_value(value), m_status(HAILO_SUCCESS)
+    // {}
+
+    /**
+     * Construct a new Expected<T> from an rvalue T where:
+     *  - value moved to this.m_value.
+     *  - other.m_status is set to HAILO_SUCCESS.
+     */
+    Expected(T &&value) :
+        m_value(std::move(value)),
+        m_status(HAILO_SUCCESS)
+    {}
+
+    /**
+     * This will prevent the T value to be of type hailo_status.
+     * The goal is to prevent bugs of returning hailo_status as int value, instead of make_unexpected() with error status.
+     */
+    Expected(hailo_status status) = delete;
+
+    /**
+     * Construct a new Expected<T> by forwarding arguments to T constructors.
+     * 
+     * NOTE: std::enable_if_t used because the variadic constructor can sometimes have a better cv-qualifier match than
+     *       the other constructors. For example, candidate is: Expected<T>::operator bool() const [with T = int]
+     *       while conversion from ‘Expected<int>’ to ‘int’.
+     *       See https://stackoverflow.com/questions/51937519/class-constructor-precedence-with-a-variadic-template-constructor-for-a-value-wr
+     */
+    template <typename... Args, std::enable_if_t<std::is_constructible<T, Args...>::value, int> = 0>
+    explicit Expected(Args &&...args) :
+        m_value(std::forward<Args>(args)...),
+        m_status(HAILO_SUCCESS)
+    {}
+
+    /**
+     * Construct a new Expected<T> using the ExpectedKey by forwarding arguments to T constructors.
+     * 
+     * NOTE: std::enable_if_t used because the variadic constructor can sometimes have a better cv-qualifier match than
+     *       the other constructors. For example, candidate is: Expected<T>::operator bool() const [with T = int]
+     *       while conversion from ‘Expected<int>’ to ‘int’.
+     *       See https://stackoverflow.com/questions/51937519/class-constructor-precedence-with-a-variadic-template-constructor-for-a-value-wr
+     * 
+     * NOTE: ExpectedKey can only be constructed from Expected<T>. This way Expected<T> will be the only class that
+     *       can call such constructors (that their first argument is ExpectedKey). 
+     * 
+     * NOTE: We are not going to support calling private constructors because:
+     *      1. Making Expected<T> a friend class will give the user the ability to construct a new Expected<T> object
+     *         without handling the returned status (if the constructor can fail).
+     *      2. std::is_constructible doesn't work on private constructors with friend class, so std::enable_if will
+     *         always be False.
+     */
+    template <typename... Args, std::enable_if_t<std::is_constructible<T, ExpectedKey, Args...>::value, int> = 0>
+    explicit Expected(Args &&...args) :
+        m_value(ExpectedKey(), std::forward<Args>(args)...),
+        m_status(HAILO_SUCCESS)
+    {}
+
+    /**
+     * Construct a new Expected<T> from an Unexpected status.
+     * 
+     * NOTE: Asserting that status is not HAILO_SUCCESS if NDEBUG is not defined.
+     */
+    Expected(const Unexpected &unexpected) :
+        m_status(unexpected.m_status)
+    {
+        assert(unexpected.m_status != HAILO_SUCCESS);
+    }
+
+    Expected<T>& operator=(const Expected<T> &other) = delete;
+    Expected<T>& operator=(Expected<T> &&other) noexcept = delete;
+    Expected<T>& operator=(const T &other) = delete;
+    Expected<T>& operator=(T &&other) noexcept = delete;
+    Expected<T>& operator=(hailo_status status) = delete;
+
+    /**
+     * Destructor.
+     * 
+     * Destruct T if has value.
+     */
+    ~Expected()
+    {
+        if (has_value()) {
+            m_status = HAILO_UNINITIALIZED;
+            m_value.~T();
+        }
+    }
+
+    /**
+     * Make an existing Expected<T> to Unexpected. Mainly used in create() functions for RVO.
+     * Destructs T if has value.
+     * NOTE: Asserting that status is not HAILO_SUCCESS if NDEBUG is not defined.
+     */
+    void make_unexpected(hailo_status status)
+    {
+        assert(status != HAILO_SUCCESS);
+        if (has_value()) {
+            m_value.~T();
+        }
+        m_status = status;
+    }
+
+    /**
+     * Checks whether the object contains a value.
+     */
+    bool has_value() const
+    {
+        return (HAILO_SUCCESS == m_status);
+    }
+
+    /**
+     * Returns the contained value.
+     * @note You must call this method with a valid value inside! otherwise it can lead to undefined behavior.
+     */
+    T& value() &
+    {
+        assert(has_value());
+        return m_value;
+    }
+
+    /**
+     * Returns the contained value.
+     * @note You must call this method with a valid value inside! otherwise it can lead to undefined behavior.
+     */
+    const T& value() const&
+    {
+        assert(has_value());
+        return m_value;
+    }
+
+    /**
+     * Returns the status.
+     */
+    hailo_status status() const
+    {
+        return m_status;
+    }
+
+    /**
+     * Releases ownership of its stored value, by returning its value and making this object Unexpected.
+     * @note You must call this method with a valid value inside! otherwise it can lead to undefined behavior. 
+     */
+    T release()
+    {
+        assert(has_value());
+        T tmp = std::move(m_value);
+        make_unexpected(HAILO_UNINITIALIZED);
+        return tmp;
+    }
+
+    /**
+     * Pointer of the contained value
+     */
+    T* operator->()
+    {
+        assert(has_value());
+        return &(value());
+    }
+
+    /**
+     * Pointer of the contained value
+     */
+    const T* operator->() const
+    {
+        assert(has_value());
+        return &(value());
+    }
+
+    /**
+     * Reference of the contained value
+     */
+    T& operator*() &
+    {
+        assert(has_value());
+        return value();
+    }
+
+    /**
+     * Checks whether the object contains a value.
+     */
+    explicit operator bool() const
+    {
+        return has_value();
+    }
+
+private:
+    union {
+        T m_value;
+    };
+    hailo_status m_status;
+};
+
+template <typename T>
+using ExpectedRef = Expected<std::reference_wrapper<T>>;
+
+} /* namespace hailort */
+
+#endif  // _HAILO_EXPECTED_HPP_
diff --git a/hailort/libhailort/include/hailo/hailort.h b/hailort/libhailort/include/hailo/hailort.h
new file mode 100644 (file)
index 0000000..c59f2fb
--- /dev/null
@@ -0,0 +1,3165 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file hailort.h
+ * @brief C API for HailoRT library.
+ *
+ * C API for Hailo runtime (HailoRT) library for neural network inference on Hailo devices.
+ **/
+
+#ifndef _HAILORT_H_
+#define _HAILORT_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <limits.h>
+
+#include "platform.h"
+
+/** @defgroup group_defines HailoRT API definitions
+ *  @{
+ */
+
+#define HAILO_MAX_ENUM (INT_MAX)
+#define HAILO_INFINITE (UINT32_MAX)
+#define HAILO_DEFAULT_ETH_SCAN_TIMEOUT_MS (10000)
+#define HAILO_DEFAULT_ETH_CONTROL_PORT (22401)
+#define HAILO_DEFAULT_ETH_DEVICE_PORT (0)
+#define HAILO_DEFAULT_ETH_MAX_PAYLOAD_SIZE (1456)
+#define HAILO_DEFAULT_ETH_MAX_NUMBER_OF_RETRIES (3)
+#define HAILO_ETH_ADDRESS_ANY ("0.0.0.0")
+#define HAILO_ETH_PORT_ANY (0)
+#define HAILO_MAX_NAME_SIZE (128)
+#define HAILO_MAX_STREAM_NAME_SIZE (HAILO_MAX_NAME_SIZE)
+#define HAILO_MAX_BOARD_NAME_LENGTH (32)
+#define HAILO_MAX_DEVICE_ID_LENGTH (32)
+#define HAILO_MAX_SERIAL_NUMBER_LENGTH (16)
+#define HAILO_MAX_PART_NUMBER_LENGTH (16)
+#define HAILO_MAX_PRODUCT_NAME_LENGTH (42)
+#define HAILO_DEFAULT_INIT_SAMPLING_PERIOD_US (HAILO_SAMPLING_PERIOD_1100US)
+#define HAILO_DEFAULT_INIT_AVERAGING_FACTOR (HAILO_AVERAGE_FACTOR_256)
+#define HAILO_DEFAULT_BUFFERS_THRESHOLD (0)
+#define HAILO_DEFAULT_MAX_ETHERNET_BANDWIDTH_BYTES_PER_SEC (106300000)
+#define HAILO_MAX_STREAMS_COUNT (32)
+#define HAILO_DEFAULT_BATCH_SIZE (1)
+#define HAILO_MAX_NETWORK_GROUPS (8)
+#define HAILO_MAX_NETWORK_GROUP_NAME_SIZE (HAILO_MAX_NAME_SIZE)
+/* Network name is always attached to network group name with '/' separator */
+#define HAILO_MAX_NETWORK_NAME_SIZE (HAILO_MAX_NETWORK_GROUP_NAME_SIZE + 1 + HAILO_MAX_NAME_SIZE)
+#define HAILO_MAX_NETWORKS_IN_NETWORK_GROUP (8)
+#define HAILO_PCIE_ANY_DOMAIN (UINT32_MAX)
+#define HAILO_DEFAULT_VSTREAM_QUEUE_SIZE (2)
+#define HAILO_DEFAULT_VSTREAM_TIMEOUT_MS (10000)
+#define HAILO_DEFAULT_DEVICE_COUNT (1)
+
+#define HAILO_SOC_ID_LENGTH (32)
+#define HAILO_ETH_MAC_LENGTH (6)
+#define HAILO_UNIT_LEVEL_TRACKING_BYTES_LENGTH (12)
+#define HAILO_SOC_PM_VALUES_BYTES_LENGTH (24)
+#define HAILO_MAX_TEMPERATURE_THROTTLING_LEVELS_NUMBER (4)
+
+typedef float float32_t;
+typedef double float64_t;
+typedef uint16_t nms_bbox_counter_t;
+
+/** HailoRT return codes */
+#define HAILO_STATUS_VARIABLES\
+    HAILO_STATUS__X(0,  HAILO_SUCCESS                                 /*!< Success - No error */)\
+    HAILO_STATUS__X(1,  HAILO_UNINITIALIZED                           /*!< No error code was initialized */)\
+    HAILO_STATUS__X(2,  HAILO_INVALID_ARGUMENT                        /*!< Invalid argument passed to function */)\
+    HAILO_STATUS__X(3,  HAILO_OUT_OF_HOST_MEMORY                      /*!< Cannot allocate more memory at host */)\
+    HAILO_STATUS__X(4,  HAILO_TIMEOUT                                 /*!< Received a timeout */)\
+    HAILO_STATUS__X(5,  HAILO_INSUFFICIENT_BUFFER                     /*!< Buffer is insufficient */)\
+    HAILO_STATUS__X(6,  HAILO_INVALID_OPERATION                       /*!< Invalid operation */)\
+    HAILO_STATUS__X(7,  HAILO_NOT_IMPLEMENTED                         /*!< Code has not been implemented */)\
+    HAILO_STATUS__X(8,  HAILO_INTERNAL_FAILURE                        /*!< Unexpected internal failure */)\
+    HAILO_STATUS__X(9,  HAILO_DATA_ALIGNMENT_FAILURE                  /*!< Data is not aligned */)\
+    HAILO_STATUS__X(10, HAILO_CHUNK_TOO_LARGE                         /*!< Chunk too large */)\
+    HAILO_STATUS__X(11, HAILO_INVALID_LOGGER_LEVEL                    /*!< Used non-compiled level */)\
+    HAILO_STATUS__X(12, HAILO_CLOSE_FAILURE                           /*!< Failed to close fd */)\
+    HAILO_STATUS__X(13, HAILO_OPEN_FILE_FAILURE                       /*!< Failed to open file */)\
+    HAILO_STATUS__X(14, HAILO_FILE_OPERATION_FAILURE                  /*!< File operation failure */)\
+    HAILO_STATUS__X(15, HAILO_UNSUPPORTED_CONTROL_PROTOCOL_VERSION    /*!< Unsupported control protocol version */)\
+    HAILO_STATUS__X(16, HAILO_UNSUPPORTED_FW_VERSION                  /*!< Unsupported firmware version */)\
+    HAILO_STATUS__X(17, HAILO_INVALID_CONTROL_RESPONSE                /*!< Invalid control response */)\
+    HAILO_STATUS__X(18, HAILO_FW_CONTROL_FAILURE                      /*!< Control failed in firmware */)\
+    HAILO_STATUS__X(19, HAILO_ETH_FAILURE                             /*!< Ethernet operation has failed */)\
+    HAILO_STATUS__X(20, HAILO_ETH_INTERFACE_NOT_FOUND                 /*!< Ethernet interface not found */)\
+    HAILO_STATUS__X(21, HAILO_ETH_RECV_FAILURE                        /*!< Ethernet failed at recv operation */)\
+    HAILO_STATUS__X(22, HAILO_ETH_SEND_FAILURE                        /*!< Ethernet failed at send operation */)\
+    HAILO_STATUS__X(23, HAILO_INVALID_FIRMWARE                        /*!< Firmware bin is invalid */)\
+    HAILO_STATUS__X(24, HAILO_INVALID_CONTEXT_COUNT                   /*!< Host build too many contexts */)\
+    HAILO_STATUS__X(25, HAILO_INVALID_FRAME                           /*!< Part or all of the result data is invalid */)\
+    HAILO_STATUS__X(26, HAILO_INVALID_HEF                             /*!< Invalid HEF */)\
+    HAILO_STATUS__X(27, HAILO_PCIE_NOT_SUPPORTED_ON_PLATFORM          /*!< PCIe not supported on platform */)\
+    HAILO_STATUS__X(28, HAILO_INTERRUPTED_BY_SIGNAL                   /*!< Blocking syscall was interrupted by a signal */)\
+    HAILO_STATUS__X(29, HAILO_START_VDMA_CHANNEL_FAIL                 /*!< Starting VDMA channel failure */)\
+    HAILO_STATUS__X(30, HAILO_SYNC_VDMA_BUFFER_FAIL                   /*!< Synchronizing VDMA buffer failure */)\
+    HAILO_STATUS__X(31, HAILO_STOP_VDMA_CHANNEL_FAIL                  /*!< Stopping VDMA channel failure */)\
+    HAILO_STATUS__X(32, HAILO_CLOSE_VDMA_CHANNEL_FAIL                 /*!< Closing VDMA channel failure */)\
+    HAILO_STATUS__X(33, HAILO_ATR_TABLES_CONF_VALIDATION_FAIL         /*!< Validating address translation tables failure, for FW control use */)\
+    HAILO_STATUS__X(34, HAILO_CONTROL_EVENT_CREATE_FAIL               /*!< Creating control event failure */)\
+    HAILO_STATUS__X(35, HAILO_READ_EVENT_FAIL                         /*!< Reading event failure */)\
+    HAILO_STATUS__X(36, HAILO_PCIE_DRIVER_FAIL                        /*!< PCIE driver failure */)\
+    HAILO_STATUS__X(37, HAILO_INVALID_FIRMWARE_MAGIC                  /*!< Invalid FW magic */)\
+    HAILO_STATUS__X(38, HAILO_INVALID_FIRMWARE_CODE_SIZE              /*!< Invalid FW code size */)\
+    HAILO_STATUS__X(39, HAILO_INVALID_KEY_CERTIFICATE_SIZE            /*!< Invalid key certificate size */)\
+    HAILO_STATUS__X(40, HAILO_INVALID_CONTENT_CERTIFICATE_SIZE        /*!< Invalid content certificate size */)\
+    HAILO_STATUS__X(41, HAILO_MISMATCHING_FIRMWARE_BUFFER_SIZES       /*!< FW buffer sizes mismatch */)\
+    HAILO_STATUS__X(42, HAILO_INVALID_FIRMWARE_CPU_ID                 /*!< Invalid CPU ID in FW */)\
+    HAILO_STATUS__X(43, HAILO_CONTROL_RESPONSE_MD5_MISMATCH           /*!< MD5 of control response does not match expected MD5 */)\
+    HAILO_STATUS__X(44, HAILO_GET_CONTROL_RESPONSE_FAIL               /*!< Get control response failed */)\
+    HAILO_STATUS__X(45, HAILO_GET_D2H_EVENT_MESSAGE_FAIL              /*!< Reading device-to-host message failure */)\
+    HAILO_STATUS__X(46, HAILO_MUTEX_INIT_FAIL                         /*!< Mutex initialization failure */)\
+    HAILO_STATUS__X(47, HAILO_OUT_OF_DESCRIPTORS                      /*!< Cannot allocate more descriptors */)\
+    HAILO_STATUS__X(48, HAILO_UNSUPPORTED_OPCODE                      /*!< Unsupported opcode was sent to device */)\
+    HAILO_STATUS__X(49, HAILO_USER_MODE_RATE_LIMITER_NOT_SUPPORTED    /*!< User mode rate limiter not supported on platform */)\
+    HAILO_STATUS__X(50, HAILO_RATE_LIMIT_MAXIMUM_BANDWIDTH_EXCEEDED   /*!< Rate limit exceeded HAILO_DEFAULT_MAX_ETHERNET_BANDWIDTH_BYTES_PER_SEC */)\
+    HAILO_STATUS__X(51, HAILO_ANSI_TO_UTF16_CONVERSION_FAILED         /*!< Failed converting ANSI string to UNICODE */)\
+    HAILO_STATUS__X(52, HAILO_UTF16_TO_ANSI_CONVERSION_FAILED         /*!< Failed converting UNICODE string to ANSI */)\
+    HAILO_STATUS__X(53, HAILO_UNEXPECTED_INTERFACE_INFO_FAILURE       /*!< Failed retrieving interface info */)\
+    HAILO_STATUS__X(54, HAILO_UNEXPECTED_ARP_TABLE_FAILURE            /*!< Failed retrieving arp table */)\
+    HAILO_STATUS__X(55, HAILO_MAC_ADDRESS_NOT_FOUND                   /*!< MAC address not found in the arp table */)\
+    HAILO_STATUS__X(56, HAILO_NO_IPV4_INTERFACES_FOUND                /*!< No interfaces found with an IPv4 address */)\
+    HAILO_STATUS__X(57, HAILO_SHUTDOWN_EVENT_SIGNALED                 /*!< A shutdown event has been signaled */)\
+    HAILO_STATUS__X(58, HAILO_THREAD_ALREADY_ACTIVATED                /*!< The given thread has already been activated */)\
+    HAILO_STATUS__X(59, HAILO_THREAD_NOT_ACTIVATED                    /*!< The given thread has not been activated */)\
+    HAILO_STATUS__X(60, HAILO_THREAD_NOT_JOINABLE                     /*!< The given thread is not joinable */)\
+    HAILO_STATUS__X(61, HAILO_NOT_FOUND                               /*!< Could not find element */)\
+    HAILO_STATUS__X(62, HAILO_STREAM_ABORTED                          /*!< Stream aborted due to an external event */)\
+    HAILO_STATUS__X(63, HAILO_STREAM_INTERNAL_ABORT                   /*!< Stream recv/send was aborted */)\
+    HAILO_STATUS__X(64, HAILO_PCIE_DRIVER_NOT_INSTALLED               /*!< Pcie driver is not installed */)\
+    HAILO_STATUS__X(65, HAILO_NOT_AVAILABLE                           /*!< Componenet is not available */)\
+    HAILO_STATUS__X(66, HAILO_TRAFFIC_CONTROL_FAILURE                 /*!< Traffic control failure */)\
+    HAILO_STATUS__X(67, HAILO_INVALID_SECOND_STAGE                    /*!< Second stage bin is invalid */)\
+    HAILO_STATUS__X(68, HAILO_INVALID_PIPELINE                        /*!< Pipeline is invalid */)\
+    HAILO_STATUS__X(69, HAILO_NETWORK_GROUP_NOT_ACTIVATED             /*!< Network group is not activated */)\
+    HAILO_STATUS__X(70, HAILO_VSTREAM_PIPELINE_NOT_ACTIVATED          /*!< VStream pipeline is not activated */)\
+    HAILO_STATUS__X(71, HAILO_OUT_OF_FW_MEMORY                        /*!< Cannot allocate more memory at fw */)\
+    HAILO_STATUS__X(72, HAILO_STREAM_NOT_ACTIVATED                    /*!< Stream is not activated */)\
+    HAILO_STATUS__X(73, HAILO_DEVICE_IN_USE                           /*!< The device is already in use */)\
+    HAILO_STATUS__X(74, HAILO_OUT_OF_PHYSICAL_DEVICES                 /*!< There are not enough physical devices */)\
+    HAILO_STATUS__X(75, HAILO_INVALID_DEVICE_ARCHITECTURE             /*!< Invalid device architecture */)\
+    HAILO_STATUS__X(76, HAILO_INVALID_DRIVER_VERSION                  /*!< Invalid driver version*/)\
+
+typedef enum {
+#define HAILO_STATUS__X(value, name) name = value,
+    HAILO_STATUS_VARIABLES
+#undef HAILO_STATUS__X
+
+    /** Must be last! */
+    HAILO_STATUS_COUNT,
+    
+    /** Max enum value to maintain ABI Integrity */
+    HAILO_STATUS_MAX_ENUM                       = HAILO_MAX_ENUM
+} hailo_status;
+
+/** HailoRT library version */
+typedef struct {
+    uint32_t major;
+    uint32_t minor;
+    uint32_t revision;
+} hailo_version_t;
+
+/** Represents the device (chip) */
+typedef struct _hailo_device *hailo_device;
+
+/** Represents a virtual device which manages several physical devices */
+typedef struct _hailo_vdevice *hailo_vdevice;
+
+/** Compiled HEF model that can be loaded to Hailo devices */
+typedef struct _hailo_hef *hailo_hef;
+
+/** Input (host to device) stream representation */
+typedef struct _hailo_input_stream *hailo_input_stream;
+
+/** Output (device to host) stream representation */
+typedef struct _hailo_output_stream *hailo_output_stream;
+
+/** Loaded network_group that can be activated */
+typedef struct _hailo_configured_network_group *hailo_configured_network_group;
+
+/** Activated network_group that can be used to send/receive data */
+typedef struct _hailo_activated_network_group *hailo_activated_network_group;
+
+/** Object used for input stream transformation, store all necessary allocated buffers */
+typedef struct _hailo_input_transform_context *hailo_input_transform_context;
+
+/** Object used for output stream transformation, store all necessary allocated buffers */
+typedef struct _hailo_output_transform_context *hailo_output_transform_context;
+
+/** Object used to demux muxed stream */
+typedef struct _hailo_output_demuxer *hailo_output_demuxer;
+
+/** Input virtual stream */
+typedef struct _hailo_input_vstream *hailo_input_vstream;
+
+/** Output virtual stream */
+typedef struct _hailo_output_vstream *hailo_output_vstream;
+
+/** Enum that represents the type of devices that would be measured */
+typedef enum hailo_dvm_options_e {
+    /** VDD_CORE DVM */
+    HAILO_DVM_OPTIONS_VDD_CORE = 0,
+
+    /** VDD_IO DVM */
+    HAILO_DVM_OPTIONS_VDD_IO,
+
+    /** MIPI_AVDD DVM */
+    HAILO_DVM_OPTIONS_MIPI_AVDD,
+
+    /** MIPI_AVDD_H DVM */
+    HAILO_DVM_OPTIONS_MIPI_AVDD_H,
+
+    /** USB_AVDD_IO DVM */
+    HAILO_DVM_OPTIONS_USB_AVDD_IO,
+
+    /** VDD_TOP DVM */
+    HAILO_DVM_OPTIONS_VDD_TOP,
+
+    /** USB_AVDD_IO_HV DVM */
+    HAILO_DVM_OPTIONS_USB_AVDD_IO_HV,
+
+    /** AVDD_H DVM */
+    HAILO_DVM_OPTIONS_AVDD_H,
+
+    /** SDIO_VDD_IO DVM */
+    HAILO_DVM_OPTIONS_SDIO_VDD_IO,
+
+    /** OVERCURRENT_PROTECTION DVM */
+    HAILO_DVM_OPTIONS_OVERCURRENT_PROTECTION,
+
+    /** Must be last! */
+    HAILO_DVM_OPTIONS_COUNT,
+    
+    /** Select the default DVM option according to the supported features */
+    HAILO_DVM_OPTIONS_AUTO = INT_MAX,
+
+    /** Max enum value to maintain ABI Integrity */
+    HAILO_DVM_OPTIONS_MAX_ENUM = HAILO_MAX_ENUM
+} hailo_dvm_options_t;
+
+/** Enum that represents what would be measured on the selected device */
+typedef enum hailo_power_measurement_types_e {
+    /** SHUNT_VOLTAGE measurement type, measured in mV */
+    HAILO_POWER_MEASUREMENT_TYPES__SHUNT_VOLTAGE = 0,
+
+    /** BUS_VOLTAGE measurement type, measured in mV */
+    HAILO_POWER_MEASUREMENT_TYPES__BUS_VOLTAGE,
+
+    /** POWER measurement type, measured in W */
+    HAILO_POWER_MEASUREMENT_TYPES__POWER,
+
+    /** CURRENT measurement type, measured in mA */
+    HAILO_POWER_MEASUREMENT_TYPES__CURRENT,
+
+    /** Must be last! */
+    HAILO_POWER_MEASUREMENT_TYPES__COUNT,
+
+    /** Select the default measurement type according to the supported features */
+    HAILO_POWER_MEASUREMENT_TYPES__AUTO = INT_MAX,
+
+    /** Max enum value to maintain ABI Integrity */
+    HAILO_POWER_MEASUREMENT_TYPES__MAX_ENUM = HAILO_MAX_ENUM
+} hailo_power_measurement_types_t;
+
+/** Enum that represents all the bit options and related conversion times for each bit setting for Bus Voltage and Shunt Voltage */
+typedef enum hailo_sampling_period_e {
+    HAILO_SAMPLING_PERIOD_140US = 0,
+    HAILO_SAMPLING_PERIOD_204US,
+    HAILO_SAMPLING_PERIOD_332US,
+    HAILO_SAMPLING_PERIOD_588US,
+    HAILO_SAMPLING_PERIOD_1100US,
+    HAILO_SAMPLING_PERIOD_2116US,
+    HAILO_SAMPLING_PERIOD_4156US,
+    HAILO_SAMPLING_PERIOD_8244US,
+
+    /** Max enum value to maintain ABI Integrity */
+    HAILO_SAMPLING_PERIOD_MAX_ENUM = HAILO_MAX_ENUM
+} hailo_sampling_period_t;
+
+/** Enum that represents all the AVG bit settings and related number of averages for each bit setting */
+typedef enum hailo_averaging_factor_e {
+    HAILO_AVERAGE_FACTOR_1 = 0,
+    HAILO_AVERAGE_FACTOR_4,
+    HAILO_AVERAGE_FACTOR_16,
+    HAILO_AVERAGE_FACTOR_64,
+    HAILO_AVERAGE_FACTOR_128,
+    HAILO_AVERAGE_FACTOR_256,
+    HAILO_AVERAGE_FACTOR_512,
+    HAILO_AVERAGE_FACTOR_1024,
+
+    /** Max enum value to maintain ABI Integrity */
+    HAILO_AVERAGE_FACTOR_MAX_ENUM = HAILO_MAX_ENUM
+} hailo_averaging_factor_t;
+
+/** Data of the power measurement samples */
+typedef struct {
+    float32_t average_value;
+    float32_t average_time_value_milliseconds;
+    float32_t min_value;
+    float32_t max_value;
+    uint32_t total_number_of_samples;
+} hailo_power_measurement_data_t;
+
+/** Ethernet device information */
+typedef struct {
+    struct sockaddr_in host_address;
+    struct sockaddr_in device_address;
+    uint32_t timeout_millis;
+    uint8_t max_number_of_attempts;
+    uint16_t max_payload_size;
+} hailo_eth_device_info_t;
+
+/** PCIe device information */
+typedef struct {
+    uint32_t domain;
+    uint32_t bus;
+    uint32_t device;
+    uint32_t func;
+} hailo_pcie_device_info_t;
+
+/** Virtual device parameters */
+typedef struct {
+    /** Requested number of physical devices. if @a device_infos is not NULL, represents the number of ::hailo_pcie_device_info_t in @a device_infos */
+    uint32_t device_count;
+    /** Specific physical devices information to create the vdevice from. If NULL, the vdevice will try to occupy devices from the available pool */
+    hailo_pcie_device_info_t *device_infos;
+} hailo_vdevice_params_t;
+
+/** Device architecture */
+typedef enum hailo_device_architecture_e {
+    HAILO_ARCH_HAILO8_A0 = 0,
+    HAILO_ARCH_HAILO8_B0,
+    HAILO_ARCH_MERCURY_CA,
+    HAILO_ARCH_MERCURY_VPU,
+    
+    /** Max enum value to maintain ABI Integrity */
+    HAILO_ARCH_MAX_ENUM = HAILO_MAX_ENUM
+} hailo_device_architecture_t;
+
+typedef enum {
+    HAILO_CPU_ID_0 = 0,
+    HAILO_CPU_ID_1,
+
+    /** Max enum value to maintain ABI Integrity */
+    HAILO_CPU_ID_MAX_ENUM = HAILO_MAX_ENUM
+} hailo_cpu_id_t;
+
+/** Hailo firmware version */
+typedef struct {
+    uint32_t major;
+    uint32_t minor;
+    uint32_t revision;
+} hailo_firmware_version_t;
+
+/** Hailo device identity */
+typedef struct {
+    uint32_t protocol_version;
+    hailo_firmware_version_t fw_version;
+    uint32_t logger_version;
+    uint8_t board_name_length;
+    char board_name[HAILO_MAX_BOARD_NAME_LENGTH];
+    bool is_release;
+    hailo_device_architecture_t device_architecture;
+    uint8_t serial_number_length;
+    char serial_number[HAILO_MAX_SERIAL_NUMBER_LENGTH];
+    uint8_t part_number_length;
+    char part_number[HAILO_MAX_PART_NUMBER_LENGTH];
+    uint8_t product_name_length;
+    char product_name[HAILO_MAX_PRODUCT_NAME_LENGTH];
+} hailo_device_identity_t;
+
+typedef struct {
+    bool is_release;
+    hailo_firmware_version_t fw_version;
+} hailo_core_information_t;
+
+/* Hailo device boot source */
+typedef enum {
+    HAILO_DEVICE_BOOT_SOURCE_INVALID = 0,
+    HAILO_DEVICE_BOOT_SOURCE_PCIE,
+    HAILO_DEVICE_BOOT_SOURCE_FLASH,
+
+    /** Max enum value to maintain ABI Integrity */
+    HAILO_DEVICE_BOOT_SOURCE_MAX = HAILO_MAX_ENUM
+} hailo_device_boot_source_t;
+
+/** Hailo device supported features */
+typedef struct {
+    /** Is ethernet supported */
+    bool ethernet;
+    /** Is mipi supported */
+    bool mipi;
+    /** Is pcie supported */
+    bool pcie;
+    /** Is current monitoring supported */
+    bool current_monitoring;
+    /** Is current mdio supported */
+    bool mdio;
+} hailo_device_supported_features_t;
+
+/** Hailo extended device information */
+typedef struct {
+    /** The core clock rate */
+    uint32_t neural_network_core_clock_rate;
+    /** Hailo device supported features */
+    hailo_device_supported_features_t supported_features;
+    /** Device boot source */
+    hailo_device_boot_source_t boot_source;
+    /** SOC id */
+    uint8_t soc_id[HAILO_SOC_ID_LENGTH];
+    /** Device lcs */
+    uint8_t lcs;
+    /** Device Ethernet Mac address */
+    uint8_t eth_mac_address[HAILO_ETH_MAC_LENGTH];
+    /** Hailo device unit level tracking id*/
+    uint8_t unit_level_tracking_id[HAILO_UNIT_LEVEL_TRACKING_BYTES_LENGTH];
+    /** Hailo device pm values */
+    uint8_t soc_pm_values[HAILO_SOC_PM_VALUES_BYTES_LENGTH]; 
+} hailo_extended_device_information_t;
+
+/** Endianness (byte order) */
+typedef enum {
+    HAILO_BIG_ENDIAN = 0,
+    HAILO_LITTLE_ENDIAN = 1,
+
+    /** Max enum value to maintain ABI Integrity */
+    HAILO_ENDIANNESS_MAX_ENUM = HAILO_MAX_ENUM
+} hailo_endianness_t;
+
+/** I2C slave configuration */
+typedef struct {
+    hailo_endianness_t endianness;
+    uint16_t slave_address;
+    uint8_t register_address_size;
+    uint8_t bus_index;
+    bool should_hold_bus;
+} hailo_i2c_slave_config_t;
+
+/** Firmware user config information */
+typedef struct {
+    uint32_t version;
+    uint32_t entry_count;
+    uint32_t total_size;
+} hailo_fw_user_config_information_t;
+
+/** Data format types */
+typedef enum {
+    /**
+     * Chosen automatically to match the format expected by the device, usually UINT8.
+     * Can be checked using ::hailo_stream_info_t format.type.
+     */
+    HAILO_FORMAT_TYPE_AUTO                  = 0,
+
+    /** Data format type uint8_t - 1 byte per item, host/device side */
+    HAILO_FORMAT_TYPE_UINT8                 = 1,
+
+    /** Data format type uint16_t - 2 bytes per item, host/device side */
+    HAILO_FORMAT_TYPE_UINT16                = 2,
+
+    /** Data format type float32_t - used only on host side (Translated in the quantization process) */
+    HAILO_FORMAT_TYPE_FLOAT32               = 3,
+
+    /** Max enum value to maintain ABI Integrity */
+    HAILO_FORMAT_TYPE_MAX_ENUM              = HAILO_MAX_ENUM
+} hailo_format_type_t;
+
+/**
+ * Data format orders, i.e. how the rows, columns and features are ordered:
+ *  - N: Number of images in the batch
+ *  - H: Height of the image
+ *  - W: Width of the image
+ *  - C: Number of channels of the image (e.g. 3 for RGB, 1 for grayscale...)
+ */
+typedef enum {
+    /**
+     * Chosen automatically to match the format expected by the device.
+     */
+    HAILO_FORMAT_ORDER_AUTO                  = 0,
+
+    /**
+     *  - Host side: [N, H, W, C]
+     *  - Device side: [N, H, W, C], where width is padded to 8 elements
+     */
+    HAILO_FORMAT_ORDER_NHWC                 = 1,
+
+    /**
+     *  - Not used for host side
+     *  - Device side: [N, H, C, W], where width is padded to 8 elements
+     */
+    HAILO_FORMAT_ORDER_NHCW                 = 2,
+
+    /**
+     * FCR means first channels (features) are sent to HW:
+     *  - Host side: [N, H, W, C]
+     *  - Device side: [N, H, W, C]:
+     *      - Input - channels are expected to be aligned to 8 elements
+     *      - Output - width is padded to 8 elements
+     */
+    HAILO_FORMAT_ORDER_FCR                  = 3,
+
+    /**
+     * F8CR means first 8-channels X width are sent to HW:
+     *  - Host side: [N, H, W, C]
+     *  - Device side: [N, H, W, 8C], where channels are padded to 8 elements:
+     *  - ROW1:
+     *      - W X 8C_1, W X 8C_2, ... , W X 8C_n
+     *  - ROW2:
+     *      - W X 8C_1, W X 8C_2, ... , W X 8C_n
+     * ...
+     */
+    HAILO_FORMAT_ORDER_F8CR                 = 4,
+
+    /**
+     * Output format of argmax layer:
+     * - Host side: [N, H, W, 1]
+     * - Device side: [N, H, W, 1], where width is padded to 8 elements
+     */
+    HAILO_FORMAT_ORDER_NHW                  = 5,
+
+    /**
+     * Channels only:
+     * - Host side: [N,C]
+     * - Device side: [N, C], where channels are padded to 8 elements
+     */
+    HAILO_FORMAT_ORDER_NC                   = 6,
+
+    /**
+     * Bayer format:
+     * - Host side: [N, H, W, 1]
+     * - Device side: [N, H, W, 1], where width is padded to 8 elements
+     */
+    HAILO_FORMAT_ORDER_BAYER_RGB            = 7,
+
+    /**
+     * Bayer format, same as ::HAILO_FORMAT_ORDER_BAYER_RGB where
+     * Channel is 12 bit
+     */
+    HAILO_FORMAT_ORDER_12_BIT_BAYER_RGB     = 8,
+
+    /**
+     * NMS bbox
+     * - Host side
+     * 
+     *      For each class (::hailo_nms_shape_t.number_of_classes), the layout is
+     *          \code
+     *          struct (packed) {
+     *              uint16_t/float32_t bbox_count;
+     *              hailo_bbox_t/hailo_bbox_float32_t bbox[bbox_count];
+     *          };
+     *          \endcode
+     *
+     *      The host format type can be either ::HAILO_FORMAT_TYPE_FLOAT32 or ::HAILO_FORMAT_TYPE_UINT16.
+     * 
+     *      Maximum amount of bboxes per class is ::hailo_nms_shape_t.max_bboxes_per_class.
+     *
+     * - Device side output (result of NMS layer):
+     *      Internal implementation
+     */
+    HAILO_FORMAT_ORDER_HAILO_NMS            = 9,
+
+    /**
+     * - Not used for host side
+     * - Device side: [N, H, W, C], where channels are 4 (RGB + 1 padded zero byte) and width is padded to 8 elements
+     */
+    HAILO_FORMAT_ORDER_RGB888               = 10,
+
+    /**
+     * - Host side: [N, C, H, W]
+     * - Not used for device side
+     */
+    HAILO_FORMAT_ORDER_NCHW                 = 11,
+
+    /**
+     * YUV format, encoding 2 pixels in 32 bits
+     *      [Y0, U0, Y1, V0] represents [Y0, U0, V0], [Y1, U0, V0]
+     * - Host side: [Y0, U0, Y1, V0]
+     * - Device side: [Y0, U0, Y1, V0]
+     */
+    HAILO_FORMAT_ORDER_YUY2                 = 12,
+
+    /** Max enum value to maintain ABI Integrity */
+    HAILO_FORMAT_ORDER_MAX_ENUM             = HAILO_MAX_ENUM
+} hailo_format_order_t;
+
+/** Data format flags */
+typedef enum {
+    HAILO_FORMAT_FLAGS_NONE                 = 0,
+
+    /**
+     * If not set, HailoRT performs the quantization (scaling) step.
+     * If set:
+     * - Input data: HailoRT assumes that the data is already quantized (scaled) by the user,
+     *   so it does not perform the quantization (scaling) step.
+     * - Output data: The data will be returned to the user without rescaling (i.e., the data won't be rescaled by HailoRT).
+     */
+    HAILO_FORMAT_FLAGS_QUANTIZED            = 1 << 0,
+
+    /**
+     * If set, the frame height/width are transposed. Supported orders:
+     *      - ::HAILO_FORMAT_ORDER_NHWC
+     *      - ::HAILO_FORMAT_ORDER_NHW
+     *      - ::HAILO_FORMAT_ORDER_BAYER_RGB
+     *      - ::HAILO_FORMAT_ORDER_12_BIT_BAYER_RGB
+     *      - ::HAILO_FORMAT_ORDER_FCR
+     *      - ::HAILO_FORMAT_ORDER_F8CR
+     * 
+     * When set on host side, ::hailo_stream_info_t shape of the stream will be a transposed version of the host buffer
+     * (The height and width will be swapped)
+     */
+    HAILO_FORMAT_FLAGS_TRANSPOSED          = 1 << 1,
+
+    /**
+     * If set, argmax will be called on the feature dimension.
+     * Only set on device side.
+     */
+    HAILO_FORMAT_FLAGS_HOST_ARGMAX         = 1 << 2,
+
+    /** Max enum value to maintain ABI Integrity */
+    HAILO_FORMAT_FLAGS_MAX_ENUM             = HAILO_MAX_ENUM
+} hailo_format_flags_t;
+
+/** Hailo data format */
+typedef struct {
+    hailo_format_type_t type;
+    hailo_format_order_t order;
+    hailo_format_flags_t flags;
+} hailo_format_t;
+
+/** Indicates how transformations on the data should be done */
+typedef enum {
+    /** The vstream will not run the transformation (The data will be in hw format) */
+    HAILO_STREAM_NO_TRANSFORM               = 0,
+
+    /** The transformation process will be part of the vstream send/recv (The data will be in host format). */
+    HAILO_STREAM_TRANSFORM_COPY             = 1,
+
+    /** Max enum value to maintain ABI Integrity */
+    HAILO_STREAM_MAX_ENUM                   = HAILO_MAX_ENUM
+} hailo_stream_transform_mode_t;
+
+/** Stream direction - host to device or device to host */
+typedef enum {
+    HAILO_H2D_STREAM                    = 0,
+    HAILO_D2H_STREAM                    = 1,
+
+    HAILO_STREAM_DIRECTION_MAX_ENUM     = HAILO_MAX_ENUM
+} hailo_stream_direction_t;
+
+/** Input or output data transform parameters */
+typedef struct {
+    hailo_stream_transform_mode_t transform_mode;
+    hailo_format_t user_buffer_format;
+} hailo_transform_params_t;
+
+/** Demuxer params */
+typedef struct {
+    EMPTY_STRUCT_PLACEHOLDER
+} hailo_demux_params_t;
+
+/** Quantization information.
+ * Property of ::hailo_stream_info_t, ::hailo_vstream_info_t.\n 
+ * Hailo devices require input data to be quantized/scaled before it is sent. Similarly, data outputted
+ * from the device needs to be 'de-quantized'/rescaled as well.
+ * Each input/output layer is assigned two floating point values that are parameters to an input/output
+ * transformation: qp_zp (zero_point) and qp_scale. These values are stored in the HEF.
+ * - Input transformation: Input data is divided by qp_scale and then qp_zp is added to the result.
+ * - Output transformation: qp_zp is subtracted from output data and then the result is multiplied by qp_scale.
+*/
+typedef struct {
+    /** zero_point */
+    float32_t qp_zp;
+
+    /** scale */
+    float32_t qp_scale;
+
+    /** min limit value */
+    float32_t limvals_min;
+
+    /** max limit value */
+    float32_t limvals_max;
+} hailo_quant_info_t;
+
+/** Ethernet input stream (host to device) parameters */
+typedef struct {
+    struct sockaddr_in host_address;
+    port_t device_port;
+    bool is_sync_enabled;
+    uint32_t frames_per_sync;
+    uint16_t max_payload_size;
+
+    /**
+     * Stream may be rate limited by setting this member to te desired rate other than zero.
+     * The limition will only effect the corresponding stream (other network traffic won't be effected).
+     * - On linux the "Traffic Control" tool will be used to limit the network rate (see `man tc`):
+     *   - This will result in external processes being created at the creation and destruction of the stream
+     *   - `sudo` privileges are required.
+     *   - Alternatively, use the command line tool `hailortcli udp-rate-limiter`, which is also implemented using "Traffic Control".
+     *     In this case this member must be set to zero.
+     * - On Windows user-mode rate limiting (via a token-bucket) is used:
+     *   - User-mode rate limiting is designed to consistently keep the stream at the desired rate, however fluctuations will occur.
+     *     This member parameter provides an upper bound on the bandwidth at which the stream will operate.
+     */
+    uint32_t rate_limit_bytes_per_sec;
+
+    uint32_t buffers_threshold;
+} hailo_eth_input_stream_params_t;
+
+/** Ethernet output stream (device to host) parameters */
+typedef struct {
+    struct sockaddr_in host_address;
+    port_t device_port;
+    bool is_sync_enabled;
+    uint16_t max_payload_size;
+    uint32_t buffers_threshold;
+} hailo_eth_output_stream_params_t;
+
+/** PCIe input stream (host to device) parameters */
+typedef struct {
+    EMPTY_STRUCT_PLACEHOLDER
+} hailo_pcie_input_stream_params_t;
+
+/** PCIe output stream (device to host) parameters */
+typedef struct {
+    EMPTY_STRUCT_PLACEHOLDER
+} hailo_pcie_output_stream_params_t;
+
+/** Indicates amount of pixels per clock on a MIPI stream **/
+typedef enum {
+    HAILO_MIPI_PIXELS_PER_CLOCK_1        = 0b0,
+    HAILO_MIPI_PIXELS_PER_CLOCK_2        = 0b01,
+    HAILO_MIPI_PIXELS_PER_CLOCK_4        = 0b10,
+
+    /** Max enum value to maintain ABI Integrity */
+    HAILO_MIPI_PIXELS_PER_CLOCK_MAX_ENUM = HAILO_MAX_ENUM
+} hailo_mipi_pixels_per_clock_t;
+
+/** Indicates Range of MIPI clock selection for a MIPI stream **/
+typedef enum {
+    HAILO_MIPI_CLOCK_SELECTION_80_TO_100_MBPS    = 0b00000,
+    HAILO_MIPI_CLOCK_SELECTION_100_TO_120_MBPS   = 0b00001,
+    HAILO_MIPI_CLOCK_SELECTION_120_TO_160_MBPS   = 0b00010,
+    HAILO_MIPI_CLOCK_SELECTION_160_TO_200_MBPS   = 0b00011,
+    HAILO_MIPI_CLOCK_SELECTION_200_TO_240_MBPS   = 0b00100,
+    HAILO_MIPI_CLOCK_SELECTION_240_TO_280_MBPS   = 0b00101,
+    HAILO_MIPI_CLOCK_SELECTION_280_TO_320_MBPS   = 0b00110,
+    HAILO_MIPI_CLOCK_SELECTION_320_TO_360_MBPS   = 0b00111,
+    HAILO_MIPI_CLOCK_SELECTION_360_TO_400_MBPS   = 0b01000,
+    HAILO_MIPI_CLOCK_SELECTION_400_TO_480_MBPS   = 0b01001,
+    HAILO_MIPI_CLOCK_SELECTION_480_TO_560_MBPS   = 0b01010,
+    HAILO_MIPI_CLOCK_SELECTION_560_TO_640_MBPS   = 0b01011,
+    HAILO_MIPI_CLOCK_SELECTION_640_TO_720_MBPS   = 0b01100,
+    HAILO_MIPI_CLOCK_SELECTION_720_TO_800_MBPS   = 0b01101,
+    HAILO_MIPI_CLOCK_SELECTION_800_TO_880_MBPS   = 0b01110,
+    HAILO_MIPI_CLOCK_SELECTION_880_TO_1040_MBPS  = 0b01111,
+    HAILO_MIPI_CLOCK_SELECTION_1040_TO_1200_MBPS = 0b10000,
+    HAILO_MIPI_CLOCK_SELECTION_1200_TO_1350_MBPS = 0b10001,
+    HAILO_MIPI_CLOCK_SELECTION_1350_TO_1500_MBPS = 0b10010,
+    HAILO_MIPI_CLOCK_SELECTION_1500_TO_1750_MBPS = 0b10011,
+    HAILO_MIPI_CLOCK_SELECTION_1750_TO_2000_MBPS = 0b10100,
+    HAILO_MIPI_CLOCK_SELECTION_2000_TO_2250_MBPS = 0b10101,
+    HAILO_MIPI_CLOCK_SELECTION_2250_TO_2500_MBPS = 0b10110,
+
+    /** The clock selection is calculated from the data rate. **/
+    HAILO_MIPI_CLOCK_SELECTION_AUTOMATIC         = 0b111111,
+
+    /** Max enum value to maintain ABI Integrity */
+    HAILO_MIPI_CLOCK_SELECTION_MAX_ENUM          = HAILO_MAX_ENUM
+} hailo_mipi_clock_selection_t;
+
+/** Indicates MIPI Rx data type **/
+typedef enum {
+    HAILO_MIPI_RX_TYPE_RGB_444  = 0x20,
+    HAILO_MIPI_RX_TYPE_RGB_555  = 0x21,
+    HAILO_MIPI_RX_TYPE_RGB_565  = 0x22,
+    HAILO_MIPI_RX_TYPE_RGB_666  = 0x23,
+    HAILO_MIPI_RX_TYPE_RGB_888  = 0x24,
+    HAILO_MIPI_RX_TYPE_RAW_6    = 0x28,
+    HAILO_MIPI_RX_TYPE_RAW_7    = 0x29,
+    HAILO_MIPI_RX_TYPE_RAW_8    = 0x2a,
+    HAILO_MIPI_RX_TYPE_RAW_10   = 0x2b,
+    HAILO_MIPI_RX_TYPE_RAW_12   = 0x2c,
+    HAILO_MIPI_RX_TYPE_RAW_14   = 0x2d,
+
+    /** Max enum value to maintain ABI Integrity */
+    HAILO_MIPI_RX_TYPE_MAX_ENUM = HAILO_MAX_ENUM
+} hailo_mipi_data_type_rx_t;
+
+/** Indicates ISP input bayer pixel order **/
+typedef enum {
+    HAILO_MIPI_ISP_IMG_IN_ORDER_B_FIRST  = 0,
+    HAILO_MIPI_ISP_IMG_IN_ORDER_GB_FIRST = 1,
+    HAILO_MIPI_ISP_IMG_IN_ORDER_GR_FIRST = 2,
+    HAILO_MIPI_ISP_IMG_IN_ORDER_R_FIRST  = 3,
+
+    /** Max enum value to maintain ABI Integrity */
+    HAILO_MIPI_ISP_IMG_IN_ORDER_MAX_ENUM = HAILO_MAX_ENUM
+} hailo_mipi_isp_image_in_order_t;
+
+/** Indicates ISP output data type **/
+typedef enum {
+    HAILO_MIPI_IMG_OUT_DATA_TYPE_RGB_888  = 0x24,
+    HAILO_MIPI_IMG_OUT_DATA_TYPE_YUV_422  = 0x1E,
+
+    /** Max enum value to maintain ABI Integrity */
+    HAILO_MIPI_IMG_OUT_DATA_TYPE_MAX_ENUM = HAILO_MAX_ENUM
+} hailo_mipi_isp_image_out_data_type_t;
+
+typedef enum {
+    HAILO_MIPI_ISP_LIGHT_FREQUENCY_60HZ = 0,
+    HAILO_MIPI_ISP_LIGHT_FREQUENCY_50HZ = 1,
+
+    /** Max enum value to maintain ABI Integrity */
+    ISP_LIGHT_FREQUENCY_MAX_ENUM = HAILO_MAX_ENUM
+} hailo_mipi_isp_light_frequency_t;
+
+/** MIPI params */
+typedef struct {
+    /** The width in pixels of the image that enter to the mipi CSI. The sensor output.
+     *  When isp_enable and isp_crop_enable is false, is also the stream input. **/
+    uint16_t img_width_pixels; // sensor_out == mipi_in == ISP_in
+
+    /** The height in pixels of the image that enter to the mipi CSI. The sensor output. 
+     *  When isp_enable and isp_crop_enable is false, is also the stream input. **/ 
+    uint16_t img_height_pixels; // sensor_out == mipi_in == ISP_in
+
+    /** Number of pixels transmitted at each clock.**/
+    hailo_mipi_pixels_per_clock_t pixels_per_clock;
+
+    /** Number of lanes to use. **/
+    uint8_t number_of_lanes;
+
+    /** Selection of clock range that would be used.
+     *  Setting ::HAILO_MIPI_CLOCK_SELECTION_AUTOMATIC means that the
+     *  clock selection is calculated from the data rate. **/
+    hailo_mipi_clock_selection_t clock_selection;
+
+    /** The virtual channel index of the MIPI dphy. **/
+    uint8_t virtual_channel_index;
+
+    /** Rate of the passed data (MHz). **/
+    uint32_t data_rate;
+} hailo_mipi_common_params_t;
+
+/** ISP params */
+typedef struct {
+    /** The ISP Rx bayer pixel order. Only relevant when the ISP is enabled. **/
+    hailo_mipi_isp_image_in_order_t isp_img_in_order;
+
+    /** The data type that the mipi will take out. Only relevant when the ISP is enabled. **/
+    hailo_mipi_isp_image_out_data_type_t isp_img_out_data_type;
+
+    /** Enable the crop feature in the ISP. Only relevant when the ISP is enabled. **/
+    bool isp_crop_enable;
+
+    /** The width in pixels of the output window that the ISP take out. The stream input.
+     *  Useful when isp_crop_enable is True. Only relevant when the ISP is enabled. **/
+    uint16_t isp_crop_output_width_pixels;
+
+    /** The height in pixels of the output window that the ISP take out. The stream input.
+     *  Useful when isp_crop_enable is True. Only relevant when the ISP is enabled. **/
+    uint16_t isp_crop_output_height_pixels;
+
+    /** The width start point of the output window that the ISP take out. 
+     *  Useful when isp_crop_enable is True. Only relevant when the ISP is enabled. **/
+    uint16_t isp_crop_output_width_start_offset_pixels;
+
+    /** The height start point of the output window that the ISP take out. 
+     *  Useful when isp_crop_enable is True. Only relevant when the ISP is enabled. **/
+    uint16_t isp_crop_output_height_start_offset_pixels;
+
+    /** Enable Test pattern from the ISP. Only relevant when the ISP is enabled. **/
+    bool isp_test_pattern_enable;
+
+    /** Don't load the ISP configuration file from the FLASH. Only relevant when the ISP is enabled. **/
+    bool isp_configuration_bypass;
+
+    /** Enable the run-time Auto Exposure in the ISP. Only relevant when the ISP is enabled. **/
+    bool isp_run_time_ae_enable;
+
+    /** Enable the run-time Auto White Balance in the ISP. Only relevant when the ISP is enabled. **/
+    bool isp_run_time_awb_enable;
+
+    /** Enable the run-time Adaptive Function in the ISP. Only relevant when the ISP is enabled. **/
+    bool isp_run_time_adt_enable;
+
+    /** Enable the run-time Auto Focus in the ISP. Only relevant when the ISP is enabled. **/
+    bool isp_run_time_af_enable;
+
+    /** Interval in milliseconds between ISP run time calculations. Only relevant when the ISP is enabled. **/
+    uint16_t isp_run_time_calculations_interval_ms;
+
+    /** Selection of the light frequency. This parameter varies depending on the power grid of the country 
+     *  where the product is running. Only relevant when the ISP is enabled. **/
+    hailo_mipi_isp_light_frequency_t isp_light_frequency;
+} hailo_isp_params_t;
+
+/** MIPI input stream (host to device) parameters */
+typedef struct {
+    hailo_mipi_common_params_t mipi_common_params;
+
+    /** Selection of which MIPI Rx device to use. **/
+    uint8_t mipi_rx_id;
+
+    /** The data type which will be passed over the MIPI. **/
+    hailo_mipi_data_type_rx_t data_type;
+
+    /** Enable the ISP block in the MIPI dataflow. The ISP is not supported yet. **/
+    bool isp_enable;
+
+    hailo_isp_params_t isp_params;
+} hailo_mipi_input_stream_params_t;
+
+/** Core input stream (host to device) parameters */
+typedef struct {
+    EMPTY_STRUCT_PLACEHOLDER
+} hailo_core_input_stream_params_t;
+
+/** Core output stream (device to host) parameters */
+typedef struct {
+    EMPTY_STRUCT_PLACEHOLDER
+} hailo_core_output_stream_params_t;
+
+typedef enum {
+    HAILO_STREAM_INTERFACE_PCIE = 0,
+    HAILO_STREAM_INTERFACE_ETH,
+    HAILO_STREAM_INTERFACE_MIPI,
+    HAILO_STREAM_INTERFACE_CORE,
+
+    /** Max enum value to maintain ABI Integrity */
+   HAILO_STREAM_INTERFACE_MAX_ENUM = HAILO_MAX_ENUM
+} hailo_stream_interface_t;
+
+/** Hailo stream parameters */
+typedef struct {
+    union {
+        #ifndef _MSC_VER
+        // Windows combaseapi.h uses `inteface` as a keyword
+        hailo_stream_interface_t interface DEPRECATED("interface is deprecated. One should use stream_interface instead.");
+        #endif
+        hailo_stream_interface_t stream_interface;    
+    };
+    
+    hailo_stream_direction_t direction;
+    union {
+        hailo_pcie_input_stream_params_t pcie_input_params;
+        hailo_core_input_stream_params_t core_input_params;
+        hailo_eth_input_stream_params_t eth_input_params;
+        hailo_mipi_input_stream_params_t mipi_input_params;
+        hailo_pcie_output_stream_params_t pcie_output_params;
+        hailo_core_output_stream_params_t core_output_params;
+        hailo_eth_output_stream_params_t eth_output_params;
+    };
+} hailo_stream_parameters_t;
+
+/** Hailo stream parameters per stream_name */
+typedef struct {
+    char name[HAILO_MAX_STREAM_NAME_SIZE];
+    hailo_stream_parameters_t stream_params;
+} hailo_stream_parameters_by_name_t;
+
+/** Virtual stream statistics flags */
+typedef enum {
+    HAILO_VSTREAM_STATS_NONE            = 0,        /*!< No stats */
+    HAILO_VSTREAM_STATS_MEASURE_FPS     = 1 << 0,   /*!< Measure vstream FPS */
+    HAILO_VSTREAM_STATS_MEASURE_LATENCY = 1 << 1,   /*!< Measure vstream latency */
+
+    /** Max enum value to maintain ABI Integrity */
+    HAILO_VSTREAM_STATS_MAX_ENUM        = HAILO_MAX_ENUM
+} hailo_vstream_stats_flags_t;
+
+/** Pipeline element statistics flags */
+typedef enum {
+    HAILO_PIPELINE_ELEM_STATS_NONE                  = 0,        /*!< No stats */
+    HAILO_PIPELINE_ELEM_STATS_MEASURE_FPS           = 1 << 0,   /*!< Measure element FPS */
+    HAILO_PIPELINE_ELEM_STATS_MEASURE_LATENCY       = 1 << 1,   /*!< Measure element latency */
+    HAILO_PIPELINE_ELEM_STATS_MEASURE_QUEUE_SIZE    = 1 << 2,   /*!< Measure element queue size */
+
+    /** Max enum value to maintain ABI Integrity */
+    HAILO_PIPELINE_ELEM_STATS_MAX_ENUM              = HAILO_MAX_ENUM
+} hailo_pipeline_elem_stats_flags_t;
+
+/** Virtual stream params */
+typedef struct {
+    hailo_format_t user_buffer_format;
+    uint32_t timeout_ms;
+    uint32_t queue_size;
+    hailo_vstream_stats_flags_t vstream_stats_flags;
+    hailo_pipeline_elem_stats_flags_t pipeline_elements_stats_flags;
+} hailo_vstream_params_t;
+
+/** Input virtual stream parameters */
+typedef struct {
+    char name[HAILO_MAX_STREAM_NAME_SIZE];
+    hailo_vstream_params_t params;
+} hailo_input_vstream_params_by_name_t;
+
+/** Output virtual stream parameters */
+typedef struct {
+    char name[HAILO_MAX_STREAM_NAME_SIZE];
+    hailo_vstream_params_t params;
+} hailo_output_vstream_params_by_name_t;
+
+/** Output virtual stream name by group */
+typedef struct {
+    char name[HAILO_MAX_STREAM_NAME_SIZE];
+    uint8_t pipeline_group_index;
+} hailo_output_vstream_name_by_group_t;
+
+/** Image shape */
+typedef struct {
+    uint32_t height;
+    uint32_t width;
+    uint32_t features;
+} hailo_3d_image_shape_t;
+
+typedef struct {
+    uint32_t class_group_index;
+    char original_name[HAILO_MAX_STREAM_NAME_SIZE];
+} hailo_nms_defuse_info_t;
+
+/** NMS Internal HW Info */
+typedef struct {
+    /** Amount of NMS classes */
+    uint32_t number_of_classes;
+    /** Maximum amount of bboxes per nms class */
+    uint32_t max_bboxes_per_class;
+    /** Internal usage */
+    uint32_t bbox_size;
+    /** Internal usage */
+    uint32_t chunks_per_frame;
+    bool is_defused;
+    hailo_nms_defuse_info_t defuse_info;
+} hailo_nms_info_t;
+
+/** NMS Fuse Input */
+typedef struct {
+    void *buffer;
+    size_t size;
+    hailo_nms_info_t nms_info;
+} hailo_nms_fuse_input_t;
+
+/** Shape of nms result */
+typedef struct {
+    /** Amount of NMS classes */
+    uint32_t number_of_classes;
+    /** Maximum amount of bboxes per nms class */
+    uint32_t max_bboxes_per_class;
+} hailo_nms_shape_t;
+
+#pragma pack(push, 1)
+typedef struct {
+    uint16_t y_min;
+    uint16_t x_min;
+    uint16_t y_max;
+    uint16_t x_max;
+    uint16_t score;
+} hailo_bbox_t;
+
+typedef struct {
+    float32_t y_min;
+    float32_t x_min;
+    float32_t y_max;
+    float32_t x_max;
+    float32_t score;
+} hailo_bbox_float32_t;
+#pragma pack(pop)
+
+/**
+ * Input or output stream information. In case of multiple inputs or outputs, each one has
+ * its own stream.
+ */
+typedef struct {
+    /* Union to contain shapes and nms parameters - they cannot exist at the same time */
+    union
+    { 
+        struct
+        {
+            hailo_3d_image_shape_t shape;
+            hailo_3d_image_shape_t hw_shape;
+        };
+
+        hailo_nms_info_t nms_info;
+    };
+
+    uint32_t hw_data_bytes;
+    uint32_t hw_frame_size;
+    hailo_format_t format;
+    hailo_stream_direction_t direction;
+    uint8_t index;
+    char name[HAILO_MAX_STREAM_NAME_SIZE];
+    hailo_quant_info_t quant_info;
+    bool is_mux;
+} hailo_stream_info_t;
+
+/**
+ * Input or output vstream information.
+ */
+typedef struct {
+    char name[HAILO_MAX_STREAM_NAME_SIZE];
+    char network_name[HAILO_MAX_NETWORK_NAME_SIZE];
+    hailo_stream_direction_t direction;
+    /* Buffer format sent/received from the vstream. The user normally override this format 
+       by passing its own user_buffer_format inside ::hailo_vstream_params_t structure.*/
+    hailo_format_t format;
+
+    union
+    {
+        /* Frame shape */
+        hailo_3d_image_shape_t shape;
+        /* NMS shape, only valid if format.order is ::HAILO_FORMAT_ORDER_NMS */
+        hailo_nms_shape_t nms_shape;
+    };
+
+    hailo_quant_info_t quant_info;
+} hailo_vstream_info_t;
+
+/** Power modes */
+typedef enum {
+    HAILO_POWER_MODE_PERFORMANCE       = 0,
+    HAILO_POWER_MODE_ULTRA_PERFORMANCE = 1,
+
+    /** Max enum value to maintain ABI Integrity */
+    HAILO_POWER_MODE_MAX_ENUM          = HAILO_MAX_ENUM
+} hailo_power_mode_t;
+
+/** Latency measurement flags */
+typedef enum {
+    HAILO_LATENCY_NONE              = 0,
+    HAILO_LATENCY_MEASURE           = 1 << 0,
+    HAILO_LATENCY_CLEAR_AFTER_GET   = 1 << 1,
+
+    /** Max enum value to maintain ABI Integrity */
+    HAILO_LATENCY_MAX_ENUM          = HAILO_MAX_ENUM
+} hailo_latency_measurement_flags_t;
+
+typedef struct {
+    /** This parameter is only used in multi-context network_groups.
+     * User is advised to modify this (single network parameter) or @a hailo_configure_network_group_params_t batch size parameter. Not both.
+     * In case user wishes to work with the same batch size for all networks inside a network group, user is advised to set batch_size in @a hailo_configure_network_group_params_t.
+     * In case user wished to work with batch size per network, user is advised to use this parameter. Default network batch size is @a HAILO_DEFAULT_BATCH_SIZE */
+    uint16_t batch_size;
+} hailo_network_parameters_t;
+
+typedef struct {
+    char name[HAILO_MAX_NETWORK_NAME_SIZE];
+    hailo_network_parameters_t network_params;
+} hailo_network_parameters_by_name_t;
+
+/** Hailo configure parameters per network_group */
+typedef struct {
+    char name[HAILO_MAX_NETWORK_GROUP_NAME_SIZE];
+    /** This parameter is only used in multi-context network_groups. In case of name missmatch, default value @a HAILO_DEFAULT_BATCH_SIZE is used */
+    uint16_t batch_size;
+    hailo_power_mode_t power_mode;
+    hailo_latency_measurement_flags_t latency;
+    size_t stream_params_by_name_count;
+    hailo_stream_parameters_by_name_t stream_params_by_name[HAILO_MAX_STREAMS_COUNT];
+    size_t network_params_by_name_count;
+    hailo_network_parameters_by_name_t network_params_by_name[HAILO_MAX_NETWORKS_IN_NETWORK_GROUP];
+} hailo_configure_network_group_params_t;
+
+/** Hailo configure parameters */
+typedef struct {
+    size_t network_group_params_count;
+    hailo_configure_network_group_params_t network_group_params[HAILO_MAX_NETWORK_GROUPS];
+} hailo_configure_params_t;
+
+/** Hailo network_group parameters */
+typedef struct {
+    EMPTY_STRUCT_PLACEHOLDER
+} hailo_activate_network_group_params_t;
+
+/** Hailo network group info */
+typedef struct {
+    char name[HAILO_MAX_NETWORK_GROUP_NAME_SIZE];
+    bool is_multi_context;
+} hailo_network_group_info_t;
+
+/** Hailo layer name */
+typedef struct {
+    char name[HAILO_MAX_STREAM_NAME_SIZE];
+} hailo_layer_name_t;
+
+typedef struct {
+    char name[HAILO_MAX_NETWORK_NAME_SIZE];
+} hailo_network_info_t;
+
+/** Hailo device ID string - BDF for PCIe devices, MAC address for Ethernet devices, "Core" for core devices. **/
+typedef struct {
+    char id[HAILO_MAX_DEVICE_ID_LENGTH];
+} hailo_device_id_t;
+
+/** Notification IDs, for each notification, one of the ::hailo_notification_message_parameters_t union will be set. */
+typedef enum {
+    /** Matches hailo_notification_message_parameters_t::rx_error_notification. */
+    HAILO_NOTIFICATION_ID_ETHERNET_RX_ERROR = 0,
+    /** Matches hailo_notification_message_parameters_t::health_monitor_temperature_alarm_notification */
+    HAILO_NOTIFICATION_ID_HEALTH_MONITOR_TEMPERATURE_ALARM,
+    /** Matches hailo_notification_message_parameters_t::health_monitor_dataflow_shutdown_notification */
+    HAILO_NOTIFICATION_ID_HEALTH_MONITOR_DATAFLOW_SHUTDOWN,
+    /** Matches hailo_notification_message_parameters_t::health_monitor_overcurrent_alert_notification */
+    HAILO_NOTIFICATION_ID_HEALTH_MONITOR_OVERCURRENT_ALARM,
+    /** Matches hailo_notification_message_parameters_t::health_monitor_lcu_ecc_error_notification */
+    HAILO_NOTIFICATION_ID_LCU_ECC_CORRECTABLE_ERROR,
+    /** Matches hailo_notification_message_parameters_t::health_monitor_lcu_ecc_error_notification */
+    HAILO_NOTIFICATION_ID_LCU_ECC_UNCORRECTABLE_ERROR,
+    /** Matches hailo_notification_message_parameters_t::health_monitor_cpu_ecc_notification */
+    HAILO_NOTIFICATION_ID_CPU_ECC_ERROR,
+    /** Matches hailo_notification_message_parameters_t::health_monitor_cpu_ecc_notification */
+    HAILO_NOTIFICATION_ID_CPU_ECC_FATAL,
+    /** Matches hailo_notification_message_parameters_t::debug_notification */
+    HAILO_NOTIFICATION_ID_DEBUG,
+    /** Matches hailo_notification_message_parameters_t::context_switch_breakpoint_reached_notification */
+    HAILO_NOTIFICATION_ID_CONTEXT_SWITCH_BREAKPOINT_REACHED,
+    /** Matches hailo_notification_message_parameters_t::health_monitor_clock_changed_notification */
+    HAILO_NOTIFICATION_ID_HEALTH_MONITOR_CLOCK_CHANGED_EVENT,
+
+    /** Must be last! */
+    HAILO_NOTIFICATION_ID_COUNT,
+
+    /** Max enum value to maintain ABI Integrity */
+    HAILO_NOTIFICATION_ID_MAX_ENUM = HAILO_MAX_ENUM
+} hailo_notification_id_t;
+
+/** Rx error notification message */
+typedef struct {
+    uint32_t error;
+    uint32_t queue_number;
+    uint32_t rx_errors_count;
+} hailo_rx_error_notification_message_t;
+
+/** Debug notification message */
+typedef struct {
+    uint32_t connection_status;
+    uint32_t connection_type;
+    uint32_t pcie_is_active;
+    uint32_t host_port;
+    uint32_t host_ip_addr;
+} hailo_debug_notification_message_t;
+
+/** Health monitor - Dataflow shutdown notification message */
+typedef struct {
+    /** Bit mask of closed input streams indices */
+    uint32_t closed_input_streams;
+    /** Bit mask of closed output streams indices */
+    uint32_t closed_output_streams;
+    float32_t ts0_temperature;
+    float32_t ts1_temperature;
+} hailo_health_monitor_dataflow_shutdown_notification_message_t;
+
+typedef enum {
+    HAILO_TEMPERATURE_PROTECTION_TEMPERATURE_ZONE__GREEN = 0,
+    HAILO_TEMPERATURE_PROTECTION_TEMPERATURE_ZONE__ORANGE = 1,
+    HAILO_TEMPERATURE_PROTECTION_TEMPERATURE_ZONE__RED = 2
+} hailo_temperature_protection_temperature_zone_t;
+
+/** Health monitor - Temperature alarm notification message */
+typedef struct {
+    hailo_temperature_protection_temperature_zone_t temperature_zone;
+    uint32_t alarm_ts_id;
+    float32_t ts0_temperature;
+    float32_t ts1_temperature;
+} hailo_health_monitor_temperature_alarm_notification_message_t;
+
+typedef enum {
+    HAILO_OVERCURRENT_PROTECTION_OVERCURRENT_ZONE__NONE = 0,
+    HAILO_OVERCURRENT_PROTECTION_OVERCURRENT_ZONE__ORANGE = 1,
+    HAILO_OVERCURRENT_PROTECTION_OVERCURRENT_ZONE__RED = 2
+} hailo_overcurrent_protection_overcurrent_zone_t;
+
+/** Health monitor - Overcurrent alert notification message */
+typedef struct {
+    hailo_overcurrent_protection_overcurrent_zone_t overcurrent_zone;
+    float32_t exceeded_alert_threshold;
+    float32_t sampled_current_during_alert;
+} hailo_health_monitor_overcurrent_alert_notification_message_t;
+
+/** Health monitor - LCU ECC error notification message */
+typedef struct {
+    /* bitmap - bit per cluster */
+    uint16_t cluster_error;
+} hailo_health_monitor_lcu_ecc_error_notification_message_t;
+
+/** Health monitor - CPU ECC error notification message */
+typedef struct {
+    uint32_t memory_bitmap;
+} hailo_health_monitor_cpu_ecc_notification_message_t;
+
+/** Context switch - breakpoint reached notification message */
+typedef struct {
+    uint8_t network_group_index;
+    uint16_t batch_index;
+    uint8_t context_index;
+    uint16_t action_index;
+} hailo_context_switch_breakpoint_reached_message_t;
+
+/** Health monitor - System's clock has been changed notification message */
+typedef struct {
+    uint32_t previous_clock;
+    uint32_t current_clock;
+} hailo_health_monitor_clock_changed_notification_message_t;
+
+/** Union of all notification messages parameters. See ::hailo_notification_t */
+typedef union {
+    /** Ethernet rx error */
+    hailo_rx_error_notification_message_t rx_error_notification;
+    /** Internal usage */
+    hailo_debug_notification_message_t debug_notification;
+    /** Dataflow shutdown due to health monitor event */
+    hailo_health_monitor_dataflow_shutdown_notification_message_t health_monitor_dataflow_shutdown_notification;
+    /** Chip temperature alarm */
+    hailo_health_monitor_temperature_alarm_notification_message_t health_monitor_temperature_alarm_notification;
+    /** Chip overcurrent alert */
+    hailo_health_monitor_overcurrent_alert_notification_message_t health_monitor_overcurrent_alert_notification;
+    /** Core ecc error notification */
+    hailo_health_monitor_lcu_ecc_error_notification_message_t health_monitor_lcu_ecc_error_notification;
+    /** Chip ecc error notification */
+    hailo_health_monitor_cpu_ecc_notification_message_t health_monitor_cpu_ecc_notification;
+    /** Internal usage */
+    hailo_context_switch_breakpoint_reached_message_t context_switch_breakpoint_reached_notification;
+    /** Neural network core clock changed due to health monitor event */
+    hailo_health_monitor_clock_changed_notification_message_t health_monitor_clock_changed_notification;
+} hailo_notification_message_parameters_t;
+
+/** Notification data that will be passed to the callback passed in ::hailo_notification_callback */
+typedef struct {
+    hailo_notification_id_t id;
+    uint32_t sequence;
+    hailo_notification_message_parameters_t body;
+} hailo_notification_t;
+
+/**
+ * A notification callback. See ::hailo_set_notification_callback
+ *
+ * @param[in] device                The ::hailo_device that got the notification.
+ * @param[in] notification          The notification data.
+ * @param[in] opaque                User specific data.
+ * @warning Throwing exceptions in the callback is not supported!
+ */
+typedef void (*hailo_notification_callback)(hailo_device, const hailo_notification_t*, void*);
+
+/** Hailo device reset modes */
+typedef enum {
+    HAILO_RESET_DEVICE_MODE_CHIP        = 0,
+    HAILO_RESET_DEVICE_MODE_NN_CORE     = 1,
+    HAILO_RESET_DEVICE_MODE_SOFT        = 2,
+    HAILO_RESET_DEVICE_MODE_FORCED_SOFT = 3,
+
+    HAILO_RESET_DEVICE_MODE_MAX_ENUM    = HAILO_MAX_ENUM
+} hailo_reset_device_mode_t;
+
+typedef enum {
+    HAILO_WATCHDOG_MODE_HW_SW           = 0,
+    HAILO_WATCHDOG_MODE_HW_ONLY         = 1,
+
+    HAILO_WATCHDOG_MODE_MAX_ENUM        = HAILO_MAX_ENUM
+} hailo_watchdog_mode_t;
+
+typedef struct {
+    float32_t ts0_temperature;
+    float32_t ts1_temperature;
+    uint16_t sample_count;
+} hailo_chip_temperature_info_t;
+
+typedef struct {
+    float32_t temperature_threshold;
+    float32_t hysteresis_temperature_threshold;
+    uint32_t throttling_nn_clock_freq;
+} hailo_throttling_level_t;
+
+typedef struct {
+    bool overcurrent_protection_active;
+    uint8_t current_overcurrent_zone;
+    float32_t red_overcurrent_threshold;
+    float32_t orange_overcurrent_threshold;
+    bool temperature_throttling_active;
+    uint8_t current_temperature_zone;
+    int8_t current_temperature_throttling_level;
+    hailo_throttling_level_t temperature_throttling_levels[HAILO_MAX_TEMPERATURE_THROTTLING_LEVELS_NUMBER];
+    int32_t orange_temperature_threshold;
+    int32_t orange_hysteresis_temperature_threshold;
+    int32_t red_temperature_threshold;
+    int32_t red_hysteresis_temperature_threshold;
+} hailo_health_info_t;
+
+typedef struct {
+    void* buffer;
+    size_t size;
+} hailo_stream_raw_buffer_t;
+
+typedef struct {
+    char name[HAILO_MAX_STREAM_NAME_SIZE];
+    hailo_stream_raw_buffer_t raw_buffer;
+} hailo_stream_raw_buffer_by_name_t;
+
+typedef struct {
+    float64_t avg_hw_latency_ms;
+} hailo_latency_measurement_result_t;
+
+typedef struct {
+    char stream_name[HAILO_MAX_STREAM_NAME_SIZE];
+    uint32_t rate;
+} hailo_rate_limit_t;
+
+typedef enum {
+    HAILO_SENSOR_TYPES_GENERIC = 0,
+    HAILO_SENSOR_TYPES_ONSEMI_AR0220AT,
+    HAILO_SENSOR_TYPES_RASPICAM,
+    HAILO_SENSOR_TYPES_ONSEMI_AS0149AT,
+    HAILO_SENSOR_TYPES_HAILO8_ISP = 0x80000000,
+
+    /** Max enum value to maintain ABI Integrity */
+    HAILO_SENSOR_TYPES_MAX_ENUM = HAILO_MAX_ENUM
+} hailo_sensor_types_t;
+
+typedef enum {
+    HAILO_FW_LOGGER_INTERFACE_PCIE     = 1 << 0,
+    HAILO_FW_LOGGER_INTERFACE_UART     = 1 << 1,
+
+    /** Max enum value to maintain ABI Integrity */
+    HAILO_FW_LOGGER_INTERFACE_MAX_ENUM = HAILO_MAX_ENUM
+} hailo_fw_logger_interface_t;
+
+typedef enum {
+    HAILO_FW_LOGGER_LEVEL_TRACE = 0,
+    HAILO_FW_LOGGER_LEVEL_DEBUG = 1,
+    HAILO_FW_LOGGER_LEVEL_INFO  = 2,
+    HAILO_FW_LOGGER_LEVEL_WARN  = 3,
+    HAILO_FW_LOGGER_LEVEL_ERROR = 4,
+    HAILO_FW_LOGGER_LEVEL_FATAL = 5,
+
+    /** Max enum value to maintain ABI Integrity */
+    HAILO_FW_LOGGER_LEVEL_MAX_ENUM = HAILO_MAX_ENUM
+} hailo_fw_logger_level_t;
+
+#define HAILO_DEFAULT_TRANSFORM_PARAMS                                       \
+    {                                                                        \
+        .transform_mode = HAILO_STREAM_TRANSFORM_COPY,                       \
+        .user_buffer_format = {                                              \
+            .type =  HAILO_FORMAT_TYPE_AUTO,                                 \
+            .order = HAILO_FORMAT_ORDER_AUTO,                                \
+            .flags = HAILO_FORMAT_FLAGS_QUANTIZED,                           \
+        },                                                                   \
+    }                                                                        \
+
+#define HAILO_DEFAULT_SOCKADDR                                               \
+    {                                                                        \
+        .sin_family = AF_INET,                                               \
+        .sin_port = 0,                                                       \
+        .sin_addr = {                                                        \
+            .s_addr = INADDR_ANY                                             \
+        },                                                                   \
+        .sin_zero = {0}                                                      \
+    }
+
+#define HAILO_ETH_INPUT_STREAM_PARAMS_DEFAULT                                \
+    {                                                                        \
+        .host_address = HAILO_DEFAULT_SOCKADDR,                              \
+        .device_port = HAILO_DEFAULT_ETH_DEVICE_PORT,                        \
+        .is_sync_enabled = false,                                            \
+        .frames_per_sync = 0,                                                \
+        .max_payload_size = HAILO_DEFAULT_ETH_MAX_PAYLOAD_SIZE,              \
+        .rate_limit_bytes_per_sec = 0,                                       \
+        .buffers_threshold = HAILO_DEFAULT_BUFFERS_THRESHOLD                 \
+    }
+
+#define HAILO_ETH_OUTPUT_STREAM_PARAMS_DEFAULT                               \
+    {                                                                        \
+        .host_address = HAILO_DEFAULT_SOCKADDR,                              \
+        .device_port = HAILO_DEFAULT_ETH_DEVICE_PORT,                        \
+        .is_sync_enabled = false,                                            \
+        .max_payload_size = HAILO_DEFAULT_ETH_MAX_PAYLOAD_SIZE,              \
+        .buffers_threshold = HAILO_DEFAULT_BUFFERS_THRESHOLD                 \
+    }
+
+#define HAILO_PCIE_STREAM_PARAMS_DEFAULT                                     \
+    {                                                                        \
+    }
+
+#define HAILO_MIPI_INPUT_STREAM_PARAMS_DEFAULT                               \
+    {                                                                        \
+        .mipi_common_params = {                                              \
+            .img_width_pixels = 1920,                                        \
+            .img_height_pixels = 1080,                                       \
+            .pixels_per_clock = HAILO_MIPI_PIXELS_PER_CLOCK_4,               \
+            .number_of_lanes = 2,                                            \
+            .clock_selection = HAILO_MIPI_CLOCK_SELECTION_AUTOMATIC,         \
+            .virtual_channel_index = 0,                                      \
+            .data_rate = 260                                                 \
+        },                                                                   \
+        .mipi_rx_id = 0,                                                     \
+        .data_type = HAILO_MIPI_RX_TYPE_RAW_8,                               \
+        .isp_enable = false,                                                 \
+        .isp_params = {                                                      \
+            .isp_img_in_order = HAILO_MIPI_ISP_IMG_IN_ORDER_GR_FIRST,        \
+            .isp_img_out_data_type = HAILO_MIPI_IMG_OUT_DATA_TYPE_RGB_888,   \
+            .isp_crop_enable = false,                                        \
+            .isp_crop_output_width_pixels = 1920,                            \
+            .isp_crop_output_height_pixels = 1080,                           \
+            .isp_crop_output_width_start_offset_pixels = 0,                  \
+            .isp_crop_output_height_start_offset_pixels = 0,                 \
+            .isp_test_pattern_enable = true,                                 \
+            .isp_configuration_bypass = false,                               \
+            .isp_run_time_ae_enable = true,                                  \
+            .isp_run_time_awb_enable = true,                                 \
+            .isp_run_time_adt_enable = true,                                 \
+            .isp_run_time_af_enable = false,                                 \
+            .isp_run_time_calculations_interval_ms = 0,                      \
+            .isp_light_frequency = HAILO_MIPI_ISP_LIGHT_FREQUENCY_50HZ       \
+        }                                                                    \
+    }
+
+#define HAILO_ACTIVATE_NETWORK_GROUP_PARAMS_DEFAULT                          \
+    {                                                                        \
+    }
+
+/**
+ * Retrieves hailort library version.
+ * 
+ * @param[out] version         Will be filled with hailort library version.
+ * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns a ::hailo_status error.
+ */
+HAILORTAPI hailo_status hailo_get_library_version(hailo_version_t *version);
+
+/**
+ * Returns a string format of @ status.
+ * 
+ * @param[in] status         A ::hailo_status to be converted to string format.
+ * @return Upon success, returns @ status as a string format. Otherwise, returns @a nullptr.
+ */
+HAILORTAPI const char* hailo_get_status_message(hailo_status status);
+
+/** @} */ // end of group_defines
+
+/** @defgroup group_device_functions Device functions
+ *  @{
+ */
+
+/**
+ * Returns information on all available pcie devices in the system.
+ *
+ * @param[out] pcie_device_infos         A pointer to a buffer of ::hailo_pcie_device_info_t that receives the
+ *                                       information.
+ * @param[in]  pcie_device_infos_length  The number of ::hailo_pcie_device_info_t elements in the buffer pointed to by
+ *                                       @a pcie_device_infos.
+ * @param[out] number_of_devices         This variable will be filled with the number of devices. If the buffer is
+ *                                       insufficient to hold the information a ::HAILO_INSUFFICIENT_BUFFER error is
+ *                                       returned.
+ * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns a ::hailo_status error.
+ */
+HAILORTAPI hailo_status hailo_scan_pcie_devices(
+   hailo_pcie_device_info_t *pcie_device_infos, size_t pcie_device_infos_length, size_t *number_of_devices);
+
+/**
+ * Parse PCIe device BDF string into hailo device info structure.
+ * 
+ * @param[in] device_info_str   BDF device info, format [\<domain\>].\<bus\>.\<device\>.\<func\>, same format as in lspci.
+ * @param[out] device_info      A pointer to a ::hailo_pcie_device_info_t that receives the parsed device info.
+ * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns an ::hailo_status error.
+ * @note Call ::hailo_scan_pcie_devices to get all available hailo pcie devices.
+ */
+HAILORTAPI hailo_status hailo_parse_pcie_device_info(const char *device_info_str,
+    hailo_pcie_device_info_t *device_info);
+
+/**
+ * Creates a PCIe device.
+ * 
+ * @param[in] device_info    Information about the device to open. If NULL is given and there is only
+ *                           one available PCIe device, use this device.
+ * @param[out] device        A pointer to a ::hailo_device that receives the allocated PCIe device.
+ * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns an ::hailo_status error.
+ * @note To release a device, call the ::hailo_release_device function with the returned ::hailo_device.
+ */
+HAILORTAPI hailo_status hailo_create_pcie_device(hailo_pcie_device_info_t *device_info, hailo_device *device);
+
+/**
+ * Returns information on all available ethernet devices in the system.
+ * 
+ * @param[in]  interface_name            The name of the network interface to scan.
+ * @param[out] eth_device_infos          A pointer to a buffer of ::hailo_eth_device_info_t that receives the
+ *                                       information.
+ * @param[in]  eth_device_infos_length   The number of ::hailo_eth_device_info_t elements in the buffer pointed to by
+ *                                       @a eth_device_infos.
+ * @param[out] number_of_devices         This variable will be filled with the number of devices. If the buffer is
+ *                                       insufficient to hold the information a ::HAILO_INSUFFICIENT_BUFFER error is
+ *                                       returned.
+ * @param[in]  timeout_ms                The time in milliseconds to scan devices.
+ * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns a ::hailo_status error.
+ */
+HAILORTAPI hailo_status hailo_scan_ethernet_devices(const char *interface_name, hailo_eth_device_info_t *eth_device_infos,
+    size_t eth_device_infos_length, size_t *number_of_devices, uint32_t timeout_ms);
+
+/**
+ * Creates an ethernet device.
+ * 
+ * @param[in]  device_info   Information about the device to open.
+ * @param[out] device        A pointer to a ::hailo_device that receives the allocated ethernet device corresponding to
+ *                           the given information.
+ * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns a ::hailo_status error.
+ * @note To release a device, call the ::hailo_release_device function with the returned ::hailo_device.
+ */
+HAILORTAPI hailo_status hailo_create_ethernet_device(hailo_eth_device_info_t *device_info, hailo_device *device);
+
+/**
+ * Release an open device.
+ * 
+ * @param[in] device   A ::hailo_device object to be released.
+ * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns a ::hailo_status error.
+ */
+HAILORTAPI hailo_status hailo_release_device(hailo_device device);
+
+/**
+ * Sends identify control to a Hailo device.
+ *
+ * @param[in] device              A ::hailo_device to be identified.
+ * @param[out] device_identity    Information about the device.
+ * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns an ::hailo_status error.
+ */
+HAILORTAPI hailo_status hailo_identify(hailo_device device, hailo_device_identity_t *device_identity);
+
+/**
+ * Receive information about the core cpu.
+ *
+ * @param[in] device              A ::hailo_device object.
+ * @param[out] core_information   Information about the device.
+ * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns an ::hailo_status error.
+ */
+HAILORTAPI hailo_status hailo_core_identify(hailo_device device, hailo_core_information_t *core_information);
+
+/**
+ * Get extended device information from a Hailo device.
+ *
+ * @param[in] device                            A ::hailo_device to get extended device info from.
+ * @param[out] extended_device_information      Extended information about the device.
+ * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns an ::hailo_status error.
+ */
+HAILORTAPI hailo_status hailo_get_extended_device_information(hailo_device device, hailo_extended_device_information_t *extended_device_information);
+
+/**
+ * Configure fw logger level and interface of sending.
+ *
+ * @param[in] device          A ::hailo_device object.
+ * @param[in] level           The minimum logger level.
+ * @param[in] interface_mask  Output interfaces (mix of ::hailo_fw_logger_interface_t).
+ * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns an ::hailo_status error.
+ */
+HAILORTAPI hailo_status hailo_set_fw_logger(hailo_device device, hailo_fw_logger_level_t level,
+    uint32_t interface_mask);
+
+/**
+ * Change throttling state of temperature protection component.
+ *
+ * @param[in] device            A ::hailo_device object.
+ * @param[in] should_activate   Should be true to enable or false to disable.
+ * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns an ::hailo_status error.
+ */
+HAILORTAPI hailo_status hailo_set_throttling_state(hailo_device device, bool should_activate);
+
+/**
+ * Get current throttling state of temperature protection component.
+ *
+ * @param[in] device            A ::hailo_device object.
+ * @param[out] is_active        A pointer to the temperature protection throttling state: true if active, false otherwise.
+ * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns an ::hailo_status error.
+ */
+HAILORTAPI hailo_status hailo_get_throttling_state(hailo_device device, bool *is_active);
+
+/**
+ * Enable firmware watchdog.
+ *
+ * @param[in] device            A ::hailo_device object.
+ * @param[in] cpu_id            A @a hailo_cpu_id_t indicating which CPU WD to enable.
+ * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns an ::hailo_status error.
+ * @note Advanced API. Please use with care.
+ */
+HAILORTAPI hailo_status hailo_wd_enable(hailo_device device, hailo_cpu_id_t cpu_id);
+
+/**
+ * Disable firmware watchdog.
+ *
+ * @param[in] device            A ::hailo_device object.
+ * @param[in] cpu_id            A @a hailo_cpu_id_t indicating which CPU WD to disable.
+ * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns an ::hailo_status error.
+ * @note Advanced API. Please use with care.
+ */
+HAILORTAPI hailo_status hailo_wd_disable(hailo_device device, hailo_cpu_id_t cpu_id);
+
+/**
+ * Configure firmware watchdog.
+ *
+ * @param[in] device            A ::hailo_device object.
+ * @param[in] cpu_id            A @a hailo_cpu_id_t indicating which CPU WD to configure.
+ * @param[in] wd_cycles         Number of cycles until watchdog is triggered.
+ * @param[in] wd_mode           A @a hailo_watchdog_mode_t indicating which WD mode to configure.
+ * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns an ::hailo_status error.
+ * @note Advanced API. Please use with care.
+ */
+HAILORTAPI hailo_status hailo_wd_config(hailo_device device, hailo_cpu_id_t cpu_id, uint32_t wd_cycles, hailo_watchdog_mode_t wd_mode);
+
+/**
+ * Read the FW previous system state.
+ *
+ * @param[in] device                  A ::hailo_device object.
+ * @param[in] cpu_id                  A @a hailo_cpu_id_t indicating which CPU to state to read.
+ * @param[out] previous_system_state  A @a uint32_t to be filled with the previous system state.
+ *                                    0 indicating external reset, 1 indicating WD HW reset,
+ *                                    2 indicating WD SW reset, 3 indicating SW control reset.
+ * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns an ::hailo_status error.
+ * @note Advanced API. Please use with care.
+ */
+HAILORTAPI hailo_status hailo_get_previous_system_state(hailo_device device, hailo_cpu_id_t cpu_id, uint32_t *previous_system_state);
+
+/**
+ * Enable/Disable Pause frames.
+ *
+ * @param[in] device                  A ::hailo_device object.
+ * @param[in] rx_pause_frames_enable  Indicating weather to enable or disable pause frames.
+ * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns an ::hailo_status error.
+ */
+HAILORTAPI hailo_status hailo_set_pause_frames(hailo_device device, bool rx_pause_frames_enable);
+
+/**
+ * Get device id which is the identification string of the device. BDF for PCIe devices, 
+ * MAC address for Ethernet devices, "Core" for core devices.
+ *
+ * @param[in]  device           A ::hailo_device object.
+ * @param[out] id               The returned device id.
+ * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns a ::hailo_status error.
+ */
+HAILORTAPI hailo_status hailo_get_device_id(hailo_device device, hailo_device_id_t *id);
+
+/**
+ * Get temperature information on the device
+ *
+ * @param[in] device          A ::hailo_device object.
+ * @param[out] temp_info      A @a hailo_chip_temperature_info_t to be filled.
+ * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns a ::hailo_status error.
+ * @note Temperature in Celsius of the 2 internal temperature sensors (TS).
+ */
+HAILORTAPI hailo_status hailo_get_chip_temperature(hailo_device device, hailo_chip_temperature_info_t *temp_info);
+
+/**
+ * Reset device
+ * 
+ * @param[in] device    A ::hailo_device object.
+ * @param[in] mode      The mode of the reset.
+ * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns a ::hailo_status error.
+ */
+HAILORTAPI hailo_status hailo_reset_device(hailo_device device, hailo_reset_device_mode_t mode);
+
+/**
+ * Updates firmware to device flash.
+ * 
+ * @param[in]  device                 A ::hailo_output_stream object.
+ * @param[in]  firmware_buffer        A pointer to a buffer that contains the firmware to be updated on the @a device.
+ * @param[in]  firmware_buffer_size   The size in bytes of the buffer pointed by @a firmware_buffer.
+ * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns a ::hailo_status error.
+ * @note Check ::hailo_extended_device_information_t.boot_source returned from ::hailo_get_extended_device_information
+ *       to verify if the fw is booted from flash.
+ */
+HAILORTAPI hailo_status hailo_update_firmware(hailo_device device, void *firmware_buffer, uint32_t firmware_buffer_size);
+
+/**
+ * Updates second stage to device flash.
+ * 
+ * @param[in]  device                 A ::hailo_output_stream object.
+ * @param[in]  second_stage_buffer        A pointer to a buffer that contains the second_stage to be updated on the @a device.
+ * @param[in]  second_stage_buffer_size   The size in bytes of the buffer pointed by @a second_stage_buffer.
+ * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns a ::hailo_status error.
+ * @note Check ::hailo_extended_device_information_t.boot_source returned from ::hailo_get_extended_device_information
+ *       to verify if the fw is booted from flash.
+ */
+HAILORTAPI hailo_status hailo_update_second_stage(hailo_device device, void *second_stage_buffer, uint32_t second_stage_buffer_size);
+
+/**
+ * Sets a callback to be called when a notification with ID @a notification_id will be received
+ *
+ * @param[in] device                A ::hailo_device to register the callback to.
+ * @param[in] callback              The callback function to be called.
+ * @param[in] notification_id       The ID of the notification.
+ * @param[in] opaque                User specific data.
+ * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns a ::hailo_status error.
+ */
+HAILORTAPI hailo_status hailo_set_notification_callback(hailo_device device,
+    hailo_notification_callback callback,
+    hailo_notification_id_t notification_id, void *opaque);
+
+/**
+ * Removes a previously set callback with ID @a notification_id
+ *
+ * @param[in] device                A ::hailo_device to register the callback to.
+ * @param[in] notification_id       The ID of the notification to remove.
+ * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns a ::hailo_status error.
+ */
+HAILORTAPI hailo_status hailo_remove_notification_callback(hailo_device device,
+    hailo_notification_id_t notification_id);
+
+/**
+ * Reset the sensor that is related to the section index config.
+ *
+ * @param[in] device                A ::hailo_device object.
+ * @param[in] section_index         Flash section index to load config from. [0-6]
+ * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns a ::hailo_status error.
+ */
+HAILORTAPI hailo_status hailo_reset_sensor(hailo_device device, uint8_t section_index);
+
+/**
+ * Set the I2C bus to which the sensor of the specified type is connected.
+ *
+ * @param[in] device                A ::hailo_device object.
+ * @param[in] sensor_type           The sensor type.
+ * @param[in] bus_index             The I2C bus index of the sensor.
+ * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns a ::hailo_status error.
+ */
+HAILORTAPI hailo_status hailo_set_sensor_i2c_bus_index(hailo_device device, hailo_sensor_types_t sensor_type, uint8_t bus_index);
+
+/**
+ * Load the configuration with I2C in the section index.
+ *
+ * @param[in] device                A ::hailo_device object.
+ * @param[in] section_index         Flash section index to load config from. [0-6]
+ * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns a ::hailo_status error.
+ */
+HAILORTAPI hailo_status hailo_load_and_start_sensor(hailo_device device, uint8_t section_index);
+
+/**
+ *  Read data from an I2C slave over a hailo device.
+ *
+ * @param[in] device                A ::hailo_device object.
+ * @param[in] slave_config          The ::hailo_i2c_slave_config_t configuration of the slave.
+ * @param[in] register_address      The address of the register from which the data will be read.
+ * @param[in] data                  Pointer to a buffer that would store the read data.
+ * @param[in] length                The number of bytes to read into the buffer pointed by @a data.
+ * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns an ::hailo_status error.
+ */
+HAILORTAPI hailo_status hailo_i2c_read(hailo_device device, const hailo_i2c_slave_config_t *slave_config, uint32_t register_address, uint8_t *data, uint32_t length);
+
+/**
+ *  Write data to an I2C slave over a hailo device.
+ *
+ * @param[in] device                A ::hailo_device object.
+ * @param[in] slave_config          The ::hailo_i2c_slave_config_t configuration of the slave.
+ * @param[in] register_address      The address of the register to which the data will be written.
+ * @param[in] data                  A pointer to a buffer that contains the data to be written to the slave.
+ * @param[in] length                The size of @a data in bytes.
+ * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns an ::hailo_status error.
+ */
+HAILORTAPI hailo_status hailo_i2c_write(hailo_device device, const hailo_i2c_slave_config_t *slave_config, uint32_t register_address, const uint8_t *data, uint32_t length);
+
+/**
+ * Dump config of given section index into a csv file.
+ *
+ * @param[in] device                A ::hailo_device object.
+ * @param[in] section_index         Flash section index to load config from. [0-7]
+ * @param[in] config_file_path      File path to dump section configuration into.
+ * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns a ::hailo_status error.
+ */
+HAILORTAPI hailo_status hailo_dump_sensor_config(hailo_device device, uint8_t section_index, const char *config_file_path);
+
+/**
+ * Store sensor configuration to Hailo chip flash memory.
+ *
+ * @param[in] device                A ::hailo_device object.
+ * @param[in] section_index         Flash section index to write to. [0-6]
+ * @param[in] sensor_type           Sensor type.
+ * @param[in] reset_config_size     Size of reset configuration.
+ * @param[in] config_height         Configuration resolution height.
+ * @param[in] config_width          Configuration resolution width.
+ * @param[in] config_fps            Configuration FPS.
+ * @param[in] config_file_path      Sensor configuration file path.
+ * @param[in] config_name           Sensor configuration name.
+ * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns a ::hailo_status error.
+ */
+HAILORTAPI hailo_status hailo_store_sensor_config(hailo_device device, uint32_t section_index,
+    hailo_sensor_types_t sensor_type, uint32_t reset_config_size, uint16_t config_height, uint16_t config_width,
+    uint16_t config_fps, const char *config_file_path, const char *config_name);
+
+/**
+ * Store sensor ISP configuration to Hailo chip flash memory.
+ *
+ * @param[in] device                            A ::hailo_device object.
+ * @param[in] reset_config_size                 Size of reset configuration.
+ * @param[in] config_height                     Configuration resolution height.
+ * @param[in] config_width                      Configuration resolution width.
+ * @param[in] config_fps                        Configuration FPS.
+ * @param[in] isp_static_config_file_path       ISP static configuration file path.
+ * @param[in] isp_runtime_config_file_path      ISP runtime configuration file path.
+ * @param[in] config_name                       Sensor configuration name.
+ * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns a ::hailo_status error.
+ */
+HAILORTAPI hailo_status hailo_store_isp_config(hailo_device device, uint32_t reset_config_size, uint16_t config_height, uint16_t config_width,
+    uint16_t config_fps, const char *isp_static_config_file_path, const char *isp_runtime_config_file_path, const char *config_name);
+
+/**
+ *  Test chip memories using smart BIST mechanism.
+ * 
+ * @param[in]     device - A ::hailo_device object.
+ * @return Upon success, returns @a HAILO_SUCCESS. Otherwise, returns an @a hailo_status error.
+ */
+HAILORTAPI hailo_status hailo_test_chip_memories(hailo_device device);
+
+/** @} */ // end of group_device_functions
+
+/** @defgroup group_vdevice_functions VDevice functions
+ *  @{
+ */
+
+/**
+ * Init vdevice params with default values.
+ *
+ * @param[out] params                   A @a hailo_vdevice_params_t to be filled.
+ * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns a ::hailo_status error.
+ */
+HAILORTAPI hailo_status hailo_init_vdevice_params(hailo_vdevice_params_t *params);
+
+/**
+ * Creates a vdevice.
+ * 
+ * @param[in]  params        A @a hailo_vdevice_params_t (may be NULL). Can be initialzed to default values using ::hailo_init_vdevice_params.
+ * @param[out] vdevice       A pointer to a ::hailo_vdevice that receives the allocated vdevice.
+ * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns an ::hailo_status error.
+ * @note To release a vdevice, call the ::hailo_release_vdevice function with the returned ::hailo_vdevice.
+ */
+HAILORTAPI hailo_status hailo_create_vdevice(hailo_vdevice_params_t *params, hailo_vdevice *vdevice);
+
+/**
+ * Configure the vdevice from an hef.
+ *
+ * @param[in]  vdevice                     A ::hailo_vdevice object to be configured.
+ * @param[in]  hef                         A ::hailo_hef object to configure the @a vdevice by.
+ * @param[in]  params                      A @a hailo_configure_params_t (may be NULL). Can be initialzed to default values using ::hailo_init_configure_params.
+ * @param[out] network_groups              Array of network_groups that were loaded from the HEF file.
+ * @param[inout] number_of_network_groups  As input - the size of network_groups array. As output - the number of network_groups loaded.
+ * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns a ::hailo_status error.
+ */
+HAILORTAPI hailo_status hailo_configure_vdevice(hailo_vdevice vdevice, hailo_hef hef,
+    hailo_configure_params_t *params, hailo_configured_network_group *network_groups, size_t *number_of_network_groups);
+
+/**
+ * Gets the underlying physical devices from a vdevice.
+ *
+ * @param[in]  vdevice                     A @a hailo_vdevice object to fetch physical devices from.
+ * @param[out] devices                     Array of ::hailo_device to be fetched from vdevice.
+ * @param[inout] number_of_devices         As input - the size of @a devices array. As output - the number of physical devices under vdevice.
+ * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns a ::hailo_status error.
+ * @note The returned physical devices are held in the scope of @a vdevice.
+ */
+HAILORTAPI hailo_status hailo_get_physical_devices(hailo_vdevice vdevice, hailo_device *devices,
+    size_t *number_of_devices);
+
+/**
+ * Gets the physical devices' infos from a vdevice.
+ *
+ * @param[in]  vdevice                     A @a hailo_vdevice object to fetch physical devices from.
+ * @param[out] devices_infos               Array of ::hailo_pcie_device_info_t to be fetched from vdevice.
+ * @param[inout] number_of_devices         As input - the size of @a devices_infos array. As output - the number of physical devices under vdevice.
+ * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns a ::hailo_status error.
+ * @note The returned physical devices are held in the scope of @a vdevice.
+ */
+HAILORTAPI hailo_status hailo_get_physical_devices_infos(hailo_vdevice vdevice, hailo_pcie_device_info_t *devices_infos,
+    size_t *number_of_devices);
+
+/**
+ * Release an open vdevice.
+ * 
+ * @param[in] vdevice   A :: hailo_vdevice object to be released.
+ * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns a ::hailo_status error.
+ */
+HAILORTAPI hailo_status hailo_release_vdevice(hailo_vdevice vdevice);
+
+/** @} */ // end of group_vdevice_functions
+
+/** @defgroup group_power_measurement_functions Power measurement functions
+ *  @{
+ */
+
+/**
+ * Perform a single power measurement.
+ * 
+ * @param[in]   device             A ::hailo_device object.
+ * @param[in]   dvm                Which DVM will be measured. Default (::HAILO_DVM_OPTIONS_AUTO) will be different according to the board: <br>
+ *                                 - Default (::HAILO_DVM_OPTIONS_AUTO) for EVB is an approximation to the total power consumption of the chip in PCIe setups.
+ *                                 It sums ::HAILO_DVM_OPTIONS_VDD_CORE, ::HAILO_DVM_OPTIONS_MIPI_AVDD and ::HAILO_DVM_OPTIONS_AVDD_H.
+ *                                 Only ::HAILO_POWER_MEASUREMENT_TYPES__POWER can measured with this option.
+ *                                 - Default (::HAILO_DVM_OPTIONS_AUTO) for platforms supporting current monitoring (such as M.2 and mPCIe): OVERCURRENT_PROTECTION.
+ * @param[in]   measurement_type   The type of the measurement. Choosing ::HAILO_POWER_MEASUREMENT_TYPES__AUTO
+ *                                 will select the default value according to the supported features.
+ * @param[out]  measurement        The measured value. Measured units are determined due to
+ *                                 ::hailo_power_measurement_types_t.
+ * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns a ::hailo_status error.
+ */
+HAILORTAPI hailo_status hailo_power_measurement(hailo_device device, hailo_dvm_options_t dvm,
+    hailo_power_measurement_types_t measurement_type, float32_t *measurement);
+
+/**
+ * Start performing a long power measurement.
+ * 
+ * @param[in]   device               A ::hailo_device object.
+ * @param[in]   delay_milliseconds   Amount of time between each measurement interval.
+ *                                   This time period is sleep time of the core.
+ * @param[in]   averaging_factor     Number of samples per time period, sensor configuration value.
+ * @param[in]   sampling_period      Related conversion time, sensor configuration value.
+ *                                   The sensor samples the power every sampling_period {ms} and averages every
+ *                                   averaging_factor samples. The sensor provides a new value every: 2 * sampling_period * averaging_factor {ms}.
+ *                                   The firmware wakes up every interval_milliseconds {ms} and checks the sensor.
+ *                                   If there is a new value to read from the sensor, the firmware reads it.
+ *                                   Note that the average calculated by the firmware is “average of averages”,
+ *                                   because it averages values that have already been averaged by the sensor.
+ * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns a ::hailo_status error.
+ */
+HAILORTAPI hailo_status hailo_start_power_measurement(hailo_device device, uint32_t delay_milliseconds,
+    hailo_averaging_factor_t averaging_factor, hailo_sampling_period_t sampling_period);
+
+/**
+ * Set parameters for long power measurement.
+ * 
+ * @param[in]   device             A ::hailo_device object.
+ * @param[in]   index              Index of the buffer on the firmware the data would be saved at.
+ * @param[in]   dvm                Which DVM will be measured. Default (::HAILO_DVM_OPTIONS_AUTO) will be different according to the board: <br>
+ *                                 - Default (::HAILO_DVM_OPTIONS_AUTO) for EVB is an approximation to the total power consumption of the chip in PCIe setups.
+ *                                 It sums ::HAILO_DVM_OPTIONS_VDD_CORE, ::HAILO_DVM_OPTIONS_MIPI_AVDD and ::HAILO_DVM_OPTIONS_AVDD_H.
+ *                                 Only ::HAILO_POWER_MEASUREMENT_TYPES__POWER can measured with this option.
+ *                                 - Default (::HAILO_DVM_OPTIONS_AUTO) for platforms supporting current monitoring (such as M.2 and mPCIe): OVERCURRENT_PROTECTION.
+ * @param[in]   measurement_type   The type of the measurement. Choosing ::HAILO_POWER_MEASUREMENT_TYPES__AUTO
+ *                                 will select the default value according to the supported features.
+ * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns a ::hailo_status error.
+ */
+HAILORTAPI hailo_status hailo_set_power_measurement(hailo_device device, uint32_t index,
+    hailo_dvm_options_t dvm, hailo_power_measurement_types_t measurement_type);
+
+/**
+ * Read measured power from a long power measurement
+ * 
+ * @param[in]   device                A ::hailo_device object.
+ * @param[in]   index                 Index of the buffer on the firmware the data would be saved at.
+ * @param[in]   should_clear          Flag indicating if the results saved at the firmware will be deleted after reading.
+ * @param[out]  measurement_data      The measurement data, ::hailo_power_measurement_data_t. Measured units are
+ *                                    determined due to ::hailo_power_measurement_types_t passed to ::hailo_set_power_measurement
+ * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns a ::hailo_status error.
+ */
+HAILORTAPI hailo_status hailo_get_power_measurement(hailo_device device, uint32_t index, bool should_clear,
+     hailo_power_measurement_data_t *measurement_data);
+
+/**
+ * Stop performing a long power measurement.
+ * 
+ * @param[in]   device             A ::hailo_device object.
+ * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns a ::hailo_status error.
+ */
+HAILORTAPI hailo_status hailo_stop_power_measurement(hailo_device device);
+
+/** @} */ // end of group_power_measurement_functions
+
+/** @defgroup group_hef_functions HEF parsing functions
+ *  @{
+ */
+
+/**
+ * Creates an HEF from file.
+ *
+ * @param[out] hef             A pointer to a @a hailo_hef that receives the allocated HEF.
+ * @param[in]  file_name       The name of the hef file.
+ * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns a ::hailo_status error.
+ * @note To release an HEF, call the ::hailo_release_hef function with the returned @a hef.
+ */
+HAILORTAPI hailo_status hailo_create_hef_file(hailo_hef *hef, const char *file_name);
+
+/**
+ * Creates an HEF from buffer.
+ *
+ * @param[out] hef             A pointer to a @a hailo_hef that receives the allocated HEF.
+ * @param[in]  buffer          A pointer to a buffer that contains the HEF content.
+ * @param[in]  size            The size in bytes of the HEF content pointed by @a buffer.
+ * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns a ::hailo_status error.
+ * @note To release an HEF, call the ::hailo_release_hef function with the returned @a hef.
+ */
+HAILORTAPI hailo_status hailo_create_hef_buffer(hailo_hef *hef, const void *buffer, size_t size);
+
+/**
+ * Release an open HEF.
+ *
+ * @param[in] hef   A ::hailo_hef object to be released.
+ * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns a ::hailo_status error.
+ */
+HAILORTAPI hailo_status hailo_release_hef(hailo_hef hef);
+
+/**
+ * Gets all stream infos.
+ *
+ * @param[in] hef                     A ::hailo_hef object that contains the information.
+ * @param[in] name                    The name of the network or network_group which contains the stream_infos.
+ *                                    In case network group name is given, the function returns all stream infos 
+ *                                    of all the networks of the given network group.
+ *                                    In case network name is given (provided by @a hailo_hef_get_network_infos), 
+ *                                    the function returns all stream infos of the given network.
+ *                                    If NULL is passed, the function returns all the stream infos of 
+ *                                    all the networks of the first network group.
+ * @param[out] stream_infos           A pointer to a buffer of ::hailo_stream_info_t that receives the informations.
+ * @param[in] stream_infos_length     The number of ::hailo_stream_info_t elements in the buffer pointed by
+ *                                    @a stream_infos
+ * @param[out] number_of_streams      This variable will be filled with the number of stream_infos if the function returns with
+ *                                    HAILO_SUCCESS or HAILO_INSUFFICIENT_BUFFER.
+ * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, if the buffer is
+ *                                    insufficient to hold the information a ::HAILO_INSUFFICIENT_BUFFER would be
+ *                                    returned. In any other case, returns a ::hailo_status error.
+ */
+HAILORTAPI hailo_status hailo_hef_get_all_stream_infos(hailo_hef hef, const char *name,
+    hailo_stream_info_t *stream_infos, size_t stream_infos_length, size_t *number_of_streams);
+
+/**
+ * Gets stream info by name.
+ *
+ * @param[in]  hef                 A ::hailo_hef object that contains the information.
+ * @param[in]  network_group_name  The name of the network_group which contains the stream_infos. If NULL is passed,
+ *                                 the first network_group in the HEF will be addressed.
+ * @param[in]  stream_name         The name of the stream as presented in the hef.
+ * @param[in]  stream_direction    Indicates the stream direction.
+ * @param[out] stream_info         A pointer to a ::hailo_stream_info_t that receives the stream information.
+ * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns a ::hailo_status error.
+ */
+HAILORTAPI hailo_status hailo_hef_get_stream_info_by_name(hailo_hef hef, const char *network_group_name,
+    const char *stream_name, hailo_stream_direction_t stream_direction, hailo_stream_info_t *stream_info);
+
+/**
+ * Gets all virtual stream infos.
+ *
+ * @param[in] hef                        A ::hailo_hef object that contains the information.
+ * @param[in] name                       The name of the network or network_group which contains the virtual stream infos.
+ *                                       In case network group name is given, the function returns all virtual stream infos 
+ *                                       of all the networks of the given network group.
+ *                                       In case network name is given (provided by @a hailo_hef_get_network_infos), 
+ *                                       the function returns all stream infos of the given network.
+ *                                       If NULL is passed, the function returns all the stream infos of 
+ *                                       all the networks of the first network group.
+ * @param[out] vstream_infos             A pointer to a buffer of ::hailo_stream_info_t that receives the informations.
+ * @param[inout] vstream_infos_count     As input - the maximum amount of entries in @a vstream_infos array.
+ *                                       As output - the actual amount of entries written if the function returns with ::HAILO_SUCCESS
+ *                                       and the amount of entries needed if the function returns ::HAILO_INSUFFICIENT_BUFFER.
+ * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns a ::hailo_status error.
+ */
+HAILORTAPI hailo_status hailo_hef_get_all_vstream_infos(hailo_hef hef, const char *name,
+    hailo_vstream_info_t *vstream_infos, size_t *vstream_infos_count);
+
+/**
+ * Gets vstream name from original layer name.
+ *
+ * @param[in]  hef                 A @a hailo_hef object that contains the information.
+ * @param[in]  network_group_name  The name of the network_group which contains the stream_infos. If NULL is passed,
+ *                                 the first network_group in the HEF will be addressed.
+ * @param[in]  original_name       The original layer name as presented in the hef.
+ * @param[out] vstream_name        The name of the vstream for the provided original name, ends with NULL terminator.
+ * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns a ::hailo_status error.
+ */
+HAILORTAPI hailo_status hailo_hef_get_vstream_name_from_original_name(hailo_hef hef, const char *network_group_name,
+    const char *original_name, hailo_layer_name_t *vstream_name);
+
+/**
+ * Gets all original layer names from vstream name.
+ *
+ * @param[in]  hef                      A @a hailo_hef object that contains the information.
+ * @param[in]  network_group_name       The name of the network_group which contains the stream_infos. If NULL is passed,
+ *                                      the first network_group in the HEF will be addressed.
+ * @param[in]  vstream_name             The name of the stream as presented in the hef.
+ * @param[out] original_names           Array of ::hailo_layer_name_t, all original names linked to the provided stream
+ *                                      (each name ends with NULL terminator).
+ * @param[inout] original_names_length  As input - the size of original_names array. As output - the number of original_layers names.
+ * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns a ::hailo_status error.
+ */
+HAILORTAPI hailo_status hailo_hef_get_original_names_from_vstream_name(hailo_hef hef, const char *network_group_name,
+    const char *vstream_name, hailo_layer_name_t *original_names, size_t *original_names_length);
+
+/**
+ * Gets all vstream names from stream name.
+ *
+ * @param[in]  hef                      A @a hailo_hef object that contains the information.
+ * @param[in]  network_group_name       The name of the network_group which contains the stream_infos. If NULL is passed,
+ *                                      the first network_group in the HEF will be addressed.
+ * @param[in]  stream_name              The name of the stream as presented in the hef.
+ * @param[out] vstream_names            Array of ::hailo_layer_name_t, all vstream names linked to the provided stream
+ *                                      (each name ends with NULL terminator).
+ * @param[inout] vstream_names_length   As input - the size of vstream_names array. As output - the number of vstream names.
+ * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns a ::hailo_status error.
+ */
+HAILORTAPI hailo_status hailo_hef_get_vstream_names_from_stream_name(hailo_hef hef, const char *network_group_name,
+    const char *stream_name, hailo_layer_name_t *vstream_names, size_t *vstream_names_length);
+
+/**
+ * Gets all stream names from vstream name.
+ *
+ * @param[in]  hef                      A @a hailo_hef object that contains the information.
+ * @param[in]  network_group_name       The name of the network_group which contains the stream_infos. If NULL is passed,
+ *                                      the first network_group in the HEF will be addressed.
+ * @param[in]  vstream_name             The name of the vstream as presented in the hef.
+ * @param[out] stream_names             Array of ::hailo_layer_name_t, all stream names linked to the provided vstream
+ *                                      (each name ends with NULL terminator).
+ * @param[inout] stream_names_length    As input - the size of stream_names array. As output - the number of stream names.
+ * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns a ::hailo_status error.
+ */
+HAILORTAPI hailo_status hailo_hef_get_stream_names_from_vstream_name(hailo_hef hef, const char *network_group_name,
+    const char *vstream_name, hailo_layer_name_t *stream_names, size_t *stream_names_length);
+
+ /**
+ * Gets sorted output names from network group name.
+ *
+ * @param[in]  hef                          A @a hailo_hef object that contains the information.
+ * @param[in]  network_group_name           The name of the network_group. If NULL is passed, the first network_group
+ *                                          in the HEF will be addressed.
+ * @param[out] sorted_output_names          List of sorted outputs names.
+ * @param[inout] sorted_output_names_count  As input - the expected size of sorted_output_names list. As output - the number of sorted_output_names.
+ * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns a ::hailo_status error.
+ */
+HAILORTAPI hailo_status hailo_hef_get_sorted_output_names(hailo_hef hef, const char *network_group_name,
+    hailo_layer_name_t *sorted_output_names, size_t *sorted_output_names_count);
+
+ /**
+ * Gets bottleneck fps from network group name.
+ *
+ * @param[in]  hef                  A @a hailo_hef object that contains the information.
+ * @param[in]  network_group_name   The name of the network_group. If NULL is passed, the first network_group
+ *                                  in the HEF will be addressed.
+ * @param[out] bottleneck_fps       Bottleneck FPS. Note: This is not relevant in the case of multi context. 
+ * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns a ::hailo_status error.
+ */
+HAILORTAPI hailo_status hailo_hef_get_bottleneck_fps(hailo_hef hef, const char *network_group_name,
+    float64_t *bottleneck_fps);
+
+/**
+ * Calculate the inputs bandwidths supported by the network described by @a hef. Rate limiting
+ * of this manner is to be used for ethernet ::hailo_input_stream.
+ *
+ * @param[in]     hef                  A ::hailo_hef object that contains the stream's information.
+ * @param[in]     network_group_name   The name of the network_group which contains the stream_infos. If NULL is passed,
+ *                                     the first network_group in the HEF will be addressed.
+ * @param[in]     fps                  The desired fps.
+ * @param[out]    rates                A pointer to an array of ::hailo_rate_limit_t that receives the rates.
+ * @param[inout]  rates_length         As input - the length of @a rates array. As output - the number of H2D streams.
+ * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns a ::hailo_status error.
+ * @note There are two options to limit the rate of an ethernet input stream to the desired bandwidth:
+ *       - Set ::hailo_eth_input_stream_params_t.rate_limit_bytes_per_sec inside ::hailo_configure_params_t
+ *         before passing it to ::hailo_configure_device.
+ *       - On Unix platforms:
+ *         - You may use the command line tool `hailortcli udp-rate-limiter` instead of using this API
+ * @note The resulting rates calculated assures that @a HAILO_DEFAULT_MAX_ETHERNET_BANDWIDTH_BYTES_PER_SEC
+ * will not be exceeded. The actual fps must be lower than given, and appropriate log will be printed.
+ */
+HAILORTAPI hailo_status hailo_calculate_eth_input_rate_limits(hailo_hef hef, const char *network_group_name,
+    uint32_t fps, hailo_rate_limit_t *rates, size_t *rates_length);
+
+/** @} */ // end of group_hef_functions
+
+/** @defgroup group_network_group_functions Network group configuration/activation functions
+ *  @{
+ */
+
+/**
+ * Init configure params with default values for a given hef.
+ *
+ * @param[in]  hef                      A  ::hailo_hef object to configure the @a device by.
+ * @param[in]  stream_interface         A @a hailo_stream_interface_t indicating which @a hailo_stream_parameters_t to create.
+ * @param[out] params                   A @a hailo_configure_params_t to be filled.
+ * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns a ::hailo_status error.
+ */
+HAILORTAPI hailo_status hailo_init_configure_params(hailo_hef hef, hailo_stream_interface_t stream_interface,
+    hailo_configure_params_t *params);
+
+/**
+ * Init configure params with default values for a given hef, where all input_streams_params are init to be MIPI type.
+ *
+ * @param[in]  hef                      A  ::hailo_hef object to configure the @a device by.
+ * @param[in]  output_interface         A @a hailo_stream_interface_t indicating which @a hailo_stream_parameters_t to
+ *                                      create for the output streams.
+ * @param[in]  mipi_params              A ::hailo_mipi_input_stream_params_t object which contains the MIPI params for
+ *                                      the input streams.
+ * @param[out] params                   A @a hailo_configure_params_t to be filled.
+ * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns a ::hailo_status error.
+ */
+HAILORTAPI hailo_status hailo_init_configure_params_mipi_input(hailo_hef hef, hailo_stream_interface_t output_interface,
+    hailo_mipi_input_stream_params_t *mipi_params, hailo_configure_params_t *params);
+
+/**
+ * Configure the device from an hef.
+ *
+ * @param[in]  device                      A ::hailo_device object to be configured.
+ * @param[in]  hef                         A ::hailo_hef object to configure the @a device by.
+ * @param[in]  params                      A @a hailo_configure_params_t (may be NULL). Can be initialzed to default values using ::hailo_init_configure_params.
+ * @param[out] network_groups              Array of network_groups that were loaded from the HEF file.
+ * @param[inout] number_of_network_groups  As input - the size of network_groups array. As output - the number of network_groups loaded.
+ * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns a ::hailo_status error.
+ */
+HAILORTAPI hailo_status hailo_configure_device(hailo_device device, hailo_hef hef,
+    hailo_configure_params_t *params, hailo_configured_network_group *network_groups, size_t *number_of_network_groups);
+
+/**
+ * Block until network_group is activated, or until timeout_ms is passed.
+ *
+ * @param[in] network_group              A ::hailo_configured_network_group to wait for activation.
+ * @param[in] timeout_ms                 The timeout in milliseconds. If @a timeout_ms is zero, the function returns immediately.
+ *                                       If @a timeout_ms is @a HAILO_INFINITE, the function returns only when the event is
+ *                                       signaled.
+ * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns a ::hailo_status error.
+ */
+HAILORTAPI hailo_status hailo_wait_for_network_group_activation(hailo_configured_network_group network_group,
+    uint32_t timeout_ms);
+
+
+/**
+ * Get network group infos from an hef.
+ *
+ * @param[in]    hef                       A ::hailo_hef object.
+ * @param[out]   infos                     Array of @a hailo_network_group_info_t to be filled.
+ * @param[inout] number_of_infos           As input - the size of infos array. As output - the number of infos loaded.
+ * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns a ::hailo_status error.
+ */
+HAILORTAPI hailo_status hailo_get_network_groups_infos(hailo_hef hef, hailo_network_group_info_t *infos,
+    size_t *number_of_infos);
+
+/**
+ * Gets all stream infos from a configured network group
+ *
+ * @param[in]  network_group          A ::hailo_configured_network_group object.
+ * @param[out] stream_infos           A pointer to a buffer of ::hailo_stream_info_t that receives the informations.
+ * @param[in]  stream_infos_length    The number of ::hailo_stream_info_t elements in the buffer pointed by
+ *                                    @a stream_infos
+ * @param[out] number_of_streams      This variable will be filled with the number of stream_infos if the function returns with
+ *                                    HAILO_SUCCESS or HAILO_INSUFFICIENT_BUFFER.
+ * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, if the buffer is
+ *                                    insufficient to hold the information a ::HAILO_INSUFFICIENT_BUFFER would be
+ *                                    returned. In any other case, returns a ::hailo_status error.
+ */
+HAILORTAPI hailo_status hailo_network_group_get_all_stream_infos(hailo_configured_network_group network_group,
+    hailo_stream_info_t *stream_infos, size_t stream_infos_length, size_t *number_of_streams);
+
+/**
+ * Gets all input stream infos from a configured network group
+ *
+ * @param[in]  network_group          A ::hailo_configured_network_group object.
+ * @param[out] stream_infos           A pointer to a buffer of ::hailo_stream_info_t that receives the informations.
+ * @param[in]  stream_infos_length    The number of ::hailo_stream_info_t elements in the buffer pointed by
+ *                                    @a stream_infos
+ * @param[out] number_of_streams      This variable will be filled with the number of stream_infos if the function returns with
+ *                                    HAILO_SUCCESS or HAILO_INSUFFICIENT_BUFFER.
+ * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, if the buffer is
+ *                                    insufficient to hold the information a ::HAILO_INSUFFICIENT_BUFFER would be
+ *                                    returned. In any other case, returns a ::hailo_status error.
+ */
+HAILORTAPI hailo_status hailo_network_group_get_input_stream_infos(hailo_configured_network_group network_group,
+    hailo_stream_info_t *stream_infos, size_t stream_infos_length, size_t *number_of_streams);
+
+/**
+ * Gets all output stream infos from a configured network group
+ *
+ * @param[in]  network_group          A ::hailo_configured_network_group object.
+ * @param[out] stream_infos           A pointer to a buffer of ::hailo_stream_info_t that receives the informations.
+ * @param[in]  stream_infos_length    The number of ::hailo_stream_info_t elements in the buffer pointed by
+ *                                    @a stream_infos
+ * @param[out] number_of_streams      This variable will be filled with the number of stream_infos if the function returns with
+ *                                    HAILO_SUCCESS or HAILO_INSUFFICIENT_BUFFER.
+ * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, if the buffer is
+ *                                    insufficient to hold the information a ::HAILO_INSUFFICIENT_BUFFER would be
+ *                                    returned. In any other case, returns a ::hailo_status error.
+ */
+HAILORTAPI hailo_status hailo_network_group_get_output_stream_infos(hailo_configured_network_group network_group,
+    hailo_stream_info_t *stream_infos, size_t stream_infos_length, size_t *number_of_streams);
+
+/**
+ * Activates hailo_device inner-resources for context_switch inference.
+ *
+ * @param[in]  network_group                NetworkGroup to be activated.
+ * @param[in]  activation_params            Optional parameters for the activation (may be NULL).
+ * @param[out] activated_network_group_out  Handle for the activated network_group.
+ * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns a ::hailo_status error.
+ */
+HAILORTAPI hailo_status hailo_activate_network_group(hailo_configured_network_group network_group,
+    hailo_activate_network_group_params_t *activation_params,
+    hailo_activated_network_group *activated_network_group_out);
+
+/**
+ * De-activates hailo_device inner-resources for context_switch inference.
+ *
+ * @param[in]  activated_network_group        NetworkGroup to deactivate.
+ * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns a ::hailo_status error.
+ */
+HAILORTAPI hailo_status hailo_deactivate_network_group(hailo_activated_network_group activated_network_group);
+
+/**
+ * Return input stream from configured network_group by stream name.
+ *
+ * @param[in]  configured_network_group      NetworkGroup to get stream from.
+ * @param[in]  stream_name                  The name of the input stream to retrieve.
+ * @param[out] stream                       The returned input stream.
+ * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns a ::hailo_status error.
+ */
+HAILORTAPI hailo_status hailo_get_input_stream(
+    hailo_configured_network_group configured_network_group, const char *stream_name, hailo_input_stream *stream);
+
+/**
+ * Return output stream from configured network_group by stream name.
+ *
+ * @param[in]  configured_network_group      NetworkGroup to get stream from.
+ * @param[in]  stream_name                  The name of the output stream to retrieve.
+ * @param[out] stream                       The returned output stream.
+ * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns a ::hailo_status error.
+ */
+HAILORTAPI hailo_status hailo_get_output_stream(
+    hailo_configured_network_group configured_network_group, const char *stream_name, hailo_output_stream *stream);
+
+/**
+ * Returns the network latency (only available if latency measurement was enabled).
+ *
+ * @param[in]  configured_network_group     NetworkGroup to get the latency measurement from.
+ * @param[in]  network_name                 Network name of the requested latency measurement.
+ *                                          If NULL is passed, all the networks in the network group will be addressed,
+ *                                          and the resulted measurement is avarage latency of all networks.
+ * @param[out] result                       Output latency result.
+ * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns a ::hailo_status error.
+ */
+HAILORTAPI hailo_status hailo_get_latency_measurement(hailo_configured_network_group configured_network_group,
+    const char *network_name, hailo_latency_measurement_result_t *result);
+
+/** @} */ // end of group_network_group_functions
+
+/** @defgroup group_stream_functions Stream functions
+ *  @{
+ */
+
+/**
+ * Set new timeout value to an input stream
+ *
+ * @param[in] stream          A ::hailo_input_stream object to get the new timeout value.
+ * @param[in] timeout_ms      the new timeout value to be set.
+ * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns a ::hailo_status error.
+ */
+HAILORTAPI hailo_status hailo_set_input_stream_timeout(hailo_input_stream stream, uint32_t timeout_ms);
+
+/**
+ * Set new timeout value to an output stream
+ *
+ * @param[in] stream          A ::hailo_output_stream object to get the new timeout value.
+ * @param[in] timeout_ms      the new timeout value to be set.
+ * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns a ::hailo_status error.
+ */
+HAILORTAPI hailo_status hailo_set_output_stream_timeout(hailo_output_stream stream, uint32_t timeout_ms);
+
+/**
+ * Gets the size of a stream's frame on the host side in bytes
+ *
+ * @param[in] stream   A ::hailo_input_stream object.
+ * @return The size of the frame on the host side in bytes
+ */
+HAILORTAPI size_t hailo_get_input_stream_frame_size(hailo_input_stream stream);
+
+/**
+ * Gets the size of a stream's frame on the host side in bytes
+ *
+ * @param[in] stream   A ::hailo_output_stream object.
+ * @return The size of the frame on the host side in bytes
+ */
+HAILORTAPI size_t hailo_get_output_stream_frame_size(hailo_output_stream stream);
+
+/**
+ * Gets stream info from the given input stream
+ *
+ * @param[in] stream        A ::hailo_input_stream object.
+ * @param[out] stream_info  An output ::hailo_stream_info_t.
+ * @return The size of the frame on the host side in bytes
+ */
+HAILORTAPI hailo_status hailo_get_input_stream_info(hailo_input_stream stream, hailo_stream_info_t *stream_info);
+
+/**
+ * Gets stream info from the given output stream
+ *
+ * @param[in] stream        A ::hailo_input_stream object.
+ * @param[out] stream_info  An output ::hailo_stream_info_t.
+ * @return The size of the frame on the host side in bytes
+ */
+HAILORTAPI hailo_status hailo_get_output_stream_info(hailo_output_stream stream, hailo_stream_info_t *stream_info);
+
+/**
+ * Synchronously reads data from a stream.
+ * 
+ * @param[in] stream            A ::hailo_output_stream object.
+ * @param[in] buffer            A pointer to a buffer that receives the data read from @a stream.
+ * @param[in] size              The amount of bytes to read, should be the frame size.
+ * 
+ * @note The output buffer format comes from the \e format field inside ::hailo_stream_info_t and the shape comes from
+ *            the \e hw_shape field inside ::hailo_stream_info_t.
+ *
+ * @note @a size is expected to be a product of stream_info.hw_frame_size (i.e. more than one frame may be read)
+ * 
+ * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns a ::hailo_status error.
+ */
+HAILORTAPI hailo_status hailo_stream_read_raw_buffer(hailo_output_stream stream, void *buffer, size_t size);
+
+/**
+ * Synchronously writes all data to a stream.
+ * 
+ * @param[in] stream   A ::hailo_input_stream object.
+ * @param[in] buffer   A pointer to a buffer that contains the data to be written to @a stream.
+ * @param[in] size     The amount of bytes to write.
+ * 
+ * @note The input buffer format comes from the \e format field inside ::hailo_stream_info_t and the shape comes from
+ *            the \e hw_shape field inside ::hailo_stream_info_t.
+ *
+ * @note @a size is expected to be a product of stream_info.hw_frame_size (i.e. more than one frame may be read)
+ * 
+ * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns a ::hailo_status error.
+ */
+HAILORTAPI hailo_status hailo_stream_write_raw_buffer(hailo_input_stream stream, const void *buffer, size_t size);
+
+/**
+ * Gets the size of a stream's frame on the host side in bytes
+ * (the size could be affected by the format type - for example using UINT16, or by the data not being quantized yet)
+ *
+ * @param[in] stream_info             The stream's info represented by ::hailo_stream_info_t
+ * @param[in] transform_params        Host side transform parameters
+ * @return The size of the frame on the host side in bytes
+ */
+HAILORTAPI size_t hailo_get_host_frame_size(const hailo_stream_info_t *stream_info, const hailo_transform_params_t *transform_params);
+
+/** @} */ // end of group_stream_functions
+
+/** @defgroup group_transform_functions Data transformation functions
+ *  @{
+ */
+
+/**
+ * Creates an input transform_context object. Allocates all necessary buffers used for the transformation (pre-process).
+ * 
+ * @param[in]     stream_info - A ::hailo_stream_info_t object
+ * @param[in]     transform_params - A ::hailo_transform_params_t user transformation parameters.
+ * @param[out]    transform_context - A ::hailo_input_transform_context
+ * 
+ * @return Upon success, returns @a HAILO_SUCCESS. Otherwise, returns an @a hailo_status error.
+ * 
+ * @note To release the transform_context, call the ::hailo_release_input_transform_context function
+ *      with the returned ::hailo_input_transform_context.
+ * 
+ */
+HAILORTAPI hailo_status hailo_create_input_transform_context(const hailo_stream_info_t *stream_info,
+    const hailo_transform_params_t *transform_params, hailo_input_transform_context *transform_context);
+
+/**
+ * Releases a transform_context object including all allocated buffers.
+ * 
+ * @param[in]    transform_context - A ::hailo_input_transform_context object.
+ * 
+ * @return Upon success, returns @a HAILO_SUCCESS. Otherwise, returns an @a hailo_status error.
+ */
+HAILORTAPI hailo_status hailo_release_input_transform_context(hailo_input_transform_context transform_context);
+
+/**
+ * Check whether or not a transformation is needed.
+ *
+ * @param[in]  src_image_shape         The shape of the src buffer (host shape).
+ * @param[in]  src_format              The format of the src buffer (host format).
+ * @param[in]  dst_image_shape         The shape of the dst buffer (hw shape).
+ * @param[in]  dst_format              The format of the dst buffer (hw format).
+ * @param[in]  quant_info              A ::hailo_quant_info_t object containing quantization information.
+ * @param[out] transformation_required Indicates whether or not a transformation is needed.
+ * @return Upon success, returns @a HAILO_SUCCESS. Otherwise, returns an @a hailo_status error.
+ * @note In case @a transformation_required is false, the src frame is ready to be sent to HW without any transformation.
+ */
+HAILORTAPI hailo_status hailo_is_input_transformation_required(
+    const hailo_3d_image_shape_t *src_image_shape, const hailo_format_t *src_format,
+    const hailo_3d_image_shape_t *dst_image_shape, const hailo_format_t *dst_format,
+    const hailo_quant_info_t *quant_info, bool *transformation_required);
+
+/**
+ * Transforms an input frame pointed to by @a src directly to the buffer pointed to by @a dst.
+ * 
+ * @param[in]  transform_context    A ::hailo_input_transform_context.
+ * @param[in]  src                  A pointer to a buffer to be transformed.
+ * @param[in]  src_size             The number of bytes to transform. This number must be equal to the input host_frame_size,
+ *                                  and less than or equal to the size of @a src buffer.
+ * @param[out] dst                  A pointer to a buffer that receives the transformed data.
+ * @param[in]  dst_size             The number of bytes in @a dst buffer. This number must be equal to the input hw_frame_size,
+ *                                  and less than or equal to the size of @a dst buffer.
+ * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns a ::hailo_status error.
+ * @warning The buffers must not overlap.
+ */
+HAILORTAPI hailo_status hailo_transform_frame_by_input_transform_context(hailo_input_transform_context transform_context,
+    const void *src, size_t src_size, void *dst, size_t dst_size);
+
+/**
+ * Creates an output transform_context object. Allocates all necessary buffers used for the transformation (post-process).
+ * 
+ * @param[in]     stream_info - A ::hailo_stream_info_t object
+ * @param[in]     transform_params - A ::hailo_transform_params_t user transformation parameters.
+ * @param[out]    transform_context - A ::hailo_output_transform_context
+ * 
+ * @return Upon success, returns @a HAILO_SUCCESS. Otherwise, returns an @a hailo_status error.
+ * 
+ * @note To release the transform_context, call the ::hailo_release_output_transform_context function
+ *      with the returned ::hailo_output_transform_context.
+ * 
+ */
+HAILORTAPI hailo_status hailo_create_output_transform_context(const hailo_stream_info_t *stream_info,
+    const hailo_transform_params_t *transform_params, hailo_output_transform_context *transform_context);
+
+/**
+ * Releases a transform_context object including all allocated buffers.
+ * 
+ * @param[in]    transform_context - A ::hailo_output_transform_context object.
+ * 
+ * @return Upon success, returns @a HAILO_SUCCESS. Otherwise, returns an @a hailo_status error.
+ */
+HAILORTAPI hailo_status hailo_release_output_transform_context(hailo_output_transform_context transform_context);
+
+/**
+ * Check whether or not a transformation is needed.
+ *
+ * @param[in]  src_image_shape         The shape of the src buffer (hw shape).
+ * @param[in]  src_format              The format of the src buffer (hw format).
+ * @param[in]  dst_image_shape         The shape of the dst buffer (host shape).
+ * @param[in]  dst_format              The format of the dst buffer (host format).
+ * @param[in]  quant_info              A ::hailo_quant_info_t object containing quantization information.
+ * @param[out] transformation_required Indicates whether or not a transformation is needed.
+ * @return Upon success, returns @a HAILO_SUCCESS. Otherwise, returns an @a hailo_status error.
+ * @note In case @a transformation_required is false, the src frame is already in the required format without any transformation.
+ */
+HAILORTAPI hailo_status hailo_is_output_transformation_required(
+    const hailo_3d_image_shape_t *src_image_shape, const hailo_format_t *src_format,
+    const hailo_3d_image_shape_t *dst_image_shape, const hailo_format_t *dst_format,
+    const hailo_quant_info_t *quant_info, bool *transformation_required);
+
+/**
+ * Transforms an output frame pointed to by @a src directly to the buffer pointed to by @a dst.
+ * 
+ * @param[in]  transform_context    A ::hailo_output_transform_context.
+ * @param[in]  src                  A pointer to a buffer to be transformed.
+ * @param[in]  src_size             The number of bytes to transform. This number must be equal to the output hw_frame_size,
+ *                                  and less than or equal to the size of @a src buffer.
+ * @param[out] dst                  A pointer to a buffer that receives the transformed data.
+ * @param[in]  dst_size             The number of bytes in @a dst buffer. This number must be equal to the output host_frame_size,
+ *                                  and less than or equal to the size of @a dst buffer.
+ * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns a ::hailo_status error.
+ * @warning The buffers must not overlap.
+ */
+HAILORTAPI hailo_status hailo_transform_frame_by_output_transform_context(hailo_output_transform_context transform_context,
+    const void *src, size_t src_size, void *dst, size_t dst_size);
+
+/**
+ * Creates an demuxer for the given mux stream. Allocates all necessary buffers
+ * used for the demux process.
+ * 
+ * @param[in]     stream - A ::hailo_output_stream object
+ * @param[in]     demux_params - A ::hailo_demux_params_t user demux parameters.
+ * @param[out]    demuxer - A ::hailo_output_demuxer, used to transform output frames
+ * 
+ * @return Upon success, returns @a HAILO_SUCCESS. Otherwise, returns an @a hailo_status error.
+ * 
+ * @note To release the demuxer, call the ::hailo_release_output_demuxer function
+ *      with the returned ::hailo_output_demuxer.
+ * 
+ */
+HAILORTAPI hailo_status hailo_create_demuxer_by_stream(hailo_output_stream stream,
+    const hailo_demux_params_t *demux_params, hailo_output_demuxer *demuxer);
+
+/**
+ * Releases a demuxer object.
+ * 
+ * @param[in]    demuxer - A ::hailo_output_demuxer object.
+ * 
+ * @return Upon success, returns @a HAILO_SUCCESS. Otherwise, returns an @a hailo_status error.
+ */
+HAILORTAPI hailo_status hailo_release_output_demuxer(hailo_output_demuxer demuxer);
+
+/**
+ * Demultiplexing an output frame pointed to by @a src directly to the buffer pointed to by @a dst.
+ * 
+ * @param[in]     demuxer            A ::hailo_output_demuxer object used for the demuxing.
+ * @param[in]     src                A pointer to a buffer to be demultiplexed.
+ * @param[in]     src_size           The number of bytes to demultiplexed. This number must be equal to the
+ *                                   hw_frame_size, and less than or equal to the size of @a src buffer.
+ * @param[in,out] raw_buffers        A pointer to an array of ::hailo_stream_raw_buffer_t that receives the
+ *                                   demultiplexed data read from the @a stream.
+ * @param[in]     raw_buffers_count  The number of ::hailo_stream_raw_buffer_t elements in the array pointed to by
+ *                                   @a raw_buffers.
+ * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns a ::hailo_status error.
+ */
+HAILORTAPI hailo_status hailo_demux_raw_frame_by_output_demuxer(hailo_output_demuxer demuxer, const void *src,
+    size_t src_size, hailo_stream_raw_buffer_t *raw_buffers, size_t raw_buffers_count);
+
+/**
+ * Gets all multiplexed stream infos.
+ * 
+ * @param[in]  demuxer                A ::hailo_output_demuxer object.
+ * @param[out] stream_infos           A pointer to a buffer of ::hailo_stream_info_t that receives the information.
+ * @param[inout] number_of_streams    The maximum amount of streams_info to fill. This variable will be filled with
+ *                                    the actual number of multiplexed stream_infos. If the buffer is insufficient
+ *                                    to hold the information this variable will be set to the requested value,
+ *                                    ::HAILO_INSUFFICIENT_BUFFER would be returned.
+ * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns a ::hailo_status error.
+ */
+HAILORTAPI hailo_status hailo_get_mux_infos_by_output_demuxer(hailo_output_demuxer demuxer, hailo_stream_info_t *stream_infos,
+    size_t *number_of_streams);
+
+/**
+ * Fuse multiple defused NMS buffers pointed by @a nms_fuse_inputs to the buffer pointed to by @a fused_buffer.
+ * This function should be called on @a nms_fuse_inputs after receiving them from HW, and before transformation.
+ * This function expects @a nms_fuse_inputs to be ordered by their @a class_group_index (lowest to highest).
+ * 
+ * @param[in]     nms_fuse_inputs    Array of @a hailo_nms_fuse_input_t structs which contain the buffers to be fused.
+ * @param[in]     inputs_count       How many members in @a nms_fuse_inputs.
+ * @param[out]    fused_buffer       A pointer to a buffer which will contain the fused buffer.
+ * @param[in]     fused_buffer_size  The fused buffer size.
+ * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns a ::hailo_status error.
+ */
+HAILORTAPI hailo_status hailo_fuse_nms_frames(const hailo_nms_fuse_input_t *nms_fuse_inputs,
+    uint32_t inputs_count, uint8_t *fused_buffer, size_t fused_buffer_size);
+
+/** @} */ // end of group_transform_functions
+
+/** @defgroup group_vstream_functions Virtual Stream functions
+ *  @{
+ */
+
+/**
+ * Creates input virtual stream params linked to a network or a network group.
+ *
+ * @param[in] hef                       A @a hailo_hef object that contains the information.
+ * @param[in] name                      The name of the network group or network which contains the input virtual streams. 
+ *                                      In case network group name is given, the function returns input virtual stream params 
+ *                                      of all the networks of the given network group.
+ *                                      In case network name is given (provided by @a hailo_hef_get_network_infos), 
+ *                                      the function returns input virtual stream params of the given network.
+ *                                      If NULL is passed, the function returns the input virtual stream params of 
+ *                                      all the networks of the first network group.
+ * @param[in] quantized                 Whether the data fed into the chip is already quantized. True means
+ *                                      the data is already quantized. False means it's HailoRT's responsibility
+ *                                      to quantize (scale) the data.
+ * @param[in] format_type               The default format type for all input virtual streams.
+ * @param[out] input_params             List of params for input virtual streams.
+ * @param[inout] input_params_count     On input: Amount of @a input_params array.
+ *                                      On output: Will be filled with the detected amount of input vstreams on the @a network or @a network_group.
+ * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns a ::hailo_status error.
+ */
+HAILORTAPI hailo_status hailo_hef_make_input_vstream_params(hailo_hef hef, const char *name, 
+    bool quantized, hailo_format_type_t format_type, 
+    hailo_input_vstream_params_by_name_t *input_params, size_t *input_params_count);
+
+/**
+ * Creates output virtual stream params linked to a network or a network group.
+ *
+ * @param[in] hef                       A @a hailo_hef object that contains the information.
+ * @param[in] name                      The name of the network group or network which contains the output virtual streams. 
+ *                                      In case network group name is given, the function returns output virtual stream params 
+ *                                      of all the networks of the given network group.
+ *                                      In case network name is given (provided by @a hailo_hef_get_network_infos), 
+ *                                      the function returns output virtual stream params of the given network.
+ *                                      If NULL is passed, the function returns the output virtual stream params of 
+ *                                      all the networks of the first network group.
+ * @param[in] quantized                 Whether the data returned from the device should be quantized. True
+ *                                      means that the data returned to the user is still quantized. False
+ *                                      means it's HailoRT's responsibility to de-quantize (rescale) the data.
+ * @param[in] format_type               The default format type for all output virtual streams.
+ * @param[out] output_params            List of params for output virtual streams.
+ * @param[inout] output_params_count    On input: Amount of @a output_params array.
+ *                                      On output: Will be filled with the detected amount of output vstreams on the @a network or @a network_group.
+ * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns a ::hailo_status error.
+ */
+HAILORTAPI hailo_status hailo_hef_make_output_vstream_params(hailo_hef hef, const char *name, 
+    bool quantized, hailo_format_type_t format_type, 
+    hailo_output_vstream_params_by_name_t *output_params, size_t *output_params_count);
+
+/**
+ * Creates input virtual stream params for a given network_group.
+ *
+ * @param[in]  network_group            Network group that owns the streams.
+ * @param[in]  quantized                Whether the data fed into the chip is already quantized. True means
+ *                                      the data is already quantized. False means it's HailoRT's responsibility
+ *                                      to quantize (scale) the data.
+ * @param[in]  format_type              The default format type for all input virtual streams.
+ * @param[out] input_params             List of params for input virtual streams.
+ * @param[inout] input_params_count     On input: Amount of @a input_params array.
+ *                                      On output: Will be filled with the detected amount of input vstreams on the @a network_group.
+ * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns a ::hailo_status error.
+ */
+HAILORTAPI hailo_status hailo_make_input_vstream_params(hailo_configured_network_group network_group, bool quantized,
+    hailo_format_type_t format_type, hailo_input_vstream_params_by_name_t *input_params, size_t *input_params_count);
+
+/**
+ * Creates output virtual stream params for given network_group.
+ *
+ * @param[in]  network_group            Network group that owns the streams.
+ * @param[in]  quantized                Whether the data returned from the device should be quantized. True
+ *                                      means that the data returned to the user is still quantized. False
+ *                                      means it's HailoRT's responsibility to de-quantize (rescale) the data.
+ * @param[in]  format_type              The default format type for all output virtual streams.
+ * @param[out] output_params            List of params for output virtual streams.
+ * @param[inout] output_params_count    On input: Amount of @a output_params array.
+ *                                      On output: Will be filled with the detected amount of output vstreams on the @a network_group.
+ * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns a ::hailo_status error.
+ */
+HAILORTAPI hailo_status hailo_make_output_vstream_params(hailo_configured_network_group network_group, bool quantized,
+    hailo_format_type_t format_type, hailo_output_vstream_params_by_name_t *output_params,
+    size_t *output_params_count);
+
+/**
+ * Gets output virtual stream groups for given network_group. The groups are splitted with respect to their low-level streams.
+ *
+ * @param[in]  network_group                   Network group that owns the streams.
+ * @param[out] output_name_by_group            List of params for output virtual streams.
+ * @param[inout] output_name_by_group_count    On input: Amount of @a output_name_by_group array.
+ *                                             On output: Will be filled with the detected amount of output vstreams on the @a network_group.
+ * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns a ::hailo_status error.
+ */
+HAILORTAPI hailo_status hailo_get_output_vstream_groups(hailo_configured_network_group network_group,
+    hailo_output_vstream_name_by_group_t *output_name_by_group, size_t *output_name_by_group_count);
+
+/**
+ * Creates input virtual streams.
+ *
+ * @param[in]  configured_network_group  Network group that owns the streams.
+ * @param[in]  inputs_params             List of input virtual stream params to create input virtual streams from.
+ * @param[in]  inputs_count              How many members in @a input_params.
+ * @param[out] input_vstreams            List of input virtual streams. 
+ * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns a ::hailo_status error.
+ * @note To release input virtual streams, call the ::hailo_release_input_vstreams function with the returned @a input_vstreams and @a inputs_count.
+ */
+HAILORTAPI hailo_status hailo_create_input_vstreams(hailo_configured_network_group configured_network_group,
+    const hailo_input_vstream_params_by_name_t *inputs_params, size_t inputs_count, hailo_input_vstream *input_vstreams);
+
+/**
+ * Creates output virtual streams.
+ *
+ * @param[in]  configured_network_group   Network group that owns the streams.
+ * @param[in]  outputs_params             List of output virtual stream params to create output virtual streams from.
+ * @param[in]  outputs_count              How many members in @a outputs_params.
+ * @param[out] output_vstreams            List of output virtual streams. 
+ * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns a ::hailo_status error.
+ * @note If not creating all output vstreams together, one should make sure all vstreams from the same group are created together. See ::hailo_get_output_vstream_groups
+ * @note To release output virtual streams, call the ::hailo_release_output_vstreams function with the returned @a output_vstreams and @a outputs_count.
+ */
+HAILORTAPI hailo_status hailo_create_output_vstreams(hailo_configured_network_group configured_network_group,
+    const hailo_output_vstream_params_by_name_t *outputs_params, size_t outputs_count, hailo_output_vstream *output_vstreams);
+
+/**
+ * Gets the size of a virtual stream's frame on the host side in bytes
+ * (the size could be affected by the format type - for example using UINT16, or by the data not being quantized yet)
+ *
+ * @param[in]  input_vstream    A ::hailo_input_vstream object.
+ * @param[out] frame_size       The size of the frame on the host side in bytes.
+ * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns a ::hailo_status error.
+ */
+HAILORTAPI hailo_status hailo_get_input_vstream_frame_size(hailo_input_vstream input_vstream, size_t *frame_size);
+
+/**
+ * Gets the ::hailo_vstream_info_t struct for the given vstream.
+ *
+ * @param[in]  input_vstream    A ::hailo_input_vstream object.
+ * @param[out] vstream_info     Will be filled with ::hailo_vstream_info_t.
+ * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns a ::hailo_status error.
+ */
+HAILORTAPI hailo_status hailo_get_input_vstream_info(hailo_input_vstream input_vstream, hailo_vstream_info_t *vstream_info);
+
+/**
+ * Gets the user buffer format struct for the given vstream.
+ *
+ * @param[in]  input_vstream        A ::hailo_input_vstream object.
+ * @param[out] user_buffer_format   Will be filled with ::hailo_format_t.
+ * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns a ::hailo_status error.
+ */
+HAILORTAPI hailo_status hailo_get_input_vstream_user_format(hailo_input_vstream input_vstream, hailo_format_t *user_buffer_format);
+
+/**
+ * Gets the size of a virtual stream's frame on the host side in bytes
+ * (the size could be affected by the format type - for example using UINT16, or by the data not being quantized yet)
+ *
+ * @param[in]  output_vstream   A ::hailo_output_vstream object.
+ * @param[out] frame_size       The size of the frame on the host side in bytes.
+ * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns a ::hailo_status error.
+ */
+HAILORTAPI hailo_status hailo_get_output_vstream_frame_size(hailo_output_vstream output_vstream, size_t *frame_size);
+
+/**
+ * Gets the ::hailo_vstream_info_t struct for the given vstream.
+ *
+ * @param[in]  output_vstream    A ::hailo_output_vstream object.
+ * @param[out] vstream_info     Will be filled with ::hailo_vstream_info_t.
+ * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns a ::hailo_status error.
+ */
+HAILORTAPI hailo_status hailo_get_output_vstream_info(hailo_output_vstream output_vstream, hailo_vstream_info_t *vstream_info);
+
+/**
+ * Gets the user buffer format struct for the given vstream.
+ *
+ * @param[in]  output_vstream       A ::hailo_output_vstream object.
+ * @param[out] user_buffer_format   Will be filled with ::hailo_format_t.
+ * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns a ::hailo_status error.
+ */
+HAILORTAPI hailo_status hailo_get_output_vstream_user_format(hailo_output_vstream output_vstream, hailo_format_t *user_buffer_format);
+
+/**
+ * Gets the size of a virtual stream's frame in bytes
+ * (the size could be affected by the format type - for example using UINT16, or by the data not being quantized yet)
+ *
+ * @param[in]  vstream_info          A ::hailo_vstream_info_t object.
+ * @param[in]  user_buffer_format    A ::hailo_format_t object.
+ * @param[out] frame_size            The size of the frame on the host side in bytes.
+ * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns a ::hailo_status error.
+ */
+HAILORTAPI hailo_status hailo_get_vstream_frame_size(hailo_vstream_info_t *vstream_info, hailo_format_t *user_buffer_format, size_t *frame_size);
+
+/**
+ * Writes buffer to hailo device via input virtual stream @a input_vstream.
+ *
+ * @param[in] input_vstream    A ::hailo_input_vstream object.
+ * @param[in] buffer           A pointer to a buffer to be sent. The buffer format comes from the vstream's \e format
+ *                             (Can be obtained using ::hailo_get_input_vstream_user_format) and the shape comes from
+ *                             \e shape inside ::hailo_vstream_info_t (Can be obtained using ::hailo_get_input_vstream_info).
+ * @param[in] buffer_size      @a buffer buffer size in bytes. The size is expected to be the size returned from
+ *                             ::hailo_get_input_vstream_frame_size.
+ * 
+ * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns a ::hailo_status error.
+ */
+HAILORTAPI hailo_status hailo_vstream_write_raw_buffer(hailo_input_vstream input_vstream, const void *buffer, size_t buffer_size);
+
+/**
+ * Blocks until the pipeline buffers of @a input_vstream are flushed.
+ * 
+ * @param[in] input_vstream                 The input vstream to flush.
+ * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns a ::hailo_status error.
+ */
+HAILORTAPI hailo_status hailo_flush_input_vstream(hailo_input_vstream input_vstream);
+
+/**
+ * Reads data from hailo device via @a output_vstream into @a dst.
+ *
+ * @param[in] output_vstream   A ::hailo_output_vstream object.
+ * @param[in] buffer           A pointer to the received buffer. The buffer format comes from the vstream's \e format
+ *                             (Can be obtained using ::hailo_get_output_vstream_user_format) and the shape comes from
+ *                             \e shape or \e nms_shape inside ::hailo_vstream_info_t (Can be obtained
+ *                             using ::hailo_get_output_vstream_info).
+ * @param[in] buffer_size      @a dst buffer size in bytes.The size is expected to be the size returned from
+ *                             ::hailo_get_output_vstream_frame_size.
+ * 
+ * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns a ::hailo_status error.
+ */
+HAILORTAPI hailo_status hailo_vstream_read_raw_buffer(hailo_output_vstream output_vstream, void *buffer, size_t buffer_size);
+
+/**
+ * Release input virtual streams.
+ * 
+ * @param[in] input_vstreams   List of input virtual streams to be released.
+ * @param[in] inputs_count     The amount of elements in @a input_params.
+ * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns a ::hailo_status error.
+ */
+HAILORTAPI hailo_status hailo_release_input_vstreams(const hailo_input_vstream *input_vstreams, size_t inputs_count);
+
+/**
+ * Release output virtual streams.
+ * 
+ * @param[in] output_vstreams  List of output virtual streams to be released.
+ * @param[in] outputs_count    The amount of elements in @a output_vstreams.
+ * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns a ::hailo_status error.
+ */
+HAILORTAPI hailo_status hailo_release_output_vstreams(const hailo_output_vstream *output_vstreams, size_t outputs_count);
+
+/**
+ * Clears the pipeline buffers of each vstream in @a input_vstreams.
+ * 
+ * @param[in] input_vstreams                List of input virtual streams to be cleared.
+ * @param[in] inputs_count                  The amount of elements in @a input_params.
+ * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns a ::hailo_status error.
+ */
+HAILORTAPI hailo_status hailo_clear_input_vstreams(const hailo_input_vstream *input_vstreams, size_t inputs_count);
+
+/**
+ * Clears the pipeline buffers of each vstream in @a output_vstreams.
+ * 
+ * @param[in] output_vstreams               List of output virtual streams to be cleared.
+ * @param[in] outputs_count                 The amount of elements in @a output_vstreams.
+ * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns a ::hailo_status error.
+ * @note If not all output vstreams from the same group are passed together, it will cause an <b> undefined behavior </b>.
+ *       See ::hailo_get_output_vstream_groups, to get the output vstreams' groups.
+ */
+HAILORTAPI hailo_status hailo_clear_output_vstreams(const hailo_output_vstream *output_vstreams, size_t outputs_count);
+
+/**
+ * Run simple inference using vstreams pipelines.
+ *
+ * @param[in] configured_network_group      A ::hailo_configured_network_group to run the inference on.
+ * @param[in] inputs_params                 Array of input virtual stream params, indicates @a input_buffers format.
+ * @param[in] input_buffers                 Array of ::hailo_stream_raw_buffer_by_name_t. Ths input dataset of the inference.
+ * @param[in] inputs_count                  The amount of elements in @a inputs_params and @a input_buffers.
+ * @param[in] outputs_params                Array of output virtual stream params, indicates @a output_buffers format.
+ * @param[out] output_buffers               Array of ::hailo_stream_raw_buffer_by_name_t. Ths results of the inference.
+ * @param[in] outputs_count                 The amount of elements in @a outputs_params and @a output_buffers.
+ * @param[in] frames_count                  The amount of inferred frames.
+ * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns a ::hailo_status error.
+ * @note @a configured_network_group should be activated before calling this function.
+ * @note the size of each element in @a input_buffers and @a output_buffers should match the product of @a frames_count
+ *       and the frame size of the matching ::hailo_input_vstream / ::hailo_output_vstream.     
+ */
+HAILORTAPI hailo_status hailo_infer(hailo_configured_network_group configured_network_group,
+    hailo_input_vstream_params_by_name_t *inputs_params, hailo_stream_raw_buffer_by_name_t *input_buffers, size_t inputs_count,
+    hailo_output_vstream_params_by_name_t *outputs_params, hailo_stream_raw_buffer_by_name_t *output_buffers, size_t outputs_count,
+    size_t frames_count);
+
+
+/** @} */ // end of group_vstream_functions
+
+/** @defgroup multi_network_functions multi network functions
+ *  @{
+ */
+
+/**
+ * Gets all network infos under a given network group
+ *
+ * @param[in]  hef                    A ::hailo_hef object that contains the information.
+ * @param[in]  network_group_name     Name of the network_group to get the network infos by. If NULL is passed,
+ *                                    the first network_group in the HEF will be addressed.
+ * @param[out] networks_infos         A pointer to a buffer of ::hailo_network_info_t that receives the informations.
+ * @param[inout] number_of_networks   As input - the maximum amount of entries in @a hailo_network_info_t array.
+ *                                    As output - the actual amount of entries written if the function returns with ::HAILO_SUCCESS
+ *                                    and the amount of entries needed if the function returns ::HAILO_INSUFFICIENT_BUFFER.
+ * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, if the buffer is
+ *                                    insufficient to hold the information a ::HAILO_INSUFFICIENT_BUFFER would be
+ *                                    returned. In any other case, returns a ::hailo_status error.
+ */
+HAILORTAPI hailo_status hailo_hef_get_network_infos(hailo_hef hef, const char *network_group_name,
+    hailo_network_info_t *networks_infos, size_t *number_of_networks);
+
+/**
+ * Gets all network infos under a given network group
+ *
+ * @param[in]  network_group          A ::hailo_configured_network_group object to get the network infos from.
+ * @param[out] networks_infos         A pointer to a buffer of ::hailo_network_info_t that receives the informations.
+ * @param[inout] number_of_networks   As input - the maximum amount of entries in @a hailo_network_info_t array.
+ *                                    As output - the actual amount of entries written if the function returns with ::HAILO_SUCCESS
+ *                                    and the amount of entries needed if the function returns ::HAILO_INSUFFICIENT_BUFFER.
+ * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, if the buffer is
+ *                                    insufficient to hold the information a ::HAILO_INSUFFICIENT_BUFFER would be
+ *                                    returned. In any other case, returns a ::hailo_status error.
+ */
+HAILORTAPI hailo_status hailo_get_network_infos(hailo_configured_network_group network_group,
+    hailo_network_info_t *networks_infos, size_t *number_of_networks);
+
+/** @} */ // end of multi_network_functions
+
+/** @defgroup group_deprecated_functions_and_defines Deprecated functions and defines
+ *  @{
+ */
+
+/**
+ * Returns the network latency (only available if latency measurement was enabled).
+ *
+ * @param[in]  configured_network_group     NetworkGroup to get the latency measurement from.
+ * @param[out] result                       Output latency result.
+ * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns a ::hailo_status error.
+ * @note This function is deprecated. One should use 'hailo_get_latency_measurement()'
+ */
+HAILORTAPI hailo_status hailo_get_latency_measurement_from_network_group(hailo_configured_network_group configured_network_group,
+    hailo_latency_measurement_result_t *result)
+    DEPRECATED("'hailo_get_latency_measurement_from_network_group' is deprecated. One should use 'hailo_get_latency_measurement()'.");
+
+
+typedef hailo_input_transform_context hailo_input_transformer DEPRECATED("hailo_input_transformer is deprecated. One should use hailo_input_transform_context");
+typedef hailo_output_transform_context hailo_output_transformer DEPRECATED("hailo_output_transformer is deprecated. One should use hailo_output_transform_context");
+
+/**
+ * Creates an input transformer object. Allocates all necessary buffers used for the transformation (pre-process).
+ * 
+ * @param[in]     stream_info - A ::hailo_stream_info_t object
+ * @param[in]     transform_params - A ::hailo_transform_params_t user transformation parameters.
+ * @param[out]    transformer - A ::hailo_input_transform_context
+ * 
+ * @return Upon success, returns @a HAILO_SUCCESS. Otherwise, returns an @a hailo_status error.
+ * 
+ * @note To release the transformer, call the ::hailo_release_input_transformer function
+ *      with the returned ::hailo_input_transform_context.
+ * 
+ */
+HAILORTAPI hailo_status hailo_create_input_transformer(const hailo_stream_info_t *stream_info,
+    const hailo_transform_params_t *transform_params, hailo_input_transform_context *transformer)
+    DEPRECATED("hailo_create_input_transformer is deprecated. One should use hailo_create_input_transform_context");
+
+/**
+ * Releases a transformer object including all allocated buffers.
+ * 
+ * @param[in]    transformer - A ::hailo_input_transform_context object.
+ * 
+ * @return Upon success, returns @a HAILO_SUCCESS. Otherwise, returns an @a hailo_status error.
+ */
+HAILORTAPI hailo_status hailo_release_input_transformer(hailo_input_transform_context transformer)
+    DEPRECATED("hailo_release_input_transformer is deprecated. One should use hailo_release_input_transform_context");
+
+/**
+ * Transforms an input frame pointed to by @a src directly to the buffer pointed to by @a dst.
+ * 
+ * @param[in]  transformer          A ::hailo_input_transform_context.
+ * @param[in]  src                  A pointer to a buffer to be transformed.
+ * @param[in]  src_size             The number of bytes to transform. This number must be equal to the input host_frame_size,
+ *                                  and less than or equal to the size of @a src buffer.
+ * @param[out] dst                  A pointer to a buffer that receives the transformed data.
+ * @param[in]  dst_size             The number of bytes in @a dst buffer. This number must be equal to the input hw_frame_size,
+ *                                  and less than or equal to the size of @a dst buffer.
+ * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns a ::hailo_status error.
+ * @warning The buffers must not overlap.
+ */
+HAILORTAPI hailo_status hailo_transform_frame_by_input_transformer(hailo_input_transform_context transformer,
+    const void *src, size_t src_size, void *dst, size_t dst_size)
+    DEPRECATED("hailo_transform_frame_by_input_transformer is deprecated. One should use hailo_transform_frame_by_input_transform_context");
+
+/**
+ * Creates an output transformer object. Allocates all necessary buffers used for the transformation (post-process).
+ * 
+ * @param[in]     stream_info - A ::hailo_stream_info_t object
+ * @param[in]     transform_params - A ::hailo_transform_params_t user transformation parameters.
+ * @param[out]    transformer - A ::hailo_output_transform_context
+ * 
+ * @return Upon success, returns @a HAILO_SUCCESS. Otherwise, returns an @a hailo_status error.
+ * 
+ * @note To release the transform_context, call the ::hailo_release_output_transform_context function
+ *      with the returned ::hailo_output_transform_context.
+ * 
+ */
+HAILORTAPI hailo_status hailo_create_output_transformer(const hailo_stream_info_t *stream_info,
+    const hailo_transform_params_t *transform_params, hailo_output_transform_context *transformer)
+    DEPRECATED("hailo_create_output_transformer is deprecated. One should use hailo_create_output_transform_context");
+
+/**
+ * Releases a transformer object including all allocated buffers.
+ * 
+ * @param[in]    transformer - A ::hailo_output_transform_context object.
+ * 
+ * @return Upon success, returns @a HAILO_SUCCESS. Otherwise, returns an @a hailo_status error.
+ */
+HAILORTAPI hailo_status hailo_release_output_transformer(hailo_output_transform_context transformer)
+    DEPRECATED("hailo_release_output_transformer is deprecated. One should use hailo_release_output_transform_context");
+
+/**
+ * Transforms an output frame pointed to by @a src directly to the buffer pointed to by @a dst.
+ * 
+ * @param[in]  transformer          A ::hailo_output_transform_context.
+ * @param[in]  src                  A pointer to a buffer to be transformed.
+ * @param[in]  src_size             The number of bytes to transform. This number must be equal to the output hw_frame_size,
+ *                                  and less than or equal to the size of @a src buffer.
+ * @param[out] dst                  A pointer to a buffer that receives the transformed data.
+ * @param[in]  dst_size             The number of bytes in @a dst buffer. This number must be equal to the output host_frame_size,
+ *                                  and less than or equal to the size of @a dst buffer.
+ * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns a ::hailo_status error.
+ * @warning The buffers must not overlap.
+ */
+HAILORTAPI hailo_status hailo_transform_frame_by_output_transformer(hailo_output_transform_context transformer,
+    const void *src, size_t src_size, void *dst, size_t dst_size)
+    DEPRECATED("hailo_transform_frame_by_output_transformer is deprecated. One should use hailo_transform_frame_by_output_transform_context");;
+
+/** @} */ // end of group_deprecated_functions_and_defines
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _HAILORT_H_ */
diff --git a/hailort/libhailort/include/hailo/hailort.hpp b/hailort/libhailort/include/hailo/hailort.hpp
new file mode 100644 (file)
index 0000000..6cf365b
--- /dev/null
@@ -0,0 +1,32 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file hailort.hpp
+ * @brief C++ API for HailoRT library.
+ *
+ * C++ API for Hailo runtime (HailoRT) library for neural network inference on Hailo devices.
+ **/
+
+#ifndef _HAILORT_HPP_
+#define _HAILORT_HPP_
+
+#include "hailo/hailort.h"
+#include "hailo/hef.hpp"
+#include "hailo/device.hpp"
+#include "hailo/vdevice.hpp"
+#include "hailo/network_group.hpp"
+#include "hailo/stream.hpp"
+#include "hailo/vstream.hpp"
+#include "hailo/inference_pipeline.hpp"
+#include "hailo/transform.hpp"
+#include "hailo/expected.hpp"
+#include "hailo/buffer.hpp"
+#include "hailo/event.hpp"
+#include "hailo/hailort_common.hpp"
+#include "hailo/runtime_statistics.hpp"
+#include "hailo/network_rate_calculator.hpp"
+#include "hailo/quantization.hpp"
+
+#endif /* _HAILORT_HPP_ */
diff --git a/hailort/libhailort/include/hailo/hailort_common.hpp b/hailort/libhailort/include/hailo/hailort_common.hpp
new file mode 100644 (file)
index 0000000..f161de1
--- /dev/null
@@ -0,0 +1,342 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file hailort_common.hpp
+ * @brief Common utility functions/macros that help manage hailort.h structures
+ **/
+
+#ifndef _HAILO_HAILORT_COMMON_HPP_
+#define _HAILO_HAILORT_COMMON_HPP_
+
+#include "hailo/hailort.h"
+#include "hailo/expected.hpp"
+#include <chrono>
+#include <string>
+
+namespace hailort
+{
+
+/*! Common utility functions and macros that help manage hailort.h structures */
+class HAILORTAPI HailoRTCommon final
+{
+public:
+    HailoRTCommon() = delete;
+    
+    static_assert(sizeof(hailo_bbox_t) / sizeof(uint16_t) == sizeof(hailo_bbox_float32_t) / sizeof(float32_t),
+        "Mismatch bbox params size");
+    static const uint32_t BBOX_PARAMS = sizeof(hailo_bbox_t) / sizeof(uint16_t);
+    static const uint32_t MAX_DEFUSED_LAYER_COUNT = 9;
+    static const size_t HW_DATA_ALIGNMENT = 8;
+    static const uint64_t NMS_DELIMITER = 0xFFFFFFFFFFFFFFFF;
+    static const uint32_t MUX_INFO_COUNT = 32;
+    static const uint32_t MAX_MUX_PREDECESSORS = 4;
+    static const uint16_t ETH_INPUT_BASE_PORT = 32401;
+    static const uint16_t ETH_OUTPUT_BASE_PORT = 32501;
+
+    /**
+     * Gets the NMS host shape size (number of elements) from NMS info.
+     *
+     * @param[in] nms_info             The NMS info to get shape size from.
+     * @return The host shape size (number of elements).
+     * @note The size in bytes can be calculated using 
+     *  get_nms_host_frame_size(const hailo_nms_info_t &nms_info, const hailo_format_t &format).
+     */
+    static constexpr uint32_t get_nms_host_shape_size(const hailo_nms_info_t &nms_info)
+    {
+        const uint32_t max_bboxes_per_class = nms_info.chunks_per_frame * nms_info.max_bboxes_per_class;
+        // Counter + bboxes
+        const uint32_t size_per_class = 1 + (BBOX_PARAMS * max_bboxes_per_class);
+        return size_per_class * nms_info.number_of_classes;
+    }
+
+    /**
+     * Gets the NMS host shape size (number of elements) from NMS shape.
+     *
+     * @param[in] nms_shape             The NMS shape to get size from.
+     * @return The host shape size (number of elements).
+     * @note The size in bytes can be calculated using 
+     *  get_nms_host_frame_size(const hailo_nms_shape_t &nms_shape, const hailo_format_t &format).
+     */
+    static constexpr uint32_t get_nms_host_shape_size(const hailo_nms_shape_t &nms_shape)
+    {
+        const uint32_t max_bboxes_per_class = nms_shape.max_bboxes_per_class;
+        // Counter + bboxes
+        const uint32_t size_per_class = 1 + (BBOX_PARAMS * max_bboxes_per_class);
+        return size_per_class * nms_shape.number_of_classes;
+    }
+
+    /**
+     * Gets the shape size.
+     *
+     * @param[in] shape             The shape to get size from.
+     * @return The shape size.
+     */
+    static constexpr uint32_t get_shape_size(const hailo_3d_image_shape_t &shape)
+    {
+        return shape.height * shape.width * shape.features; 
+    }
+
+    /**
+     * Gets the size of each element in bytes from buffer's format type.
+     *
+     * @param[in] type             A ::hailo_format_type_t object.
+     * @return The data bytes.
+     */
+    static constexpr uint8_t get_data_bytes(hailo_format_type_t type)
+    {
+        if (type == HAILO_FORMAT_TYPE_FLOAT32) {
+            return 4;
+        } else if (type == HAILO_FORMAT_TYPE_UINT16) {
+            return 2;
+        } else if (type == HAILO_FORMAT_TYPE_UINT8) {
+            return 1;
+        }
+
+        return 1;
+    }
+
+    /**
+     * Gets the format type of a stream by the hw data bytes parameter.
+     *
+     * @param[in] hw_data_bytes             The stream's info's hw_data_bytes parameter.
+     * @return Upon success, returns Expected of ::hailo_format_type_t, The format type that the hw_data_type correlates to.
+     *         Otherwise, returns Unexpected of ::hailo_status error.
+     */
+    static Expected<hailo_format_type_t> get_format_type(uint32_t hw_data_bytes)
+    {
+        switch (hw_data_bytes) {
+            case 1:
+                return HAILO_FORMAT_TYPE_UINT8;
+            case 2:
+                return HAILO_FORMAT_TYPE_UINT16;
+            default:
+                return make_unexpected(HAILO_INVALID_ARGUMENT);
+        }
+    }
+
+    /**
+     * Gets a string reprenestation of the given format type.
+     *
+     * @param[in] type             A ::hailo_format_type_t object.
+     * @return The string representation of the format type.
+     */
+    static std::string get_format_type_str(const hailo_format_type_t &type)
+    {
+        switch (type)
+        {
+        case HAILO_FORMAT_TYPE_UINT8:
+            return "UINT8";
+        case HAILO_FORMAT_TYPE_UINT16:
+            return "UINT16";
+        case HAILO_FORMAT_TYPE_FLOAT32:
+            return "FLOAT32";
+        default:
+            return "Nan";
+        }
+    }
+
+    /**
+     * Gets a string reprenestation of the given format order.
+     *
+     * @param[in] order             A ::hailo_format_order_t object.
+     * @return The string representation of the format order.
+     */
+    static std::string get_format_order_str(const hailo_format_order_t &order)
+    {
+        switch (order)
+        {
+        case HAILO_FORMAT_ORDER_NHWC:
+            return "NHWC";
+        case HAILO_FORMAT_ORDER_NHCW:
+            return "NHCW";
+        case HAILO_FORMAT_ORDER_FCR:
+            return "FCR";
+        case HAILO_FORMAT_ORDER_F8CR:
+            return "F8CR";
+        case HAILO_FORMAT_ORDER_NHW:
+            return "NHW";
+        case HAILO_FORMAT_ORDER_NC:
+            return "NC";
+        case HAILO_FORMAT_ORDER_BAYER_RGB:
+            return "BAYER RGB";
+        case HAILO_FORMAT_ORDER_12_BIT_BAYER_RGB:
+            return "12 BIT BAYER RGB";
+        case HAILO_FORMAT_ORDER_HAILO_NMS:
+            return "HAILO NMS";
+        case HAILO_FORMAT_ORDER_RGB888:
+            return "RGB 888";
+        case HAILO_FORMAT_ORDER_NCHW:
+            return "NCHW";
+        case HAILO_FORMAT_ORDER_YUY2:
+            return "YUY2";
+        default:
+            return "Nan";
+        }
+    }
+
+    /**
+     * Gets the size of each element in bytes from buffer's format.
+     *
+     * @param[in] format             A ::hailo_format_t object.
+     * @return The format's data bytes.
+     */
+    static constexpr uint8_t get_format_data_bytes(const hailo_format_t &format)
+    {
+        return get_data_bytes(format.type);
+    }
+
+    /**
+     * Gets NMS host frame size in bytes by nms info and buffer format.
+     *
+     * @param[in] nms_info           A ::hailo_nms_info_t object.
+     * @param[in] format             A ::hailo_format_t object.
+     * @return The NMS host frame size in bytes.
+     */
+    static constexpr uint32_t get_nms_host_frame_size(const hailo_nms_info_t &nms_info, const hailo_format_t &format)
+    {
+        return get_nms_host_shape_size(nms_info) * get_format_data_bytes(format);
+    }
+
+    /**
+     * Gets NMS host frame size in bytes by nms shape and buffer format.
+     *
+     * @param[in] nms_shape         A ::hailo_nms_shape_t object.
+     * @param[in] format            A ::hailo_format_t object.
+     * @return The NMS host frame size in bytes.
+     */
+    static constexpr uint32_t get_nms_host_frame_size(const hailo_nms_shape_t &nms_shape, const hailo_format_t &format)
+    {
+        return get_nms_host_shape_size(nms_shape) * get_format_data_bytes(format);
+    }
+
+    /**
+     * Gets NMS hw frame size in bytes by nms info.
+     *
+     * @param[in] nms_info          A ::hailo_nms_info_t object.
+     * @return The NMS hw frame size in bytes.
+     */
+    static constexpr uint32_t get_nms_hw_frame_size(const hailo_nms_info_t &nms_info)
+    {
+        const uint32_t size_per_class = static_cast<uint32_t>(sizeof(nms_bbox_counter_t)) +
+            nms_info.bbox_size * nms_info.max_bboxes_per_class;
+        const uint32_t size_per_chunk = nms_info.number_of_classes * size_per_class;
+        // 1 delimiter for an entire frame (since we are reading delimiters directly into the buffer and replacing them)
+        return nms_info.bbox_size + (nms_info.chunks_per_frame * size_per_chunk);
+    }
+
+    /**
+     * Gets frame size in bytes by image shape and format.
+     *
+     * @param[in] shape         A ::hailo_3d_image_shape_t object.
+     * @param[in] format        A ::hailo_format_t object.
+     * @return The frame's size in bytes.
+     */
+    static constexpr uint32_t get_frame_size(const hailo_3d_image_shape_t &shape, const hailo_format_t &format)
+    {
+        return get_shape_size(shape) * get_format_data_bytes(format);
+    }
+
+    /**
+     * Gets frame size in bytes by stream info and transformation params.
+     *
+     * @param[in] stream_info         A ::hailo_stream_info_t object.
+     * @param[in] trans_params        A ::hailo_transform_params_t object.
+     * @return The frame's size in bytes.
+     */
+    static constexpr uint32_t get_frame_size(const hailo_stream_info_t &stream_info,
+        hailo_transform_params_t trans_params)
+    {
+        if (HAILO_FORMAT_TYPE_AUTO == trans_params.user_buffer_format.type) {
+            trans_params.user_buffer_format.type = stream_info.format.type;
+        }
+
+        if (HAILO_FORMAT_ORDER_HAILO_NMS == stream_info.format.order) {
+            return get_nms_host_frame_size(stream_info.nms_info, trans_params.user_buffer_format);
+        } else {
+            auto shape = (HAILO_STREAM_NO_TRANSFORM == trans_params.transform_mode) ? stream_info.hw_shape :
+                stream_info.shape;
+            return get_frame_size(shape, trans_params.user_buffer_format);
+        }
+    }
+
+    /**
+     * Gets frame size in bytes by stream info and transformation params.
+     *
+     * @param[in] vstream_info         A ::hailo_vstream_info_t object.
+     * @param[in] format               A ::hailo_format_t object.
+     * @return The frame's size in bytes.
+     */
+    static constexpr uint32_t get_frame_size(const hailo_vstream_info_t &vstream_info,
+        hailo_format_t format)
+    {
+        if (HAILO_FORMAT_TYPE_AUTO == format.type) {
+            format.type = vstream_info.format.type;
+        }
+
+        if (HAILO_FORMAT_ORDER_HAILO_NMS == vstream_info.format.order) {
+            return get_nms_host_frame_size(vstream_info.nms_shape, format);
+        } else {
+            return get_frame_size(vstream_info.shape, format);
+        }
+    }
+};
+
+#ifndef HAILO_EMULATOR
+constexpr std::chrono::milliseconds DEFAULT_TRANSFER_TIMEOUT(std::chrono::seconds(10));
+#else /* ifndef HAILO_EMULATOR */
+constexpr std::chrono::milliseconds DEFAULT_TRANSFER_TIMEOUT(std::chrono::seconds(5000));
+#endif /* ifndef HAILO_EMULATOR */
+
+constexpr std::chrono::milliseconds HAILO_INFINITE_TIMEOUT(std::chrono::milliseconds::max());
+
+inline hailo_latency_measurement_flags_t operator|(hailo_latency_measurement_flags_t a,
+    hailo_latency_measurement_flags_t b)
+{
+    return static_cast<hailo_latency_measurement_flags_t>(static_cast<int>(a) | static_cast<int>(b));
+}
+
+inline hailo_latency_measurement_flags_t& operator|=(hailo_latency_measurement_flags_t &a,
+    hailo_latency_measurement_flags_t b)
+{
+    a = a | b;
+    return a;
+}
+
+inline constexpr hailo_format_flags_t operator|(hailo_format_flags_t a, hailo_format_flags_t b)
+{
+    return static_cast<hailo_format_flags_t>(static_cast<int>(a) | static_cast<int>(b));
+}
+
+inline constexpr hailo_format_flags_t& operator|=(hailo_format_flags_t &a, hailo_format_flags_t b)
+{
+    a = a | b;
+    return a;
+}
+
+inline constexpr hailo_vstream_stats_flags_t operator|(hailo_vstream_stats_flags_t a, hailo_vstream_stats_flags_t b)
+{
+    return static_cast<hailo_vstream_stats_flags_t>(static_cast<int>(a) | static_cast<int>(b));
+}
+
+inline constexpr hailo_vstream_stats_flags_t& operator|=(hailo_vstream_stats_flags_t &a, hailo_vstream_stats_flags_t b)
+{
+    a = a | b;
+    return a;
+}
+
+inline constexpr hailo_pipeline_elem_stats_flags_t operator|(hailo_pipeline_elem_stats_flags_t a, hailo_pipeline_elem_stats_flags_t b)
+{
+    return static_cast<hailo_pipeline_elem_stats_flags_t>(static_cast<int>(a) | static_cast<int>(b));
+}
+
+inline constexpr hailo_pipeline_elem_stats_flags_t& operator|=(hailo_pipeline_elem_stats_flags_t &a, hailo_pipeline_elem_stats_flags_t b)
+{
+    a = a | b;
+    return a;
+}
+
+} /* namespace hailort */
+
+#endif /* _HAILO_HAILORT_COMMON_HPP_ */
diff --git a/hailort/libhailort/include/hailo/hef.hpp b/hailort/libhailort/include/hailo/hef.hpp
new file mode 100644 (file)
index 0000000..54b25ad
--- /dev/null
@@ -0,0 +1,450 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file hef.hpp
+ * @brief Hef parsing and configuration functions
+ **/
+
+#ifndef _HAILO_HEF_HPP_
+#define _HAILO_HEF_HPP_
+
+#include "hailo/hailort.h"
+#include "hailo/expected.hpp"
+#include "hailo/buffer.hpp"
+
+#include <vector>
+#include <memory>
+#include <map>
+
+namespace hailort
+{
+
+/*! Hailo configure parameters per network_group. Analogical to hailo_configure_network_group_params_t */
+struct ConfigureNetworkParams
+{
+    ConfigureNetworkParams() = default;
+    ConfigureNetworkParams(const hailo_configure_network_group_params_t &params) : batch_size(params.batch_size),
+        power_mode(params.power_mode), latency(params.latency)
+    {
+        for (size_t i = 0; i < params.stream_params_by_name_count; i++) {
+            stream_params_by_name.insert(std::make_pair(std::string(params.stream_params_by_name[i].name),
+                params.stream_params_by_name[i].stream_params));
+        }
+        for (size_t i = 0; i < params.network_params_by_name_count; i++) {
+            network_params_by_name.insert(std::make_pair(std::string(params.network_params_by_name[i].name),
+                params.network_params_by_name[i].network_params));
+        }
+    }
+
+    uint16_t batch_size;
+    hailo_power_mode_t power_mode;
+    hailo_latency_measurement_flags_t latency;
+    std::map<std::string, hailo_stream_parameters_t> stream_params_by_name;
+    std::map<std::string, hailo_network_parameters_t> network_params_by_name;
+};
+
+/** @addtogroup group_type_definitions */
+/*@{*/
+
+/** Represents a mapping of network group name to its' params */
+using NetworkGroupsParamsMap = std::map<std::string, ConfigureNetworkParams>;
+
+/*@}*/
+
+/*! HEF model that can be loaded to Hailo devices */
+class HAILORTAPI Hef final
+{
+public:
+
+    /**
+     * Creates an Hef from a file.
+     *
+     * @param[in] hef_path            The path of the Hef file.
+     * @return Upon success, returns Expected of Hef. Otherwise, returns Unexpected of ::hailo_status error.
+     *         
+     */
+    static Expected<Hef> create(const std::string &hef_path);
+
+    /**
+     * Creates an Hef from a buffer.
+     *
+     * @param[in] hef_buffer          A buffer that contains the Hef content.
+     * @return Upon success, returns Expected of Hef. Otherwise, returns Unexpected of ::hailo_status error.
+     */
+    static Expected<Hef> create(const MemoryView &hef_buffer);
+
+    /**
+     * Gets input streams informations.
+     *
+     * @param[in] name                    The name of the network or network_group which contains the input stream_infos.
+     *                                    In case network group name is given, the function returns the input stream infos 
+     *                                    of all the networks of the given network group.
+     *                                    In case network name is given (provided by @a get_network_infos), 
+     *                                    the function returns the input stream infos of the given network.
+     *                                    If NULL is passed, the function returns the input stream infos of 
+     *                                    all the networks of the first network group.
+     * @return Upon success, returns a vector of ::hailo_stream_info_t, containing each stream's informmation.
+     *         Otherwise, returns a ::hailo_status error.
+     */
+    Expected<std::vector<hailo_stream_info_t>> get_input_stream_infos(const std::string &name="");
+    
+    /**
+     * Gets output streams informations.
+     *
+     * @param[in] name                    The name of the network or network_group which contains the output stream_infos.
+     *                                    In case network group name is given, the function returns the output stream infos 
+     *                                    of all the networks of the given network group.
+     *                                    In case network name is given (provided by @a get_network_infos), 
+     *                                    the function returns the output stream infos of the given network.
+     *                                    If NULL is passed, the function returns the output stream infos of 
+     *                                    all the networks of the first network group.
+     * @return Upon success, returns a vector of ::hailo_stream_info_t, containing each stream's informmation.
+     *         Otherwise, returns a ::hailo_status error.
+     */
+    Expected<std::vector<hailo_stream_info_t>> get_output_stream_infos(const std::string &name="");
+
+    /**
+     * Gets all streams informations.
+     *
+     * @param[in] name                    The name of the network or network_group which contains the stream_infos.
+     *                                    In case network group name is given, the function returns all stream infos 
+     *                                    of all the networks of the given network group.
+     *                                    In case network name is given (provided by @a get_network_infos), 
+     *                                    the function returns all stream infos of the given network.
+     *                                    If NULL is passed, the function returns all the stream infos of 
+     *                                    all the networks of the first network group.
+     * @return Upon success, returns Expected of a vector of ::hailo_stream_info_t, containing each stream's informmation.
+     *         Otherwise, returns Unexpected of ::hailo_status error.
+     */
+    Expected<std::vector<hailo_stream_info_t>> get_all_stream_infos(const std::string &name="");
+
+    /**
+     * Gets stream's information from it's name.
+     *
+     * @param[in] stream_name         The name of the stream as presented in the Hef.
+     * @param[in] stream_direction    Indicates the stream direction.
+     * @param[in] net_group_name      The name of the network_group which contains the stream's information.
+     *                                If not passed, the first network_group in the Hef will be addressed.
+     * @return Upon success, returns Expected of ::hailo_stream_info_t. Otherwise, returns Unexpected of ::hailo_status error.
+     * 
+     */
+    Expected<hailo_stream_info_t> get_stream_info_by_name(const std::string &stream_name,
+        hailo_stream_direction_t stream_direction, const std::string &net_group_name="");
+
+    /**
+     * Gets input virtual streams infos.
+     *
+     * @param[in] name                    The name of the network or network_group which contains the input virtaul stream_infos.
+     *                                    In case network group name is given, the function returns the input virtual stream infos 
+     *                                    of all the networks of the given network group.
+     *                                    In case network name is given (provided by @a get_network_infos), 
+     *                                    the function returns the input virtual stream infos of the given network.
+     *                                    If NULL is passed, the function returns the input virtual stream infos of 
+     *                                    all the networks of the first network group.
+     * @return Upon success, returns Expected of a vector of ::hailo_vstream_info_t.
+     *         Otherwise, returns Unexpected of ::hailo_status error.
+     */
+    Expected<std::vector<hailo_vstream_info_t>> get_input_vstream_infos(const std::string &name="");
+
+    /**
+     * Gets output virtual streams infos.
+     *
+     * @param[in] name                    The name of the network or network_group which contains the output virtual stream_infos.
+     *                                    In case network group name is given, the function returns the output virtual stream infos 
+     *                                    of all the networks of the given network group.
+     *                                    In case network name is given (provided by @a get_network_infos), 
+     *                                    the function returns the output virtual stream infos of the given network.
+     *                                    If NULL is passed, the function returns the output virtual stream infos of 
+     *                                    all the networks of the first network group.
+     * 
+     * @return Upon success, returns Expected of a vector of ::hailo_vstream_info_t.
+     *         Otherwise, returns Unexpected of ::hailo_status error.
+     */
+    Expected<std::vector<hailo_vstream_info_t>> get_output_vstream_infos(const std::string &name="");
+
+    /**
+     * Gets all virtual streams infos.
+     *
+     * @param[in] name                    The name of the network or network_group which contains the virtual stream_infos.
+     *                                    In case network group name is given, the function returns all virtual stream infos 
+     *                                    of all the networks of the given network group.
+     *                                    In case network name is given (provided by @a get_network_infos), 
+     *                                    the function returns all virtual stream infos of the given network.
+     *                                    If NULL is passed, the function returns all the virtual stream infos of 
+     *                                    all the networks of the first network group.
+     * 
+     * @return Upon success, returns Expected of a vector of ::hailo_vstream_info_t.
+     *         Otherwise, returns Unexpected of ::hailo_status error.
+     */
+    Expected<std::vector<hailo_vstream_info_t>> get_all_vstream_infos(const std::string &name="");
+
+    /**
+     * Gets sorted output vstreams names.
+     *
+     * @param[in] net_group_name      The name of the network_group which contains the streams information.
+     *                                If not passed, the first network_group in the Hef will be addressed.
+     * @return Upon success, returns Expected of a sorted vector of output vstreams names.
+     *         Otherwise, returns Unexpected of ::hailo_status error.
+     */
+    Expected<std::vector<std::string>> get_sorted_output_names(const std::string &net_group_name="");
+
+    /**
+     * Gets the number of low-level input streams.
+     *
+     * @param[in] net_group_name      The name of the network_group which contains the streams information.
+     *                                If not passed, the first network_group in the Hef will be addressed.
+     * @return Upon success, returns Expected containing the number of low-level input streams.
+     *         Otherwise, returns Unexpected of ::hailo_status error.
+     */
+    Expected<size_t> get_number_of_input_streams(const std::string &net_group_name="");
+
+    /**
+     * Gets the number of low-level output streams.
+     *
+     * @param[in] net_group_name      The name of the network_group which contains the streams information.
+     *                                If not passed, the first network_group in the Hef will be addressed.
+     * @return Upon success, returns Expected containing the number of low-level output streams.
+     *         Otherwise, returns Unexpected of ::hailo_status error.
+     */
+    Expected<size_t> get_number_of_output_streams(const std::string &net_group_name="");
+
+    /**
+     * Gets bottleneck FPS.
+     *
+     * @param[in] net_group_name      The name of the network_group which contains the information.
+     *                                If not passed, the first network_group in the Hef will be addressed.
+     * @return Upon success, returns Expected containing the bottleneck FPS number.
+     *         Otherwise, returns Unexpected of ::hailo_status error.
+     */
+    Expected<float64_t> get_bottleneck_fps(const std::string &net_group_name="");
+
+    /**
+     * Gets all stream names under the given vstream name
+     *
+     * @param[in] vstream_name        The name of the vstream.
+     * @param[in] net_group_name      The name of the network_group which contains the streams information.
+     *                                If not passed, the first network_group in the Hef will be addressed.
+     * @return Upon success, returns Expected of a vector of all stream names linked to the provided vstream.
+     *         Otherwise, returns Unexpected of ::hailo_status error.
+     */
+    Expected<std::vector<std::string>> get_stream_names_from_vstream_name(const std::string &vstream_name,
+        const std::string &net_group_name="");
+
+    /**
+     * Get all vstream names under the given stream name
+     *
+     * @param[in] stream_name         The name of the low-level stream.
+     * @param[in] net_group_name      The name of the network_group which contains the streams information.
+     *                                If not passed, the first network_group in the Hef will be addressed.
+     * @return Upon success, returns Expected of a vector of all stream names linked to the provided vstream.
+     * @return Upon success, returns  of a vector of all vstream names linked to the provided stream.
+     *         Otherwise, returns Unexpected of ::hailo_status error.
+     */
+    Expected<std::vector<std::string>> get_vstream_names_from_stream_name(const std::string &stream_name,
+        const std::string &net_group_name="");
+
+    /**
+     * Gets vstream name from original layer name.
+     *
+     * @param[in] original_name       The original layer name as presented in the Hef.
+     * @param[in] net_group_name      The name of the network_group which contains the streams information.
+     *                                If not passed, the first network_group in the Hef will be addressed.
+     * @return Upon success, returns Expected containing the vstream's name.
+     *         Otherwise, returns Unexpected of ::hailo_status error.
+     */
+    Expected<std::string> get_vstream_name_from_original_name(const std::string &original_name,
+        const std::string &net_group_name="");
+
+    /**
+     * Gets original names from vstream name.
+     *
+     * @param[in] vstream_name        The name of the vstream as presented in the Hef.
+     * @param[in] net_group_name      The name of the network_group which contains the streams information.
+     *                                If not passed, the first network_group in the Hef will be addressed.
+     * @return Upon success, returns Expected of a vector of all original names linked to the provided vstream.
+     *         Otherwise, returns Unexpected of ::hailo_status error.
+     */
+    Expected<std::vector<std::string>> get_original_names_from_vstream_name(const std::string &vstream_name,
+        const std::string &net_group_name="");
+
+    /**
+     * Gets all network groups names in the Hef.
+     *
+     * @return Returns a vector of all network groups names.
+     */
+    std::vector<std::string> get_network_groups_names();
+
+    /**
+     * Gets all network groups infos in the Hef.
+     *
+     * @return Upon success, returns Expected of a vector of ::hailo_network_group_info_t.
+     *         Otherwise, returns Unexpected of ::hailo_status error.
+     */
+    Expected<std::vector<hailo_network_group_info_t>> get_network_groups_infos();
+
+    /**
+     * Creates the default configure params for the Hef. The user can modify the given params before
+     * configuring the network.
+     * 
+     * @param[in] stream_interface     Stream interface to use on the network.
+     * @return Upon success, returns Expected of NetworkGroupsParamsMap. Otherwise, returns Unexpected of ::hailo_status error.
+     */
+    Expected<NetworkGroupsParamsMap> create_configure_params(hailo_stream_interface_t stream_interface);
+
+    /**
+     * Creates the default configure params for the Hef. The user can modify the given params before
+     * configuring the network.
+     * 
+     * @param[in] stream_interface    Stream interface to use on the network.
+     * @param[in] network_group_name  Name of network_group to make configure params for.
+     * @return Upon success, returns Expected of ConfigureNetworkParams. Otherwise, returns Unexpected of ::hailo_status error.
+     */
+    Expected<ConfigureNetworkParams> create_configure_params(hailo_stream_interface_t stream_interface, const std::string &network_group_name);
+
+   /**
+     * Creates the default configure params for the Hef, where all inputs stream params are init to be MIPI type.
+     * The user can modify the given params before configuring the network.
+     * 
+     * @param[in] output_interface  Stream interface to use on the output streams.
+     * @param[in] mipi_params       Specific mipi params.
+     * @return Upon success, returns Expected of NetworkGroupsParamsMap, which maps network group name to configured network group params.
+     *         Otherwise, returns Unexpected of ::hailo_status error.
+     */
+    Expected<NetworkGroupsParamsMap> create_configure_params_mipi_input(hailo_stream_interface_t output_interface,
+        const hailo_mipi_input_stream_params_t &mipi_params);
+
+    /**
+     * Creates the default configure params for the Hef, where all inputs stream params are init to be MIPI type.
+     * The user can modify the given params before configuring the network.
+     * 
+     * @param[in] output_interface    Stream interface to use on the output streams.
+     * @param[in] mipi_params         Specific mipi params
+     * @param[in] network_group_name  Name of network_group to make configure params for.
+     * @return Upon success, returns Expected of ConfigureNetworkParams.
+     *         Otherwise, returns Unexpected of ::hailo_status error.
+     */
+    Expected<ConfigureNetworkParams> create_configure_params_mipi_input(hailo_stream_interface_t output_interface,
+        const hailo_mipi_input_stream_params_t &mipi_params, const std::string &network_group_name);
+
+    /**
+     * Creates streams params with default values.
+     *
+     * @param[in] net_group_name    The name of the network_group for which to create the stream parameters for.
+     *                              If an empty string is given, the first network_group in the Hef will be addressed.
+     * @param[in] stream_interface  A ::hailo_stream_interface_t indicating which ::hailo_stream_parameters_t to
+     *                              create for the output streams.
+     * @return Upon success, returns Expected of a map of stream name to stream params.
+     *         Otherwise, returns Unexpected of ::hailo_status error.
+     */
+    Expected<std::map<std::string, hailo_stream_parameters_t>> create_stream_parameters_by_name(
+        const std::string &net_group_name, hailo_stream_interface_t stream_interface);
+
+    /**
+     * Creates networks params with default values.
+     *
+     * @param[in] net_group_name    The name of the network_group for which to create the network parameters for.
+     *                              If an empty string is given, the first network_group in the Hef will be addressed.
+     * @return Upon success, returns Expected of a map of network name to network params.
+     *         Otherwise, returns Unexpected of ::hailo_status error.
+     */
+    Expected<std::map<std::string, hailo_network_parameters_t>> create_network_parameters_by_name(
+        const std::string &net_group_name);
+
+    /**
+     * Creates MIPI input stream params.
+     *
+     * @param[in] net_group_name    The name of the network_group for which to create the stream parameters for.
+     *                              If an empty string is given, the first network_group in the Hef will be addressed.
+     * @param[in] output_interface  A hailo_stream_interface_t indicating which hailo_stream_parameters_t to
+     *                              create for the input streams params.
+     * @param[in] mipi_params       A hailo_mipi_input_stream_params_t object which contains the MIPI params.
+     * @return Upon success, returns Expected of a map of input stream name to stream params.
+     *         Otherwise, returns Unexpected of ::hailo_status error.
+     */
+    Expected<std::map<std::string, hailo_stream_parameters_t>> create_stream_parameters_by_name_mipi_input(
+        const std::string &net_group_name, hailo_stream_interface_t output_interface,
+        const hailo_mipi_input_stream_params_t &mipi_params);
+
+    /**
+     * Creates input virtual stream params for a given network_group.
+     *
+     * @param[in] name              The name of the network or network_group which user wishes to create input virtual stream params for.
+     *                              In case network group name is given, the function returns the input virtual stream params 
+     *                              of all the networks of the given network group.
+     *                              In case network name is given (provided by @a get_network_infos), 
+     *                              the function returns the input virtual stream params of the given network.
+     *                              If NULL is passed, the function returns the input virtual stream params of 
+     *                              all the networks of the first network group.
+     * @param[in] quantized         Whether the data fed into the chip is already quantized. True means
+     *                              the data is already quantized. False means it's HailoRT's responsibility
+     *                              to quantize (scale) the data.
+     * @param[in] format_type       The default format type for all input virtual streams.
+     * @param[in] timeout_ms        The default timeout in milliseconds for all input virtual streams.
+     * @param[in] queue_size        The default queue size for all input virtual streams.
+     * @return Upon success, returns Expected of a map of input virtual stream name to params.
+     *         Otherwise, returns Unexpected of ::hailo_status error.
+     */
+    Expected<std::map<std::string, hailo_vstream_params_t>> make_input_vstream_params(
+        const std::string &name, bool quantized, hailo_format_type_t format_type,
+        uint32_t timeout_ms, uint32_t queue_size);
+
+    /**
+     * Creates output virtual stream params for a given network_group.
+     *
+     * @param[in] name              The name of the network or network_group which user wishes to create output virtual stream params for.
+     *                              In case network group name is given, the function returns the output virtual stream params 
+     *                              of all the networks of the given network group.
+     *                              In case network name is given (provided by @a get_network_infos), 
+     *                              the function returns the output virtual stream params of the given network.
+     *                              If NULL is passed, the function returns the output virtual stream params of
+     *                              all the networks of the first network group.
+     * @param[in] quantized         Whether the data fed into the chip is already quantized. True means
+     *                              the data is already quantized. False means it's HailoRT's responsibility
+     *                              to quantize (scale) the data.
+     * @param[in] format_type       The default format type for all output virtual streams.
+     * @param[in] timeout_ms        The default timeout in milliseconds for all output virtual streams.
+     * @param[in] queue_size        The default queue size for all output virtual streams.
+     * @return Upon success, returns Expected of a map of output virtual stream name to params.
+     *         Otherwise, returns Unexpected of ::hailo_status error.
+     */
+    Expected<std::map<std::string, hailo_vstream_params_t>> make_output_vstream_params(
+        const std::string &name, bool quantized, hailo_format_type_t format_type,
+        uint32_t timeout_ms, uint32_t queue_size);
+
+    /**
+     * Gets all networks informations.
+     *
+     * @param[in] net_group_name      The name of the network_group which contains the network information.
+     *                                If NULL is passed, the function returns the network infos of 
+     *                                all the networks of the first network group.
+     * @return Upon success, returns Expected of a vector of ::hailo_network_info_t, containing each networks's informmation.
+     *         Otherwise, returns Unexpected of ::hailo_status error.
+     */
+    Expected<std::vector<hailo_network_info_t>> get_network_infos(const std::string &net_group_name="");
+
+    ~Hef();
+    Hef(Hef &&);
+    Hef &operator=(Hef &&);
+    Hef(const Hef &) = delete;
+    Hef &operator=(const Hef &) = delete;
+
+private:
+    friend class DeviceBase;
+    friend class ConfigManager;
+    friend class VdmaConfigManager;
+    friend class HcpConfigManager;
+    friend class InputStream;
+    friend class OutputStream;
+    friend class PyhailortInternal;
+    friend class ConfiguredNetworkGroupBase;
+
+    class Impl;
+    Hef(std::unique_ptr<Impl> pimpl);
+    std::unique_ptr<Impl> pimpl;
+};
+
+} /* namespace hailort */
+
+#endif /* _HAILO_HEF_HPP_ */
diff --git a/hailort/libhailort/include/hailo/inference_pipeline.hpp b/hailort/libhailort/include/hailo/inference_pipeline.hpp
new file mode 100644 (file)
index 0000000..1bbd7f0
--- /dev/null
@@ -0,0 +1,113 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file inference_pipeline.hpp
+ * @brief Pipeline used to run inference, builds all necessary flags
+ **/
+
+#ifndef _HAILO_INFERENCE_PIPELINE_HPP_
+#define _HAILO_INFERENCE_PIPELINE_HPP_
+
+#include "hailo/vstream.hpp"
+
+#include <unordered_map>
+#include <chrono>
+
+namespace hailort
+{
+
+/*! Pipeline used to run inference */
+// TODO: HRT-3157 - Fix doc after multi-network support.
+class HAILORTAPI InferVStreams final
+{
+public:
+
+    /**
+     * Creates vstreams pipelines to be used later for inference by calling the InferVStreams::infer() function. 
+     *
+     * @param[in] net_group                    A ConfiguredNetworkGroup to run the inference on.
+     * @param[in] input_params                 A mapping of input vstream name to its' params. Can be achieved by calling 
+     *                                         ConfiguredNetworkGroup::make_input_vstream_params() or Hef::make_input_vstream_params
+     *                                         functions.
+     * @param[in] output_params                A mapping of output vstream name to its' params. Can be achieved by calling 
+     *                                         ConfiguredNetworkGroup::make_output_vstream_params() or Hef::make_output_vstream_params()
+     *                                         functions.
+     * @return Upon success, returns Expected of InferVStreams. Otherwise, returns Unexpected of ::hailo_status error.
+     * @note If at least one input/output of some network is present, all inputs and outputs of that network must also be present.
+     */
+    static Expected<InferVStreams> create(ConfiguredNetworkGroup &net_group,
+        const std::map<std::string, hailo_vstream_params_t> &input_params,
+        const std::map<std::string, hailo_vstream_params_t> &output_params);
+
+    /**
+     * Run inference on dataset @a input_data.
+     *
+     * @param[in] input_data                    A mapping of vstream name to MemoryView containing input dataset for inference.
+     * @param[out] output_data                  A mapping of vstream name to MemoryView containing the inference output data. 
+     * @param[in] batch_size                    The amount of inferred frames.
+     * 
+     * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns a ::hailo_status error.
+     * @note ConfiguredNetworkGroup must be activated before calling this function.
+     * @note The size of each element in @a input_data and @a output_data must match the frame size
+     *       of the matching vstream name multiplied by @a batch_size.
+     * @note If at least one input/output of some network is present, all inputs and outputs of that network must also be present.
+     */
+    hailo_status infer(const std::map<std::string, MemoryView>& input_data,
+                       std::map<std::string, MemoryView>& output_data, size_t batch_size);
+
+    /**
+     * Get InputVStream by name.
+     *
+     * @param[in] name      The vstream's name.
+     * @return Upon success, returns Expected of InputVStream. Otherwise, returns Unexpected of ::hailo_status error.
+     */
+    Expected<std::reference_wrapper<InputVStream>> get_input_by_name(const std::string &name);
+
+    /**
+     * Get OutputVStream by name.
+     *
+     * @param[in] name      The vstream's name.
+     * @return Upon success, returns Expected of OutputVStream. Otherwise, returns Unexpected of ::hailo_status error.
+     */
+    Expected<std::reference_wrapper<OutputVStream>> get_output_by_name(const std::string &name);
+
+    /**
+     * @return Returns a vector of all InputVStream%s.
+     */
+    std::vector<std::reference_wrapper<InputVStream>> get_input_vstreams();
+
+    /**
+     * @return Returns a vector of all OutputVStream%s.
+     */
+    std::vector<std::reference_wrapper<OutputVStream>> get_output_vstreams();
+
+    InferVStreams(const InferVStreams &other) = delete;
+    InferVStreams &operator=(const InferVStreams &other) = delete;
+    InferVStreams &operator=(InferVStreams &&other) = delete;
+    InferVStreams(InferVStreams &&other) :
+        m_inputs(std::move(other.m_inputs)),
+        m_outputs(std::move(other.m_outputs)),
+        m_is_multi_context(std::move(other.m_is_multi_context)),
+        m_network_name_to_input_count(std::move(other.m_network_name_to_input_count)),
+        m_network_name_to_output_count(std::move(other.m_network_name_to_output_count))
+        {};
+private:
+    InferVStreams(std::vector<InputVStream> &&inputs, std::vector<OutputVStream> &&outputs, bool is_multi_context);
+    hailo_status verify_network_inputs_and_outputs(const std::map<std::string, MemoryView>& inputs_name_mem_view_map,
+                                                   const std::map<std::string, MemoryView>& outputs_name_mem_view_map);
+    hailo_status verify_memory_view_size(const std::map<std::string, MemoryView>& inputs_name_mem_view_map,
+                                         const std::map<std::string, MemoryView>& outputs_name_mem_view_map,
+                                         size_t batch_count);
+
+    std::vector<InputVStream> m_inputs;
+    std::vector<OutputVStream> m_outputs;
+    bool m_is_multi_context;
+    std::map<std::string, size_t> m_network_name_to_input_count;
+    std::map<std::string, size_t> m_network_name_to_output_count;
+};
+
+} /* namespace hailort */
+
+#endif /* _HAILO_INFERENCE_PIPELINE_HPP_ */
diff --git a/hailort/libhailort/include/hailo/network_group.hpp b/hailort/libhailort/include/hailo/network_group.hpp
new file mode 100644 (file)
index 0000000..9c192e0
--- /dev/null
@@ -0,0 +1,319 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file network_group.hpp
+ * @brief A module describing the base classes for ConfiguredNetworkGroup and ActivatedNetworkGroup 
+ **/
+
+#ifndef _HAILO_NETWORK_GROUP_HPP_
+#define _HAILO_NETWORK_GROUP_HPP_
+
+#include "hailo/stream.hpp"
+#include "hailo/runtime_statistics.hpp"
+
+#include <string>
+#include <map>
+#include <unordered_map>
+#include <thread>
+#include <utility>
+
+namespace hailort
+{
+
+/** @addtogroup group_type_definitions */
+/*@{*/
+
+/** Represents a vector of InputStream */
+using InputStreamRefVector = std::vector<std::reference_wrapper<InputStream>>;
+
+/** Represents a vector of OutputStream */
+using OutputStreamRefVector = std::vector<std::reference_wrapper<OutputStream>>;
+
+/** Represents a mapping of vstream name to its' params */
+using NameToVStreamParamsMap = std::unordered_map<std::string, hailo_vstream_params_t>;
+
+/** Represents a vector of pairs of OutputStream and NameToVStreamParamsMap */
+using OutputStreamWithParamsVector = std::vector<std::pair<std::reference_wrapper<OutputStream>, NameToVStreamParamsMap>>;
+
+/** Latency measurement result info */
+struct LatencyMeasurementResult {
+    std::chrono::nanoseconds avg_hw_latency;
+};
+/*@}*/
+
+using src_context_t = uint8_t;
+using src_stream_index_t = uint8_t;
+using IntermediateBufferKey = std::pair<src_context_t, src_stream_index_t>;
+
+/*! Activated network_group that can be used to send/receive data */
+class HAILORTAPI ActivatedNetworkGroup
+{
+public:
+    virtual ~ActivatedNetworkGroup() = default;
+    ActivatedNetworkGroup(const ActivatedNetworkGroup &other) = delete;
+    ActivatedNetworkGroup &operator=(const ActivatedNetworkGroup &other) = delete;
+    ActivatedNetworkGroup &operator=(ActivatedNetworkGroup &&other) = delete;
+    ActivatedNetworkGroup(ActivatedNetworkGroup &&other) noexcept = default;
+
+    virtual Expected<Buffer> get_intermediate_buffer(const IntermediateBufferKey &key) = 0;
+    
+    /**
+     * @return The number of invalid frames.
+     */
+    virtual uint32_t get_invalid_frames_count() = 0;
+
+protected:
+    ActivatedNetworkGroup() = default;
+};
+
+/*! Loaded network_group that can be activated */
+class HAILORTAPI ConfiguredNetworkGroup
+{
+public:
+    virtual ~ConfiguredNetworkGroup() = default;
+    ConfiguredNetworkGroup(const ConfiguredNetworkGroup &other) = delete;
+    ConfiguredNetworkGroup &operator=(const ConfiguredNetworkGroup &other) = delete;
+    ConfiguredNetworkGroup &operator=(ConfiguredNetworkGroup &&other) = delete;
+    ConfiguredNetworkGroup(ConfiguredNetworkGroup &&other) noexcept = default;
+
+    /**
+     * @return The network group name.
+     */
+    virtual const std::string &get_network_group_name() const = 0;
+
+    /**
+     * Gets the stream's default interface.
+     * 
+     * @return Upon success, returns Expected of ::hailo_stream_interface_t.
+     *         Otherwise, returns Unexpected of ::hailo_status error.
+     */
+    virtual Expected<hailo_stream_interface_t> get_default_streams_interface() = 0;
+
+    /**
+     * Gets all input streams with the given @a interface.
+     * 
+     * @param[in] stream_interface         A ::hailo_stream_interface_t indicating which InputStream to return.
+     * @return Upon success, returns a vector of InputStream.
+     */
+    virtual std::vector<std::reference_wrapper<InputStream>> get_input_streams_by_interface(hailo_stream_interface_t stream_interface) = 0;
+
+    /**
+     * Gets all output streams with the given @a interface.
+     * 
+     * @param[in] stream_interface         A ::hailo_stream_interface_t indicating which OutputStream to return.
+     * @return Upon success, returns a vector of OutputStream.
+     */
+    virtual std::vector<std::reference_wrapper<OutputStream>> get_output_streams_by_interface(hailo_stream_interface_t stream_interface) = 0;
+
+    /**
+     * Gets input stream by stream name.
+     *
+     * @param[in] name                  The name of the input stream to retrieve.
+     * @return Upon success, returns ExpectedRef of InputStream.
+     *         Otherwise, returns Unexpected of ::hailo_status error.
+     */
+    virtual ExpectedRef<InputStream> get_input_stream_by_name(const std::string &name) = 0;
+
+    /**
+     * Gets output stream by stream name.
+     *
+     * @param[in] name                  The name of the input stream to retrieve.
+     * @return Upon success, returns ExpectedRef of OutputStream.
+     *         Otherwise, returns Unexpected of ::hailo_status error.
+     */
+    virtual ExpectedRef<OutputStream> get_output_stream_by_name(const std::string &name) = 0;
+
+    /**
+     *
+     * @param[in]  network_name             Network name of the requested input streams.
+     *                                      If not passed, all the networks in the network group will be addressed.
+     * @return Upon success, returns Expected of vector InputStream.
+     *         Otherwise, returns Unexpected of ::hailo_status error.
+     */
+    virtual Expected<InputStreamRefVector> get_input_streams_by_network(const std::string &network_name="") = 0;
+
+    /**
+     *
+     * @param[in]  network_name             Network name of the requested output streams.
+     *                                      If not passed, all the networks in the network group will be addressed.
+     * @return Upon success, returns Expected of vector OutputStream.
+     *         Otherwise, returns Unexpected of ::hailo_status error.
+     */
+    virtual Expected<OutputStreamRefVector> get_output_streams_by_network(const std::string &network_name="") = 0;
+
+    /**
+     * @return All input streams.
+     */
+    virtual InputStreamRefVector get_input_streams() = 0;
+
+    /**
+     * @return All output streams.
+     */
+    virtual OutputStreamRefVector get_output_streams() = 0;
+
+    /**
+     *
+     * @param[in]  network_name             Network name of the requested latency measurement.
+     *                                      If not passed, all the networks in the network group will be addressed,
+     *                                      and the resulted measurement is avarage latency of all networks.
+     * @return Upon success, returns Expected of LatencyMeasurementResult object containing the output latency result.
+     *         Otherwise, returns Unexpected of ::hailo_status error.
+     */
+    virtual Expected<LatencyMeasurementResult> get_latency_measurement(const std::string &network_name="") = 0;
+
+    /**
+     * Gets output streams and their vstream params from vstreams names.
+     *
+     * @param[in] outputs_params            Map of output vstream name and params.
+     * @return Upon success, returns Expected of OutputStreamWithParamsVector.
+     *         Otherwise, returns Unexpected of ::hailo_status error.
+     * @note The output streams returned here are streams that corresponds to the names in outputs_params.
+     * If outputs_params contains a demux edge name, then all its predecessors names must be in outputs_params as well
+     * and the return value will contain the stream that corresponds to those edges.
+     */
+    virtual Expected<OutputStreamWithParamsVector> get_output_streams_from_vstream_names(
+        const std::map<std::string, hailo_vstream_params_t> &outputs_params) = 0;
+
+    /**
+     * Activates hailo device inner-resources for context_switch inference.
+
+     * @return Upon success, returns Expected of a pointer to ActivatedNetworkGroup object.
+     *         Otherwise, returns Unexpected of ::hailo_status error.
+     */
+    Expected<std::unique_ptr<ActivatedNetworkGroup>> activate();
+
+    /**
+     * Activates hailo device inner-resources for context_switch inference.
+     *
+     * @param[in] network_group_params         Parameters for the activation.
+     * @return Upon success, returns Expected of a pointer to ActivatedNetworkGroup object.
+     *         Otherwise, returns Unexpected of ::hailo_status error.
+     */
+    virtual Expected<std::unique_ptr<ActivatedNetworkGroup>> activate(
+        const hailo_activate_network_group_params_t &network_group_params) = 0;
+
+    /**
+     * Block until network group is activated, or until timeout is passed.
+     *
+     * @param[in] timeout                 The timeout in milliseconds. If @a timeout is zero, the function returns immediately.
+     *                                    If @a timeout is HAILO_INFINITE, the function returns only when the event is
+     *                                    signaled.
+     * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns a ::hailo_status error.
+     */
+    virtual hailo_status wait_for_activation(const std::chrono::milliseconds &timeout) = 0;
+
+    /**
+     * Creates input virtual stream params.
+     *
+     * @param[in]  quantized                Whether the data fed into the chip is already quantized. True means
+     *                                      the data is already quantized. False means it's HailoRT's responsibility
+     *                                      to quantize (scale) the data.
+     * @param[in]  format_type              The default format type for all input virtual streams.
+     * @param[in]  timeout_ms               The default timeout in milliseconds for all input virtual streams.
+     * @param[in]  queue_size               The default queue size for all input virtual streams.
+     * @param[in]  network_name             Network name of the requested virtual stream params.
+     *                                      If not passed, all the networks in the network group will be addressed.
+     * @return Upon success, returns Expected of a map of name to vstream params.
+     *         Otherwise, returns Unexpected of ::hailo_status error.
+     */
+    virtual Expected<std::map<std::string, hailo_vstream_params_t>> make_input_vstream_params(
+        bool quantized, hailo_format_type_t format_type, uint32_t timeout_ms, uint32_t queue_size,
+        const std::string &network_name="") = 0;
+
+    /**
+     * Creates output virtual stream params.
+     *
+     * @param[in]  quantized                Whether the data fed into the chip is already quantized. True means
+     *                                      the data is already quantized. False means it's HailoRT's responsibility
+     *                                      to quantize (scale) the data.
+     * @param[in]  format_type              The default format type for all output virtual streams.
+     * @param[in]  timeout_ms               The default timeout in milliseconds for all output virtual streams.
+     * @param[in]  queue_size               The default queue size for all output virtual streams.
+     * @param[in]  network_name             Network name of the requested virtual stream params.
+     *                                      If not passed, all the networks in the network group will be addressed.
+     * @return Upon success, returns Expected of a map of name to vstream params.
+     *         Otherwise, returns Unexpected of ::hailo_status error.
+     */
+    virtual Expected<std::map<std::string, hailo_vstream_params_t>> make_output_vstream_params(
+        bool quantized, hailo_format_type_t format_type, uint32_t timeout_ms, uint32_t queue_size,
+        const std::string &network_name="") = 0;
+
+    /**
+     * Creates output virtual stream params. The groups are splitted with respect to their low-level streams.
+     *
+     * @param[in]  quantized                Whether the data fed into the chip is already quantized. True means
+     *                                      the data is already quantized. False means it's HailoRT's responsibility
+     *                                      to quantize (scale) the data.
+     * @param[in]  format_type              The default format type for all output virtual streams.
+     * @param[in]  timeout_ms               The default timeout in milliseconds for all output virtual streams.
+     * @param[in]  queue_size               The default queue size for all output virtual streams.
+     * @return Upon success, returns Expected of a vector of maps, mapping name to vstream params, where each map represents a params group.
+     *         Otherwise, returns Unexpected of ::hailo_status error.
+     */
+    virtual Expected<std::vector<std::map<std::string, hailo_vstream_params_t>>> make_output_vstream_params_groups(
+        bool quantized, hailo_format_type_t format_type, uint32_t timeout_ms, uint32_t queue_size) = 0;
+
+    /**
+     * Gets output virtual stream groups for given network_group. The groups are splitted with respect to their low-level streams.
+     *
+     * @return Upon success, returns Expected of a map of vstream name to group index.
+     *         Otherwise, returns Unexpected of ::hailo_status error.
+     */
+    virtual Expected<std::vector<std::vector<std::string>>> get_output_vstream_groups() = 0;
+
+    /**
+     * Gets all network infos of the configured network group.
+     *
+     * @return Upon success, returns Expected of a vector of all network infos of the configured network group.
+     *         Otherwise, returns Unexpected of ::hailo_status error.
+     */
+    virtual Expected<std::vector<hailo_network_info_t>> get_network_infos() const = 0;
+
+    /**
+     * @param[in]  network_name        Network name of the requested stream infos.
+     *                                 If not passed, all the networks in the network group will be addressed.
+     * @return Upon success, returns Expected of all stream infos of the configured network group.
+     *         Otherwise, returns Unexpected of ::hailo_status error.
+     */
+    virtual Expected<std::vector<hailo_stream_info_t>> get_all_stream_infos(const std::string &network_name="") const = 0;
+
+    /**
+     * @param[in]  network_name        Network name of the requested virtual stream infos.
+     *                                 If not passed, all the networks in the network group will be addressed.
+     * @return Upon success, returns Expected of all input vstreams infos of the configured network group.
+     *         Otherwise, returns Unexpected of ::hailo_status error.
+     */
+    virtual Expected<std::vector<hailo_vstream_info_t>> get_input_vstream_infos(const std::string &network_name="") const = 0;
+
+    /**
+     * @param[in]  network_name        Network name of the requested virtual stream infos.
+     *                                 If not passed, all the networks in the network group will be addressed.
+     * @return Upon success, returns Expected of all output vstreams infos of the configured network group.
+     *         Otherwise, returns Unexpected of ::hailo_status error.
+     */
+    virtual Expected<std::vector<hailo_vstream_info_t>> get_output_vstream_infos(const std::string &network_name="") const = 0;
+
+    /**
+     * @param[in]  network_name        Network name of the requested virtual stream infos.
+     *                                 If not passed, all the networks in the network group will be addressed.
+     * @return Upon success, returns Expected of all vstreams infos of the configured network group.
+     *         Otherwise, returns Unexpected of ::hailo_status error.
+     */
+    virtual Expected<std::vector<hailo_vstream_info_t>> get_all_vstream_infos(const std::string &network_name="") const = 0;
+
+    virtual AccumulatorPtr get_activation_time_accumulator() const = 0;
+    virtual AccumulatorPtr get_deactivation_time_accumulator() const = 0;
+
+protected:
+    ConfiguredNetworkGroup() = default;
+
+private:
+    friend class ActivatedNetworkGroup;
+};
+using ConfiguredNetworkGroupVector = std::vector<std::shared_ptr<ConfiguredNetworkGroup>>;
+
+} /* namespace hailort */
+
+#endif /* _HAILO_NETWORK_GROUP_HPP_ */
diff --git a/hailort/libhailort/include/hailo/network_rate_calculator.hpp b/hailort/libhailort/include/hailo/network_rate_calculator.hpp
new file mode 100644 (file)
index 0000000..047cb1c
--- /dev/null
@@ -0,0 +1,84 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file network_rate_calculator.hpp
+ * @brief Calculate the input bandwidth limit for ethernet streams.
+ * The calculation is based on the following parameters:
+ * - The frame sizes of the network's input (retrieved from the hef)
+ * - The frame sizes of the network's outputs (retrieved from the hef)
+ * - The fps
+ **/
+
+#ifndef _NETWORK_RATE_CALCULATOR_HPP_
+#define _NETWORK_RATE_CALCULATOR_HPP_
+
+#include "hailo/hailort.h"
+#include "hailo/expected.hpp"
+#include "hailo/hef.hpp"
+#include "hailo/network_group.hpp"
+
+#include <vector>
+
+namespace hailort
+{
+
+using StreamInfoVector = std::vector<hailo_stream_info_t>;
+
+class HAILORTAPI NetworkUdpRateCalculator {
+private:
+    std::map<std::string, uint32_t> m_input_edge_shapes;
+    std::map<std::string, uint32_t> m_output_edge_shapes;
+
+    static Expected<StreamInfoVector> get_streams_from_hef(Hef* hef, const std::string &network_group_name);
+
+    // Use the static create() function
+    NetworkUdpRateCalculator(std::map<std::string, uint32_t> &&input_edge_shapes,
+        std::map<std::string, uint32_t> &&output_edge_shapes);
+
+public:
+    virtual ~NetworkUdpRateCalculator() = default;
+    
+    static Expected<NetworkUdpRateCalculator> create(Hef* hef, const std::string &network_group_name="");
+    static Expected<NetworkUdpRateCalculator> create(ConfiguredNetworkGroup &net_group);
+
+    /**
+     * Calculate the inputs bandwidths supported by the configured network.
+     * Rate limiting of this manner is to be used for ethernet input streams.
+     *
+     * @param[in]     fps                           The desired fps.
+     * @param[in]     max_supported_bandwidth       The maximun supported bandwidth.
+     *         Neither the calculated input rate, nor the corresponding output rate will exceed this value.
+     *         If no value is given, those rates will not exceed @a HAILO_DEFAULT_MAX_ETHERNET_BANDWIDTH_BYTES_PER_SEC.
+     * @return Upon success, returns Expected of a map of stream names and their corresponding rates.
+     *         Otherwise, returns Unexpected of ::hailo_status error.
+     * @note There are two options to limit the rate of an ethernet input stream to the desired bandwidth:
+     *       - Set ::hailo_eth_input_stream_params_t.rate_limit_bytes_per_sec inside ::hailo_stream_parameters_t, under NetworkGroupsParamsMap
+     *         before passing it to Device::configure.
+     *       - On Unix platforms:
+     *         - You may use the command line tool `hailortcli udp-rate-limiter` instead of using this API
+     */
+    Expected<std::map<std::string, uint32_t>> calculate_inputs_bandwith(uint32_t fps,
+        uint32_t max_supported_bandwidth = HAILO_DEFAULT_MAX_ETHERNET_BANDWIDTH_BYTES_PER_SEC);
+
+    /**
+     * Calculate the inputs bandwidths supported by the configured network, and returns a map of 
+     * stream ports and their corresponding rates.
+     *
+     * @param[in]     udp_input_streams             UDP input streams.
+     * @param[in]     fps                           The desired fps.
+     * @param[in]     max_supported_bandwidth       The maximun supported bandwidth.
+     *         Neither the calculated input rate, nor the corresponding output rate will exceed this value.
+     *         If no value is given, those rates will not exceed @a HAILO_DEFAULT_MAX_ETHERNET_BANDWIDTH_BYTES_PER_SEC.
+     * @return Upon success, returns Expected of a map of stream ports and their corresponding rates.
+     *         Otherwise, returns Unexpected of ::hailo_status error.
+     */
+    Expected<std::map<uint16_t, uint32_t>> get_udp_ports_rates_dict(
+        std::vector<std::reference_wrapper<InputStream>> &udp_input_streams,
+        uint32_t fps, uint32_t max_supported_bandwidth = HAILO_DEFAULT_MAX_ETHERNET_BANDWIDTH_BYTES_PER_SEC);
+};
+
+} /* namespace hailort */
+
+#endif /* _NETWORK_RATE_CALCULATOR_HPP_ */
diff --git a/hailort/libhailort/include/hailo/platform.h b/hailort/libhailort/include/hailo/platform.h
new file mode 100644 (file)
index 0000000..8fa3468
--- /dev/null
@@ -0,0 +1,115 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file platform.h
+ * @brief Platform dependent includes and definitions
+ **/
+
+#ifndef _HAILO_PLATFORM_H_
+#define _HAILO_PLATFORM_H_
+
+#if !defined(_MSC_VER) && !defined(__GNUC__)
+#error "OS must be defined (UNIX/WIN32)"
+#endif
+
+
+/** Exported symbols define */
+
+#if defined(_MSC_VER)
+#if defined(_HAILO_EXPORTING)
+#define HAILORTAPI __declspec(dllexport)
+#else
+#define HAILORTAPI __declspec(dllimport)
+#endif
+#else
+#define HAILORTAPI __attribute__ ((visibility ("default")))
+#endif
+
+
+/** Includes */
+
+#if defined(_MSC_VER)
+// Windows headers
+#include <winsock2.h>
+#include <Ws2tcpip.h>
+#include <ws2ipdef.h>
+#else
+// UNIX headers
+#include <unistd.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#endif
+
+
+/** Typedefs */
+
+// underlying_handle_t
+#ifndef underlying_handle_t
+#if defined(_MSC_VER)
+typedef HANDLE underlying_handle_t; 
+#else
+typedef int underlying_handle_t; 
+#endif
+#endif
+
+// port_t
+#ifndef port_t
+#if defined(_MSC_VER)
+typedef USHORT port_t;
+#else
+typedef in_port_t port_t;
+#endif
+#endif
+
+// socket_t
+#ifndef socket_t
+#if defined(_MSC_VER)
+typedef SOCKET socket_t;
+#else
+typedef int socket_t;
+#endif
+#endif
+
+// timeval_t
+#ifndef timeval_t
+typedef struct timeval timeval_t;
+#endif
+
+
+/** Defines and Macros */
+
+// TODO: Fix this hack
+#ifndef MSG_CONFIRM
+#define MSG_CONFIRM (0)
+#endif
+
+#if !defined(_MSC_VER) && !defined(INVALID_SOCKET)
+// Already defined in Windows
+#define INVALID_SOCKET (socket_t)(-1)
+#endif
+
+#if !defined(_MSC_VER) && !defined(SOCKET_ERROR)
+// Already defined in Windows
+#define SOCKET_ERROR (int)(-1)
+#endif
+
+#ifdef __GNUC__
+#define DEPRECATED(msg) __attribute((deprecated(msg)))
+#else
+#define DEPRECATED(msg)
+#endif
+
+#define EMPTY_STRUCT_PLACEHOLDER uint8_t reserved;
+
+#ifndef MILLISECONDS_IN_SECOND
+#define MILLISECONDS_IN_SECOND (1000)
+#endif
+#ifndef MICROSECONDS_IN_MILLISECOND
+#define MICROSECONDS_IN_MILLISECOND (1000)
+#endif
+
+#endif /* _HAILO_PLATFORM_H_ */
diff --git a/hailort/libhailort/include/hailo/quantization.hpp b/hailort/libhailort/include/hailo/quantization.hpp
new file mode 100644 (file)
index 0000000..fa80905
--- /dev/null
@@ -0,0 +1,184 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file quantization.hpp
+ * @brief Implemention of quantization functions.
+ **/
+
+#ifndef _HAILO_QUANTIZATION_HPP_
+#define _HAILO_QUANTIZATION_HPP_
+
+#include "hailo/hailort.h"
+#include <math.h>
+#include <fenv.h>
+
+namespace hailort
+{
+
+class RoundingToNearestGuard final
+{
+public:
+    RoundingToNearestGuard() :
+        m_original_rounding_method(fegetround())
+    {
+        fesetround(FE_TONEAREST);
+    }
+
+    ~RoundingToNearestGuard()
+    {
+        fesetround(m_original_rounding_method);
+    }
+
+private:
+    int m_original_rounding_method;
+};
+
+/*! Hailo device requires input data to be quantized/scaled before it is sent. Similarly, data outputted
+ * from the device needs to be 'de-quantized'/rescaled as well.
+ * When a neural network is compiled, each input/output layer in the neural network is assigned two floating point values
+ * that are parameters to an input/output transformation:
+ * ::hailo_quant_info_t::qp_zp (zero_point) and ::hailo_quant_info_t::qp_scale (fields of the struct ::hailo_quant_info_t).
+ * These values are stored in the Hef.
+ * - Input transformation: input data is divided by ::hailo_quant_info_t::qp_scale and
+ *                         then ::hailo_quant_info_t::qp_zp is added to the result.
+ * - Output transformation: ::hailo_quant_info_t::qp_zp is subtracted from output data and
+ *                          then the result is multiplied by ::hailo_quant_info_t::qp_scale.
+ **/
+class Quantization final
+{
+public:
+    Quantization() = delete;
+    
+    /**
+     * De-quantize output buffer pointed by @a src_ptr from data type @a Q into the buffer pointed by @a dst_ptr of data type @a T.
+     *
+     * @param[in] src_ptr                   A pointer to the buffer containing the data that will be de-quantized.
+     * @param[out] dst_ptr                  A pointer to the buffer that will contain the output de-quantized data.
+     * @param[in] buffer_elements_count     The number of elements in @a src_ptr and @a dst_ptr arrays.
+     * @param[in] quant_info                Quantization info.
+     */
+    template <typename T, typename Q>
+    static void dequantize_output_buffer(Q *src_ptr, T *dst_ptr, uint32_t buffer_elements_count, hailo_quant_info_t quant_info)
+    {
+        if (is_identity_qp(quant_info)) {
+            for (uint32_t i = 0; i < buffer_elements_count; i++) {
+                dst_ptr[i] = (T)(src_ptr[i]);
+            }
+        } else {
+            auto rounding_tonearest_guard = RoundingToNearestGuard();
+            for (uint32_t i = 0; i < buffer_elements_count; i++) {
+                dst_ptr[i] = dequantize_output<T, Q>(src_ptr[i], quant_info);
+            }
+        }
+    }
+
+    /**
+     * De-quantize in place the output buffer pointed by @a dst_ptr from data type @a Q to data type @a T.
+     * 
+     * @param[inout] dst_ptr                A pointer to the buffer to be de-quantized.
+     * @param[in] buffer_elements_count     The number of elements in @a dst_ptr array.
+     * @param[in] quant_info                Quantization info.
+     */
+    template <typename T, typename Q>
+    static void dequantize_output_buffer_in_place(T *dst_ptr, uint32_t buffer_elements_count, hailo_quant_info_t quant_info)
+    {
+        if (is_identity_qp(quant_info)) {
+            for (int32_t i = (int32_t)buffer_elements_count - 1; i >= 0; i--) {
+                dst_ptr[i] = (T)(*((Q*)dst_ptr + i));
+            }
+        } else {
+            auto rounding_tonearest_guard = RoundingToNearestGuard();
+            for (int32_t i = (int32_t)buffer_elements_count - 1; i >= 0; i--) {
+                dst_ptr[i] = dequantize_output<T, Q>(*((Q*)dst_ptr + i), quant_info);
+            }
+        }
+    }
+
+    /**
+     * Quantize input buffer pointed by @a src_ptr of data type @a T, into the buffer pointed by @a dst_ptr of data type @a Q.
+     * 
+     * @param[in] src_ptr                   A pointer to the buffer containing the data that will be quantized.
+     * @param[out] dst_ptr                  A pointer to the buffer that will contain the output quantized data.
+     * @param[in] buffer_elements_count     The number of elements in @a src_ptr and @a dst_ptr arrays.
+     * @param[in] quant_info                Quantization info.
+     */
+    template <typename T, typename Q>
+    static void quantize_input_buffer(T *src_ptr, Q *dst_ptr, uint32_t buffer_elements_count, hailo_quant_info_t quant_info)
+    {
+        auto rounding_tonearest_guard = RoundingToNearestGuard();
+        if (is_identity_qp(quant_info)) {
+            for (uint32_t i = 0; i < buffer_elements_count; i++) {
+                dst_ptr[i] = (Q)rintf(src_ptr[i]);
+            }
+        } else {
+            for (uint32_t i = 0; i < buffer_elements_count; i++) {
+                dst_ptr[i] = quantize_input<T, Q>(src_ptr[i], quant_info);
+            }
+        }
+    }
+
+    /**
+     * De-quantize output NMS buffer pointed by @a src_ptr of data type @a Q, into the buffer pointed by @a dst_ptr of data type @a T.
+     * 
+     * @param[in] src_ptr                   A pointer to the buffer containing the data that will be de-quantized.
+     * @param[out] dst_ptr                  A pointer to the buffer that will contain the output de-quantized data.
+     * @param[in] buffer_elements_count     The number of elements in @a src_ptr and @a dst_ptr arrays.
+     * @param[in] quant_info                Quantization info.
+     * @param[in] number_of_classes         Amount of NMS classes.
+     */
+    template <typename T, typename Q>
+    static void dequantize_output_buffer_nms(Q *src_ptr, T *dst_ptr, uint32_t buffer_elements_count, hailo_quant_info_t quant_info, uint32_t number_of_classes)
+    {
+        auto rounding_tonearest_guard = RoundingToNearestGuard();
+        (void)buffer_elements_count;
+        size_t offset = 0;
+        for (uint32_t i = 0; i < number_of_classes; i++) {
+            size_t bbox_count = src_ptr[offset];
+            dst_ptr[offset] = (T)(src_ptr[offset]);
+            offset++;
+            size_t class_end_offset = offset + (HailoRTCommon::BBOX_PARAMS * bbox_count);
+            assert(class_end_offset <= buffer_elements_count);
+            for (; offset < class_end_offset; offset++) {
+                dst_ptr[offset] = dequantize_output<T, Q>(src_ptr[offset], quant_info);
+            }
+        }
+    }
+
+    static inline bool is_identity_qp(const hailo_quant_info_t &quant_info)
+    {
+        return ((1 == quant_info.qp_scale) && (0 == quant_info.qp_zp));
+    }
+
+private:
+    template <typename T, typename Q>
+    static inline Q quantize_input(T number, hailo_quant_info_t quant_info)
+    {
+        float32_t clipped_number = clip((float32_t)number, quant_info.limvals_min, quant_info.limvals_max);
+        return (Q)rintf((clipped_number / quant_info.qp_scale) + quant_info.qp_zp);
+    }
+
+    static inline float32_t clip(float32_t n, float32_t limval_min, float32_t limval_max)
+    {
+        if (n >= limval_max) {
+            return limval_max;
+        }
+        else if (n <= limval_min) {
+            return limval_min;
+        }
+        else {
+            return n;
+        }
+    }
+
+    template <typename T, typename Q>
+    static inline T dequantize_output(Q number, hailo_quant_info_t quant_info)
+    {
+        return (T)((number - quant_info.qp_zp) * quant_info.qp_scale);
+    }
+};
+
+} /* namespace hailort */
+
+#endif /* _HAILO_QUANTIZATION_HPP_ */
diff --git a/hailort/libhailort/include/hailo/runtime_statistics.hpp b/hailort/libhailort/include/hailo/runtime_statistics.hpp
new file mode 100644 (file)
index 0000000..b73c8c7
--- /dev/null
@@ -0,0 +1,193 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file runtime_statistics.hpp
+ * @brief Runtime statistics
+ **/
+
+#ifndef _HAILO_RUNTIME_STATISTICS_HPP_
+#define _HAILO_RUNTIME_STATISTICS_HPP_
+
+#include "hailo/hailort.h"
+#include "hailo/expected.hpp"
+
+#include <type_traits>
+#include <memory>
+
+namespace hailort
+{
+
+/*! Results obtained by an Accumulator at a given point in time via Accumulator::get_and_clear or Accumulator::get */
+class AccumulatorResults final
+{
+public:
+    AccumulatorResults(const Expected<size_t> &count, const Expected<double> &min, const Expected<double> &max,
+                       const Expected<double> &mean, const Expected<double> &var, const Expected<double> &sd,
+                       const Expected<double> &mean_sd) :
+        m_count(count),
+        m_min(min),
+        m_max(max),
+        m_mean(mean),
+        m_var(var),
+        m_sd(sd),
+        m_mean_sd(mean_sd)
+    {}
+
+    AccumulatorResults(AccumulatorResults &&) = default;
+    AccumulatorResults(const AccumulatorResults &) = delete;
+    AccumulatorResults &operator=(AccumulatorResults &&) = delete;
+    AccumulatorResults &operator=(const AccumulatorResults &) = delete;
+    ~AccumulatorResults() = default;
+
+    /**
+     * @return Returns Expected of the number of datapoints added to the Accumulator.
+     */
+    Expected<size_t> count() const { return m_count; }
+
+    /**
+     * @return Returns Expected of the minimal value added to the Accumulator,
+     *         or Unexpected of ::HAILO_UNINITIALIZED if no data has been added.
+     */
+    Expected<double> min() const { return m_min; }
+
+    /**
+     * @return Returns Expected of the maximal value added to the Accumulator,
+     *         or Unexpected of ::HAILO_UNINITIALIZED if no data has been added.
+     */
+    Expected<double> max() const { return m_max; }
+
+    /**
+     * @return Returns Expected of the mean of the values added to the Accumulator,
+     *         or Unexpected of ::HAILO_UNINITIALIZED if no data has been added.
+     */
+    Expected<double> mean() const { return m_mean; }
+
+    /**
+     * @return Returns Expected of the sample variance of the values added to the Accumulator,
+     *         or Unexpected of ::HAILO_UNINITIALIZED if no data has been added.
+     */
+    Expected<double> var() const { return m_var; }
+
+    /**
+     * @return Returns Expected of the sample standard deviation of the values added to the Accumulator,
+     *         or Unexpected of ::HAILO_UNINITIALIZED if no data has been added.
+     */
+    Expected<double> sd() const { return m_sd; }
+
+    /**
+     * @return Returns Expected of the sample standard deviation of the mean of values added to the Accumulator,
+     *         or Unexpected of ::HAILO_UNINITIALIZED if no data has been added.
+     */
+    Expected<double> mean_sd() const { return m_mean_sd; }
+
+private:
+    const Expected<size_t> m_count;
+    const Expected<double> m_min;
+    const Expected<double> m_max;
+    const Expected<double> m_mean;
+    const Expected<double> m_var;
+    const Expected<double> m_sd;
+    const Expected<double> m_mean_sd;
+};
+
+/*! The Accumulator interface supports the measurement of various statistics incrementally. I.e. upon each addition of
+    a measurement to the Accumulator, via Accumulator::add_data_point, all the statistics are updated.
+    Implementations of this interface are to be thread-safe, meaning that adding measurements in one thread, while another
+    thread reads the current statistics (via the various getters provided by the interface) will produce correct values.
+*/
+template<typename T, std::enable_if_t<std::is_arithmetic<T>::value, int> = 0>
+class Accumulator
+{
+public:
+    /**
+     * Constructs a new Accumulator with a given @a data_type
+     *
+     * @param data_type             The type of data that will be measured by the Accumulator.
+     *                              Used to differentiate between different types of measurements (e.g. fps, latency).
+     */
+    Accumulator(const std::string& data_type) :
+        m_data_type(data_type)
+    {}
+
+    Accumulator(Accumulator &&) = default;
+    Accumulator(const Accumulator &) = delete;
+    Accumulator &operator=(Accumulator &&) = delete;
+    Accumulator &operator=(const Accumulator &) = delete;
+    virtual ~Accumulator() = default;
+
+    /**
+     * @return The data_type of the Accumulator.
+     */
+    std::string get_data_type() const { return m_data_type; };
+
+    /**
+     * Add a new measurement to the Accumulator, updating the statistics measured.
+     * 
+     * @param data                  The measurement to be added.
+     * @note Implementations of this interface are to update the statistics in constant time
+     */
+    virtual void add_data_point(T data) = 0;
+
+    /**
+     * Gets the current statistics of the data added to the Accumulator, clearing the statistics afterwards.
+     * 
+     * @return The current statistics of the data added to the Accumulator
+     */
+    virtual AccumulatorResults get_and_clear() = 0;
+    
+    /**
+     * @return The current statistics of the data added to the Accumulator
+     */
+    virtual AccumulatorResults get() const = 0;
+    
+    /**
+     * @return The number of datapoints added to the Accumulator.
+     */
+    virtual Expected<size_t> count() const = 0;
+    
+    /**
+     * @return Returns Expected of the minimal value added to the Accumulator,
+     *         or Unexpected of ::HAILO_UNINITIALIZED if no data has been added.
+     */
+    virtual Expected<double> min() const = 0;
+    
+    /**
+     * @return Returns Expected of the maximal value added to the Accumulator,
+     *         or Unexpected of ::HAILO_UNINITIALIZED if no data has been added.
+     */
+    virtual Expected<double> max() const = 0;
+    
+    /**
+     * @return Returns Expected of the mean of the values added to the Accumulator,
+     *         or Unexpected of ::HAILO_UNINITIALIZED if no data has been added.
+     */
+    virtual Expected<double> mean() const = 0;
+    
+    /**
+     * @return Returns Expected of the sample variance of the values added to the Accumulator,
+     *         or Unexpected of ::HAILO_UNINITIALIZED if no data has been added.
+     */
+    virtual Expected<double> var() const = 0;
+    
+    /**
+     * @return Returns Expected of the sample standard deviation of the values added to the Accumulator,
+     *         or Unexpected of ::HAILO_UNINITIALIZED if no data has been added.
+     */
+    virtual Expected<double> sd() const = 0;
+
+    /**
+     * @return Returns Expected of the sample standard deviation of the mean of values added to the Accumulator,
+     *         or Unexpected of ::HAILO_UNINITIALIZED if no data has been added.
+     */
+    virtual Expected<double> mean_sd() const = 0;
+
+private:
+    const std::string m_data_type;
+};
+using AccumulatorPtr = std::shared_ptr<Accumulator<double>>;
+
+} /* namespace hailort */
+
+#endif /* _HAILO_RUNTIME_STATISTICS_HPP_ */
diff --git a/hailort/libhailort/include/hailo/stream.hpp b/hailort/libhailort/include/hailo/stream.hpp
new file mode 100644 (file)
index 0000000..4269a3f
--- /dev/null
@@ -0,0 +1,257 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file stream.hpp
+ * @brief Input/Output streaming to the device
+ **/
+
+#ifndef _HAILO_STREAM_HPP_
+#define _HAILO_STREAM_HPP_
+
+#include "hailo/hailort.h"
+#include "hailo/buffer.hpp"
+#include "hailo/event.hpp"
+
+#include <memory>
+#include <map>
+#include <chrono>
+#include <atomic>
+
+namespace hailort
+{
+
+// Forward declaration
+struct LayerInfo;
+
+/*! Input (host to device) stream representation */
+class HAILORTAPI InputStream
+{
+public:
+    virtual ~InputStream() = default;
+
+    InputStream(const InputStream&) = delete;
+    InputStream& operator=(const InputStream&) = delete;
+
+    /**
+     * Set new timeout value to the input stream
+     *
+     * @param[in] timeout      The new timeout value to be set in milliseconds.
+     * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns a ::hailo_status error.
+     */
+    virtual hailo_status set_timeout(std::chrono::milliseconds timeout) = 0;
+
+    /**
+     * @return The input stream's timeout in milliseconds.
+     */
+    virtual std::chrono::milliseconds get_timeout() const = 0;
+
+    /**
+     * @return The input stream's interface.
+     */
+    virtual hailo_stream_interface_t get_interface() const = 0;
+
+    /**
+     * Aborting the stream.
+     * 
+     * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns a ::hailo_status error.
+     */
+    virtual hailo_status abort() = 0;
+
+    /**
+     * Clearing the aborted state of the stream.
+     * 
+     * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns a ::hailo_status error.
+     */
+    virtual hailo_status clear_abort() = 0;
+
+    /**
+     * Writes all pending data to the underlying stream.
+     * 
+     * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns a ::hailo_status error.
+     */
+    virtual hailo_status flush();
+
+    /**
+     * @returns a pointer for network group activated event.
+     */
+    virtual EventPtr &get_network_group_activated_event() = 0;
+
+    /**
+     * Writes the entire buffer to the stream without transformations
+     *
+     * @param[in] buffer    The buffer to be written.
+     * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns an ::hailo_status error.
+     * @note @a buffer is expected to be in the format dictated by this.stream_info.format
+     * @note @a size is expected to be a product of this.stream_info.hw_frame_size (i.e. more than one frame may be written)
+     */
+    virtual hailo_status write(const MemoryView &buffer);
+
+    /**
+     * @returns A ::hailo_stream_info_t object containing the stream's info.
+     */
+    const hailo_stream_info_t &get_info() const
+    {
+        return m_stream_info;
+    }
+
+    /**
+     * @returns the stream's hw frame size in bytes.
+     */
+    virtual inline size_t get_frame_size() const
+    {
+        return m_stream_info.hw_frame_size;
+    }
+
+    /**
+     * @returns the stream's name.
+     */
+    std::string name() const
+    {
+        return m_stream_info.name;
+    }
+
+    /**
+     * @returns the stream's description containing it's name and index.
+     */
+    virtual std::string to_string() const;
+
+protected:
+    InputStream() = default;
+    InputStream(InputStream &&) = default;
+
+    // Note: Implement sync_write_all_raw_buffer_no_transform_impl for the actual stream interaction in sub classes
+    virtual hailo_status sync_write_all_raw_buffer_no_transform_impl(void *buffer, size_t offset, size_t size) = 0;
+
+    virtual hailo_status activate_stream() = 0;
+    virtual hailo_status deactivate_stream() = 0;
+
+    virtual Expected<size_t> sync_write_raw_buffer(const MemoryView &buffer) = 0;
+
+    hailo_stream_info_t m_stream_info;
+    uint8_t m_dataflow_manager_id;
+
+private:
+    friend class HefConfigurator;
+    friend class ActivatedNetworkGroupBase;
+};
+
+/*! Output (device to host) stream representation */
+class HAILORTAPI OutputStream
+{
+public:
+    virtual ~OutputStream() = default;
+
+    OutputStream(const OutputStream&) = delete;
+    OutputStream& operator=(const OutputStream&) = delete;
+
+    /**
+     * Set new timeout value to the output stream
+     *
+     * @param[in] timeout      The new timeout value to be set in milliseconds.
+     * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns a ::hailo_status error.
+     */
+    virtual hailo_status set_timeout(std::chrono::milliseconds timeout) = 0;
+
+    /**
+     * @return returns the output stream's timeout in milliseconds.
+     */
+    virtual std::chrono::milliseconds get_timeout() const = 0;
+    
+    /**
+     * @return returns the output stream's interface.
+     */
+    virtual hailo_stream_interface_t get_interface() const = 0;
+
+    /**
+     * Aborting the stream.
+     * 
+     * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns a ::hailo_status error.
+     */
+    virtual hailo_status abort() = 0;
+
+    /**
+     * Clearing the abort flag of the stream.
+     * 
+     * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns a ::hailo_status error.
+     */
+    virtual hailo_status clear_abort() = 0;
+
+    /**
+     * @returns a pointer for network group activated event.
+     */
+    virtual EventPtr &get_network_group_activated_event() = 0;
+    
+    /**
+     * @returns the stream's info.
+     */
+    const hailo_stream_info_t &get_info() const
+    {
+        return m_stream_info;
+    }
+
+    /**
+     * @returns the stream's hw frame size.
+     */
+    virtual inline size_t get_frame_size() const
+    {
+        return m_stream_info.hw_frame_size;
+    }
+
+    /**
+     * @returns the stream's name.
+     */
+    std::string name() const
+    {
+        return m_stream_info.name;
+    }
+
+    /**
+     * @returns the stream's description containing it's name and index.
+     */
+    virtual std::string to_string() const;
+
+    /**
+     * @returns the number of invalid frames received by the stream.
+     */
+    uint32_t get_invalid_frames_count() const;
+
+    /**
+     * Reads the entire buffer from the stream without transformations
+     *
+     * @param[out] buffer   A pointer to a buffer that receives the data read from the stream.
+     * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns an ::hailo_status error.
+     * @note Upon return, @a buffer is expected to be in the format dictated by this.stream_info.format
+     * @note @a size is expected to be a product of this.stream_info.hw_frame_size (i.e. more than one frame may be read)
+     */
+    virtual hailo_status read(MemoryView buffer);
+
+protected:
+    OutputStream() = default;
+    OutputStream(OutputStream&&);
+
+    virtual hailo_status activate_stream() = 0;
+    virtual hailo_status deactivate_stream() = 0;
+    virtual hailo_status read_all(MemoryView &buffer) = 0;
+
+    virtual Expected<size_t> sync_read_raw_buffer(MemoryView &buffer) = 0;
+
+    hailo_stream_info_t m_stream_info;
+    uint8_t m_dataflow_manager_id;
+    std::atomic<uint32_t> m_invalid_frames_count;
+
+private:
+    virtual const LayerInfo& get_layer_info() = 0;
+    hailo_status read_nms(void *buffer, size_t offset, size_t size);
+    void increase_invalid_frames_count(uint32_t value);
+
+    friend class HefConfigurator;
+    friend class ActivatedNetworkGroupBase;
+    friend class HwReadElement;
+    friend class OutputDemuxer;
+};
+
+} /* namespace hailort */
+
+#endif /* _HAILO_STREAM_HPP_ */
diff --git a/hailort/libhailort/include/hailo/transform.hpp b/hailort/libhailort/include/hailo/transform.hpp
new file mode 100644 (file)
index 0000000..4d059f4
--- /dev/null
@@ -0,0 +1,342 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file transform.hpp
+ * @brief Pre/post infer transformations
+ **/
+
+#ifndef _HAILO_TRANSFORM_HPP_
+#define _HAILO_TRANSFORM_HPP_
+
+#include "hailo/hailort.h"
+#include "hailo/expected.hpp"
+#include "hailo/hailort_common.hpp"
+#include "hailo/buffer.hpp"
+#include "hailo/hef.hpp"
+#include "hailo/stream.hpp"
+
+#include <map>
+#include <vector>
+
+namespace hailort
+{
+
+/*! Object used for input stream transformation*/
+class HAILORTAPI InputTransformContext final
+{
+public:
+
+    /**
+     * Creates input transform_context.
+     * 
+     * @param[in] src_image_shape          The shape of the src buffer to be transformed.
+     * @param[in] src_format               The format of the src buffer to be transformed.
+     * @param[in] dst_image_shape          The shape of the dst buffer that receives the transformed data.
+     * @param[in] dst_format               The format of the dst buffer that receives the transformed data.
+     * @param[in] dst_quant_info           A ::hailo_quant_info_t object containing quantization information.
+     * @return Upon success, returns Expected of a pointer to InputTransformContext.
+     *         Otherwise, returns Unexpected of ::hailo_status error.
+     */
+    static Expected<std::unique_ptr<InputTransformContext>> create(const hailo_3d_image_shape_t &src_image_shape,
+        const hailo_format_t &src_format, const hailo_3d_image_shape_t &dst_image_shape,
+        const hailo_format_t &dst_format, const hailo_quant_info_t &dst_quant_info);
+
+    /**
+     * Creates input transform_context.
+     * 
+     * @param[in] stream_info       Creates transform_context that fits this stream info.
+     * @param[in] transform_params  A ::hailo_transform_params_t object containing user transformation parameters.
+     * @return Upon success, returns Expected of a pointer to InputTransformContext.
+     *         Otherwise, returns Unexpected of ::hailo_status error.
+     */
+    static Expected<std::unique_ptr<InputTransformContext>> create(const hailo_stream_info_t &stream_info,
+        const hailo_transform_params_t &transform_params);
+
+    /**
+     * Creates input transform_context.
+     * 
+     * @param[in] stream_info    Creates transform_context that fits this stream info.
+     * @param[in] quantized      Whether the data fed into the transform_context is already quantized. True means
+     *                           the data is already quantized. False means it's transform_context responsibility to
+     *                           quantize (scale) the data.
+     * @param[in] format_type    The type of the buffer sent to the transform_context.
+     * @return Upon success, returns Expected of a pointer to InputTransformContext.
+     *         Otherwise, returns Unexpected of ::hailo_status error.
+     */
+    static Expected<std::unique_ptr<InputTransformContext>> create(const hailo_stream_info_t &stream_info, bool quantized,
+        hailo_format_type_t format_type);
+
+    /**
+     * Transforms an input frame referred by @a src directly to the buffer referred by @a dst.
+     * 
+     * @param[in]  src          A src buffer to be transformed.
+     * @param[out] dst          A dst buffer that receives the transformed data.
+     * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns a ::hailo_status error.
+     */
+    hailo_status transform(const MemoryView src, MemoryView dst);
+
+    /**
+     * @return The size of the src frame on the host side in bytes.
+     */
+    size_t get_src_frame_size() const;
+
+    /**
+     * @return The size of the dst frame on the hw side in bytes.
+     */
+    size_t get_dst_frame_size() const;
+
+    /**
+     * Check whether or not a transformation is needed. 
+     *
+     * @param[in] src_image_shape          The shape of the src buffer (host shape).
+     * @param[in] src_format               The format of the src buffer (host format).
+     * @param[in] dst_image_shape          The shape of the dst buffer (hw shape).
+     * @param[in] dst_format               The format of the dst buffer (hw format).
+     * @param[in] quant_info               A ::hailo_quant_info_t object containing quantization information.
+     * @return Returns whether or not a transformation is needed.
+     * @note In case the function returns false, the src frame is ready to be sent to HW without any transformation.
+     */
+    static bool is_transformation_required(const hailo_3d_image_shape_t &src_image_shape, const hailo_format_t &src_format,
+        const hailo_3d_image_shape_t &dst_image_shape, const hailo_format_t &dst_format,
+        const hailo_quant_info_t &quant_info);
+
+    /**
+     * @return A human-readable description of the transformation parameters.
+     */
+    virtual std::string description() const;
+
+private:
+    InputTransformContext(size_t src_frame_size, const hailo_3d_image_shape_t &src_image_shape,
+        const hailo_format_t &src_format, size_t dst_frame_size, const hailo_3d_image_shape_t &dst_image_shape,
+        const hailo_format_t &dst_format, const hailo_quant_info_t &dst_quant_info, Buffer &&quant_buffer,
+        Buffer &&transpose_buffer, const bool should_quantize, const bool should_transpose, const bool should_reorder);
+
+    inline MemoryView quant_buffer() {
+        return MemoryView(m_quant_buffer);
+    }
+
+    inline MemoryView transpose_buffer() {
+        return MemoryView(m_transpose_buffer);
+    }
+
+    hailo_status transform_inner(const void *src_ptr, void *quant_buffer, void *dst_ptr, 
+        MemoryView transpose_buffer);
+
+    hailo_status quantize_stream(const void *src_ptr, void *quant_buffer);
+
+    const size_t m_src_frame_size;
+    const hailo_3d_image_shape_t m_src_image_shape;
+    const hailo_format_t m_src_format;
+    const size_t m_dst_frame_size;
+    const hailo_3d_image_shape_t m_dst_image_shape;
+    const hailo_format_t m_dst_format;
+    const hailo_quant_info_t m_dst_quant_info;
+    const bool m_should_quantize;
+    const bool m_should_transpose;
+    const bool m_should_reorder;
+
+    Buffer m_quant_buffer;
+    Buffer m_transpose_buffer;
+};
+
+typedef InputTransformContext InputTransformer DEPRECATED("InputTransformer is deprecated. One should use InputTransformContext");
+
+/*! Object used for output stream transformation*/
+class HAILORTAPI OutputTransformContext
+{
+public:
+
+    virtual ~OutputTransformContext() = default;
+    OutputTransformContext(OutputTransformContext &&) = default;
+    OutputTransformContext(const OutputTransformContext &) = delete;
+    OutputTransformContext& operator=(const OutputTransformContext &) = delete;
+
+    /**
+     * Creates output transform_context.
+     * 
+     * @param[in] src_image_shape          The shape of the src buffer to be transformed.
+     * @param[in] src_format               The format of the src buffer to be transformed.
+     * @param[in] dst_image_shape          The shape of the dst buffer that receives the transformed data.
+     * @param[in] dst_format               The format of the dst buffer that receives the transformed data.
+     * @param[in] dst_quant_info           A ::hailo_quant_info_t object containing quantization information.
+     * @param[in] nms_info                 A ::hailo_nms_info_t object containing nms information.
+     * @return Upon success, returns Expected of a pointer to OutputTransformContext.
+     *         Otherwise, returns Unexpected of ::hailo_status error.
+     */
+    static Expected<std::unique_ptr<OutputTransformContext>> create(const hailo_3d_image_shape_t &src_image_shape,
+        const hailo_format_t &src_format, const hailo_3d_image_shape_t &dst_image_shape,
+        const hailo_format_t &dst_format, const hailo_quant_info_t &dst_quant_info, const hailo_nms_info_t &nms_info);
+
+    /**
+     * Creates output transform_context.
+     * @param[in] stream_info       Creates transform_context that fits this stream info.
+     * @param[in] transform_params  A ::hailo_transform_params_t object containing user transformation parameters.
+     * @return Upon success, returns Expected of a pointer to OutputTransformContext.
+     *         Otherwise, returns Unexpected of ::hailo_status error.
+     */
+    static Expected<std::unique_ptr<OutputTransformContext>> create(const hailo_stream_info_t &stream_info,
+        const hailo_transform_params_t &transform_params);
+
+    /**
+     * Creates output transform_context with default transform parameters
+     * 
+     * @param[in] stream_info    Creates transform_context that fits this stream info.
+     * @param[in] quantized      Whether the data returned from the transform_context should be quantized. True
+     *                           means that the data returned to the user is still quantized. False means it's the
+     *                           transform_context responsibility to de-quantize (rescale) the data.
+     * @param[in] format_type    The type of the buffer returned from the transform_context
+     * @return Upon success, returns Expected of a pointer to OutputTransformContext.
+     *         Otherwise, returns Unexpected of ::hailo_status error.
+     */
+    static Expected<std::unique_ptr<OutputTransformContext>> create(const hailo_stream_info_t &stream_info, bool quantized,
+        hailo_format_type_t format_type);
+
+    /**
+     * Transforms an output frame referred by @a src directly to the buffer referred by @a dst.
+     * 
+     * @param[in]  src             A src buffer to be transformed.
+     * @param[out] dst             A dst buffer that receives the transformed data.
+     * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns a ::hailo_status error.
+     */
+    virtual hailo_status transform(const MemoryView src, MemoryView dst) = 0;
+
+    /**
+     * @return The size of the src frame on the hw side in bytes.
+     */
+    size_t get_src_frame_size() const;
+
+    /**
+     * @return The size of the dst frame on the host side in bytes.
+     */
+    size_t get_dst_frame_size() const;
+
+    /**
+     * Check whether or not a transformation is needed.
+     * 
+     * @param[in] src_image_shape          The shape of the src buffer (hw shape).
+     * @param[in] src_format               The format of the src buffer (hw format).
+     * @param[in] dst_image_shape          The shape of the dst buffer (host shape).
+     * @param[in] dst_format               The format of the dst buffer (host format).
+     * @param[in] quant_info               A ::hailo_quant_info_t object containing quantization information.
+     * @return Returns whether or not a transformation is needed.
+     * @note In case the function returns false, the src frame is already in the required format without any transformation.
+     */
+    static bool is_transformation_required(const hailo_3d_image_shape_t &src_image_shape, const hailo_format_t &src_format,
+        const hailo_3d_image_shape_t &dst_image_shape, const hailo_format_t &dst_format, 
+        const hailo_quant_info_t &quant_info);
+
+    /**
+     * @return A human-readable description of the transformation parameters.
+     */
+    virtual std::string description() const = 0;
+
+protected:
+    OutputTransformContext(size_t src_frame_size, const hailo_format_t &src_format, size_t dst_frame_size,
+        const hailo_format_t &dst_format, const hailo_quant_info_t &dst_quant_info, const bool should_quantize,
+        const bool should_transpose, const bool should_reorder);
+
+    const size_t m_src_frame_size;
+    const hailo_format_t m_src_format;
+    const size_t m_dst_frame_size;
+    const hailo_format_t m_dst_format;
+    const hailo_quant_info_t m_dst_quant_info;
+    const bool m_should_quantize;
+    const bool m_should_transpose;
+    const bool m_should_reorder;
+};
+
+typedef OutputTransformContext OutputTransformer DEPRECATED("OutputTransformer is deprecated. One should use OutputTransformContext");
+
+/*! Object used to demux muxed stream */
+class HAILORTAPI OutputDemuxer {
+public:
+    virtual ~OutputDemuxer() = default;
+
+    OutputDemuxer(const OutputDemuxer &) = delete;
+    OutputDemuxer& operator=(const OutputDemuxer &) = delete;
+    OutputDemuxer(OutputDemuxer &&) = default;
+
+    /**
+     * Creates an output demuxer for the given @a output_stream.
+     * 
+     * @param[in] output_stream     An OutputStream object to create demuxer from.
+     * @return Upon success, returns Expected of a pointer to OutputDemuxer.
+     *         Otherwise, returns Unexpected of ::hailo_status error.
+     */
+    static Expected<std::unique_ptr<OutputDemuxer>> create(OutputStream &output_stream);
+
+    /**
+     * @return The demuxer's edges information.
+     */
+    virtual std::vector<hailo_stream_info_t> get_edges_stream_info() = 0;
+
+    /**
+     * Demultiplexing an output frame referred by @a src directly into the buffers referred by @a dst_ptrs.
+     * 
+     * @param[in]  src             A buffer to be demultiplexed.
+     * @param[out] dst_ptrs        A mapping of output_name to its resulted demuxed data.
+     * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns a ::hailo_status error.
+     */
+    virtual hailo_status transform_demux(const MemoryView src, const std::map<std::string, MemoryView> &dst_ptrs) = 0;
+
+    /**
+     * Demultiplexing an output frame referred by @a src directly into the buffers referred by @a raw_buffers.
+     * 
+     * @param[in]  src               A buffer to be demultiplexed.
+     * @param[out] raw_buffers       A vector of buffers that receives the demultiplexed data read from the stream.
+     *                               The order of @a raw_buffers vector will remain as is.
+     * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns a ::hailo_status error.
+     */
+    virtual hailo_status transform_demux(const MemoryView src, std::vector<MemoryView> &raw_buffers) = 0;
+
+    const size_t src_frame_size;
+
+protected:
+    OutputDemuxer(size_t src_frame_size) : src_frame_size(src_frame_size) {}
+};
+
+/** @defgroup group_transform Transformations functions
+ *  @{
+ */
+
+/**
+ * Transposed @a src buffer (whose shape and format are given) to @a dst buffer (whose shape will be
+ * the same as `shape` but the height and width are replaced)
+ * 
+ * @param[in]  src           A buffer containing the data to be transposed.
+ * @param[in]  shape         The shape of @a src.
+ * @param[in]  format        The format of @a src.
+ * @param[out] dst           The dst buffer to contain the transposed data.
+ * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns a ::hailo_status error.
+ * @note assumes src and dst not overlas
+ */
+HAILORTAPI hailo_status transpose_buffer(const MemoryView src, const hailo_3d_image_shape_t &shape,
+    const hailo_format_t &format, MemoryView dst);
+
+/**
+ * @return The size of transpose buffer by its src shape and dst format type.
+ */
+HAILORTAPI inline size_t get_transpose_buffer_size(const hailo_3d_image_shape_t &src_shape, const hailo_format_type_t &dst_type)
+{
+    return HailoRTCommon::get_shape_size(src_shape) * HailoRTCommon::get_data_bytes(dst_type);
+}
+
+/**
+ * Fuse multiple defused NMS buffers referred by @a buffers to the buffer referred by @a dst.
+ * This function expects @a buffers to be ordered by their @a class_group_index (lowest to highest).
+ * 
+ * @param[in]  buffers                  A vector of buffers to be fused.
+ * @param[in]  infos_of_buffers         A vector of ::hailo_nms_info_t containing the @a buffers information.
+ * @param[out] dst                      A pointer to a buffer which will contain the fused buffer.
+ * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns a ::hailo_status error.
+ */
+HAILORTAPI hailo_status fuse_buffers(const std::vector<MemoryView> &buffers,
+    const std::vector<hailo_nms_info_t> &infos_of_buffers, MemoryView dst);
+
+/** @} */ // end of group_transform
+
+} /* namespace hailort */
+
+#endif /* _HAILO_TRANSFORM_HPP_ */
diff --git a/hailort/libhailort/include/hailo/vdevice.hpp b/hailort/libhailort/include/hailo/vdevice.hpp
new file mode 100644 (file)
index 0000000..4ae48cc
--- /dev/null
@@ -0,0 +1,85 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file vdevice.hpp
+ * @brief Hailo virtual device representation of multiple physical pcie devices
+ **/
+
+#ifndef _HAILO_VDEVICE_HPP_
+#define _HAILO_VDEVICE_HPP_
+
+#include "hailo/hailort.h"
+#include "hailo/expected.hpp"
+#include "hailo/hef.hpp"
+#include "hailo/network_group.hpp"
+#include "hailo/device.hpp"
+
+namespace hailort
+{
+
+/*! Represents a bundle of physical devices. */
+class HAILORTAPI VDevice
+{
+public:
+
+    /**
+     * Creates a vdevice.
+     * 
+     * @param[in]  params        A @a hailo_vdevice_params_t.
+     * @return Upon success, returns Expected of a unique_ptr to VDevice object.
+     *         Otherwise, returns Unexpected of ::hailo_status error.
+     */
+    static Expected<std::unique_ptr<VDevice>> create(const hailo_vdevice_params_t &params);
+
+    /**
+     * Creates a vdevice.
+     * 
+     * @return Upon success, returns Expected of a unique_ptr to VDevice object.
+     *         Otherwise, returns Unexpected of ::hailo_status error.
+     * @note calling this create method will apply default vdevice params.
+     */
+    static Expected<std::unique_ptr<VDevice>> create();
+
+    /**
+     * Configure the vdevice from an hef.
+     *
+     * @param[in] hef                         A reference to an Hef object to configure the vdevice by.
+     * @param[in] configure_params            A map of configured network group name and parameters.
+     * @return Upon success, returns Expected of a vector of configured network groups.
+     *         Otherwise, returns Unexpected of ::hailo_status error.
+     */
+    virtual Expected<ConfiguredNetworkGroupVector> configure(Hef &hef,
+        const NetworkGroupsParamsMap &configure_params={}) = 0;
+
+    /**
+     * Gets the underlying physical devices.
+     * 
+     * @return Upon success, returns Expected of a vector of device objects.
+     *         Otherwise, returns Unexpected of ::hailo_status error.
+     * @note The returned physical devices are held in the scope of @a vdevice.
+     */
+    virtual Expected<std::vector<std::reference_wrapper<Device>>> get_physical_devices() = 0;
+
+    /**
+     * Gets the devices informations.
+     * 
+     * @return Upon success, returns Expected of a vector of ::hailo_pcie_device_info_t objects.
+     *         Otherwise, returns Unexpected of ::hailo_status error.
+     */
+    virtual Expected<std::vector<hailo_pcie_device_info_t>> get_physical_devices_infos() = 0;
+
+    virtual ~VDevice() = default;
+    VDevice(const VDevice &) = delete;
+    VDevice &operator=(const VDevice &) = delete;
+    VDevice(VDevice &&) = delete;
+    VDevice &operator=(VDevice &&other) = delete;
+
+protected:
+    VDevice() = default;
+};
+
+} /* namespace hailort */
+
+#endif /* _HAILO_VDEVICE_HPP_ */
diff --git a/hailort/libhailort/include/hailo/vstream.hpp b/hailort/libhailort/include/hailo/vstream.hpp
new file mode 100644 (file)
index 0000000..3b62be9
--- /dev/null
@@ -0,0 +1,311 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file vstream.hpp
+ * @brief Virtual Stream
+ **/
+
+#ifndef _HAILO_VSTREAM_HPP_
+#define _HAILO_VSTREAM_HPP_
+
+#include "hailo/transform.hpp"
+#include "hailo/stream.hpp"
+#include "hailo/network_group.hpp"
+#include "hailo/runtime_statistics.hpp"
+
+namespace hailort
+{
+
+class SinkElement;
+class PipelineElement;
+
+/*! Virtual stream base class */
+class HAILORTAPI BaseVStream
+{
+public:
+    BaseVStream(BaseVStream &&other) noexcept;
+    BaseVStream& operator=(BaseVStream &&other) noexcept;
+    virtual ~BaseVStream();
+
+    /**
+     * @return the size of a virtual stream's frame on the host side in bytes.
+     * @note The size could be affected by the format type - using UINT16, or by the data not being quantized yet.
+     */
+    size_t get_frame_size() const;
+
+    /**
+     * @return ::hailo_vstream_info_t object containing the vstream info.
+     */
+    const hailo_vstream_info_t &get_info() const;
+
+    /**
+     * @return ::hailo_format_t object containing the user buffer format.
+     */
+    const hailo_format_t &get_user_buffer_format() const;
+
+    /**
+     * @return the virtual stream's name.
+     */
+    std::string name() const;
+
+    /**
+     * @return the virtual stream's network name.
+     */
+    std::string network_name() const;
+
+    /**
+     * Gets a reference to a map between pipeline element names to their respective fps accumulators.
+     * These accumulators measure the net throughput of each pipeline element. This means that the effects
+     * of queuing in the vstream pipeline (between elements) are not accounted for by these accumulators.
+     * 
+     * @return A const reference to a map between pipeline element names to their respective fps accumulators.
+     * @note FPS accumulators are created for pipeline elements, if the vstream is created with the flag
+     *       ::HAILO_PIPELINE_ELEM_STATS_MEASURE_FPS set under the @a pipeline_elements_stats_flags field of ::hailo_vstream_params_t.
+     */
+    const std::map<std::string, AccumulatorPtr> &get_fps_accumulators() const;
+    
+    /**
+     * Gets a reference to a map between pipeline element names to their respective latency accumulators.
+     * These accumulators measure the net latency of each pipeline element. This means that the effects
+     * of queuing in the vstream pipeline (between elements) are not accounted for by these accumulators.
+     * 
+     * @return A const reference to a map between pipeline element names to their respective latency accumulators.
+     * @note Latency accumulators are created for pipeline elements, if the vstream is created with the flag
+     *       ::HAILO_PIPELINE_ELEM_STATS_MEASURE_LATENCY set under the @a pipeline_elements_stats_flags field of ::hailo_vstream_params_t.
+     */
+    const std::map<std::string, AccumulatorPtr> &get_latency_accumulators() const;
+
+    /**
+     * Gets a reference to a map between pipeline element names to their respective queue size accumulators.
+     * These accumulators measure the number of free buffers in the queue, right before a buffer is removed
+     * from the queue to be used.
+     * 
+     * @return A const reference to a map between pipeline element names to their respective queue size accumulators.
+     * @note Queue size accumulators are created for pipeline elements, if the vstream is created with the flag
+     *       ::HAILO_PIPELINE_ELEM_STATS_MEASURE_QUEUE_SIZE set under the @a pipeline_elements_stats_flags field of ::hailo_vstream_params_t.
+     */
+    const std::map<std::string, std::vector<AccumulatorPtr>> &get_queue_size_accumulators() const;
+    
+    /**
+     * Gets a shared_ptr to the vstream's latency accumulator. This accumulator measures the time it takes for a frame to pass
+     * through an entire vstream pipeline. Specifically:
+     * * For InputVStream%s: The time it takes a frame from the call to InputVStream::write, until the frame is written to the HW.
+     * * For OutputVStream%s: The time it takes a frame from being read from the HW, until it's returned to the user via OutputVStream::read.
+     * 
+     * @return A shared pointer to the vstream's latency accumulator.
+     * @note A pipeline-wide latency accumulator is created for the vstream, if the vstream is created with the flag
+     *       ::HAILO_VSTREAM_STATS_MEASURE_LATENCY set under the @a vstream_stats_flags field of ::hailo_vstream_params_t.
+     */
+    AccumulatorPtr get_pipeline_latency_accumulator() const;
+    
+    /**
+     * @return A const reference to the @a PipelineElement%s that this vstream is comprised of.
+     */
+    const std::vector<std::shared_ptr<PipelineElement>> &get_pipeline() const;
+
+protected:
+    BaseVStream(const hailo_vstream_info_t &vstream_info, const hailo_vstream_params_t &vstream_params,
+        std::shared_ptr<PipelineElement> pipeline_entry, std::vector<std::shared_ptr<PipelineElement>> &&pipeline,
+        std::shared_ptr<std::atomic<hailo_status>> &&pipeline_status, EventPtr shutdown_event, AccumulatorPtr pipeline_latency_accumulator,
+        EventPtr &&network_group_activated_event, hailo_status &output_status);
+
+    virtual std::string get_pipeline_description() const = 0;
+    hailo_status start_vstream();
+    hailo_status stop_vstream();
+    hailo_status stop_and_clear();
+
+    hailo_vstream_info_t m_vstream_info;
+    hailo_vstream_params_t m_vstream_params;
+    bool m_measure_pipeline_latency;
+    std::shared_ptr<PipelineElement> m_entry_element;
+    std::vector<std::shared_ptr<PipelineElement>> m_pipeline;
+    volatile bool m_is_activated;
+    std::shared_ptr<std::atomic<hailo_status>> m_pipeline_status;
+    EventPtr m_shutdown_event;
+    EventPtr m_network_group_activated_event;
+    std::map<std::string, AccumulatorPtr> m_fps_accumulators;
+    std::map<std::string, AccumulatorPtr> m_latency_accumulators;
+    std::map<std::string, std::vector<AccumulatorPtr>> m_queue_size_accumulators;
+    AccumulatorPtr m_pipeline_latency_accumulator;
+};
+
+/*! Input virtual stream, used to stream data to device */
+class HAILORTAPI InputVStream : public BaseVStream
+{
+public:
+    static Expected<InputVStream> create(const hailo_vstream_info_t &vstream_info,
+        const hailo_vstream_params_t &vstream_params, std::shared_ptr<PipelineElement> pipeline_entry,
+        std::shared_ptr<SinkElement> pipeline_exit, std::vector<std::shared_ptr<PipelineElement>> &&pipeline,
+        std::shared_ptr<std::atomic<hailo_status>> &&pipeline_status, EventPtr shutdown_event, EventPtr network_group_activated_event,
+        AccumulatorPtr pipeline_latency_accumulator);
+    InputVStream(InputVStream &&other) noexcept = default;
+    InputVStream &operator=(InputVStream &&other) noexcept = default;
+    virtual ~InputVStream() = default;
+
+    /**
+     * Writes @a buffer to hailo device.
+     *
+     * @param[in] buffer            The buffer containing the data to be sent to device.
+     *                              The buffer's format can be obtained by get_user_buffer_format(), 
+     *                              and the buffer's shape can be obtained by calling get_info().shape.
+     * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns a ::hailo_status error.
+     */
+    hailo_status write(const MemoryView &buffer);
+
+    /**
+     * Flushes the vstream pipeline buffers. This will block until the vstream pipeline is clear.
+     * 
+     * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns a ::hailo_status error.
+     */
+    hailo_status flush();
+
+    /**
+     * Clears the vstreams' pipeline buffers.
+     * 
+     * @param[in] vstreams            The vstreams to be cleared.
+     * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns a ::hailo_status error.
+     */
+    static hailo_status clear(std::vector<InputVStream> &vstreams);
+    
+    /**
+     * Clears the vstreams' pipeline buffers.
+     * 
+     * @param[in] vstreams            The vstreams to be cleared.
+     * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns a ::hailo_status error.
+     */
+    static hailo_status clear(std::vector<std::reference_wrapper<InputVStream>> &vstreams);
+
+private:
+    InputVStream(const hailo_vstream_info_t &vstream_info, const hailo_vstream_params_t &vstream_params,
+        std::shared_ptr<PipelineElement> pipeline_entry, std::vector<std::shared_ptr<PipelineElement>> &&pipeline,
+        std::shared_ptr<std::atomic<hailo_status>> &&pipeline_status, EventPtr shutdown_event, AccumulatorPtr pipeline_latency_accumulator,
+        EventPtr network_group_activated_event, hailo_status &output_status);
+
+    virtual std::string get_pipeline_description() const override;
+    friend class VStreamsBuilderUtils;
+};
+
+/*! Output virtual stream, used to read data from device */
+class HAILORTAPI OutputVStream : public BaseVStream
+{
+public:
+    static Expected<OutputVStream> create(
+        const hailo_vstream_info_t &vstream_info, const hailo_vstream_params_t &vstream_params,
+        std::shared_ptr<PipelineElement> pipeline_entry, std::vector<std::shared_ptr<PipelineElement>> &&pipeline,
+        std::shared_ptr<std::atomic<hailo_status>> &&pipeline_status, EventPtr shutdown_event,
+        EventPtr network_group_activated_event, AccumulatorPtr pipeline_latency_accumulator);
+    OutputVStream(OutputVStream &&other) noexcept = default;
+    OutputVStream &operator=(OutputVStream &&other) noexcept = default;
+    virtual ~OutputVStream() = default;
+
+    /**
+     * Reads data from hailo device into @a buffer.
+     *
+     * @param[in] buffer            The buffer to read data into.
+     *                              The buffer's format can be obtained by get_user_buffer_format(), 
+     *                              and the buffer's shape can be obtained by calling get_info().shape.
+     * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns a ::hailo_status error.
+     */
+    hailo_status read(MemoryView buffer);
+
+    /**
+     * Clears the vstreams' pipeline buffers.
+     * 
+     * @param[in] vstreams            The vstreams to be cleared.
+     * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns a ::hailo_status error.
+     * @note If not all output vstreams from the same group are passed together, it will cause an <b> undefined behavior </b>.
+     *       See ConfiguredNetworkGroup::get_output_vstream_groups, to get the output vstreams groups.
+     */
+    static hailo_status clear(std::vector<OutputVStream> &vstreams);
+
+    /**
+     * Clears the vstreams' pipeline buffers.
+     * 
+     * @param[in] vstreams            The vstreams to be cleared.
+     * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns a ::hailo_status error.
+     * @note If not all output vstreams from the same group are passed together, it will cause an <b> undefined behavior </b>.
+     *       See ConfiguredNetworkGroup::get_output_vstream_groups, to get the output vstreams groups.
+     */
+    static hailo_status clear(std::vector<std::reference_wrapper<OutputVStream>> &vstreams);
+
+private:
+    OutputVStream(const hailo_vstream_info_t &vstream_info, const hailo_vstream_params_t &vstream_params,
+        std::shared_ptr<PipelineElement> pipeline_entry, std::vector<std::shared_ptr<PipelineElement>> &&pipeline,
+        std::shared_ptr<std::atomic<hailo_status>> &&pipeline_status, EventPtr shutdown_event, AccumulatorPtr pipeline_latency_accumulator,
+        EventPtr network_group_activated_event, hailo_status &output_status);
+
+    virtual std::string get_pipeline_description() const override;
+    friend class VStreamsBuilderUtils;
+};
+
+/*! Contains the virtual streams creation functions */
+class HAILORTAPI VStreamsBuilder
+{
+public:
+    VStreamsBuilder() = delete;
+
+    /**
+     * Creates input virtual streams and output virtual streams.
+     *
+     * @param[in] net_group            Configured network group that owns the streams.
+     * @param[in] quantized            Default quantized parameter for all virtual streams.
+     *                                 For input vstreams indicates whether the data fed into the chip is already quantized.
+     *                                 True means the data is already quantized.
+     *                                 False means it's HailoRT's responsibility to quantize (scale) the data.
+     *                                 For output vstreams indicates whether the data returned from the device should be quantized.
+     *                                 True means that the data returned to the user is still quantized.
+     *                                 False means it's HailoRT's responsibility to de-quantize (rescale) the data.
+     * @param[in] format_type          The default format type for all virtual streams.
+     * @param[in] network_name         Request to create vstreams of specific network inside the configured network group.
+     *                                 If not passed, all the networks in the network group will be addressed.
+     * @return Upon success, returns Expected of a pair of input vstreams and output vstreams.
+     *         Otherwise, returns Unexpected of ::hailo_status error.
+     */
+    static Expected<std::pair<std::vector<InputVStream>, std::vector<OutputVStream>>> create_vstreams(
+        ConfiguredNetworkGroup &net_group, bool quantized, hailo_format_type_t format_type,
+        const std::string &network_name="");
+
+    /**
+     * Creates input virtual streams and output virtual streams.
+     *
+     * @param[in] net_group            Configured network group that owns the streams.
+     * @param[in] vstreams_params      A ::hailo_vstream_params_t containing default params for all virtual streams.
+     * @param[in] network_name         Request to create vstreams of specific network inside the configured network group.
+     *                                 If not passed, all the networks in the network group will be addressed.
+     * @return Upon success, returns Expected of a vector of input virtual streams.
+     *         Otherwise, returns Unexpected of ::hailo_status error.
+     */
+    static Expected<std::pair<std::vector<InputVStream>, std::vector<OutputVStream>>> create_vstreams(
+        ConfiguredNetworkGroup &net_group, const hailo_vstream_params_t &vstreams_params,
+        const std::string &network_name="");
+
+    /**
+     * Creates input virtual streams.
+     *
+     * @param[in] net_group            Configured network group that owns the streams.
+     * @param[in] inputs_params        Map of input vstreams <name, params> to create input vstreams from.
+     * @return Upon success, returns Expected of a vector of input virtual streams.
+     *         Otherwise, returns Unexpected of ::hailo_status error.
+     */
+    static Expected<std::vector<InputVStream>> create_input_vstreams(ConfiguredNetworkGroup &net_group,
+        const std::map<std::string, hailo_vstream_params_t> &inputs_params);
+
+    /**
+     * Creates output virtual streams.
+     *
+     * @param[in] net_group            Configured network group that owns the streams.
+     * @param[in] outputs_params       Map of output vstreams <name, params> to create output vstreams from.
+     * @return Upon success, returns Expected of a vector of output virtual streams.
+     *         Otherwise, returns Unexpected of ::hailo_status error.
+     * @note If not creating all output vstreams together, one should make sure all vstreams from the same group are created together.
+     *       See ConfiguredNetworkGroup::make_output_vstream_params_groups
+     */
+    static Expected<std::vector<OutputVStream>> create_output_vstreams(ConfiguredNetworkGroup &net_group,
+        const std::map<std::string, hailo_vstream_params_t> &outputs_params);
+};
+
+} /* namespace hailort */
+
+#endif /* _HAILO_VSTREAM_HPP_ */
diff --git a/hailort/libhailort/src/CMakeLists.txt b/hailort/libhailort/src/CMakeLists.txt
new file mode 100644 (file)
index 0000000..3a59d9c
--- /dev/null
@@ -0,0 +1,180 @@
+cmake_minimum_required(VERSION 3.0.0)
+
+find_package(Threads REQUIRED)
+
+include(${CMAKE_CURRENT_SOURCE_DIR}/../cmake/common_compiler_options.cmake)
+
+FUNCTION(relative_to_absolute_paths output)
+    SET(listVar "")
+    FOREACH(rel_path ${ARGN})
+        get_filename_component(abs_path "${rel_path}" ABSOLUTE)
+        LIST(APPEND listVar ${abs_path})
+    ENDFOREACH(rel_path)
+    SET(${output} "${listVar}" PARENT_SCOPE)
+ENDFUNCTION(relative_to_absolute_paths)
+
+add_subdirectory(os)
+
+set(HAILORT_CPP_SOURCES
+    device.cpp
+    device_internal.cpp
+    control.cpp
+    stream.cpp
+    stream_internal.cpp
+    transform.cpp
+    buffer.cpp
+    network_rate_calculator.cpp
+    hailort_logger.cpp
+    hailort.cpp
+    hailort_common.cpp
+    sensor_config_utils.cpp
+    pipeline.cpp
+
+    eth_device.cpp
+    eth_stream.cpp
+    udp.cpp
+
+    hef.cpp
+    context_switch/hef_metadata.cpp
+    context_switch/hcp_config_manager.cpp
+    context_switch/hcp_config_network_group.cpp
+    context_switch/hcp_config_activated_network_group.cpp
+    context_switch/vdma_config_manager.cpp
+    context_switch/vdma_config_network_group.cpp
+    context_switch/vdma_config_activated_network_group.cpp
+    context_switch/network_group.cpp
+    context_switch/resource_manager.cpp
+    intermediate_buffer.cpp
+    d2h_events_parser.cpp
+    mipi_stream.cpp
+
+    hlpcie.cpp
+    vdma_channel.cpp
+    vdma_buffer.cpp
+    vdma_descriptor_list.cpp
+    vdma_device.cpp
+    vdma_stream.cpp
+
+    pcie_device.cpp
+    pcie_stream.cpp
+
+    core_device.cpp
+    core_stream.cpp
+
+    vdevice.cpp
+    vdevice_stream.cpp
+
+    control_protocol.cpp
+
+    vstream.cpp
+    inference_pipeline.cpp
+)
+
+set(common_dir "${PROJECT_SOURCE_DIR}/common/src")
+set(COMMON_C_SOURCES
+    ${common_dir}/firmware_status.c
+    ${common_dir}/md5.c
+    ${common_dir}/firmware_header_utils.c
+)
+
+# Global var to be used by test projects to compile hailort sources
+relative_to_absolute_paths(HAILORT_CPP_SOURCES ${HAILORT_CPP_SOURCES})
+relative_to_absolute_paths(C_OS_SOURCES ${C_OS_SOURCES})
+relative_to_absolute_paths(COMMON_C_SOURCES ${COMMON_C_SOURCES})
+relative_to_absolute_paths(HAILO_OS_DIR ${HAILO_OS_DIR})
+relative_to_absolute_paths(HAILO_FULL_OS_DIR ${HAILO_FULL_OS_DIR})
+set(HAILO_OS_DIR ${HAILO_OS_DIR} CACHE INTERNAL "Absolute path of os-dir")
+set(HAILO_FULL_OS_DIR ${HAILO_FULL_OS_DIR} CACHE INTERNAL "Absolute Full path of os-dir")
+set(HAILORT_CPP_SOURCES ${HAILORT_CPP_SOURCES} CACHE INTERNAL "Absolute paths of hailort's cpp source files")
+set(HAILORT_CPP_OS_SOURCES ${HAILORT_CPP_OS_SOURCES} CACHE INTERNAL "Absolute paths of os-related source files")
+set(COMMON_C_SOURCES ${COMMON_C_SOURCES} CACHE INTERNAL "Absolute paths of common source files")
+set(HAILORT_SRCS_ABS ${HAILORT_CPP_SOURCES} ${HAILORT_CPP_OS_SOURCES} ${HAILORT_COMMON_CPP_SOURCES} ${COMMON_C_SOURCES} CACHE INTERNAL "All absolute paths of hailort's source files")
+
+SET_SOURCE_FILES_PROPERTIES(${C_SOURCES} PROPERTIES LANGUAGE CXX)
+add_library(libhailort SHARED ${HAILORT_SRCS_ABS})
+
+# Include libraries
+if(WIN32)
+    target_link_libraries(libhailort PRIVATE
+        Ws2_32
+        Iphlpapi
+        Shlwapi
+    )
+    set_property(TARGET libhailort PROPERTY
+        MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>DLL")
+else()
+    target_link_libraries(libhailort PRIVATE
+        m # libmath
+        atomic
+    )
+    set(THREADS_PREFER_PTHREAD_FLAG ON)
+    # Hack to support cross-compilation - https://stackoverflow.com/a/49086560
+    set(THREADS_PTHREAD_ARG "0" CACHE STRING "Result from TRY_RUN" FORCE)
+endif()
+
+target_link_libraries(libhailort PRIVATE Threads::Threads)
+target_link_libraries(libhailort PRIVATE hef_proto)
+target_link_libraries(libhailort PRIVATE spdlog::spdlog)
+target_link_libraries(libhailort PRIVATE readerwriterqueue)
+
+set(HAILORT_PUBLIC_HEADERS
+    ${HAILORT_INC_DIR}/hailo/hailort.h
+    ${HAILORT_INC_DIR}/hailo/platform.h
+
+    ${HAILORT_INC_DIR}/hailo/buffer.hpp
+    ${HAILORT_INC_DIR}/hailo/device.hpp
+    ${HAILORT_INC_DIR}/hailo/event.hpp
+    ${HAILORT_INC_DIR}/hailo/expected.hpp
+    ${HAILORT_INC_DIR}/hailo/hailort_common.hpp
+    ${HAILORT_INC_DIR}/hailo/hailort.hpp
+    ${HAILORT_INC_DIR}/hailo/hef.hpp
+    ${HAILORT_INC_DIR}/hailo/network_group.hpp
+    ${HAILORT_INC_DIR}/hailo/stream.hpp
+    ${HAILORT_INC_DIR}/hailo/transform.hpp
+    ${HAILORT_INC_DIR}/hailo/vstream.hpp
+    ${HAILORT_INC_DIR}/hailo/inference_pipeline.hpp
+    ${HAILORT_INC_DIR}/hailo/runtime_statistics.hpp
+    ${HAILORT_INC_DIR}/hailo/network_rate_calculator.hpp
+    ${HAILORT_INC_DIR}/hailo/vdevice.hpp
+    ${HAILORT_INC_DIR}/hailo/quantization.hpp
+)
+
+set_target_properties(libhailort PROPERTIES
+    PUBLIC_HEADER "${HAILORT_PUBLIC_HEADERS}"
+    PREFIX ""
+    VERSION ${HAILORT_MAJOR_VERSION}.${HAILORT_MINOR_VERSION}.${HAILORT_REVISION_VERSION}
+    # SOVERSION ${HAILORT_MAJOR_VERSION}
+
+    CXX_STANDARD              14
+    CXX_STANDARD_REQUIRED     YES
+    CXX_EXTENSIONS            NO
+    C_VISIBILITY_PRESET       hidden
+    CXX_VISIBILITY_PRESET     hidden
+    # VISIBILITY_INLINES_HIDDEN YES
+)
+
+target_compile_options(libhailort PRIVATE ${HAILORT_COMPILE_OPTIONS})
+disable_exceptions(libhailort)
+exclude_archive_libs_symbols(libhailort)
+
+target_include_directories(libhailort
+    PUBLIC
+    $<BUILD_INTERFACE:${HAILORT_INC_DIR}>
+    $<BUILD_INTERFACE:${HAILORT_COMMON_DIR}>
+    PRIVATE
+    $<BUILD_INTERFACE:${HAILORT_SRC_DIR}>
+    $<BUILD_INTERFACE:${COMMON_INC_DIR}>
+    $<BUILD_INTERFACE:${DRIVER_INC_DIR}>
+)
+
+target_compile_definitions(libhailort PUBLIC
+    -DHAILORT_MAJOR_VERSION=${HAILORT_MAJOR_VERSION}
+    -DHAILORT_MINOR_VERSION=${HAILORT_MINOR_VERSION}
+    -DHAILORT_REVISION_VERSION=${HAILORT_REVISION_VERSION}
+)
+
+install(TARGETS libhailort
+   LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+   CONFIGURATIONS Release
+   PUBLIC_HEADER DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/hailo"
+   CONFIGURATIONS Release)
diff --git a/hailort/libhailort/src/buffer.cpp b/hailort/libhailort/src/buffer.cpp
new file mode 100644 (file)
index 0000000..7cfe613
--- /dev/null
@@ -0,0 +1,257 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file buffer.cpp
+ * @brief TODO: brief
+ *
+ * TODO: doc
+ **/
+
+#include "hailo/buffer.hpp"
+#include "common/logger_macros.hpp"
+#include "common/utils.hpp"
+
+#include <algorithm>
+#include <string>
+#include <cstring>
+#include <iostream>
+#include <iomanip>
+
+namespace hailort
+{
+
+Buffer::Buffer() :
+    m_data(nullptr),
+    m_size(0)
+{}
+
+Buffer::Buffer(Buffer&& other) :
+    m_data(std::move(other.m_data)),
+    m_size(std::exchange(other.m_size, 0))
+{}
+
+Expected<Buffer> Buffer::create(size_t size)
+{
+    std::unique_ptr<uint8_t[]> data(new (std::nothrow) uint8_t[size]);
+    if (data == nullptr) {
+        LOGGER__ERROR("Failed allocating {} bytes", size);
+        return make_unexpected(HAILO_OUT_OF_HOST_MEMORY);
+    }
+
+    return Buffer(std::move(data), size);
+}
+
+Expected<Buffer> Buffer::create(size_t size, uint8_t default_value)
+{
+    auto buffer = create(size);
+    CHECK_EXPECTED(buffer);
+    std::memset(static_cast<void*>(buffer->m_data.get()), default_value, size);
+    return buffer;
+}
+
+Expected<Buffer> Buffer::create(const uint8_t *src, size_t size)
+{
+    auto buffer = create(size);
+    CHECK_EXPECTED(buffer);
+    std::memcpy(static_cast<void*>(buffer->m_data.get()), static_cast<const void*>(src), size);
+    return buffer;
+}
+
+Expected<Buffer> Buffer::create(std::initializer_list<uint8_t> init)
+{
+    auto buffer = create(init.size());
+    CHECK_EXPECTED(buffer);
+    size_t index = 0;
+    for (const auto& n : init) {
+        // Hackzzz
+        buffer->m_data[index++] = n;
+    }
+
+    return buffer;
+}
+
+Expected<Buffer> Buffer::copy() const
+{
+    return Buffer::create(m_data.get(), m_size);
+}
+
+Buffer& Buffer::operator=(Buffer&& other)
+{
+    m_data = std::move(other.m_data);
+    m_size = std::exchange(other.m_size, 0);
+    return *this;
+}
+
+bool Buffer::operator==(const Buffer& rhs) const
+{
+    if (m_size != rhs.m_size) {
+        return false;
+    }
+    return (0 == std::memcmp(data(), rhs.data(), m_size));
+}
+
+bool Buffer::operator!=(const Buffer& rhs) const
+{
+    if (m_size != rhs.m_size) {
+        return true;
+    }
+    return (0 != std::memcmp(data(), rhs.data(), m_size));
+}
+
+uint8_t& Buffer::operator[](size_t pos)
+{
+    assert(pos < m_size);
+    return m_data[pos];
+}
+
+const uint8_t& Buffer::operator[](size_t pos) const
+{
+    assert(pos < m_size);
+    return m_data[pos];
+}
+
+Buffer::iterator Buffer::begin()
+{
+    return iterator(data());
+}
+
+Buffer::iterator Buffer::end()
+{
+    return iterator(data() + m_size);
+}
+
+uint8_t* Buffer::data() noexcept
+{
+    return m_data.get();
+}
+
+const uint8_t* Buffer::data() const noexcept
+{
+    return m_data.get();
+}
+
+size_t Buffer::size() const noexcept
+{
+    return m_size;
+}
+
+uint8_t* Buffer::release() noexcept
+{
+    m_size = 0;
+    return m_data.release();
+}
+
+std::string Buffer::to_string() const
+{
+    for (size_t i = 0; i < m_size; i++) {
+        if (m_data[i] == 0) {
+            // We'll return a string that ends at the first null in the buffer
+            return std::string(reinterpret_cast<const char*>(m_data.get()));
+        }
+    }
+
+    return std::string(reinterpret_cast<const char*>(m_data.get()), m_size);
+}
+
+// Note: This is a friend function
+std::ostream& operator<<(std::ostream& stream, const Buffer& buffer)
+{
+    for (size_t i = 0; i < buffer.size(); i++) {
+        // Without the cast, the numbers are printed as though they were chars
+        stream << std::hex << std::setfill('0') << std::setw(2) << static_cast<int>(buffer[i]) << "\t";
+    }
+    stream << "\n[size = " << std::dec << buffer.size() << "]";
+
+    return stream;
+}
+
+uint16_t Buffer::as_uint16() const
+{
+    return as_type<uint16_t>();
+}
+
+uint32_t Buffer::as_uint32() const
+{
+    return as_type<uint32_t>();
+}
+
+uint64_t Buffer::as_uint64() const
+{
+    return as_type<uint64_t>();
+}
+
+uint16_t& Buffer::as_uint16()
+{
+    return as_type<uint16_t>();
+}
+
+uint32_t& Buffer::as_uint32()
+{
+    return as_type<uint32_t>();
+}
+
+uint64_t& Buffer::as_uint64()
+{
+    return as_type<uint64_t>();
+}
+
+Buffer::Buffer(std::unique_ptr<uint8_t[]> data, size_t size) :
+    m_data(std::move(data)),
+    m_size(size)
+ {}
+
+MemoryView::MemoryView() :
+    m_data(nullptr),
+    m_size(0)
+{}
+
+MemoryView::MemoryView(Buffer &buffer) :
+    m_data(buffer.data()),
+    m_size(buffer.size())
+{}
+
+MemoryView::MemoryView(void *data, size_t size) :
+    m_data(data),
+    m_size(size)
+{}
+
+const MemoryView MemoryView::create_const(const void *data, size_t size)
+{
+    return std::move(MemoryView(const_cast<void *>(data), size));
+}
+
+uint8_t* MemoryView::data() noexcept
+{
+    return reinterpret_cast<uint8_t*>(m_data);
+}
+
+const uint8_t* MemoryView::data() const noexcept
+{
+    return reinterpret_cast<const uint8_t*>(m_data);
+}
+
+size_t MemoryView::size() const noexcept
+{
+    return m_size;
+}
+
+bool MemoryView::empty() const noexcept
+{
+    return (m_data == nullptr);
+}
+
+// Note: This is a friend function
+std::ostream& operator<<(std::ostream& stream, const MemoryView& buffer)
+{
+    for (size_t i = 0; i < buffer.size(); i++) {
+        // Without the cast, the numbers are printed as though they were chars
+        stream << std::hex << std::setfill('0') << std::setw(2) << static_cast<int>(buffer.data()[i]) << "\t";
+    }
+    stream << "\n[size = " << std::dec << buffer.size() << "]";
+
+    return stream;
+}
+
+} /* namespace hailort */
diff --git a/hailort/libhailort/src/context_switch/active_network_group_holder.hpp b/hailort/libhailort/src/context_switch/active_network_group_holder.hpp
new file mode 100644 (file)
index 0000000..e8d1da6
--- /dev/null
@@ -0,0 +1,53 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file active_network_group_holder.hpp
+ * @brief place_holder stored in ConfigManager indicating which ConfiguredNetworkGroup is currently active
+ *
+ **/
+
+#ifndef _HAILO_CONTEXT_SWITCH_ACTIVE_NETWORK_GROUP_HOLDER_HPP_
+#define _HAILO_CONTEXT_SWITCH_ACTIVE_NETWORK_GROUP_HOLDER_HPP_
+
+// TODO: can’t we just have ActiveNetworkGroup ref under device?
+
+#include "hailo/hailort.h"
+#include "common/utils.hpp"
+
+namespace hailort
+{
+
+template <typename T> 
+class ActiveNetworkGroupHolder final
+{
+  public:
+    ActiveNetworkGroupHolder() : m_net_group(nullptr) {}
+
+    ExpectedRef<T> get()
+    {
+        CHECK(nullptr != m_net_group, HAILO_INVALID_OPERATION);
+        return std::ref(*m_net_group);
+    }
+    void set(T &net_group)
+    {
+        assert(!is_any_active());
+        m_net_group = &net_group;
+    }
+
+    bool is_any_active() { return nullptr != m_net_group; }
+
+    void clear() { m_net_group = nullptr; }
+
+    ActiveNetworkGroupHolder(ActiveNetworkGroupHolder&) = delete;
+    ActiveNetworkGroupHolder& operator=(ActiveNetworkGroupHolder&) = delete;
+    ActiveNetworkGroupHolder& operator=(ActiveNetworkGroupHolder&&) = delete;
+    ActiveNetworkGroupHolder(ActiveNetworkGroupHolder&&) = default;
+  private:
+    T *m_net_group;
+};
+
+} /* namespace hailort */
+
+#endif //_HAILO_CONTEXT_SWITCH_ACTIVE_NETWORK_GROUP_HOLDER_HPP_
\ No newline at end of file
diff --git a/hailort/libhailort/src/context_switch/config_manager.hpp b/hailort/libhailort/src/context_switch/config_manager.hpp
new file mode 100644 (file)
index 0000000..f224700
--- /dev/null
@@ -0,0 +1,60 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file config_manager.hpp
+ * @brief Manager of HEF parsing and network groups resources
+ *
+ *
+ **/
+
+#ifndef HAILO_CONFIG_MANAGER_HPP_
+#define HAILO_CONFIG_MANAGER_HPP_
+
+#include "hailo/hailort.h"
+#include "hailo/device.hpp"
+#include "hailo/expected.hpp"
+#include "common/utils.hpp"
+
+#include <vector>
+#include <map>
+#include <algorithm>
+
+namespace hailort
+{
+
+enum class ConfigManagerType {
+    NotSet = 0,
+    HcpConfigManager = 1,
+    VdmaConfigManager = 2,
+};
+
+class ConfigManager
+{
+  public:
+    virtual ~ConfigManager() {}
+    virtual ConfigManagerType get_manager_type() = 0;
+    virtual Expected<ConfiguredNetworkGroupVector> add_hef(Hef &hef, const std::map<std::string,
+        ConfigureNetworkParams> &configure_params={}) = 0;
+
+  protected:
+    hailo_status validate_boundary_streams_were_created(Hef &hef, const std::string &network_group_name, ConfiguredNetworkGroup &network_group)
+    {
+        auto number_of_inputs = hef.get_number_of_input_streams(network_group_name);
+        CHECK_EXPECTED_AS_STATUS(number_of_inputs);
+        CHECK((number_of_inputs.value() == network_group.get_input_streams().size()),
+            HAILO_INVALID_ARGUMENT, "passed configure_params for network group {} did not contain all input streams", network_group_name);
+
+        auto number_of_outputs = hef.get_number_of_output_streams(network_group_name);
+        CHECK_EXPECTED_AS_STATUS(number_of_inputs);
+        CHECK((number_of_outputs.value() == network_group.get_output_streams().size()),
+            HAILO_INVALID_ARGUMENT, "passed configure_params for network group {} did not contain all output streams", network_group_name);
+        
+        return HAILO_SUCCESS;
+    }
+};
+
+} /* namespace hailort */
+
+#endif /* HAILO_CONFIG_MANAGER_HPP_ */
\ No newline at end of file
diff --git a/hailort/libhailort/src/context_switch/hcp_config_activated_network_group.cpp b/hailort/libhailort/src/context_switch/hcp_config_activated_network_group.cpp
new file mode 100644 (file)
index 0000000..7fe37b8
--- /dev/null
@@ -0,0 +1,69 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file hcp_config_activated_network_group.cpp
+ * @brief HcpConfigActivatedNetworkGroup implementation
+ **/
+
+#include "context_switch/single_context/hcp_config_activated_network_group.hpp"
+#include "control.hpp"
+
+namespace hailort
+{
+
+Expected<HcpConfigActivatedNetworkGroup> HcpConfigActivatedNetworkGroup::create(Device &device, std::vector<WriteMemoryInfo> &config,
+        const hailo_activate_network_group_params_t &network_group_params,
+        std::map<std::string, std::unique_ptr<InputStream>> &input_streams,
+        std::map<std::string, std::unique_ptr<OutputStream>> &output_streams,
+        HcpConfigActiveAppHolder &active_net_group_holder,
+        hailo_power_mode_t power_mode, EventPtr network_group_activated_event)
+{
+    CHECK(!active_net_group_holder.is_any_active(), make_unexpected(HAILO_INVALID_OPERATION),
+        "network group is currently active. You must deactivate before activating another network_group");
+
+    // Close older dataflows
+    auto status = Control::close_all_streams(device);
+    CHECK_SUCCESS_AS_EXPECTED(status);
+
+    // Reset nn_core before writing configurations
+    status = device.reset(HAILO_RESET_DEVICE_MODE_NN_CORE);
+    CHECK_SUCCESS_AS_EXPECTED(status);
+
+    for (auto &m : config) {
+        status = device.write_memory(m.address, MemoryView(m.data));
+        CHECK_SUCCESS_AS_EXPECTED(status);
+    }
+
+    HcpConfigActivatedNetworkGroup object(device, active_net_group_holder, network_group_params, input_streams, output_streams,
+        power_mode, std::move(network_group_activated_event), status);
+    CHECK_SUCCESS_AS_EXPECTED(status);
+    return object;
+}
+
+HcpConfigActivatedNetworkGroup::HcpConfigActivatedNetworkGroup(Device &device,
+    HcpConfigActiveAppHolder &active_net_group_holder, const hailo_activate_network_group_params_t &network_group_params,
+    std::map<std::string, std::unique_ptr<InputStream>> &input_streams,
+    std::map<std::string, std::unique_ptr<OutputStream>> &output_streams,    
+    hailo_power_mode_t power_mode, EventPtr &&network_group_activated_event, hailo_status &status) :
+      ActivatedNetworkGroupBase(network_group_params, input_streams, output_streams,
+        std::move(network_group_activated_event), status),
+      m_active_net_group_holder(active_net_group_holder), m_is_active(true), m_power_mode(power_mode), m_device(device)
+{
+    // Validate ActivatedNetworkGroup status
+    if (HAILO_SUCCESS != status) {
+        return;
+    }
+    m_active_net_group_holder.set(*this);
+}
+
+HcpConfigActivatedNetworkGroup::~HcpConfigActivatedNetworkGroup()
+{
+    if (m_is_active) {
+        m_active_net_group_holder.clear();
+        deactivate_resources();
+    }
+}
+
+} /* namespace hailort */
diff --git a/hailort/libhailort/src/context_switch/hcp_config_manager.cpp b/hailort/libhailort/src/context_switch/hcp_config_manager.cpp
new file mode 100644 (file)
index 0000000..d0e67ff
--- /dev/null
@@ -0,0 +1,126 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file hcp_config_manager.cpp
+ * @brief Manager of HEF parsing and network groups resources
+ *
+ *
+ **/
+
+// https://github.com/protocolbuffers/protobuf/tree/master/cmake#notes-on-compiler-warnings
+#if defined(_MSC_VER)
+#pragma warning(push)
+#pragma warning(disable: 4244 4267 4127)
+#else
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wconversion"
+#endif
+#include "hef.pb.h"
+#if defined(_MSC_VER)
+#pragma warning( pop ) 
+#else
+#pragma GCC diagnostic pop
+#endif
+
+#include "context_switch/single_context/hcp_config_manager.hpp"
+#include "hailo/hailort.h"
+#include "common/utils.hpp"
+#include "hailo/device.hpp"
+#include "hailo/hef.hpp"
+#include "control.hpp"
+#include "pcie_device.hpp"
+#include "hlpcie.hpp"
+
+#include <new>
+#include <algorithm>
+
+namespace hailort
+{
+
+Expected<ConfiguredNetworkGroupVector> HcpConfigManager::add_hef(Hef &hef,
+    const NetworkGroupsParamsMap &configure_params)
+{
+    auto &hef_network_groups = hef.pimpl->network_groups();
+    auto current_net_group_index = static_cast<uint8_t>(m_net_groups.size());
+    
+    /* Validate that all network_groups are single context */
+    for (const auto &net_group : hef_network_groups) {
+        CHECK_NOT_NULL_AS_EXPECTED(net_group, HAILO_INTERNAL_FAILURE);
+        CHECK(1 == net_group->contexts_size(), make_unexpected(HAILO_INTERNAL_FAILURE),
+            "Only single_context network_groups is supported!. Network group {} has {} contexts.",
+            net_group->network_group_metadata().network_group_name(), net_group->contexts_size());
+        CHECK_AS_EXPECTED(!(Hef::Impl::contains_ddr_layers(*net_group)), HAILO_INVALID_OPERATION,
+            "DDR layers are only supported for PCIe device. Network group {} contains DDR layers.",
+            net_group->network_group_metadata().network_group_name());
+        auto status = Hef::Impl::validate_net_group_unique_layer_names(net_group);
+        CHECK_SUCCESS_AS_EXPECTED(status);
+    }
+
+    // Reset FW state_machine status
+    auto status = Control::reset_context_switch_state_machine(m_device);
+    CHECK_SUCCESS_AS_EXPECTED(status);
+
+    ConfiguredNetworkGroupVector added_network_groups;
+    added_network_groups.reserve(hef_network_groups.size());
+
+    /* Update preliminary_config and dynamic_contexts recepies */
+    auto configure_params_copy = configure_params;
+    for (const auto &net_group : hef_network_groups) {
+        CHECK_NOT_NULL_AS_EXPECTED(net_group, HAILO_INTERNAL_FAILURE);
+        auto &proto_preliminary_config = net_group->preliminary_config();
+        auto net_group_config = Hef::Impl::create_single_context_network_group_config(proto_preliminary_config);
+        CHECK_EXPECTED(net_group_config);
+
+        ConfigureNetworkParams config_params = {};
+        if (contains(configure_params_copy, net_group->network_group_metadata().network_group_name())) {
+            config_params = configure_params_copy.at(net_group->network_group_metadata().network_group_name());
+            configure_params_copy.erase(net_group->network_group_metadata().network_group_name());
+        } else {
+            auto interface = m_device.get_default_streams_interface();
+            CHECK_EXPECTED(interface);
+            auto config_params_exp = hef.create_configure_params(interface.value(), net_group->network_group_metadata().network_group_name());
+            CHECK_EXPECTED(config_params_exp);
+            config_params = config_params_exp.release();
+        }
+
+        auto network_group_metadata = hef.pimpl->get_network_group_metadata(net_group->network_group_metadata().network_group_name());
+        CHECK_EXPECTED(network_group_metadata);
+
+        auto single_context_app = HcpConfigNetworkGroup(m_device, m_active_net_group_holder, net_group_config.release(),
+            config_params, current_net_group_index, network_group_metadata.release(), status);
+        CHECK_SUCCESS_AS_EXPECTED(status);
+
+        auto net_group_ptr = make_shared_nothrow<HcpConfigNetworkGroup>(std::move(single_context_app));
+        CHECK_AS_EXPECTED(nullptr != net_group_ptr, HAILO_OUT_OF_HOST_MEMORY);
+        m_net_groups.emplace_back(net_group_ptr);
+        added_network_groups.emplace_back(std::static_pointer_cast<ConfiguredNetworkGroup>(net_group_ptr));
+
+        current_net_group_index++;
+
+        // TODO: move this func into HcpConfigNetworkGroup c'tor
+        status = net_group_ptr->create_streams_from_config_params(m_device);
+        CHECK_SUCCESS_AS_EXPECTED(status);
+
+        // Check that all boundary streams were created
+        status = validate_boundary_streams_were_created(hef, net_group->network_group_metadata().network_group_name(), *net_group_ptr);
+        CHECK_SUCCESS_AS_EXPECTED(status);
+    }
+    std::string unmatched_keys = "";
+    for (const auto &pair : configure_params_copy) {
+        unmatched_keys.append(" ");
+        unmatched_keys.append(pair.first);
+    }
+    CHECK_AS_EXPECTED(unmatched_keys.size() == 0, HAILO_INVALID_ARGUMENT,
+        "Some network group names in the configuration are not found in the hef file:{}", unmatched_keys);
+
+    return added_network_groups;
+}
+
+ConfigManagerType HcpConfigManager::get_manager_type()
+{
+    return ConfigManagerType::HcpConfigManager;
+}
+
+} /* namespace hailort */
diff --git a/hailort/libhailort/src/context_switch/hcp_config_network_group.cpp b/hailort/libhailort/src/context_switch/hcp_config_network_group.cpp
new file mode 100644 (file)
index 0000000..735597c
--- /dev/null
@@ -0,0 +1,49 @@
+#include "context_switch/single_context/hcp_config_network_group.hpp"
+#include "network_group_internal.hpp"
+#include "control.hpp"
+
+#define OUTPUT_CHANNEL_INDEX_OFFSET (16)
+
+
+namespace hailort
+{
+
+HcpConfigNetworkGroup::HcpConfigNetworkGroup(Device &device, HcpConfigActiveAppHolder &active_net_group_holder,
+    std::vector<WriteMemoryInfo> &&config, const ConfigureNetworkParams &config_params, uint8_t net_group_index, 
+    NetworkGroupMetadata &&network_group_metadata, hailo_status &status)
+        : ConfiguredNetworkGroupBase(config_params, net_group_index, network_group_metadata, status),
+          m_config(std::move(config)), m_active_net_group_holder(active_net_group_holder), m_device(device)
+{}
+
+Expected<std::unique_ptr<ActivatedNetworkGroup>> HcpConfigNetworkGroup::activate(
+    const hailo_activate_network_group_params_t &network_group_params)
+{
+    auto start_time = std::chrono::steady_clock::now();
+
+    auto activated_net_group = HcpConfigActivatedNetworkGroup::create(m_device, m_config, network_group_params,
+        m_input_streams, m_output_streams, m_active_net_group_holder, m_config_params.power_mode,
+        m_network_group_activated_event);
+    CHECK_EXPECTED(activated_net_group);
+
+    std::unique_ptr<ActivatedNetworkGroup> activated_net_group_ptr = make_unique_nothrow<HcpConfigActivatedNetworkGroup>(activated_net_group.release());
+    CHECK_AS_EXPECTED(nullptr != activated_net_group_ptr, HAILO_OUT_OF_HOST_MEMORY);
+
+    auto elapsed_time_ms = std::chrono::duration<double, std::milli>(std::chrono::steady_clock::now() - start_time).count();
+    LOGGER__INFO("Activating {} took {} milliseconds. Note that the function is asynchronous and thus the network is not fully activated yet.", get_network_group_name(), elapsed_time_ms);
+
+    return activated_net_group_ptr;
+}
+
+Expected<hailo_stream_interface_t> HcpConfigNetworkGroup::get_default_streams_interface()
+{
+    return m_device.get_default_streams_interface();
+}
+
+Expected<uint8_t> HcpConfigNetworkGroup::get_boundary_channel_index(uint8_t stream_index,
+    hailo_stream_direction_t direction, const std::string &/* layer_name */)
+{
+    return (direction == HAILO_H2D_STREAM) ? stream_index :
+        static_cast<uint8_t>(stream_index + OUTPUT_CHANNEL_INDEX_OFFSET);
+}
+
+} /* namespace hailort */
diff --git a/hailort/libhailort/src/context_switch/hef_metadata.cpp b/hailort/libhailort/src/context_switch/hef_metadata.cpp
new file mode 100644 (file)
index 0000000..c749eb8
--- /dev/null
@@ -0,0 +1,912 @@
+#include <hailo/hailort.h>
+#include "common/utils.hpp"
+
+#include "context_switch/hef_metadata.hpp"
+#include "control_protocol.h"
+#include "context_switch_defs.h"
+#include "byte_order.h"
+
+#include <limits>
+
+namespace hailort
+{
+
+CONTROL_PROTOCOL__TRIGGER_t HEF_METADATA__build_none_trigger()
+{
+    CONTROL_PROTOCOL__TRIGGER_t trigger{};
+    trigger.type = CONTROL_PROTOCOL__CONTEXT_SWITCH_TRIGGER_TYPE_NONE;
+    // Note: none_trigger is empty
+
+    return trigger;
+}
+
+CONTROL_PROTOCOL__TRIGGER_t HEF_METADATA__build_input_stream_trigger(uint8_t stream_index)
+{
+    CONTROL_PROTOCOL__TRIGGER_t trigger{};
+    trigger.type = CONTROL_PROTOCOL__CONTEXT_SWITCH_TRIGGER_TYPE_INPUT_STREAM;
+    trigger.params.input_stream_trigger.stream_index = stream_index;
+
+    return trigger;
+}
+
+CONTROL_PROTOCOL__TRIGGER_t HEF_METADATA__build_output_stream_trigger(uint8_t stream_index)
+{
+    CONTROL_PROTOCOL__TRIGGER_t trigger{};
+    trigger.type = CONTROL_PROTOCOL__CONTEXT_SWITCH_TRIGGER_TYPE_OUTPUT_STREAM;
+    trigger.params.output_stream_trigger.stream_index = stream_index;
+
+    return trigger;
+}
+
+CONTROL_PROTOCOL__TRIGGER_t HEF_METADATA__build_lcu_trigger(uint8_t cluster_index, uint8_t lcu_index)
+{
+    CONTROL_PROTOCOL__TRIGGER_t trigger{};
+    trigger.type = CONTROL_PROTOCOL__CONTEXT_SWITCH_TRIGGER_TYPE_LCU;
+    trigger.params.lcu_trigger.cluster_index = cluster_index;
+    trigger.params.lcu_trigger.lcu_index = lcu_index;
+
+    return trigger;
+}
+
+CONTROL_PROTOCOL__TRIGGER_t HEF_METADATA__build_nms_trigger(uint8_t aggregator_index,
+    uint8_t pred_cluster_ob_index, uint8_t pred_cluster_ob_cluster_index, uint8_t pred_cluster_ob_interface,
+    uint8_t succ_prepost_ob_index, uint8_t succ_prepost_ob_interface)
+{
+    CONTROL_PROTOCOL__TRIGGER_t trigger{};
+    trigger.type = CONTROL_PROTOCOL__CONTEXT_SWITCH_TRIGGER_TYPE_NMS_IDLE;
+    trigger.params.nms_idle_trigger.aggregator_index = aggregator_index;
+    trigger.params.nms_idle_trigger.pred_cluster_ob_index = pred_cluster_ob_index;
+    trigger.params.nms_idle_trigger.pred_cluster_ob_cluster_index = pred_cluster_ob_cluster_index;
+    trigger.params.nms_idle_trigger.pred_cluster_ob_interface = pred_cluster_ob_interface;
+    trigger.params.nms_idle_trigger.succ_prepost_ob_index = succ_prepost_ob_index;
+    trigger.params.nms_idle_trigger.succ_prepost_ob_interface = succ_prepost_ob_interface;
+
+    return trigger;
+}
+
+CONTROL_PROTOCOL__TRIGGER_t HEF_METADATA__build_dma_idle_trigger(uint8_t stream_index)
+{
+    CONTROL_PROTOCOL__TRIGGER_t trigger{};
+    trigger.type = CONTROL_PROTOCOL__CONTEXT_SWITCH_TRIGGER_TYPE_DMA_IDLE;
+    trigger.params.dma_idle_trigger.stream_index = stream_index;
+
+    return trigger;
+}
+
+uint16_t hef_metadata__return_network_data_offset(
+        CONTROL_PROTOCOL__context_switch_context_info_t *context_info)
+{
+    uint8_t current_slice_index = 0;
+    uint32_t current_offset_local = 0;
+
+    current_offset_local = context_info->control_slicing_data.current_buillding_offset_inside_slice;
+    current_slice_index = context_info->control_slicing_data.current_building_slice_index;
+
+    if (0 < current_slice_index) {
+        current_offset_local += context_info->control_slicing_data.control_slicing_offsets[current_slice_index - 1];
+    }
+
+    return ((uint16_t)(current_offset_local));
+}
+
+void hef_metadata__add_none_trigger_without_updating_slicing_info(
+        CONTROL_PROTOCOL__context_switch_context_info_t *context_info,
+        uint8_t **trigger_group_data_current_offset)
+{
+    CONTROL_PROTOCOL__trigger_group_t trigger_group = {};
+
+    trigger_group.trigger = HEF_METADATA__build_none_trigger();
+    trigger_group.triggers_action_count = 0;
+
+    context_info->control_slicing_data.current_building_trigger = 
+        (CONTROL_PROTOCOL__trigger_group_t *)(*trigger_group_data_current_offset);
+
+    memcpy((*trigger_group_data_current_offset), &(trigger_group), sizeof(trigger_group));
+    *(trigger_group_data_current_offset) += sizeof(trigger_group);
+}
+
+hailo_status hef_metadata__update_slicing_info(
+    CONTROL_PROTOCOL__context_switch_context_info_t *context_info,
+    uint8_t **struct_current_offset,
+    uint8_t struct_size,
+    bool is_action) 
+{
+    uint16_t current_building_slice_index = 0;
+    bool would_exceed_slice_limit = false;
+    bool would_exceed_max_trigger_action_count = false;
+
+    CHECK(CONTROL_PROTOCOL__CONTEXT_NETWORK_DATA_SINGLE_CONTROL_MAX_SIZE >= static_cast<uint32_t>(struct_size),
+        HAILO_CHUNK_TOO_LARGE);
+
+    /* extract current slice */
+    current_building_slice_index = context_info->control_slicing_data.current_building_slice_index;
+
+    const auto last_trigger_index = (current_building_slice_index == 0) ?  0 : (current_building_slice_index - 1);
+    const auto last_trigger_offset = context_info->control_slicing_data.control_slicing_offsets[last_trigger_index];
+    const auto acummulated_metadata_offset = 
+        last_trigger_offset + context_info->control_slicing_data.current_buillding_offset_inside_slice + struct_size; 
+
+    if (acummulated_metadata_offset > CONTROL_PROTOCOL__CONTEXT_NETWORK_DATA_MAX_SIZE) {
+        /* If condition failed - consider increasing 'CONTROL_PROTOCOL__CONTEXT_NETWORK_DATA_MAX_SIZE' define */
+        LOGGER__ERROR("context metadata is larger than max data size. Acuumulated metadata offset is {}. Max size is {} ", 
+            acummulated_metadata_offset, CONTROL_PROTOCOL__CONTEXT_NETWORK_DATA_MAX_SIZE);
+        return HAILO_INTERNAL_FAILURE;
+    }
+
+    would_exceed_slice_limit = CONTROL_PROTOCOL__CONTEXT_NETWORK_DATA_SINGLE_CONTROL_MAX_SIZE 
+            < (context_info->control_slicing_data.current_buillding_offset_inside_slice + struct_size);
+    if (is_action) {
+        /* context_info->control_slicing_data.current_building_trigger will be null if !is_action */
+        static_assert(sizeof(uint16_t) == sizeof(context_info->control_slicing_data.current_building_trigger->triggers_action_count),
+            "triggers_action_count field isn't uint16_t");
+        would_exceed_max_trigger_action_count = context_info->control_slicing_data.current_building_trigger->triggers_action_count
+            == (std::numeric_limits<uint16_t>::max() - 1);
+    }
+
+    /* If the next written struct would exceed the slice limit or max trigger action count */
+    if (would_exceed_slice_limit || would_exceed_max_trigger_action_count) {
+        /* Save slice end offset */
+        context_info->control_slicing_data.control_slicing_offsets[current_building_slice_index] = 
+            hef_metadata__return_network_data_offset(context_info);
+
+        /* Advance slice index */
+        (context_info->control_slicing_data.current_building_slice_index)++;
+        current_building_slice_index++;
+        context_info->control_slicing_data.current_buillding_offset_inside_slice = 0;
+
+        /* If slice was inside action - add NONE trigger to start slice with trigger */
+        if (is_action) {
+            static_assert(CONTROL_PROTOCOL__CONTEXT_NETWORK_DATA_SINGLE_CONTROL_MAX_SIZE >= sizeof(CONTROL_PROTOCOL__trigger_group_t),
+                "Trigger cant fit the slice size");
+
+            /* Build trigger */
+            hef_metadata__add_none_trigger_without_updating_slicing_info(context_info, struct_current_offset);
+
+            /* Add the trigger offset */
+            context_info->control_slicing_data.current_buillding_offset_inside_slice = sizeof(CONTROL_PROTOCOL__trigger_group_t);
+            context_info->control_slicing_data.slice_triggers[current_building_slice_index]++;
+        }
+    }
+
+    if (is_action) {
+        context_info->control_slicing_data.current_building_trigger->triggers_action_count++;
+    }
+
+    /* Add the struct offset */
+    context_info->control_slicing_data.current_buillding_offset_inside_slice = 
+        (uint16_t)(context_info->control_slicing_data.current_buillding_offset_inside_slice + struct_size);
+
+    return HAILO_SUCCESS;
+}
+
+hailo_status HEF_METADATA__add_trigger_to_trigger_group(
+        CONTROL_PROTOCOL__context_switch_context_info_t *context_info,
+        uint8_t **trigger_group_data_current_offset,
+        const CONTROL_PROTOCOL__TRIGGER_t *trigger)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    CONTROL_PROTOCOL__trigger_group_t trigger_group = {};
+    uint8_t control_slice_index = 0; 
+
+    CHECK_ARG_NOT_NULL(context_info);
+    CHECK_ARG_NOT_NULL(trigger_group_data_current_offset);
+    CHECK_ARG_NOT_NULL(*trigger_group_data_current_offset);
+    CHECK_ARG_NOT_NULL(trigger);
+
+    trigger_group.trigger = *trigger;
+    trigger_group.triggers_action_count = 0;
+
+    /* Update slice data (before actual write) */
+    context_info->control_slicing_data.current_building_trigger = 
+        (CONTROL_PROTOCOL__trigger_group_t *)(*trigger_group_data_current_offset);
+    status = hef_metadata__update_slicing_info(context_info, 
+            trigger_group_data_current_offset, 
+            sizeof(trigger_group),
+            false);
+    CHECK_SUCCESS(status);
+    control_slice_index = context_info->control_slicing_data.current_building_slice_index;
+    
+    /* Check for overflow */
+    static_assert(sizeof(uint8_t) == sizeof(context_info->control_slicing_data.slice_triggers[control_slice_index]),
+            "slice_triggers field isn't uint8_t");
+    CHECK(context_info->control_slicing_data.slice_triggers[control_slice_index] < std::numeric_limits<uint8_t>::max(),
+        HAILO_INTERNAL_FAILURE);
+    context_info->control_slicing_data.slice_triggers[control_slice_index]++;
+
+    memcpy((*trigger_group_data_current_offset), &(trigger_group), sizeof(trigger_group));
+    *(trigger_group_data_current_offset) += sizeof(trigger_group);
+
+    return HAILO_SUCCESS;
+}
+
+hailo_status hef_metadata__add_general_action(
+        CONTROL_PROTOCOL__context_switch_context_info_t *context_info,
+        uint8_t **action_data_current_offset,
+        CONTROL_PROTOCOL__ACTION_TYPE_t action_type, 
+        uint8_t action_data,
+        bool is_repeated)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    CONTROL_PROTOCOL__ACTION_HEADER_t header{};
+
+    CHECK_ARG_NOT_NULL(action_data_current_offset);
+    CHECK_ARG_NOT_NULL(*action_data_current_offset);
+
+    header.action_type = static_cast<uint8_t>(action_type);
+    header.is_repeated = is_repeated;
+
+    /* Update slice data (before actual write) */
+    status = hef_metadata__update_slicing_info(context_info, 
+            action_data_current_offset, 
+            sizeof(header) + sizeof(action_data),
+            true);
+    CHECK_SUCCESS(status);
+
+    /* Setting action header */
+    memcpy((*action_data_current_offset), &header, sizeof(header));
+    *(action_data_current_offset) += sizeof(header);
+    /* Setting action_data */
+    memcpy((*action_data_current_offset), &action_data, sizeof(action_data));
+    *(action_data_current_offset) += sizeof(action_data);
+
+    return HAILO_SUCCESS;
+}
+
+hailo_status HEF_METADATA__add_enable_sequencer_action(
+        CONTROL_PROTOCOL__context_switch_context_info_t *context_info,
+        uint8_t **action_data_current_offset,
+        uint8_t cluster_index,
+        uint8_t initial_l3_cut,
+        uint16_t initial_l3_offset,
+        uint32_t active_apu, 
+        uint32_t active_ia, 
+        uint64_t active_sc,
+        uint64_t active_l2, 
+        uint64_t l2_offset_0, 
+        uint64_t l2_offset_1,
+        bool is_repeated)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    CONTROL_PROTOCOL__TRIGGER_SEQUENCER_ACTION_t trigger_sequencer_action{};
+
+    CHECK_ARG_NOT_NULL(action_data_current_offset);
+    CHECK_ARG_NOT_NULL(*action_data_current_offset);
+
+    trigger_sequencer_action.header.action_type = CONTROL_PROTOCOL__CONTEXT_SWITCH_ACTION_TRIGGER_SEQUENCER;
+    trigger_sequencer_action.header.is_repeated = is_repeated;
+    trigger_sequencer_action.cluster_index = cluster_index;
+    trigger_sequencer_action.sequencer_config.initial_l3_cut = initial_l3_cut;
+    trigger_sequencer_action.sequencer_config.initial_l3_offset = initial_l3_offset;
+    trigger_sequencer_action.sequencer_config.active_apu = active_apu;
+    trigger_sequencer_action.sequencer_config.active_ia = active_ia;
+    trigger_sequencer_action.sequencer_config.active_sc = active_sc;
+    trigger_sequencer_action.sequencer_config.active_l2 = active_l2;
+    trigger_sequencer_action.sequencer_config.l2_offset_0 = l2_offset_0;
+    trigger_sequencer_action.sequencer_config.l2_offset_1 = l2_offset_1;
+
+    status = hef_metadata__update_slicing_info(context_info, 
+            action_data_current_offset, 
+            sizeof(trigger_sequencer_action),
+            true);
+    CHECK_SUCCESS(status);
+
+    memcpy((*action_data_current_offset), &trigger_sequencer_action, sizeof(trigger_sequencer_action));
+    *(action_data_current_offset) += sizeof(trigger_sequencer_action);
+
+    return HAILO_SUCCESS;
+}
+
+hailo_status HEF_METADATA__add_read_vdma_action(
+        CONTROL_PROTOCOL__context_switch_context_info_t *context_info,
+        uint8_t **action_data_current_offset,
+        uint16_t descriptors_count,
+        uint8_t cfg_channel_handle,
+        bool is_repeated)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    CONTROL_PROTOCOL__READ_VDMA_ACTION_t read_vdma_action{};
+
+    CHECK_ARG_NOT_NULL(action_data_current_offset);
+    CHECK_ARG_NOT_NULL(*action_data_current_offset);
+
+    read_vdma_action.header.action_type = CONTROL_PROTOCOL__CONTEXT_SWITCH_ACTION_READ_VDMA;
+    read_vdma_action.header.is_repeated = is_repeated;
+    read_vdma_action.descriptors_count = descriptors_count;
+    read_vdma_action.cfg_channel_handle = cfg_channel_handle;
+
+    status = hef_metadata__update_slicing_info(context_info, 
+            action_data_current_offset, 
+            sizeof(read_vdma_action),
+            true);
+    CHECK_SUCCESS(status);
+
+    memcpy((*action_data_current_offset), &read_vdma_action, sizeof(read_vdma_action));
+    *(action_data_current_offset) += sizeof(read_vdma_action);
+
+    return HAILO_SUCCESS;
+}
+
+hailo_status HEF_METADATA__add_ccw_bursts_action(
+    CONTROL_PROTOCOL__context_switch_context_info_t *context_info,
+    uint8_t **action_data_current_offset,
+    uint16_t ccw_bursts,
+    uint8_t cfg_channel_handle,
+    bool is_repeated)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    CONTROL_PROTOCOL__FETCH_CCW_BURSTS_ACTION_t fetch_ccw_bursts{};
+
+    CHECK_ARG_NOT_NULL(action_data_current_offset);
+    CHECK_ARG_NOT_NULL(*action_data_current_offset);
+
+    fetch_ccw_bursts.header.action_type = CONTROL_PROTOCOL__CONTEXT_SWITCH_ACTION_FETCH_CCW_BURSTS;
+    fetch_ccw_bursts.header.is_repeated = is_repeated;
+    fetch_ccw_bursts.ccw_bursts = ccw_bursts;
+    fetch_ccw_bursts.cfg_channel_handle = cfg_channel_handle;
+
+    status = hef_metadata__update_slicing_info(context_info, 
+        action_data_current_offset, 
+        sizeof(fetch_ccw_bursts),
+        true);
+    CHECK_SUCCESS(status);
+
+    memcpy((*action_data_current_offset), &fetch_ccw_bursts, sizeof(fetch_ccw_bursts));
+    *(action_data_current_offset) += sizeof(fetch_ccw_bursts);
+
+    return HAILO_SUCCESS;
+}
+
+hailo_status HEF_METADATA__add_repeated_header_action(
+        CONTROL_PROTOCOL__context_switch_context_info_t *context_info,
+        uint8_t **action_data_current_offset, 
+        CONTROL_PROTOCOL__ACTION_TYPE_t sub_action_type,
+        uint8_t num_actions
+)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    CONTROL_PROTOCOL__REPEATED_ACTION_t repeated_action{};
+
+    CHECK_ARG_NOT_NULL(action_data_current_offset);
+    CHECK_ARG_NOT_NULL(*action_data_current_offset);
+
+    repeated_action.header.action_type = CONTROL_PROTOCOL__CONTEXT_SWITCH_ACTION_ADD_REPEATED;
+    repeated_action.header.is_repeated = false;
+    repeated_action.sub_action_type = sub_action_type;
+    repeated_action.num_actions = num_actions;
+
+    status = hef_metadata__update_slicing_info(context_info, 
+            action_data_current_offset, 
+            sizeof(repeated_action),
+            true);
+    CHECK_SUCCESS(status);
+
+    memcpy((*action_data_current_offset), &repeated_action, sizeof(repeated_action));
+    *(action_data_current_offset) += sizeof(repeated_action);
+
+    return HAILO_SUCCESS;
+}
+
+hailo_status HEF_METADATA__add_wait_for_sequencer_action(
+        CONTROL_PROTOCOL__context_switch_context_info_t *context_info,
+        uint8_t **action_data_current_offset,
+        uint8_t sequencer_index,
+        bool is_repeated)
+{
+    return hef_metadata__add_general_action(
+            context_info,
+            action_data_current_offset,
+            CONTROL_PROTOCOL__CONTEXT_SWITCH_ACTION_WAIT_FOR_SEQUENCER_DONE,
+            sequencer_index,
+            is_repeated);
+}
+
+hailo_status HEF_METADATA__add_fetch_new_data_action(
+        CONTROL_PROTOCOL__context_switch_context_info_t *context_info,
+        uint8_t **action_data_current_offset, 
+        uint8_t shmifo_index,
+        bool is_repeated)
+{
+    return hef_metadata__add_general_action(
+            context_info,
+            action_data_current_offset,
+            CONTROL_PROTOCOL__CONTEXT_SWITCH_ACTION_TRIGGER_NEW_DATA_FROM_DATA_INPUT,
+            shmifo_index,
+            is_repeated);
+}
+
+hailo_status HEF_METADATA__add_enable_lcu_non_default_action(
+        CONTROL_PROTOCOL__context_switch_context_info_t *context_info,
+        uint8_t **action_data_current_offset, 
+        uint8_t cluster_index,
+        uint8_t lcu_index, 
+        uint16_t kernel_done_address, 
+        uint32_t kernel_done_count,
+        uint8_t network_index,
+        bool is_repeated)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    CONTROL_PROTOCOL__ENABLE_LCU_NON_DEAFULT_ACTION_t enable_lcu_action{};
+
+    CHECK_ARG_NOT_NULL(action_data_current_offset);
+    CHECK_ARG_NOT_NULL(*action_data_current_offset);
+
+    enable_lcu_action.header.action_type = CONTROL_PROTOCOL__CONTEXT_SWITCH_ACTION_ENABLE_LCU_NON_DEFAULT;
+    enable_lcu_action.header.is_repeated = is_repeated;
+    enable_lcu_action.cluster_index = cluster_index;
+    enable_lcu_action.lcu_index = lcu_index;
+    enable_lcu_action.kernel_done_address = kernel_done_address;
+    enable_lcu_action.kernel_done_count = kernel_done_count;
+    enable_lcu_action.network_index = network_index;
+
+    status = hef_metadata__update_slicing_info(context_info,  action_data_current_offset,
+        sizeof(enable_lcu_action), true);
+    CHECK_SUCCESS(status);
+
+    memcpy((*action_data_current_offset), &enable_lcu_action, sizeof(enable_lcu_action));
+    *(action_data_current_offset) += sizeof(enable_lcu_action);
+
+    return HAILO_SUCCESS;
+}
+
+hailo_status HEF_METADATA__add_enable_lcu_default_action(
+        CONTROL_PROTOCOL__context_switch_context_info_t *context_info,
+        uint8_t **action_data_current_offset,
+        uint8_t cluster_index,
+        uint8_t lcu_index,
+        bool is_repeated)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    CONTROL_PROTOCOL__ENABLE_LCU_DEFAULT_ACTION_t enable_lcu_action{};
+
+    CHECK_ARG_NOT_NULL(action_data_current_offset);
+    CHECK_ARG_NOT_NULL(*action_data_current_offset);
+
+    enable_lcu_action.header.action_type = CONTROL_PROTOCOL__CONTEXT_SWITCH_ACTION_ENABLE_LCU_DEFAULT;
+    enable_lcu_action.header.is_repeated = is_repeated;
+    enable_lcu_action.cluster_index = cluster_index;
+    enable_lcu_action.lcu_index = lcu_index;
+
+    status = hef_metadata__update_slicing_info(context_info,  action_data_current_offset,
+        sizeof(enable_lcu_action), true);
+    CHECK_SUCCESS(status);
+
+    memcpy((*action_data_current_offset), &enable_lcu_action, sizeof(enable_lcu_action));
+    *(action_data_current_offset) += sizeof(enable_lcu_action);
+   
+    return HAILO_SUCCESS;
+}
+
+hailo_status HEF_METADATA__add_disable_lcu_action(
+        CONTROL_PROTOCOL__context_switch_context_info_t *context_info,
+        uint8_t **action_data_current_offset, 
+        uint8_t cluster_index,
+        uint8_t lcu_index,
+        bool is_repeated)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    CONTROL_PROTOCOL__DISABLE_LCU_ACTION_t disable_lcu_action{};
+
+    CHECK_ARG_NOT_NULL(action_data_current_offset);
+    CHECK_ARG_NOT_NULL(*action_data_current_offset);
+
+    disable_lcu_action.header.action_type = CONTROL_PROTOCOL__CONTEXT_SWITCH_ACTION_DISABLE_LCU;
+    disable_lcu_action.header.is_repeated = is_repeated;
+    disable_lcu_action.cluster_index = cluster_index;
+    disable_lcu_action.lcu_index = lcu_index;
+
+    status = hef_metadata__update_slicing_info(context_info, 
+            action_data_current_offset, 
+            sizeof(disable_lcu_action),
+            true);
+    CHECK_SUCCESS(status);
+
+    memcpy((*action_data_current_offset), &disable_lcu_action, sizeof(disable_lcu_action));
+    *(action_data_current_offset) += sizeof(disable_lcu_action);
+
+    return HAILO_SUCCESS;
+}
+
+hailo_status HEF_METADATA__add_wait_for_module_config_done_action(
+        CONTROL_PROTOCOL__context_switch_context_info_t *context_info,
+        uint8_t **action_data_current_offset,
+        uint8_t module_index,
+        bool is_repeated)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    CONTORL_PROTOCOL__WAIT_FOR_MODULE_CONFIG_DONE_ACTION_t wait_for_module_done_action{};
+
+    CHECK_ARG_NOT_NULL(action_data_current_offset);
+    CHECK_ARG_NOT_NULL(*action_data_current_offset);
+
+    wait_for_module_done_action.header.action_type = CONTROL_PROTOCOL__CONTEXT_SWITCH_ACTION_WAIT_FOR_MODULE_CONFIG_DONE;
+    wait_for_module_done_action.header.is_repeated = is_repeated;
+    wait_for_module_done_action.module_index = module_index;
+
+    status = hef_metadata__update_slicing_info(context_info, 
+            action_data_current_offset, 
+            sizeof(wait_for_module_done_action),
+            true);
+    CHECK_SUCCESS(status);
+
+    memcpy((*action_data_current_offset), &wait_for_module_done_action, sizeof(wait_for_module_done_action));
+    *(action_data_current_offset) += sizeof(wait_for_module_done_action);
+
+    return HAILO_SUCCESS;
+}
+
+/* build edge layers functions */
+void hef_metadata__add_edge_layer_header(
+    uint8_t **edge_layer_current_offset,
+    CONTROL_PROTOCOL__EDGE_CONNECTION_TYPE_t edge_connection_type)
+{
+    CONTROL_PROTOCOL__edge_layer_header_t *edge_layer_header = nullptr;
+
+    edge_layer_header = (CONTROL_PROTOCOL__edge_layer_header_t *)(*edge_layer_current_offset);
+    edge_layer_header->edge_connection_type = static_cast<uint8_t>(edge_connection_type);
+    *(edge_layer_current_offset) += sizeof(*edge_layer_header);
+}
+
+void hef_metadata__fill_edge_layer_common_info(
+        CONTROL_PROTOCOL__edge_layer_common_info_t *edge_layer_common_info,
+        uint8_t stream_index, 
+        uint8_t vdma_channel_index,
+        uint8_t network_index,
+        const CONTROL_PROTOCOL__nn_stream_config_t &nn_stream_config)
+{
+    edge_layer_common_info->stream_index = stream_index;
+    edge_layer_common_info->vdma_channel_index = vdma_channel_index;
+    edge_layer_common_info->network_index = network_index;
+    edge_layer_common_info->nn_stream_config.core_bytes_per_buffer = BYTE_ORDER__htons(nn_stream_config.core_bytes_per_buffer);
+    edge_layer_common_info->nn_stream_config.core_buffers_per_frame = BYTE_ORDER__htons(nn_stream_config.core_buffers_per_frame);
+    edge_layer_common_info->nn_stream_config.periph_bytes_per_buffer = BYTE_ORDER__htons(nn_stream_config.periph_bytes_per_buffer);
+    edge_layer_common_info->nn_stream_config.feature_padding_payload = BYTE_ORDER__htons(nn_stream_config.feature_padding_payload);
+    edge_layer_common_info->nn_stream_config.buffer_padding_payload = BYTE_ORDER__htons(nn_stream_config.buffer_padding_payload);
+    edge_layer_common_info->nn_stream_config.buffer_padding = BYTE_ORDER__htons(nn_stream_config.buffer_padding);
+}
+
+hailo_status HEF_METADATA__add_network_boundary_output_edge_layer(
+        CONTROL_PROTOCOL__context_switch_context_info_t *context_info,
+        uint8_t **edge_layer_current_offset,
+        uint8_t stream_index, 
+        uint8_t vdma_channel_index,
+        uint8_t network_index,
+        const CONTROL_PROTOCOL__nn_stream_config_t &nn_stream_config,
+        uint32_t frame_credits_in_bytes,
+        uint16_t desc_page_size)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    CONTROL_PROTOCOL__network_boundary_output_t *edge_layer_info = nullptr;
+    uint8_t control_slice_index = 0;
+
+    CHECK_ARG_NOT_NULL(edge_layer_current_offset);
+    CHECK_ARG_NOT_NULL(*edge_layer_current_offset);
+
+    status = hef_metadata__update_slicing_info(context_info, 
+            edge_layer_current_offset, 
+            (sizeof(CONTROL_PROTOCOL__edge_layer_header_t ) + sizeof(*edge_layer_info)),
+            false);
+    CHECK_SUCCESS(status);
+    control_slice_index = context_info->control_slicing_data.current_building_slice_index;  
+    context_info->control_slicing_data.slice_edge_layers[control_slice_index]++;
+
+    hef_metadata__add_edge_layer_header(edge_layer_current_offset,
+        CONTROL_PROTOCOL__EDGE_CONNECTION_TYPE_NETWORK_BOUNDARY_OUTPUT);
+
+    edge_layer_info = (CONTROL_PROTOCOL__network_boundary_output_t *)(*edge_layer_current_offset);
+
+    hef_metadata__fill_edge_layer_common_info(&(edge_layer_info->common_info),
+            stream_index, 
+            vdma_channel_index,
+            network_index,
+            nn_stream_config);
+
+    edge_layer_info->frame_credits_in_bytes = frame_credits_in_bytes;
+    edge_layer_info->desc_page_size = desc_page_size;
+
+    *(edge_layer_current_offset) += sizeof(*edge_layer_info);
+
+    return HAILO_SUCCESS;
+}
+
+hailo_status HEF_METADATA__add_inter_context_output_edge_layer(
+        CONTROL_PROTOCOL__context_switch_context_info_t *context_info,
+        uint8_t **edge_layer_current_offset,
+        uint8_t stream_index, 
+        uint8_t vdma_channel_index,
+        uint8_t network_index,
+        const CONTROL_PROTOCOL__nn_stream_config_t &nn_stream_config,
+        uint32_t frame_credits_in_bytes,
+        uint64_t host_descriptors_base_address,
+        uint16_t initial_host_available_descriptors,
+        uint16_t desc_page_size,
+        uint8_t desc_list_depth)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    CONTROL_PROTOCOL__inter_context_output_t *edge_layer_info = nullptr;
+    uint8_t control_slice_index = 0;
+
+    CHECK_ARG_NOT_NULL(edge_layer_current_offset);
+    CHECK_ARG_NOT_NULL(*edge_layer_current_offset);
+
+    status = hef_metadata__update_slicing_info(context_info, 
+            edge_layer_current_offset, 
+            (sizeof(CONTROL_PROTOCOL__edge_layer_header_t ) + sizeof(*edge_layer_info)),
+            false);
+    CHECK_SUCCESS(status);
+    control_slice_index = context_info->control_slicing_data.current_building_slice_index;  
+    context_info->control_slicing_data.slice_edge_layers[control_slice_index]++;
+
+    hef_metadata__add_edge_layer_header(edge_layer_current_offset,
+        CONTROL_PROTOCOL__EDGE_CONNECTION_TYPE_INTERMEDIATE_BUFFER_OUTPUT);
+
+    edge_layer_info = (CONTROL_PROTOCOL__inter_context_output_t *)(*edge_layer_current_offset);
+
+    hef_metadata__fill_edge_layer_common_info(&(edge_layer_info->common_info),
+            stream_index, 
+            vdma_channel_index,
+            network_index,
+            nn_stream_config);
+
+    edge_layer_info->frame_credits_in_bytes = frame_credits_in_bytes;
+    edge_layer_info->host_desc_address_info.host_descriptors_base_address = host_descriptors_base_address; 
+    edge_layer_info->host_desc_address_info.initial_host_available_descriptors = initial_host_available_descriptors; 
+    edge_layer_info->desc_page_size = desc_page_size;
+    edge_layer_info->host_desc_address_info.desc_list_depth = desc_list_depth;
+
+    *(edge_layer_current_offset) += sizeof(*edge_layer_info);
+
+    return HAILO_SUCCESS;
+}
+
+hailo_status HEF_METADATA__add_ddr_buffer_output_edge_layer(
+        CONTROL_PROTOCOL__context_switch_context_info_t *context_info,
+        uint8_t **edge_layer_current_offset,
+        uint8_t stream_index, 
+        uint8_t vdma_channel_index,
+        uint8_t network_index,
+        const CONTROL_PROTOCOL__nn_stream_config_t &nn_stream_config,
+        uint32_t frame_credits_in_bytes,
+        uint64_t host_descriptors_base_address,
+        uint16_t initial_host_available_descriptors,
+        uint16_t desc_page_size,
+        uint8_t desc_list_depth,
+        bool fw_managed_channel)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    CONTROL_PROTOCOL__ddr_buffer_output_t *edge_layer_info = nullptr;
+    uint8_t control_slice_index = 0;
+
+    CHECK_ARG_NOT_NULL(edge_layer_current_offset);
+    CHECK_ARG_NOT_NULL(*edge_layer_current_offset);
+
+    status = hef_metadata__update_slicing_info(context_info, 
+            edge_layer_current_offset, 
+            (sizeof(CONTROL_PROTOCOL__edge_layer_header_t ) + sizeof(*edge_layer_info)),
+            false);
+    CHECK_SUCCESS(status);
+    control_slice_index = context_info->control_slicing_data.current_building_slice_index;  
+    context_info->control_slicing_data.slice_edge_layers[control_slice_index]++;
+
+    hef_metadata__add_edge_layer_header(edge_layer_current_offset,
+        CONTROL_PROTOCOL__EDGE_CONNECTION_TYPE_DDR_BUFFER_OUTPUT);
+
+    edge_layer_info = (CONTROL_PROTOCOL__ddr_buffer_output_t *)(*edge_layer_current_offset);
+
+    hef_metadata__fill_edge_layer_common_info(&(edge_layer_info->common_info),
+            stream_index, 
+            vdma_channel_index,
+            network_index,
+            nn_stream_config);
+
+    *(edge_layer_current_offset) += sizeof(*edge_layer_info);
+
+    edge_layer_info->frame_credits_in_bytes = frame_credits_in_bytes;
+    edge_layer_info->host_desc_address_info.host_descriptors_base_address = host_descriptors_base_address; 
+    edge_layer_info->host_desc_address_info.initial_host_available_descriptors = initial_host_available_descriptors; 
+    edge_layer_info->desc_page_size = desc_page_size;
+    edge_layer_info->host_desc_address_info.desc_list_depth = desc_list_depth;
+    edge_layer_info->fw_managed_channel = fw_managed_channel;
+
+    return HAILO_SUCCESS;
+}
+
+hailo_status HEF_METADATA__add_network_boundary_input_edge_layer(
+        CONTROL_PROTOCOL__context_switch_context_info_t *context_info,
+        uint8_t **edge_layer_current_offset,
+        uint8_t stream_index, 
+        uint8_t vdma_channel_index,
+        uint8_t network_index,
+        const CONTROL_PROTOCOL__nn_stream_config_t &nn_stream_config,
+        uint16_t desc_page_size)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    CONTROL_PROTOCOL__network_boundary_input_t *edge_layer_info = nullptr;
+    uint8_t control_slice_index = 0;
+
+    CHECK_ARG_NOT_NULL(edge_layer_current_offset);
+    CHECK_ARG_NOT_NULL(*edge_layer_current_offset);
+
+    status = hef_metadata__update_slicing_info(context_info, 
+            edge_layer_current_offset, 
+            (sizeof(CONTROL_PROTOCOL__edge_layer_header_t ) + sizeof(*edge_layer_info)),
+            false);
+    CHECK_SUCCESS(status);
+    control_slice_index = context_info->control_slicing_data.current_building_slice_index;  
+    context_info->control_slicing_data.slice_edge_layers[control_slice_index]++;
+
+    hef_metadata__add_edge_layer_header(edge_layer_current_offset,
+        CONTROL_PROTOCOL__EDGE_CONNECTION_TYPE_NETWORK_BOUNDARY_INPUT);
+
+    edge_layer_info = (CONTROL_PROTOCOL__network_boundary_input_t *)(*edge_layer_current_offset);
+
+    hef_metadata__fill_edge_layer_common_info(&(edge_layer_info->common_info),
+            stream_index, 
+            vdma_channel_index,
+            network_index,
+            nn_stream_config);
+
+    edge_layer_info->desc_page_size = desc_page_size;
+
+    *(edge_layer_current_offset) += sizeof(*edge_layer_info);
+
+    return HAILO_SUCCESS;
+}
+
+hailo_status HEF_METADATA__add_inter_context_input_edge_layer(
+        CONTROL_PROTOCOL__context_switch_context_info_t *context_info,
+        uint8_t **edge_layer_current_offset,
+        uint8_t stream_index, 
+        uint8_t vdma_channel_index,
+        uint8_t network_index,
+        const CONTROL_PROTOCOL__nn_stream_config_t &nn_stream_config,
+        uint16_t context_credits_in_descriptors,
+        uint64_t host_descriptors_base_address,
+        uint8_t desc_list_depth,
+        uint16_t desc_page_size)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    CONTROL_PROTOCOL__inter_context_input_t *edge_layer_info = nullptr;
+    uint8_t control_slice_index = 0;
+
+    CHECK_ARG_NOT_NULL(edge_layer_current_offset);
+    CHECK_ARG_NOT_NULL(*edge_layer_current_offset);
+
+    status = hef_metadata__update_slicing_info(context_info, 
+            edge_layer_current_offset, 
+            (sizeof(CONTROL_PROTOCOL__edge_layer_header_t ) + sizeof(*edge_layer_info)),
+            false);
+    CHECK_SUCCESS(status);
+    control_slice_index = context_info->control_slicing_data.current_building_slice_index;  
+    context_info->control_slicing_data.slice_edge_layers[control_slice_index]++;
+
+    hef_metadata__add_edge_layer_header(edge_layer_current_offset,
+        CONTROL_PROTOCOL__EDGE_CONNECTION_TYPE_INTERMEDIATE_BUFFER_INPUT);
+
+    edge_layer_info = (CONTROL_PROTOCOL__inter_context_input_t *)(*edge_layer_current_offset);
+
+    hef_metadata__fill_edge_layer_common_info(&(edge_layer_info->common_info),
+            stream_index, 
+            vdma_channel_index,
+            network_index,
+            nn_stream_config);
+
+    edge_layer_info->context_credits_in_descriptors = context_credits_in_descriptors;
+    edge_layer_info->host_desc_address_info.host_descriptors_base_address = host_descriptors_base_address; 
+    edge_layer_info->host_desc_address_info.initial_host_available_descriptors = 0;
+    edge_layer_info->host_desc_address_info.desc_list_depth = desc_list_depth;
+    edge_layer_info->desc_page_size = desc_page_size;
+
+    *(edge_layer_current_offset) += sizeof(*edge_layer_info);
+
+    return HAILO_SUCCESS;
+}
+
+hailo_status HEF_METADATA__add_ddr_buffer_input_edge_layer(
+        CONTROL_PROTOCOL__context_switch_context_info_t *context_info,
+        uint8_t **edge_layer_current_offset,
+        uint8_t stream_index, 
+        uint8_t vdma_channel_index,
+        uint8_t network_index,
+        const CONTROL_PROTOCOL__nn_stream_config_t &nn_stream_config,
+        uint64_t host_descriptors_base_address,
+        uint8_t desc_list_depth,
+        bool fw_managed_channel)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    CONTROL_PROTOCOL__ddr_buffer_input_t *edge_layer_info = nullptr;
+    uint8_t control_slice_index = 0;
+
+    CHECK_ARG_NOT_NULL(edge_layer_current_offset);
+    CHECK_ARG_NOT_NULL(*edge_layer_current_offset);
+
+    status = hef_metadata__update_slicing_info(context_info, 
+            edge_layer_current_offset, 
+            (sizeof(CONTROL_PROTOCOL__edge_layer_header_t ) + sizeof(*edge_layer_info)),
+            false);
+    CHECK_SUCCESS(status);
+    control_slice_index = context_info->control_slicing_data.current_building_slice_index;  
+    context_info->control_slicing_data.slice_edge_layers[control_slice_index]++;
+
+    hef_metadata__add_edge_layer_header(edge_layer_current_offset,
+        CONTROL_PROTOCOL__EDGE_CONNECTION_TYPE_DDR_BUFFER_INPUT);
+
+    edge_layer_info = (CONTROL_PROTOCOL__ddr_buffer_input_t *)(*edge_layer_current_offset);
+
+    hef_metadata__fill_edge_layer_common_info(&(edge_layer_info->common_info),
+            stream_index, 
+            vdma_channel_index,
+            network_index,
+            nn_stream_config);
+
+    edge_layer_info->host_desc_address_info.host_descriptors_base_address = host_descriptors_base_address; 
+    edge_layer_info->host_desc_address_info.initial_host_available_descriptors = 0;
+    edge_layer_info->host_desc_address_info.desc_list_depth = desc_list_depth;
+    edge_layer_info->fw_managed_channel = fw_managed_channel;
+
+    *(edge_layer_current_offset) += sizeof(*edge_layer_info);
+
+    return HAILO_SUCCESS;
+}
+
+hailo_status HEF_METADATA__add_ddr_pair_info(
+        CONTROL_PROTOCOL__context_switch_context_info_t *context_info,
+        uint8_t **action_data_current_offset,
+        const uint8_t h2d_vdma_channel_index, 
+        const uint8_t d2h_vdma_channel_index,
+        const uint32_t descriptors_per_frame,
+        const uint16_t programmed_descriptors_count,
+        bool is_repeated)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    CONTROL_PROTOCOL__ADD_DDR_PAIR_ACTION_t ddr_pair_action{};
+
+    CHECK_ARG_NOT_NULL(action_data_current_offset);
+    CHECK_ARG_NOT_NULL(*action_data_current_offset);
+
+    ddr_pair_action.header.action_type = CONTROL_PROTOCOL__CONTEXT_SWITCH_ACTION_ADD_DDR_PAIR_INFO;
+    ddr_pair_action.header.is_repeated = is_repeated;
+    ddr_pair_action.h2d_vdma_channel_index = h2d_vdma_channel_index;
+    ddr_pair_action.d2h_vdma_channel_index = d2h_vdma_channel_index;
+    ddr_pair_action.descriptors_per_frame = descriptors_per_frame;
+    ddr_pair_action.programmed_descriptors_count = programmed_descriptors_count;
+
+    status = hef_metadata__update_slicing_info(context_info, 
+            action_data_current_offset, 
+            sizeof(ddr_pair_action),
+            true);
+    CHECK_SUCCESS(status);
+
+    memcpy((*action_data_current_offset), &ddr_pair_action, sizeof(ddr_pair_action));
+    *(action_data_current_offset) += sizeof(ddr_pair_action);
+
+    return HAILO_SUCCESS;
+}
+
+hailo_status HEF_METADATA__add_ddr_buffering_start(
+        CONTROL_PROTOCOL__context_switch_context_info_t *context_info,
+        uint8_t **action_data_current_offset,
+        bool is_repeated)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    CONTROL_PROTOCOL__ADD_DDR_BUFFERING_START_ACTION_t ddr_buffering_start{};
+
+    CHECK_ARG_NOT_NULL(action_data_current_offset);
+    CHECK_ARG_NOT_NULL(*action_data_current_offset);
+
+    ddr_buffering_start.header.action_type = CONTROL_PROTOCOL__CONTEXT_SWITCH_ACTION_ADD_DDR_BUFFERING_START;
+    ddr_buffering_start.header.is_repeated = is_repeated;
+
+    status = hef_metadata__update_slicing_info(context_info, 
+            action_data_current_offset, 
+            sizeof(ddr_buffering_start),
+            true);
+    CHECK_SUCCESS(status);
+
+    memcpy((*action_data_current_offset), &ddr_buffering_start, sizeof(ddr_buffering_start));
+    *(action_data_current_offset) += sizeof(ddr_buffering_start);
+
+    return HAILO_SUCCESS;
+}
+/* End of context switch info build functions */
+
+} /* namespace hailort */
diff --git a/hailort/libhailort/src/context_switch/hef_metadata.hpp b/hailort/libhailort/src/context_switch/hef_metadata.hpp
new file mode 100644 (file)
index 0000000..ae39ecf
--- /dev/null
@@ -0,0 +1,467 @@
+#ifndef __CONTEXT_SWITCH_HPP__
+#define __CONTEXT_SWITCH_HPP__
+
+#include <hailo/hailort.h>
+#include "common/utils.hpp"
+#include "control_protocol.h"
+#include "control_protocol.hpp"
+
+namespace hailort
+{
+
+/**
+ * Build context switch none trigger
+ *
+ */
+CONTROL_PROTOCOL__TRIGGER_t HEF_METADATA__build_none_trigger();
+
+/**
+ * Build context switch input stream trigger
+ *
+ * @param[in]        stream_index - input stream index
+ *
+ */
+CONTROL_PROTOCOL__TRIGGER_t HEF_METADATA__build_input_stream_trigger(uint8_t stream_index);
+
+/**
+ * Build context switch output stream trigger
+ *
+ * @param[in]        stream_index - output stream index
+ *
+ */
+CONTROL_PROTOCOL__TRIGGER_t HEF_METADATA__build_output_stream_trigger(uint8_t stream_index);
+
+/**
+ * Build context switch lcu trigger
+ *
+ * @param[in]        cluster_index - cluster index
+ * @param[in]        lcu_index - lcu index
+ *
+ */
+CONTROL_PROTOCOL__TRIGGER_t HEF_METADATA__build_lcu_trigger(uint8_t cluster_index, uint8_t lcu_index);
+
+/**
+ * Build context switch nms trigger
+ *
+ * @param[in]        aggregator_index - ppu aggregator index running the nms transformation
+ * @param[in]        pred_cluster_ob_index - index of the preceding output buffer connected to the ppu
+ * @param[in]        pred_cluster_ob_cluster_index - index of the preceding cluster whose output buffer
+ *                                                   is connected to the ppu
+ * @param[in]        pred_cluster_ob_interface - the interface of the preceding output buffer
+ * @param[in]        succ_prepost_ob_index - index of the succeeding output buffer connected to the ppu
+ * @param[in]        succ_prepost_ob_interface - the interface of the succeeding output buffer
+ *
+ */
+CONTROL_PROTOCOL__TRIGGER_t HEF_METADATA__build_nms_trigger(uint8_t aggregator_index,
+    uint8_t pred_cluster_ob_index, uint8_t pred_cluster_ob_cluster_index, uint8_t pred_cluster_ob_interface,
+    uint8_t succ_prepost_ob_index, uint8_t succ_prepost_ob_interface);
+
+/**
+ * Build context switch dma idle trigger
+ *
+ * @param[in]        stream_index - the dma checked as idle is connected to this stream
+ *
+ */
+CONTROL_PROTOCOL__TRIGGER_t HEF_METADATA__build_dma_idle_trigger(uint8_t stream_index);
+
+/**
+ * Build context switch trigger group header 
+ *
+ * @param[in]        context_info - struct holding all the context info
+ * @param[in/out]    trigger_group_data_current_offset - pointer to the trigger group header
+ * @param[in]        trigger - pointer to the trigger to be added (it's contents will be copied)
+ *
+ */
+hailo_status HEF_METADATA__add_trigger_to_trigger_group(
+    CONTROL_PROTOCOL__context_switch_context_info_t *context_info,
+    uint8_t **trigger_group_data_current_offset,
+    const CONTROL_PROTOCOL__TRIGGER_t *trigger);
+
+/**
+ * Build read vdma action
+ *
+ * @param[in]     context_info - struct holding all the context info
+ * @param[out]    action - pointer to the action
+ * @param[in]     descriptors_count - descriptors_count to fetch
+ * @param[in]     cfg_channel_handle - index of the cfg channel (not the PCIe channel number!)
+ * @param[in]     is_repeated - 'true' if the action is part of a "repeated sequence" (a group of consecutive actions
+ *                              with the same type)
+ *
+ */
+hailo_status HEF_METADATA__add_read_vdma_action(
+    CONTROL_PROTOCOL__context_switch_context_info_t *context_info,
+    uint8_t **action_data_current_offset, 
+    uint16_t descriptors_count,
+    uint8_t cfg_channel_handle,
+    bool is_repeated);
+
+/**
+ * Build add ccw bursts action
+ *
+ * @param[in]     context_info - struct holding all the context info
+ * @param[out]    action - pointer to the action
+ * @param[in]     ccw_bursts - ccw bursts to fetch
+ * @param[in]     cfg_channel_handle - index of the cfg channel (not the PCIe channel number!)
+ * @param[in]     is_repeated - 'true' if the action is part of a "repeated sequence" (a group of consecutive actions
+ *                              with the same type)
+ *
+ */
+hailo_status HEF_METADATA__add_ccw_bursts_action(
+    CONTROL_PROTOCOL__context_switch_context_info_t *context_info,
+    uint8_t **action_data_current_offset, 
+    uint16_t ccw_bursts,
+    uint8_t cfg_channel_handle,
+    bool is_repeated);
+
+/**
+ * Build repeated (header) action
+ *
+ * @param[in]     context_info - struct holding all the context info
+ * @param[out]    action - pointer to the action
+ * @param[in]     sub_action - sub action type
+ * @param[in]     num_actions - number of actions in the repeated group
+ */
+hailo_status HEF_METADATA__add_repeated_header_action(
+    CONTROL_PROTOCOL__context_switch_context_info_t *context_info,
+    uint8_t **action_data_current_offset, 
+    CONTROL_PROTOCOL__ACTION_TYPE_t sub_action_type,
+    uint8_t num_actions
+);
+
+/**
+ * Build wait for sequencer action
+ *
+ * @param[in]     context_info - struct holding all the context info
+ * @param[out]    action - pointer to the action
+ * @param[in]     sequencer_index - sequencer index
+ * @param[in]     is_repeated - 'true' if the action is part of a "repeated sequence" (a group of consecutive actions
+ *                              with the same type)
+ *
+ */
+hailo_status HEF_METADATA__add_wait_for_sequencer_action(
+    CONTROL_PROTOCOL__context_switch_context_info_t *context_info,
+    uint8_t **action_data_current_offset,
+    uint8_t sequencer_index,
+    bool is_repeated);
+
+/**
+ * Build fetch new data action
+ *
+ * @param[in]     context_info - struct holding all the context info
+ * @param[out]    action - pointer to the action
+ * @param[in]     shmifo_index - shmifo index
+ * @param[in]     is_repeated - 'true' if the action is part of a "repeated sequence" (a group of consecutive actions
+ *                              with the same type)
+ *
+ */
+hailo_status HEF_METADATA__add_fetch_new_data_action(
+    CONTROL_PROTOCOL__context_switch_context_info_t *context_info,
+    uint8_t **action_data_current_offset, 
+    uint8_t shmifo_index,
+    bool is_repeated);
+
+/**
+ * Build context switch trigger sequencer action
+ *
+ * @param[in]     context_info - struct holding all the context info
+ * @param[out]    action - pointer to the action
+ * @param[in]     cluster_index - cluster index
+ * @param[in]     initial_l3_cut - initial l3 cut
+ * @param[in]     initial_l3_offset - initial_l3_offset
+ * @param[in]     active_apu - bit map of active APUs
+ * @param[in]     active_ia - bit map of active input aligners
+ * @param[in]     active_sc - bit map of active subclusters
+ * @param[in]     active_l2 - bit map of active l2 cuts
+ * @param[in]     l2_offset_0 - offsets of write start for each active L2 cut (for first 32 subclusters)
+ * @param[in]     l2_offset_1 - offsets of write start for each active L2 cut (for last 32 subclusters)
+ * @param[in]     is_repeated - 'true' if the action is part of a "repeated sequence" (a group of consecutive actions
+ *                              with the same type)
+ * 
+ */
+hailo_status HEF_METADATA__add_enable_sequencer_action(
+    CONTROL_PROTOCOL__context_switch_context_info_t *context_info,
+    uint8_t **action_data_current_offset,
+    uint8_t cluster_index,
+    uint8_t initial_l3_cut,
+    uint16_t initial_l3_offset,
+    uint32_t active_apu, 
+    uint32_t active_ia, 
+    uint64_t active_sc,
+    uint64_t active_l2, 
+    uint64_t l2_offset_0, 
+    uint64_t l2_offset_1,
+    bool is_repeated);
+
+/**
+ * build non default enable lcu action
+ *
+ * @param[in]     context_info - struct holding all the context info
+ * @param[out]    action - pointer to the action
+ * @param[in]     cluster_index - cluster index
+ * @param[in]     lcu_index - lcu_index
+ * @param[in]     kernel_done_address - kernel done address
+ * @param[in]     kernel_done_count - kernel done count
+ * @param[in]     network_index - network index
+ * @param[in]     batch_size - batch size
+ * @param[in]     is_repeated - 'true' if the action is part of a "repeated sequence" (a group of consecutive actions
+ *                              with the same type)
+ *
+ */
+hailo_status HEF_METADATA__add_enable_lcu_non_default_action(
+    CONTROL_PROTOCOL__context_switch_context_info_t *context_info,
+    uint8_t **action_data_current_offset,
+    uint8_t cluster_index,
+    uint8_t lcu_index,
+    uint16_t kernel_done_address, 
+    uint32_t kernel_done_count,
+    uint8_t network_index,
+    bool is_repeated);
+
+/**
+ * build non default enable lcu action
+ *
+ * @param[in]     context_info - struct holding all the context info
+ * @param[out]    action - pointer to the action
+ * @param[in]     cluster_index - cluster index
+ * @param[in]     lcu_index - lcu_index
+ * @param[in]     is_repeated - 'true' if the action is part of a "repeated sequence" (a group of consecutive actions
+ *                              with the same type)
+ *
+ */
+hailo_status HEF_METADATA__add_enable_lcu_default_action(
+    CONTROL_PROTOCOL__context_switch_context_info_t *context_info,
+    uint8_t **action_data_current_offset,
+    uint8_t cluster_index,
+    uint8_t lcu_index,
+    bool is_repeated);
+
+/**
+ * build disable lcu action
+ *
+ * @param[in]     context_info - struct holding all the context info
+ * @param[out]    action - pointer to the action
+ * @param[in]     cluster_index - cluster index
+ * @param[in]     lcu_index - lcu_index
+ * @param[in]     is_repeated - 'true' if the action is part of a "repeated sequence" (a group of consecutive actions
+ *                              with the same type)
+ *
+ */
+hailo_status HEF_METADATA__add_disable_lcu_action(
+    CONTROL_PROTOCOL__context_switch_context_info_t *context_info,
+    uint8_t **action_data_current_offset, 
+    uint8_t cluster_index,
+    uint8_t lcu_index,
+    bool is_repeated);
+
+/**
+ * build wait for module config done
+ *
+ * @param[in]     context_info - struct holding all the context info
+ * @param[out]    action - pointer to the action
+ * @param[in]     module_index - module index
+ * @param[in]     is_repeated - 'true' if the action is part of a "repeated sequence" (a group of consecutive actions
+ *                              with the same type)
+ *
+ */
+hailo_status HEF_METADATA__add_wait_for_module_config_done_action(
+    CONTROL_PROTOCOL__context_switch_context_info_t *context_info,
+    uint8_t **action_data_current_offset,
+    uint8_t module_index,
+    bool is_repeated);
+
+/**
+ * build edge layer - vdma network boundary
+ *
+ * @param[in]     context_info - struct holding all the context info
+ * @param[out]    edge_layer_current_offset - pointer to the location of the edge layer struct
+ * @param[in]     stream_index - stream index 
+ * @param[in]     vdma_channel_index - channel index 
+ * @param[in]     network_index - network index 
+ * @param[in]     nn_stream_config
+ * @param[in]     frame_credits_in_bytes - context credits in bytes
+ * @param[in]     desc_page_size - desc page size in bytes
+ *
+ */
+hailo_status HEF_METADATA__add_network_boundary_output_edge_layer(
+    CONTROL_PROTOCOL__context_switch_context_info_t *context_info,
+    uint8_t **edge_layer_current_offset,
+    uint8_t stream_index, 
+    uint8_t vdma_channel_index, 
+    uint8_t network_index,
+    const CONTROL_PROTOCOL__nn_stream_config_t &nn_stream_config,
+    uint32_t frame_credits_in_bytes,
+    uint16_t desc_page_size);
+
+/**
+ * build edge layer - vdma intermediate buffer output
+ *
+ * @param[in]     context_info - struct holding all the context info
+ * @param[out]    edge_layer_current_offset - pointer to the location of the edge layer struct 
+ * @param[in]     stream_index - stream index 
+ * @param[in]     vdma_channel_index - channel index
+ * @param[in]     network_index - network index 
+ * @param[in]     nn_stream_config
+ * @param[in]     frame_credits_in_bytes - context credits in bytes
+ * @param[in]     host_descriptors_base_address - host descritpors base address
+ * @param[in]     initial_host_available_descriptors - initial host available descriptors, initialized by the FW once new context
+ *                start
+ * @param[in]     desc_page_size - descriptor page_size in bytes
+ * @param[in]     desc_list_depth - descriptor list depth
+ * 
+ */
+hailo_status HEF_METADATA__add_inter_context_output_edge_layer(
+    CONTROL_PROTOCOL__context_switch_context_info_t *context_info,
+    uint8_t **edge_layer_current_offset,
+    uint8_t stream_index, 
+    uint8_t vdma_channel_index, 
+    uint8_t network_index,
+    const CONTROL_PROTOCOL__nn_stream_config_t &nn_stream_config,
+    uint32_t frame_credits_in_bytes,
+    uint64_t host_descriptors_base_address,
+    uint16_t initial_host_available_descriptors,
+    uint16_t desc_page_size,
+    uint8_t desc_list_depth);
+
+/**
+ * build edge layer - vdma DDR buffer output
+ *
+ * @param[in]     context_info - struct holding all the context info
+ * @param[out]    edge_layer_current_offset - pointer to the location of the edge layer struct 
+ * @param[in]     stream_index - stream index 
+ * @param[in]     vdma_channel_index - channel index
+ * @param[in]     network_index - network index 
+ * @param[in]     nn_stream_config
+ * @param[in]     frame_credits_in_bytes - context credits in bytes
+ * @param[in]     host_descriptors_base_address - host descritpors base address
+ * @param[in]     initial_host_available_descriptors - initial host available descriptors, initialized by the FW once new context
+ *                start
+ * @param[in]     desc_page_size - descriptor page_size in bytes
+ * @param[in]     desc_list_depth - descriptor list depth
+ * @param[in]     fw_managed_channel - descriptor list depth
+ *
+ */
+hailo_status HEF_METADATA__add_ddr_buffer_output_edge_layer(
+    CONTROL_PROTOCOL__context_switch_context_info_t *context_info,
+    uint8_t **edge_layer_current_offset,
+    uint8_t stream_index, 
+    uint8_t vdma_channel_index, 
+    uint8_t network_index,
+    const CONTROL_PROTOCOL__nn_stream_config_t &nn_stream_config,
+    uint32_t frame_credits_in_bytes,
+    uint64_t host_descriptors_base_address,
+    uint16_t initial_host_available_descriptors,
+    uint16_t desc_page_size,
+    uint8_t desc_list_depth,
+    bool fw_managed_channel);
+
+/**
+ * build edge layer - vdma network boundary input
+ *
+ * @param[in]     context_info - struct holding all the context info
+ * @param[out]    edge_layer_current_offset - pointer to the location of the edge layer struct 
+ * @param[in]     stream_index - stream index 
+ * @param[in]     vdma_channel_index - channel index
+ * @param[in]     network_index - network index 
+ * @param[in]     nn_stream_config
+ * @param[in]     desc_page_size - desc page size in bytes
+ *
+ */
+hailo_status HEF_METADATA__add_network_boundary_input_edge_layer(
+    CONTROL_PROTOCOL__context_switch_context_info_t *context_info,
+    uint8_t **edge_layer_current_offset,
+    uint8_t stream_index, 
+    uint8_t vdma_channel_index, 
+    uint8_t network_index,
+    const CONTROL_PROTOCOL__nn_stream_config_t &nn_stream_config,
+    uint16_t desc_page_size);
+
+/**
+ * build edge layer - vdma intermediate buffer input
+ *
+ * @param[in]     context_info - struct holding all the context info
+ * @param[out]    edge_layer_current_offset - pointer to the location of the edge layer struct 
+ * @param[in]     stream_index - stream index 
+ * @param[in]     vdma_channel_index - channel index
+ * @param[in]     network_index - network index 
+ * @param[in]     nn_stream_config
+ * @param[in]     context_credits_in_descriptors - context credits in descriptors
+ * @param[in]     host_descriptors_base_address - host descritpors base address
+ * @param[in]     desc_page_size - desc page size in bytes
+ *
+ */
+hailo_status HEF_METADATA__add_inter_context_input_edge_layer(
+    CONTROL_PROTOCOL__context_switch_context_info_t *context_info,
+    uint8_t **edge_layer_current_offset,
+    uint8_t stream_index, 
+    uint8_t vdma_channel_index, 
+    uint8_t network_index,
+    const CONTROL_PROTOCOL__nn_stream_config_t &nn_stream_config,
+    uint16_t context_credits_in_descriptors,
+    uint64_t host_descriptors_base_address,
+    uint8_t desc_list_depth,
+    uint16_t desc_page_size);
+
+/**
+ * build edge layer - vdma ddr buffer input
+ *
+ * @param[in]     context_info - struct holding all the context info
+ * @param[out]    edge_layer_current_offset - pointer to the location of the edge layer struct 
+ * @param[in]     stream_index - stream index 
+ * @param[in]     vdma_channel_index - channel index
+ * @param[in]     network_index - network index 
+ * @param[in]     nn_stream_config
+ * @param[in]     context_credits_in_descriptors - context credits in descriptors
+ * @param[in]     host_descriptors_base_address - host descritpors base address
+ * @param[in]     desc_list_depth - descriptor list depth
+ * @param[in]     fw_managed_channel - descriptor list depth
+ *
+ */
+hailo_status HEF_METADATA__add_ddr_buffer_input_edge_layer(
+    CONTROL_PROTOCOL__context_switch_context_info_t *context_info,
+    uint8_t **edge_layer_current_offset,
+    uint8_t stream_index, 
+    uint8_t vdma_channel_index, 
+    uint8_t network_index,
+    const CONTROL_PROTOCOL__nn_stream_config_t &nn_stream_config,
+    uint64_t host_descriptors_base_address,
+    uint8_t desc_list_depth,
+    bool fw_managed_channel);
+
+/**
+ * Build add ddr pair info action
+ *
+ * @param[in]     context_info - struct holding all the context info
+ * @param[out]    action_data_current_offset - pointer to the action
+ * @param[in]     h2d_vdma_channel_index - DDR pair host to device channel index
+ * @param[in]     d2h_vdma_channel_index - DDR pair device to host channel index
+ * @param[in]     descriptors_per_frame - expected total descritors transfered (per one frame)
+ * @param[in]     programmed_descriptors_count - total size of the programed descriptors list
+ * @param[in]     is_repeated - 'true' if the action is part of a "repeated sequence" (a group of consecutive actions
+ *                              with the same type)
+ *
+ */
+hailo_status HEF_METADATA__add_ddr_pair_info(
+    CONTROL_PROTOCOL__context_switch_context_info_t *context_info,
+    uint8_t **action_data_current_offset,
+    const uint8_t h2d_vdma_channel_index, 
+    const uint8_t d2h_vdma_channel_index,
+    const uint32_t descriptors_per_frame,
+    const uint16_t programmed_descriptors_count,
+    bool is_repeated);
+/**
+ * Build add ddr buffering start
+ *
+ * @param[in]     context_info - struct holding all the context info
+ * @param[out]    action_data_current_offset - pointer to the action
+ * @param[in]     is_repeated - 'true' if the action is part of a "repeated sequence" (a group of consecutive actions
+ *                              with the same type)
+ *
+ */
+hailo_status HEF_METADATA__add_ddr_buffering_start(
+    CONTROL_PROTOCOL__context_switch_context_info_t *context_info,
+    uint8_t **action_data_current_offset,
+    bool is_repeated);
+
+} /* namespace hailort */
+
+#endif /* __CONTEXT_SWITCH__ */
diff --git a/hailort/libhailort/src/context_switch/multi_context/resource_manager.hpp b/hailort/libhailort/src/context_switch/multi_context/resource_manager.hpp
new file mode 100644 (file)
index 0000000..081ceb7
--- /dev/null
@@ -0,0 +1,311 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file resource_manager.hpp
+ * @brief Manager for vdma-config network group resources, for a specific physical device
+ *
+ * ResourceManager is used on 2 possible flows with the following dependencies:
+ *
+ * !-Working with physical device-!
+ * VdmaDevice (either PcieDevice or CoreDevice)
+ * └── VdmaConfigManager
+ *     └──vector of VdmaConfigNetworkGroup
+ *                  └──ResourceManager <only one>
+ *                     └──reference to physical device
+ *
+ * !-Working with virtual device-!
+ * VDevice
+ * └──vector of PcieDevice
+ * └──vector of VdmaConfigNetworkGroup
+ *              └── vector of ResourceManager <one per phys device>
+ *                            └──reference to physical device
+  **/
+
+#ifndef _HAILO_CONTEXT_SWITCH_RESOURCE_MANAGER_HPP_
+#define _HAILO_CONTEXT_SWITCH_RESOURCE_MANAGER_HPP_
+
+#include "hailo/hailort.h"
+#include "intermediate_buffer.hpp"
+#include "vdma_buffer.hpp"
+#include "vdma_channel.hpp"
+#include "vdma_descriptor_list.hpp"
+#include "control_protocol.hpp"
+#include "pcie_device.hpp"
+
+
+namespace hailort
+{
+
+#define MIN_H2D_CHANNEL_INDEX (0)
+#define MAX_H2D_CHANNEL_INDEX (15)
+#define MIN_D2H_CHANNEL_INDEX (16)
+#define MAX_D2H_CHANNEL_INDEX (31)
+
+#define DDR_NUMBER_OF_ROWS_PER_INTERRUPT (1)
+#define DDR_THREAD_DEFAULT_TIMEOUT_MS (20 * 1000)
+#define DDR_THREADS_MIN_BUFFERED_ROWS_INITIAL_SCALE (1)
+
+
+class DdrChannelsInfo
+{
+public:
+    uint8_t d2h_channel_index;
+    uint8_t d2h_stream_index;
+    uint8_t h2d_channel_index;
+    uint8_t h2d_stream_index;
+    uint16_t row_size;
+    uint32_t min_buffered_rows;
+    uint32_t desc_list_size_mask;
+    uint8_t context_index;
+    uint16_t initial_programed_descs;
+    uint32_t descriptors_per_frame;
+    // Ref to ResourcesManager's m_ddr_buffer_channels
+    VdmaChannel *h2d_ch;
+    VdmaChannel *d2h_ch;
+
+    // Ref to intermediate buffer;
+    IntermediateBuffer *intermediate_buffer;
+};
+class ChannelInfo
+{
+public:
+    enum class Type : uint8_t 
+    {
+        NOT_SET = 0,
+        BOUNDARY = 1,
+        INTER_CONTEXT = 2,
+        DDR = 3,
+        CFG = 4
+    };
+
+    ChannelInfo() : m_info(Type::NOT_SET), m_pcie_stream_index(UINT8_MAX), m_layer_name() {}
+
+    void set_type(Type type)
+    { 
+        m_info = type;
+    }
+
+    bool is_type(Type type) const
+    {
+        return (m_info == type);
+    }
+
+    bool is_used() const
+    {
+        return (m_info != Type::NOT_SET); 
+    }
+
+    uint8_t get_pcie_stream_index() 
+    {
+        assert(UINT8_MAX != m_pcie_stream_index);
+        return m_pcie_stream_index;
+    }
+
+    void set_pcie_stream_index(uint8_t pcie_channel_index) 
+    {
+        m_pcie_stream_index = pcie_channel_index;
+    }
+
+    void set_layer_name(const std::string &name) 
+    {
+        m_layer_name = name;
+    }
+
+    const std::string &get_layer_name() 
+    {
+        return m_layer_name;
+    }
+
+private:
+    Type m_info;
+    uint8_t m_pcie_stream_index;
+    std::string m_layer_name;
+};
+
+
+class ConfigResources final
+{
+public:
+    ConfigResources(HailoRTDriver &driver, VdmaBuffer &&buffer, VdmaDescriptorList &&descriptor,
+        uint16_t requested_desc_page_size, size_t total_buffer_size);
+
+    // Write data to config channel
+    hailo_status write(const void *data, size_t data_size);
+
+    // Program the descriptors for the data written so far
+    Expected<uint16_t> program_descriptors();
+
+    uint16_t get_page_size();
+
+    size_t get_current_buffer_size();
+
+    /* Get all the config size. It's not the same as the VdmaBuffer::size() 
+    since we might add NOPs to the data (Pre-fetch mode) */
+    size_t get_total_cfg_size();
+
+private:
+    VdmaBuffer m_buffer;
+    VdmaDescriptorList m_descriptor;
+    const uint16_t m_desc_page_size;
+    const size_t m_total_buffer_size; 
+    size_t m_acc_buffer_offset;
+    uint16_t m_acc_desc_count;
+    size_t m_current_buffer_size;
+
+    friend class ResourcesManager;
+};
+
+
+class ResourcesManager final
+{
+public:
+    static Expected<ResourcesManager> create(VdmaDevice &vdma_device, HailoRTDriver &driver,
+        const ConfigureNetworkParams &config_params, ProtoHEFNetworkGroupPtr network_group_proto,
+        std::shared_ptr<NetworkGroupMetadata> network_group_metadata, const HefParsingInfo &parsing_info,
+        uint8_t net_group_index);
+
+    ~ResourcesManager() = default;
+    ResourcesManager(const ResourcesManager &other) = delete;
+    ResourcesManager &operator=(const ResourcesManager &other) = delete;
+    ResourcesManager &operator=(ResourcesManager &&other) = delete;
+    ResourcesManager(ResourcesManager &&other) noexcept :
+        m_ddr_infos(std::move(other.m_ddr_infos)), m_contexts(std::move(other.m_contexts)),
+        m_channels_info(std::move(other.m_channels_info)), m_vdma_device(other.m_vdma_device),
+        m_driver(other.m_driver), m_config_params(other.m_config_params),
+        m_preliminary_config(std::move(other.m_preliminary_config)),
+        m_dynamic_config(std::move(other.m_dynamic_config)),
+        m_intermediate_buffers(std::move(other.m_intermediate_buffers)),
+        m_inter_context_channels(std::move(other.m_inter_context_channels)),
+        m_config_channels(std::move(other.m_config_channels)), m_ddr_buffer_channels(std::move(other.m_ddr_buffer_channels)),
+        m_network_group_metadata(std::move(other.m_network_group_metadata)), m_net_group_index(other.m_net_group_index),
+        m_network_index_map(std::move(other.m_network_index_map)) {}
+
+    ExpectedRef<IntermediateBuffer> create_inter_context_buffer(uint32_t transfer_size, uint8_t src_stream_index,
+        uint8_t src_context_index, const std::string &partial_network_name);
+    ExpectedRef<IntermediateBuffer> get_intermediate_buffer(const IntermediateBufferKey &key);
+    Expected<std::pair<uint16_t, uint32_t>> get_desc_buffer_sizes_for_boundary_channel(uint32_t transfer_size,
+        const std::string &partial_network_name);
+    ExpectedRef<IntermediateBuffer> create_ddr_buffer(DdrChannelsInfo &ddr_info, uint8_t context_index);
+
+    Expected<CONTROL_PROTOCOL__application_header_t> get_control_network_group_header();
+
+    using context_info_t = CONTROL_PROTOCOL__context_switch_context_info_t;
+
+    Expected<std::reference_wrapper<context_info_t>> add_new_context()
+    {
+        return std::ref(*m_contexts.emplace(m_contexts.end()));
+    }
+
+    const std::vector<context_info_t>& get_contexts()
+    {
+        return m_contexts;
+    }
+
+    std::vector<ConfigResources> &preliminary_config() 
+    {
+        return m_preliminary_config; 
+    }
+
+    std::vector<ConfigResources> &dynamic_config(uint8_t context_index)
+    {
+        assert(context_index < m_dynamic_config.size());
+        return m_dynamic_config[context_index]; 
+    }
+
+    std::vector<DdrChannelsInfo> &ddr_infos()
+    {
+        return m_ddr_infos;
+    }
+
+    hailo_power_mode_t get_power_mode()
+    {
+        return m_config_params.power_mode;
+    }
+
+    const NetworkGroupSupportedFeatures &get_supported_features() const
+    {
+        return m_network_group_metadata->supported_features();
+    }
+
+    uint8_t get_network_group_index()
+    {
+        return m_net_group_index;
+    }
+
+    VdmaDevice &get_device()
+    {
+        return m_vdma_device;
+    }
+
+    Expected<uint8_t> get_available_channel_index(std::set<uint8_t>& blacklist, ChannelInfo::Type required_type,
+        VdmaChannel::Direction direction, const std::string &layer_name="");
+
+    Expected<std::reference_wrapper<ChannelInfo>> get_channel_info(uint8_t index)
+    {
+        CHECK_AS_EXPECTED(index < m_channels_info.max_size(), HAILO_INVALID_ARGUMENT);
+        return std::ref(m_channels_info[index]);
+    }
+
+    const char* get_dev_id() const
+    {
+        return m_vdma_device.get_dev_id();
+    }
+
+    Expected<uint8_t> get_boundary_channel_index(uint8_t stream_index,
+        hailo_stream_direction_t direction, const std::string &layer_name);
+    Expected<hailo_stream_interface_t> get_default_streams_interface();
+
+    Expected<Buffer> read_intermediate_buffer(const IntermediateBufferKey &key);
+
+    hailo_status set_number_of_cfg_channels(const uint8_t number_of_cfg_channels);
+    static Expected<ConfigResources> create_config_resources(uint8_t channel_index,
+        const std::vector<uint32_t> &cfg_sizes, HailoRTDriver &driver);
+    void update_preliminary_config_buffer_info();
+    void update_dynamic_contexts_buffer_info();
+
+    hailo_status create_vdma_channels();
+    hailo_status register_fw_managed_vdma_channels();
+    hailo_status unregister_fw_managed_vdma_channels();
+    hailo_status open_ddr_channels();
+    void abort_ddr_channels();
+    void close_ddr_channels();
+    hailo_status enable_state_machine();
+    hailo_status reset_state_machine();
+    Expected<uint16_t> get_network_batch_size_from_partial_name(const std::string &partial_network_name) const;
+    hailo_status fill_network_batch_size(CONTROL_PROTOCOL__application_header_t &app_header);
+private:
+    ExpectedRef<IntermediateBuffer> create_intermediate_buffer(uint32_t transfer_size, uint16_t batch_size,
+        const IntermediateBufferKey &key);
+
+    std::vector<DdrChannelsInfo> m_ddr_infos;
+    std::vector<CONTROL_PROTOCOL__context_switch_context_info_t> m_contexts;
+    std::array<ChannelInfo, MAX_HOST_CHANNELS_COUNT> m_channels_info;
+    VdmaDevice &m_vdma_device;
+    HailoRTDriver &m_driver;
+    const ConfigureNetworkParams m_config_params;
+    std::vector<ConfigResources> m_preliminary_config;
+    // m_dynamic_config[context_index][config_index]
+    std::vector<std::vector<ConfigResources>> m_dynamic_config;
+    std::map<IntermediateBufferKey, std::unique_ptr<IntermediateBuffer>> m_intermediate_buffers;
+    std::vector<VdmaChannel> m_inter_context_channels;
+    std::vector<VdmaChannel> m_config_channels;
+    std::vector<VdmaChannel> m_ddr_buffer_channels;
+    std::shared_ptr<NetworkGroupMetadata> m_network_group_metadata;
+    uint8_t m_net_group_index;
+    const std::vector<std::string> m_network_index_map;
+
+    ResourcesManager(VdmaDevice &vdma_device, HailoRTDriver &driver,
+        const ConfigureNetworkParams config_params, std::vector<ConfigResources> &&preliminary_config,
+        std::vector<std::vector<ConfigResources>> &&dynamic_config, std::shared_ptr<NetworkGroupMetadata> &&network_group_metadata, uint8_t net_group_index,
+        const std::vector<std::string> &&network_index_map) :
+          m_vdma_device(vdma_device), m_driver(driver), m_config_params(config_params),
+          m_preliminary_config(std::move(preliminary_config)), m_dynamic_config(std::move(dynamic_config)),
+          m_network_group_metadata(std::move(network_group_metadata)), m_net_group_index(net_group_index), m_network_index_map(std::move(network_index_map)) {};
+
+};
+
+} /* namespace hailort */
+
+#endif /* _HAILO_CONTEXT_SWITCH_RESOURCE_MANAGER_HPP_ */
diff --git a/hailort/libhailort/src/context_switch/multi_context/vdma_config_activated_network_group.hpp b/hailort/libhailort/src/context_switch/multi_context/vdma_config_activated_network_group.hpp
new file mode 100644 (file)
index 0000000..980bee9
--- /dev/null
@@ -0,0 +1,78 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file vdma_config_activated_network_group.hpp
+ * @brief TODO: Represent activated network_group from HEF
+ **/
+
+#ifndef _HAILO_CONTEXT_SWITCH_VDMA_CONFIG_ACTIVATED_NETWORK_GROUP_HPP_
+#define _HAILO_CONTEXT_SWITCH_VDMA_CONFIG_ACTIVATED_NETWORK_GROUP_HPP_
+
+#include "hailo/expected.hpp"
+#include "vdma_channel.hpp"
+#include "pcie_stream.hpp"
+#include "context_switch/active_network_group_holder.hpp"
+#include "context_switch/network_group_internal.hpp"
+#include "context_switch/multi_context/resource_manager.hpp"
+
+#include <vector>
+#include <map>
+#include <functional>
+
+namespace hailort
+{
+
+class VdmaConfigActivatedNetworkGroup : public ActivatedNetworkGroupBase
+{
+public:
+
+    static Expected<VdmaConfigActivatedNetworkGroup> create(
+        VdmaConfigActiveAppHolder &active_net_group_holder,
+        std::vector<std::shared_ptr<ResourcesManager>> resources_managers,
+        const hailo_activate_network_group_params_t &network_group_params,
+        std::map<std::string, std::unique_ptr<InputStream>> &input_streams,
+        std::map<std::string, std::unique_ptr<OutputStream>> &output_streams,         
+        EventPtr network_group_activated_event,
+        AccumulatorPtr deactivation_time_accumulator);
+
+    virtual ~VdmaConfigActivatedNetworkGroup();
+
+    VdmaConfigActivatedNetworkGroup(const VdmaConfigActivatedNetworkGroup &other) = delete;
+    VdmaConfigActivatedNetworkGroup &operator=(const VdmaConfigActivatedNetworkGroup &other) = delete;
+    VdmaConfigActivatedNetworkGroup &operator=(VdmaConfigActivatedNetworkGroup &&other) = delete;
+    VdmaConfigActivatedNetworkGroup(VdmaConfigActivatedNetworkGroup &&other) noexcept;
+
+    virtual Expected<Buffer> get_intermediate_buffer(const IntermediateBufferKey &key) override;
+
+private:
+    VdmaConfigActivatedNetworkGroup(
+      const hailo_activate_network_group_params_t &network_group_params,
+      std::map<std::string, std::unique_ptr<InputStream>> &input_streams,
+      std::map<std::string, std::unique_ptr<OutputStream>> &output_streams,
+      std::vector<std::shared_ptr<ResourcesManager>> &&resources_managers,
+      VdmaConfigActiveAppHolder &active_net_group_holder,
+      EventPtr &&network_group_activated_event,
+      AccumulatorPtr deactivation_time_accumulator, hailo_status &status);
+
+    hailo_status init_ddr_resources();
+    hailo_status cleanup_ddr_resources();
+
+    static void ddr_recv_thread_main(DdrChannelsInfo ddr_info,
+      std::shared_ptr<std::atomic<uint16_t>> desc_list_num_ready);
+    static void ddr_send_thread_main(DdrChannelsInfo ddr_info,
+      std::shared_ptr<std::atomic<uint16_t>> desc_list_num_ready);
+
+  bool m_should_reset_state_machine;
+  VdmaConfigActiveAppHolder &m_active_net_group_holder;
+  // One ResourcesManager per connected physical device. Currently only one device is supported.
+  std::vector<std::shared_ptr<ResourcesManager>> m_resources_managers;
+  std::vector<std::thread> m_ddr_send_threads;
+  std::vector<std::thread> m_ddr_recv_threads;
+  AccumulatorPtr m_deactivation_time_accumulator;
+};
+
+} /* namespace hailort */
+
+#endif /* _HAILO_CONTEXT_SWITCH_VDMA_CONFIG_ACTIVATED_NETWORK_GROUP_HPP_ */
diff --git a/hailort/libhailort/src/context_switch/multi_context/vdma_config_manager.hpp b/hailort/libhailort/src/context_switch/multi_context/vdma_config_manager.hpp
new file mode 100644 (file)
index 0000000..4dd8d99
--- /dev/null
@@ -0,0 +1,63 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file vdma_config_manager.hpp
+ * @brief Manager of HEF parsing and vdma-configured network groups resources for Pcie devices (both single and multi context)
+ *
+ **/
+
+#ifndef HAILO_VDMA_CONFIG_MANAGER_HPP_
+#define HAILO_VDMA_CONFIG_MANAGER_HPP_
+
+#include "context_switch/config_manager.hpp"
+#include "context_switch/multi_context/vdma_config_network_group.hpp"
+#include "hailo/hailort.h"
+#include "hailo/device.hpp"
+#include "hailo/vdevice.hpp"
+#include "hailo/expected.hpp"
+#include "common/utils.hpp"
+#include "hlpcie.hpp"
+#include "vdma_channel.hpp"
+#include "vdma_buffer.hpp"
+#include "vdma_descriptor_list.hpp"
+
+#include <vector>
+#include <map>
+#include <algorithm>
+#include <bitset>
+
+namespace hailort
+{
+
+class VdmaConfigManager : public ConfigManager
+{
+public:
+    static Expected<VdmaConfigManager> create(VdmaDevice &device);
+    static Expected<VdmaConfigManager> create(VDevice &vdevice);
+    virtual ConfigManagerType get_manager_type();
+    virtual Expected<ConfiguredNetworkGroupVector> add_hef(Hef &hef,
+        const NetworkGroupsParamsMap &configure_params={});
+
+    static hailo_status update_network_batch_size(ConfigureNetworkParams &configure_params);
+
+    virtual ~VdmaConfigManager() {}
+    VdmaConfigManager(const VdmaConfigManager &other) noexcept = delete;
+    VdmaConfigManager &operator=(const VdmaConfigManager &other) = delete;
+    VdmaConfigManager &operator=(VdmaConfigManager &&other) = delete;
+    VdmaConfigManager(VdmaConfigManager &&other) noexcept = default;
+
+  private:
+    VdmaConfigManager(std::vector<std::reference_wrapper<VdmaDevice>> &&devices, bool is_vdevice);
+
+    // TODO: (SDK-16665) Dont need is_active flag for dtor?
+    std::vector<std::reference_wrapper<VdmaDevice>> m_devices;
+    std::vector<std::shared_ptr<VdmaConfigNetworkGroup>> m_net_groups;
+    VdmaConfigActiveAppHolder m_active_net_group_holder;
+    bool m_is_vdevice;
+};
+
+} /* namespace hailort */
+
+#endif /* HAILO_VDMA_CONFIG_MANAGER_HPP_ */
diff --git a/hailort/libhailort/src/context_switch/multi_context/vdma_config_network_group.hpp b/hailort/libhailort/src/context_switch/multi_context/vdma_config_network_group.hpp
new file mode 100644 (file)
index 0000000..f57b130
--- /dev/null
@@ -0,0 +1,85 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file vdma_config_network_group.hpp
+ * @brief Represent network_group from HEF file that can be activated 
+ *
+ * This network_group can be used for both single or multi context network_groups but for PCIE only
+  **/
+
+#ifndef _HAILO_CONTEXT_SWITCH_VDMA_CONFIG_NETWORK_GROUP_HPP_
+#define _HAILO_CONTEXT_SWITCH_VDMA_CONFIG_NETWORK_GROUP_HPP_
+
+#include "hailo/hailort.h"
+#include "vdma_buffer.hpp"
+#include "vdma_channel.hpp"
+#include "vdma_descriptor_list.hpp"
+#include "common/utils.hpp"
+#include "context_switch/multi_context/vdma_config_activated_network_group.hpp"
+#include "control_protocol.h"
+#include "context_switch/active_network_group_holder.hpp"
+#include "hailort_defaults.hpp"
+#include "context_switch/network_group_internal.hpp"
+#include "context_switch/multi_context/resource_manager.hpp"
+
+#include <cstdint>
+#include <assert.h>
+#include <map>
+#include <set>
+
+namespace hailort
+{
+
+#define MAX_CONTEXTS_COUNT (CONTROL_PROTOCOL__MAX_TOTAL_CONTEXTS)
+
+
+class VdmaConfigNetworkGroup : public ConfiguredNetworkGroupBase
+{
+public:
+    static Expected<VdmaConfigNetworkGroup> create(VdmaConfigActiveAppHolder &active_net_group_holder,
+        const ConfigureNetworkParams &config_params, 
+        std::vector<std::shared_ptr<ResourcesManager>> resources_managers,
+        std::shared_ptr<NetworkGroupMetadata> network_group_metadata);
+
+    std::vector<std::shared_ptr<ResourcesManager>> &get_resources_managers()
+    {
+        return m_resources_managers;
+    }
+
+    hailo_status create_vdevice_streams_from_config_params();
+    hailo_status create_output_vdevice_stream_from_config_params(
+        const hailo_stream_parameters_t &stream_params, const std::string &stream_name);
+    hailo_status create_input_vdevice_stream_from_config_params(
+        const hailo_stream_parameters_t &stream_params, const std::string &stream_name);
+
+    virtual Expected<std::unique_ptr<ActivatedNetworkGroup>> activate(
+      const hailo_activate_network_group_params_t &network_group_params = HailoRTDefaults::get_network_group_params()) override;
+
+    virtual Expected<hailo_stream_interface_t> get_default_streams_interface() override;
+
+    virtual Expected<uint8_t> get_boundary_channel_index(uint8_t stream_index, hailo_stream_direction_t direction,
+        const std::string &layer_name) override;
+
+    virtual ~VdmaConfigNetworkGroup() = default;
+    VdmaConfigNetworkGroup(const VdmaConfigNetworkGroup &other) = delete;
+    VdmaConfigNetworkGroup &operator=(const VdmaConfigNetworkGroup &other) = delete;
+    VdmaConfigNetworkGroup &operator=(VdmaConfigNetworkGroup &&other) = delete;
+    VdmaConfigNetworkGroup(VdmaConfigNetworkGroup &&other) noexcept : ConfiguredNetworkGroupBase(std::move(other)),
+      m_active_net_group_holder(other.m_active_net_group_holder),
+      m_resources_managers(std::move(other.m_resources_managers)) {}
+
+private:
+    VdmaConfigNetworkGroup(VdmaConfigActiveAppHolder &active_net_group_holder,
+        const ConfigureNetworkParams &config_params, 
+        std::vector<std::shared_ptr<ResourcesManager>> &&resources_managers,
+        const NetworkGroupMetadata &network_group_metadata, hailo_status &status);
+
+    VdmaConfigActiveAppHolder &m_active_net_group_holder;
+    std::vector<std::shared_ptr<ResourcesManager>> m_resources_managers;
+};
+
+} /* namespace hailort */
+
+#endif /* _HAILO_CONTEXT_SWITCH_VDMA_CONFIG_NETWORK_GROUP_HPP_ */
diff --git a/hailort/libhailort/src/context_switch/network_group.cpp b/hailort/libhailort/src/context_switch/network_group.cpp
new file mode 100644 (file)
index 0000000..41ce33f
--- /dev/null
@@ -0,0 +1,742 @@
+#include "hailo/transform.hpp"
+#include "network_group_internal.hpp"
+#include "hef_internal.hpp"
+#include "common/utils.hpp"
+#include "hailort_defaults.hpp"
+#include "eth_stream.hpp"
+#include "pcie_stream.hpp"
+#include "core_stream.hpp"
+#include "mipi_stream.hpp"
+#include "control.hpp"
+#include "common/runtime_statistics_internal.hpp"
+
+namespace hailort
+{
+
+ActivatedNetworkGroupBase::ActivatedNetworkGroupBase(const hailo_activate_network_group_params_t &network_group_params,
+        std::map<std::string, std::unique_ptr<InputStream>> &input_streams, std::map<std::string, std::unique_ptr<OutputStream>> &output_streams,         
+        EventPtr &&network_group_activated_event, hailo_status &status) :
+    m_network_group_params(network_group_params),
+    m_input_streams(input_streams),
+    m_output_streams(output_streams),
+    m_network_group_activated_event(std::move(network_group_activated_event))
+{
+    status = validate_network_group_params(network_group_params);
+    if (HAILO_SUCCESS != status) {
+        LOGGER__ERROR("Failed to validate network_group params");
+        return;
+    }
+
+    status = activate_low_level_streams();
+    if (HAILO_SUCCESS != status) {
+        LOGGER__ERROR("Failed to activate low level streams");
+        return;
+    }
+
+    status = m_network_group_activated_event->signal();
+    if (HAILO_SUCCESS != status) {
+        LOGGER__ERROR("Failed to signal network activation event");
+        return;
+    }
+}
+
+Expected<LatencyMeterPtr> ConfiguredNetworkGroupBase::create_hw_latency_meter(Device &device,
+    const std::vector<LayerInfo> &layers)
+{
+    std::set<uint32_t> d2h_channel_indexes;
+
+    // TODO: dont support hw latency meter with MIPI input
+
+    if (Device::Type::PCIE != device.get_type()) {
+        LOGGER__WARNING("HW Latency measurement is supported only on PCIe devices");
+        return make_unexpected(HAILO_INVALID_OPERATION);
+    }
+
+    size_t h2d_streams_count = 0;
+    for (const auto &layer : layers) {
+        if (layer.direction == HAILO_D2H_STREAM) {
+            if (HAILO_FORMAT_ORDER_HAILO_NMS == layer.format.order) {
+                LOGGER__WARNING("HW Latency measurement is not supported on NMS networks");
+                return make_unexpected(HAILO_INVALID_OPERATION);
+            }
+
+            d2h_channel_indexes.insert(layer.index);
+        }
+        else {
+            h2d_streams_count++;
+        }
+    }
+
+    if (h2d_streams_count > 1) {
+        LOGGER__WARNING("HW Latency measurement is supported on networks with a single input");
+        return make_unexpected(HAILO_INVALID_OPERATION);
+    }
+
+    return make_shared_nothrow<LatencyMeter>(d2h_channel_indexes, MAX_IRQ_TIMESTAMPS_SIZE);
+}
+
+hailo_status ActivatedNetworkGroupBase::activate_low_level_streams()
+{
+    for (auto &name_pair : m_input_streams) {
+        auto status = name_pair.second->activate_stream();
+        CHECK_SUCCESS(status);
+    }
+    for (auto &name_pair : m_output_streams) {
+        auto status = name_pair.second->activate_stream();
+        CHECK_SUCCESS(status);
+    }
+
+    return HAILO_SUCCESS;
+}
+
+uint32_t ActivatedNetworkGroupBase::get_invalid_frames_count()
+{
+    uint32_t total_invalid_frames_count = 0;
+    for (auto& name_stream_pair : m_output_streams) {
+        total_invalid_frames_count += name_stream_pair.second->get_invalid_frames_count();
+    }
+    return total_invalid_frames_count;
+}
+
+void ActivatedNetworkGroupBase::deactivate_resources()
+{
+    if (nullptr != m_network_group_activated_event) {
+        for (auto &name_pair : m_input_streams) {
+            auto status = name_pair.second->deactivate_stream();
+            if (HAILO_SUCCESS != status) {
+                LOGGER__ERROR("Failed to deactivate input stream name {}", name_pair.first);
+            }
+        }
+    
+        for (auto &name_pair : m_output_streams) {
+            auto status = name_pair.second->deactivate_stream();
+            if (HAILO_SUCCESS != status) {
+                LOGGER__ERROR("Failed to deactivate output stream name {}", name_pair.first);
+            }
+        }
+        m_network_group_activated_event->reset();
+    }
+
+}
+
+// TODO: Implement function (HRT-3174)
+hailo_status ActivatedNetworkGroupBase::validate_network_group_params(
+    const hailo_activate_network_group_params_t &/*network_group_params*/)
+{
+    return HAILO_SUCCESS;
+}
+
+Expected<std::unique_ptr<ActivatedNetworkGroup>> ConfiguredNetworkGroup::activate()
+{
+    const auto network_group_params = HailoRTDefaults::get_network_group_params();
+    return activate(network_group_params);
+}
+
+Expected<std::chrono::nanoseconds> get_latency(LatencyMeterPtr &latency_meter, bool clear)
+{
+    auto hw_latency = latency_meter->get_latency(clear);
+    if (HAILO_NOT_AVAILABLE == hw_latency.status()) {
+        return make_unexpected(HAILO_NOT_AVAILABLE);
+    }
+    CHECK_EXPECTED(hw_latency, "Failed getting latency");
+    return hw_latency.release();
+}
+
+/* Network group base functions */
+Expected<LatencyMeasurementResult> ConfiguredNetworkGroupBase::get_latency_measurement(const std::string &network_name)
+{
+    bool clear = ((m_config_params.latency & HAILO_LATENCY_CLEAR_AFTER_GET) == HAILO_LATENCY_CLEAR_AFTER_GET);
+    LatencyMeasurementResult result = {};
+
+    if (network_name.empty()) {
+        std::chrono::nanoseconds latency_sum(0);
+        uint32_t measurements_count = 0;
+        for (auto &latency_meter_pair : m_latency_meter) {
+            auto hw_latency = get_latency(latency_meter_pair.second, clear);
+            if (HAILO_NOT_AVAILABLE == hw_latency.status()) {
+                continue;
+            }
+            CHECK_EXPECTED(hw_latency);
+            latency_sum += hw_latency.value();
+            measurements_count++;
+        }
+        if (0 == measurements_count) {
+            LOGGER__DEBUG("No latency measurements was found");
+            return make_unexpected(HAILO_NOT_AVAILABLE);
+        }
+        result.avg_hw_latency = latency_sum / measurements_count;
+    } else {
+        auto partial_network_name = m_network_group_metadata.get_partial_network_name(network_name);
+        CHECK_EXPECTED(partial_network_name);
+        if(!contains(m_latency_meter, partial_network_name.value())) {
+            LOGGER__DEBUG("No latency measurements was found for network {}", partial_network_name.value());
+            return make_unexpected(HAILO_NOT_AVAILABLE);
+        }
+        auto hw_latency = get_latency(m_latency_meter.at(partial_network_name.value()), clear);
+        if (HAILO_NOT_AVAILABLE == hw_latency.status()) {
+            return make_unexpected(HAILO_NOT_AVAILABLE);
+        }
+        CHECK_EXPECTED(hw_latency);
+        result.avg_hw_latency = hw_latency.value();
+    }
+    return result;
+}
+
+
+
+Expected<OutputStreamWithParamsVector> ConfiguredNetworkGroupBase::get_output_streams_from_vstream_names(
+    const std::map<std::string, hailo_vstream_params_t> &outputs_params)
+{
+    OutputStreamWithParamsVector results;
+    std::unordered_map<std::string, hailo_vstream_params_t> outputs_edges_params;
+    for (auto &name_params_pair : outputs_params) {
+        auto stream_names = m_network_group_metadata.get_stream_names_from_vstream_name(name_params_pair.first);
+        CHECK_EXPECTED(stream_names);
+
+        for (auto &stream_name : stream_names.value()) {
+            auto output_stream = get_output_stream_by_name(stream_name);
+            CHECK_EXPECTED(output_stream);
+
+            if (output_stream->get().get_info().is_mux) {
+                outputs_edges_params.emplace(name_params_pair);
+            }
+            else {
+                NameToVStreamParamsMap name_to_params = {name_params_pair};
+                results.emplace_back(output_stream.release(), name_to_params);
+            }
+        }
+    }
+    // Add non mux streams to result
+    hailo_status status = add_mux_streams_by_edges_names(results, outputs_edges_params); 
+    CHECK_SUCCESS_AS_EXPECTED(status);
+
+    return results;
+}
+
+// This function adds to results the OutputStreams that correspond to the edges in outputs_edges_params.
+// If an edge name appears in outputs_edges_params then all of its predecessors must appear in outputs_edges_params as well, Otherwise, an error is returned.
+// We use the set seen_edges in order to mark the edges already evaluated by one of its' predecessor.
+hailo_status ConfiguredNetworkGroupBase::add_mux_streams_by_edges_names(OutputStreamWithParamsVector &results,
+    const std::unordered_map<std::string, hailo_vstream_params_t> &outputs_edges_params)
+{
+    std::unordered_set<std::string> seen_edges;
+    for (auto &name_params_pair : outputs_edges_params) {
+        if (seen_edges.end() != seen_edges.find(name_params_pair.first)) {
+            // Edge has already been seen by one of its predecessors
+            continue;
+        }
+        auto output_streams = get_output_streams_by_vstream_name(name_params_pair.first);
+        CHECK_EXPECTED_AS_STATUS(output_streams);
+        CHECK(output_streams->size() == 1, HAILO_INVALID_ARGUMENT,
+            "mux streams cannot be separated into multiple streams");
+        auto output_stream = output_streams.release()[0];
+
+        // TODO: Find a better way to get the mux edges without creating OutputDemuxer
+        auto expected_demuxer = OutputDemuxer::create(output_stream.get());
+        CHECK_EXPECTED_AS_STATUS(expected_demuxer);
+
+        NameToVStreamParamsMap name_to_params;
+        for (auto &edge : expected_demuxer.value()->get_edges_stream_info()) {
+            auto edge_name_params_pair = outputs_edges_params.find(edge.name);
+            CHECK(edge_name_params_pair != outputs_edges_params.end(), HAILO_INVALID_ARGUMENT,
+                "All edges of stream {} must be in output vstream params. edge {} is missing.",
+                name_params_pair.first, edge.name);
+            seen_edges.insert(edge.name);
+            name_to_params.insert(*edge_name_params_pair);
+        }
+        results.emplace_back(output_stream, name_to_params);
+    }
+    return HAILO_SUCCESS;
+}
+
+Expected<OutputStreamRefVector> ConfiguredNetworkGroupBase::get_output_streams_by_vstream_name(const std::string &name)
+{
+    auto stream_names = m_network_group_metadata.get_stream_names_from_vstream_name(name);
+    CHECK_EXPECTED(stream_names);
+
+    OutputStreamRefVector output_streams;
+    output_streams.reserve(stream_names->size());
+    for (const auto &stream_name : stream_names.value()) {
+        auto output_stream = get_output_stream_by_name(stream_name);
+        CHECK_EXPECTED(output_stream);
+
+        output_streams.emplace_back(output_stream.release());
+    }
+
+    return output_streams;
+}
+
+Expected<LayerInfo> ConfiguredNetworkGroupBase::get_layer_info(const std::string &stream_name)
+{
+    auto layer_infos = m_network_group_metadata.get_all_layer_infos();
+    CHECK_EXPECTED(layer_infos);
+    for (auto layer_info : layer_infos.release()) {
+        if (layer_info.name == stream_name) {
+            return layer_info;
+        }
+    }
+    LOGGER__ERROR("Failed to find layer with name {}", stream_name);
+    return make_unexpected(HAILO_NOT_FOUND);
+}
+
+ConfiguredNetworkGroupBase::ConfiguredNetworkGroupBase(
+    const ConfigureNetworkParams &config_params, const uint8_t net_group_index, 
+    const NetworkGroupMetadata &network_group_metadata, hailo_status &status) :
+        ConfiguredNetworkGroup::ConfiguredNetworkGroup(),
+        m_config_params(config_params),
+        m_net_group_index(net_group_index),
+        m_latency_meter(),
+        m_network_group_metadata(network_group_metadata),
+        m_activation_time_accumulator(),
+        m_deactivation_time_accumulator()
+{
+    auto event = Event::create_shared(Event::State::not_signalled);
+    if (nullptr == event) {
+        LOGGER__ERROR("Failed to create activation event");
+        status = HAILO_INTERNAL_FAILURE;
+        return;
+    }
+    m_network_group_activated_event = std::move(std::move(event));
+
+    m_activation_time_accumulator = make_shared_nothrow<FullAccumulator<double>>("activation_time");
+    if (nullptr == m_activation_time_accumulator) {
+        LOGGER__ERROR("Failed to create activation time accumulator");
+        status = HAILO_OUT_OF_HOST_MEMORY;
+        return;
+    };
+
+    m_deactivation_time_accumulator = make_shared_nothrow<FullAccumulator<double>>("deactivation_time");
+    if (nullptr == m_deactivation_time_accumulator) {
+        LOGGER__ERROR("Failed to create deactivation time accumulator");
+        status = HAILO_OUT_OF_HOST_MEMORY;
+        return;
+    };
+
+    status = HAILO_SUCCESS;
+}
+
+const std::string &ConfiguredNetworkGroupBase::get_network_group_name() const
+{
+    return m_network_group_metadata.network_group_name();
+}
+
+Expected<uint16_t> ConfiguredNetworkGroupBase::get_stream_batch_size(const std::string &stream_name)
+{
+    auto layer_infos = m_network_group_metadata.get_all_layer_infos();
+    CHECK_EXPECTED(layer_infos);
+    for (const auto &layer_info : layer_infos.release()) {
+        if (layer_info.name == stream_name) {
+            auto partial_network_name = layer_info.partial_network_name;
+            for (auto const &network_params_pair : m_config_params.network_params_by_name) {
+                auto network_name = network_params_pair.first;
+                auto found = network_name.find(HAILO_DEFAULT_NETWORK_NAME_QUALIFIER + partial_network_name);
+                if (found != std::string::npos) {
+                    auto batch_size = network_params_pair.second.batch_size;
+                    return batch_size;
+                }
+            }
+        }
+    }
+    LOGGER__ERROR("Failed to find network name output stream {}", stream_name);
+    return make_unexpected(HAILO_NOT_FOUND);
+}
+
+hailo_status ConfiguredNetworkGroupBase::create_input_stream_from_config_params(Device &device,
+    const hailo_stream_parameters_t &stream_params, const std::string &stream_name)
+{
+    auto edge_layer = get_layer_info(stream_name);
+    CHECK_EXPECTED_AS_STATUS(edge_layer);
+
+    auto partial_network_name = edge_layer->partial_network_name;
+    auto latency_meter = (contains(m_latency_meter, partial_network_name)) ? m_latency_meter.at(partial_network_name) : nullptr;
+
+    CHECK(device.is_stream_interface_supported(stream_params.stream_interface), HAILO_INVALID_OPERATION,
+        "Device does not supports the given stream interface streams. Please update input_stream_params for stream {}.",
+        stream_name);
+
+    switch (stream_params.stream_interface) {
+        case HAILO_STREAM_INTERFACE_PCIE:
+            {
+                auto batch_size_exp = get_stream_batch_size(stream_name);
+                CHECK_EXPECTED_AS_STATUS(batch_size_exp);
+                const auto stream_index = edge_layer->index;
+                const auto channel_index = get_boundary_channel_index(stream_index, HAILO_H2D_STREAM, stream_name);
+                CHECK_EXPECTED_AS_STATUS(channel_index, "Failed to get channel index for input stream {}", stream_index);
+
+                auto input_stream = PcieInputStream::create(device, channel_index.value(),
+                    edge_layer.value(), batch_size_exp.value(), m_network_group_activated_event, latency_meter);
+                CHECK_EXPECTED_AS_STATUS(input_stream);
+                m_input_streams.insert(make_pair(stream_name, input_stream.release()));
+            }
+            break;
+        case HAILO_STREAM_INTERFACE_CORE:
+            {
+                auto batch_size_exp = get_stream_batch_size(stream_name);
+                CHECK_EXPECTED_AS_STATUS(batch_size_exp);
+                const auto stream_index = edge_layer->index;
+                const auto channel_index = get_boundary_channel_index(stream_index, HAILO_H2D_STREAM, stream_name);
+                CHECK_EXPECTED_AS_STATUS(channel_index, "Failed to get channel index for input stream {}", stream_index);
+
+                auto input_stream = CoreInputStream::create(device, channel_index.value(),
+                    edge_layer.value(), batch_size_exp.value(), m_network_group_activated_event, latency_meter);
+                CHECK_EXPECTED_AS_STATUS(input_stream);
+                m_input_streams.insert(make_pair(stream_name, input_stream.release()));
+            }
+            break;
+        case HAILO_STREAM_INTERFACE_ETH:
+            {
+                auto input_stream = EthernetInputStream::create(device,
+                    edge_layer.value(), stream_params.eth_input_params, m_network_group_activated_event);
+                CHECK_EXPECTED_AS_STATUS(input_stream);
+                m_input_streams.insert(make_pair(stream_name, input_stream.release()));
+            }
+            break;
+        case HAILO_STREAM_INTERFACE_MIPI:
+            {
+                auto input_stream = MipiInputStream::create(device,
+                    edge_layer.value(), stream_params.mipi_input_params, m_network_group_activated_event);
+                CHECK_EXPECTED_AS_STATUS(input_stream);
+                m_input_streams.insert(make_pair(stream_name, input_stream.release()));
+            }
+            break;
+        default:
+            {
+                LOGGER__ERROR("{} interface is not supported.", stream_params.stream_interface);
+                return HAILO_NOT_IMPLEMENTED;
+            }
+    }
+
+    return HAILO_SUCCESS;
+}
+
+hailo_status ConfiguredNetworkGroupBase::create_output_stream_from_config_params(Device &device,
+    const hailo_stream_parameters_t &stream_params, const std::string &stream_name)
+{
+    auto edge_layer = get_layer_info(stream_name);
+    CHECK_EXPECTED_AS_STATUS(edge_layer);
+
+    auto partial_network_name = edge_layer->partial_network_name;
+    auto latency_meter = (contains(m_latency_meter, partial_network_name)) ? m_latency_meter.at(partial_network_name) : nullptr;
+
+    CHECK(device.is_stream_interface_supported(stream_params.stream_interface), HAILO_INVALID_OPERATION,
+        "Device does not supports the given stream interface streams. Please update input_stream_params for stream {}.",
+        stream_name);
+
+    switch (stream_params.stream_interface) {
+        case HAILO_STREAM_INTERFACE_PCIE:
+            {
+                auto batch_size_exp = get_stream_batch_size(stream_name);
+                CHECK_EXPECTED_AS_STATUS(batch_size_exp);
+                const auto stream_index = edge_layer->index;
+                const auto channel_index = get_boundary_channel_index(stream_index, HAILO_D2H_STREAM, stream_name);
+                CHECK_EXPECTED_AS_STATUS(channel_index, "Failed to get channel index for output stream {}", stream_index);
+
+                auto output_stream = PcieOutputStream::create(device, channel_index.value(),
+                    edge_layer.value(), batch_size_exp.value(), m_network_group_activated_event, latency_meter);
+                CHECK_EXPECTED_AS_STATUS(output_stream);
+                m_output_streams.insert(make_pair(stream_name, output_stream.release()));
+            }
+            break;
+        case HAILO_STREAM_INTERFACE_CORE:
+            {
+                auto batch_size_exp = get_stream_batch_size(stream_name);
+                CHECK_EXPECTED_AS_STATUS(batch_size_exp);
+                const auto stream_index = edge_layer->index;
+                const auto channel_index = get_boundary_channel_index(stream_index, HAILO_D2H_STREAM, stream_name);
+                CHECK_EXPECTED_AS_STATUS(channel_index, "Failed to get channel index for output stream {}", stream_index);
+
+                auto output_stream = CoreOutputStream::create(device, channel_index.value(),
+                    edge_layer.value(), batch_size_exp.value(), m_network_group_activated_event,
+                    latency_meter);
+                CHECK_EXPECTED_AS_STATUS(output_stream);
+                m_output_streams.insert(make_pair(stream_name, output_stream.release()));
+            }
+            break;
+        case HAILO_STREAM_INTERFACE_ETH:
+            {
+                auto output_stream =  EthernetOutputStream::create(device,
+                    edge_layer.value(), stream_params.eth_output_params, 
+                    m_network_group_activated_event);
+                CHECK_EXPECTED_AS_STATUS(output_stream);
+                m_output_streams.insert(make_pair(stream_name, output_stream.release()));
+            }
+            break;
+        default:
+            {
+                LOGGER__ERROR("{} interface is not supported.", stream_params.stream_interface);
+                return HAILO_NOT_IMPLEMENTED;
+            }
+    }
+
+    return HAILO_SUCCESS;
+}
+
+hailo_status ConfiguredNetworkGroupBase::create_streams_from_config_params(Device &device)
+{
+    if ((m_config_params.latency & HAILO_LATENCY_MEASURE) == HAILO_LATENCY_MEASURE) {
+        // Best affort for starting latency meter.
+        auto networks_names = m_network_group_metadata.get_partial_network_names();
+        for (auto &network_name : networks_names) {
+            auto layer_infos = m_network_group_metadata.get_all_layer_infos(network_name);
+            CHECK_EXPECTED_AS_STATUS(layer_infos);
+            auto latency_meter = ConfiguredNetworkGroupBase::create_hw_latency_meter(device, layer_infos.value());
+            if (latency_meter) {
+                m_latency_meter.emplace(network_name, latency_meter.release());
+                LOGGER__DEBUG("Starting hw latency measurement for network {}", network_name);
+            }
+        }
+    }
+
+    for (const auto &stream_parameters_pair : m_config_params.stream_params_by_name) {
+        switch (stream_parameters_pair.second.direction) {
+            case HAILO_H2D_STREAM:
+                {
+                    auto status = create_input_stream_from_config_params(device,
+                        stream_parameters_pair.second,
+                        stream_parameters_pair.first);
+                    CHECK_SUCCESS(status);
+                }
+                break;
+            case HAILO_D2H_STREAM:
+                {
+                    auto status = create_output_stream_from_config_params(device,
+                    stream_parameters_pair.second,
+                    stream_parameters_pair.first);
+                    CHECK_SUCCESS(status);
+                }
+                break;
+            default:
+                LOGGER__ERROR("stream name {} direction is invalid.", stream_parameters_pair.first);
+                return HAILO_INVALID_ARGUMENT;
+        }
+    }
+
+    return HAILO_SUCCESS;
+}
+
+Expected<InputStreamRefVector> ConfiguredNetworkGroupBase::get_input_streams_by_network(const std::string &network_name)
+{
+    auto partial_network_name = m_network_group_metadata.get_partial_network_name(network_name);
+    CHECK_EXPECTED(partial_network_name);
+
+    auto input_stream_infos = m_network_group_metadata.get_input_stream_infos(partial_network_name.value());
+    CHECK_EXPECTED(input_stream_infos);
+
+    InputStreamRefVector result;
+    for (auto &stream_info : input_stream_infos.value()) {
+        auto stream_ref = get_input_stream_by_name(stream_info.name);
+        CHECK_EXPECTED(stream_ref);
+        result.push_back(stream_ref.release());
+    }
+    return result;
+}
+
+Expected<OutputStreamRefVector> ConfiguredNetworkGroupBase::get_output_streams_by_network(const std::string &network_name)
+{
+    auto partial_network_name = m_network_group_metadata.get_partial_network_name(network_name);
+    CHECK_EXPECTED(partial_network_name);
+
+    auto output_stream_infos = m_network_group_metadata.get_output_stream_infos(partial_network_name.value());
+    CHECK_EXPECTED(output_stream_infos);
+
+    OutputStreamRefVector result;
+    for (auto &stream_info : output_stream_infos.value()) {
+        auto stream_ref = get_output_stream_by_name(stream_info.name);
+        CHECK_EXPECTED(stream_ref);
+        result.push_back(stream_ref.release());
+    }
+    return result;
+}
+
+InputStreamRefVector ConfiguredNetworkGroupBase::get_input_streams()
+{
+    InputStreamRefVector result;
+    for (auto& name_stream_pair : m_input_streams) {
+        result.emplace_back(std::ref(*name_stream_pair.second));
+    }
+    return result;
+}
+
+OutputStreamRefVector ConfiguredNetworkGroupBase::get_output_streams()
+{
+    OutputStreamRefVector result;
+    for (auto& name_stream_pair : m_output_streams) {
+        result.emplace_back(std::ref(*name_stream_pair.second));
+    }
+    return result;
+}
+
+ExpectedRef<InputStream> ConfiguredNetworkGroupBase::get_input_stream_by_name(const std::string& name)
+{
+    auto iterator = m_input_streams.find(name);
+    if (m_input_streams.end() == iterator) {
+        LOGGER__ERROR("Input stream name {} not found", name);
+        return make_unexpected(HAILO_NOT_FOUND);
+    }
+
+    return std::ref<InputStream>(*iterator->second);
+}
+
+ExpectedRef<OutputStream> ConfiguredNetworkGroupBase::get_output_stream_by_name(const std::string& name)
+{
+    auto iterator = m_output_streams.find(name);
+    if (m_output_streams.end() == iterator) {
+        LOGGER__ERROR("Output stream name {} not found", name);
+        return make_unexpected(HAILO_NOT_FOUND);
+    }
+
+    return std::ref<OutputStream>(*iterator->second);
+}
+
+std::vector<std::reference_wrapper<InputStream>> ConfiguredNetworkGroupBase::get_input_streams_by_interface(
+    hailo_stream_interface_t stream_interface)
+{
+    std::vector<std::reference_wrapper<InputStream>> results;
+    for (auto &name_pair : m_input_streams) {
+        if (stream_interface == name_pair.second->get_interface()) {
+            results.push_back(std::ref(*name_pair.second));
+        }
+    }
+    return results;
+}
+
+std::vector<std::reference_wrapper<OutputStream>> ConfiguredNetworkGroupBase::get_output_streams_by_interface(
+    hailo_stream_interface_t stream_interface)
+{
+    std::vector<std::reference_wrapper<OutputStream>> results;
+    for (auto &name_pair : m_output_streams) {
+        if (stream_interface == name_pair.second->get_interface()) {
+            results.push_back(std::ref(*name_pair.second));
+        }
+    }
+    return results;
+}
+
+hailo_status ConfiguredNetworkGroupBase::wait_for_activation(const std::chrono::milliseconds &timeout)
+{
+    return m_network_group_activated_event->wait(timeout);
+}
+
+Expected<std::vector<std::vector<std::string>>> ConfiguredNetworkGroupBase::get_output_vstream_groups()
+{
+    std::vector<std::vector<std::string>> results;
+
+    for (auto output_stream : get_output_streams()) {
+        auto vstreams_group = get_vstream_names_from_stream_name(output_stream.get().name());
+        CHECK_EXPECTED(vstreams_group);
+        results.push_back(vstreams_group.release());
+    }
+
+    return results;
+}
+
+Expected<std::vector<std::map<std::string, hailo_vstream_params_t>>> ConfiguredNetworkGroupBase::make_output_vstream_params_groups(
+    bool quantized, hailo_format_type_t format_type, uint32_t timeout_ms, uint32_t queue_size)
+{
+    auto params = make_output_vstream_params(quantized, format_type, timeout_ms, queue_size);
+    CHECK_EXPECTED(params);
+
+    auto groups = get_output_vstream_groups();
+    CHECK_EXPECTED(groups);
+
+    std::vector<std::map<std::string, hailo_vstream_params_t>> results(groups->size(), std::map<std::string, hailo_vstream_params_t>());
+
+    size_t pipeline_group_index = 0;
+    for (const auto &group : groups.release()) {
+        for (const auto &name_pair : params.value()) {
+            if (contains(group, name_pair.first)) {
+                results[pipeline_group_index].insert(name_pair);
+            }
+        }
+        pipeline_group_index++;
+    }
+
+    return results;
+}
+
+Expected<std::map<std::string, hailo_vstream_params_t>> ConfiguredNetworkGroupBase::make_input_vstream_params(
+    bool quantized, hailo_format_type_t format_type, uint32_t timeout_ms, uint32_t queue_size,
+    const std::string &network_name)
+{
+    auto partial_network_name = m_network_group_metadata.get_partial_network_name(network_name);
+    CHECK_EXPECTED(partial_network_name);
+
+    auto input_vstream_infos = m_network_group_metadata.get_input_vstream_infos(partial_network_name.value());
+    CHECK_EXPECTED(input_vstream_infos);
+
+    std::map<std::string, hailo_vstream_params_t> res;
+    auto status = Hef::Impl::fill_missing_vstream_params_with_default(res, input_vstream_infos.value(), quantized, 
+        format_type, timeout_ms, queue_size);
+    CHECK_SUCCESS_AS_EXPECTED(status);
+    return res;
+}
+
+Expected<std::map<std::string, hailo_vstream_params_t>> ConfiguredNetworkGroupBase::make_output_vstream_params(
+    bool quantized, hailo_format_type_t format_type, uint32_t timeout_ms, uint32_t queue_size,
+    const std::string &network_name)
+{
+    auto partial_network_name = m_network_group_metadata.get_partial_network_name(network_name);
+    CHECK_EXPECTED(partial_network_name);
+
+    auto output_vstream_infos = m_network_group_metadata.get_output_vstream_infos(partial_network_name.value());
+    CHECK_EXPECTED(output_vstream_infos);
+
+    std::map<std::string, hailo_vstream_params_t> res;
+    auto status = Hef::Impl::fill_missing_vstream_params_with_default(res, output_vstream_infos.value(), quantized, 
+        format_type, timeout_ms, queue_size);
+    CHECK_SUCCESS_AS_EXPECTED(status);
+    return res;
+}
+
+Expected<std::vector<hailo_network_info_t>> ConfiguredNetworkGroupBase::get_network_infos() const
+{
+    return m_network_group_metadata.get_network_infos();
+}
+
+Expected<std::vector<hailo_stream_info_t>> ConfiguredNetworkGroupBase::get_all_stream_infos(
+    const std::string &network_name) const
+{
+    auto partial_network_name = m_network_group_metadata.get_partial_network_name(network_name);
+    CHECK_EXPECTED(partial_network_name);
+
+    return m_network_group_metadata.get_all_stream_infos(partial_network_name.value());
+}
+
+Expected<std::vector<hailo_vstream_info_t>> ConfiguredNetworkGroupBase::get_input_vstream_infos(
+    const std::string &network_name) const
+{
+    auto partial_network_name = m_network_group_metadata.get_partial_network_name(network_name);
+    CHECK_EXPECTED(partial_network_name);
+
+    return m_network_group_metadata.get_input_vstream_infos(partial_network_name.value());
+}
+
+Expected<std::vector<hailo_vstream_info_t>> ConfiguredNetworkGroupBase::get_output_vstream_infos(
+    const std::string &network_name) const
+{
+    auto partial_network_name = m_network_group_metadata.get_partial_network_name(network_name);
+    CHECK_EXPECTED(partial_network_name);
+
+    return m_network_group_metadata.get_output_vstream_infos(partial_network_name.value());
+}
+
+Expected<std::vector<hailo_vstream_info_t>> ConfiguredNetworkGroupBase::get_all_vstream_infos(
+    const std::string &network_name) const
+{
+    auto partial_network_name = m_network_group_metadata.get_partial_network_name(network_name);
+    CHECK_EXPECTED(partial_network_name);
+
+    return m_network_group_metadata.get_all_vstream_infos(partial_network_name.value());
+}
+
+AccumulatorPtr ConfiguredNetworkGroupBase::get_activation_time_accumulator() const
+{
+    return m_activation_time_accumulator;
+}
+
+AccumulatorPtr ConfiguredNetworkGroupBase::get_deactivation_time_accumulator() const
+{
+    return m_deactivation_time_accumulator;
+}
+
+} /* namespace hailort */
diff --git a/hailort/libhailort/src/context_switch/network_group_internal.hpp b/hailort/libhailort/src/context_switch/network_group_internal.hpp
new file mode 100644 (file)
index 0000000..bab75ce
--- /dev/null
@@ -0,0 +1,159 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file network_group_internal.hpp
+ * @brief Class declaration for ConfiguredNetworkGroupBase and ActivatedNetworkGroupBase that implement the basic ConfiguredNetworkGroup
+ *        and ActivatedNetworkGroup interfaces. All internal classes that are relavant should inherit from the
+ *        ConfiguredNetworkGroupBase and ActivatedNetworkGroupBase classes.
+ *        Hence, the hiearchy is as follows:
+ *        -----------------------------------------------------------------------------
+ *        |                        ConfiguredNetworkGroup                             |  (External "interface")
+ *        |                                  |                                        |
+ *        |                      ConfiguredNetworkGroupBase                           |  (Base classes)
+ *        |                          /                \                               |
+ *        |           VdmaConfigNetworkGroup       HcpConfigNetworkGroup              | (Actual implementations)
+ *        -----------------------------------------------------------------------------
+ *        |                         ActivatedNetworkGroup                             |  (External "interface")
+ *        |                                   |                                       |
+ *        |                       ActivatedNetworkGroupBase                           |  (Base classes)
+ *        |                 __________________|__________________                     |
+ *        |                /                                     \                    |
+ *        |    VdmaConfigActivatedNetworkGroup         HcpConfigActivatedNetworkGroup |  (Actual implementations)
+ *        -----------------------------------------------------------------------------
+ **/
+
+#ifndef _HAILO_NETWORK_GROUP_INTERNAL_HPP_
+#define _HAILO_NETWORK_GROUP_INTERNAL_HPP_
+
+#include "hailo/hailort.h"
+#include "hailo/network_group.hpp"
+#include "hef_internal.hpp"
+#include "common/latency_meter.hpp"
+
+namespace hailort
+{
+
+class ActivatedNetworkGroupBase : public ActivatedNetworkGroup
+{
+public:
+    virtual ~ActivatedNetworkGroupBase() = default;
+    ActivatedNetworkGroupBase(const ActivatedNetworkGroupBase &other) = delete;
+    ActivatedNetworkGroupBase &operator=(const ActivatedNetworkGroupBase &other) = delete;
+    ActivatedNetworkGroupBase &operator=(ActivatedNetworkGroupBase &&other) = delete;
+    ActivatedNetworkGroupBase(ActivatedNetworkGroupBase &&other) = default;
+
+    virtual uint32_t get_invalid_frames_count() override;
+    // Must be called on d'tor of derived class
+    void deactivate_resources();
+
+protected:
+    hailo_activate_network_group_params_t m_network_group_params;
+
+    ActivatedNetworkGroupBase(const hailo_activate_network_group_params_t &network_group_params,
+        std::map<std::string, std::unique_ptr<InputStream>> &input_streams,
+        std::map<std::string, std::unique_ptr<OutputStream>> &output_streams,         
+        EventPtr &&network_group_activated_event, hailo_status &status);
+
+private:
+    hailo_status activate_low_level_streams();
+    hailo_status validate_network_group_params(const hailo_activate_network_group_params_t &network_group_params);
+
+    std::map<std::string, std::unique_ptr<InputStream>> &m_input_streams;
+    std::map<std::string, std::unique_ptr<OutputStream>> &m_output_streams;
+    EventPtr m_network_group_activated_event;
+};
+
+class ConfiguredNetworkGroupBase : public ConfiguredNetworkGroup
+{
+public:
+    virtual ~ConfiguredNetworkGroupBase() = default;
+    ConfiguredNetworkGroupBase(const ConfiguredNetworkGroupBase &other) = delete;
+    ConfiguredNetworkGroupBase &operator=(const ConfiguredNetworkGroupBase &other) = delete;
+    ConfiguredNetworkGroupBase &operator=(ConfiguredNetworkGroupBase &&other) = delete;
+    ConfiguredNetworkGroupBase(ConfiguredNetworkGroupBase &&other) = default;
+
+    virtual hailo_status wait_for_activation(const std::chrono::milliseconds &timeout) override;
+
+    virtual const std::string &get_network_group_name() const override;
+
+    virtual Expected<InputStreamRefVector> get_input_streams_by_network(const std::string &network_name="") override;
+    virtual Expected<OutputStreamRefVector> get_output_streams_by_network(const std::string &network_name="") override;
+    virtual InputStreamRefVector get_input_streams() override;
+    virtual OutputStreamRefVector get_output_streams() override;
+    virtual std::vector<std::reference_wrapper<InputStream>> get_input_streams_by_interface(hailo_stream_interface_t stream_interface) override;
+    virtual std::vector<std::reference_wrapper<OutputStream>> get_output_streams_by_interface(hailo_stream_interface_t stream_interface) override;
+    virtual ExpectedRef<InputStream> get_input_stream_by_name(const std::string& name) override;
+    virtual ExpectedRef<OutputStream> get_output_stream_by_name(const std::string& name) override;
+    virtual Expected<OutputStreamWithParamsVector> get_output_streams_from_vstream_names(
+        const std::map<std::string, hailo_vstream_params_t> &outputs_params) override;
+    virtual Expected<LatencyMeasurementResult> get_latency_measurement(const std::string &network_name="") override;
+
+    virtual Expected<std::map<std::string, hailo_vstream_params_t>> make_input_vstream_params(
+        bool quantized, hailo_format_type_t format_type, uint32_t timeout_ms, uint32_t queue_size,
+        const std::string &network_name="") override;
+    virtual Expected<std::map<std::string, hailo_vstream_params_t>> make_output_vstream_params(
+        bool quantized, hailo_format_type_t format_type, uint32_t timeout_ms, uint32_t queue_size,
+        const std::string &network_name="") override;
+        
+    virtual Expected<std::vector<std::map<std::string, hailo_vstream_params_t>>> make_output_vstream_params_groups(
+        bool quantized, hailo_format_type_t format_type, uint32_t timeout_ms, uint32_t queue_size) override;
+
+    virtual Expected<std::vector<std::vector<std::string>>> get_output_vstream_groups() override;
+
+    virtual Expected<std::vector<hailo_network_info_t>> get_network_infos() const override;
+    virtual Expected<std::vector<hailo_stream_info_t>> get_all_stream_infos(const std::string &network_name="") const override;
+    virtual Expected<std::vector<hailo_vstream_info_t>> get_input_vstream_infos(const std::string &network_name="") const override;
+    virtual Expected<std::vector<hailo_vstream_info_t>> get_output_vstream_infos(const std::string &network_name="") const override;
+    virtual Expected<std::vector<hailo_vstream_info_t>> get_all_vstream_infos(const std::string &network_name="") const override;
+    virtual AccumulatorPtr get_activation_time_accumulator() const override;
+    virtual AccumulatorPtr get_deactivation_time_accumulator() const override;
+    hailo_status create_streams_from_config_params(Device &device);
+
+    static Expected<LatencyMeterPtr> create_hw_latency_meter(Device &device,
+        const std::vector<LayerInfo> &layers);
+
+    Expected<std::vector<std::string>> get_vstream_names_from_stream_name(const std::string &stream_name)
+    {
+        return m_network_group_metadata.get_vstream_names_from_stream_name(stream_name);
+    }
+
+    const NetworkGroupSupportedFeatures &get_supported_features()
+    {
+        return m_network_group_metadata.supported_features();
+    }
+    
+    Expected<uint16_t> get_stream_batch_size(const std::string &stream_name);
+
+protected:
+    ConfiguredNetworkGroupBase(const ConfigureNetworkParams &config_params, const uint8_t m_net_group_index, 
+        const NetworkGroupMetadata &network_group_metadata, hailo_status &status);
+
+    hailo_status create_output_stream_from_config_params(Device &device,
+        const hailo_stream_parameters_t &stream_params, const std::string &stream_name);
+    hailo_status create_input_stream_from_config_params(Device &device,
+        const hailo_stream_parameters_t &stream_params, const std::string &stream_name);
+    hailo_status add_mux_streams_by_edges_names(OutputStreamWithParamsVector &result,
+        const std::unordered_map<std::string, hailo_vstream_params_t> &outputs_edges_params);
+    Expected<OutputStreamRefVector> get_output_streams_by_vstream_name(const std::string &name);
+
+    Expected<LayerInfo> get_layer_info(const std::string &stream_name);
+
+    virtual Expected<uint8_t> get_boundary_channel_index(uint8_t stream_index, hailo_stream_direction_t direction,
+        const std::string &layer_name) = 0;
+
+    const ConfigureNetworkParams m_config_params;
+    uint8_t m_net_group_index;
+    std::map<std::string, LatencyMeterPtr> m_latency_meter; // Latency meter per network
+    std::map<std::string, std::unique_ptr<InputStream>> m_input_streams;
+    std::map<std::string, std::unique_ptr<OutputStream>> m_output_streams;
+    EventPtr m_network_group_activated_event;
+    const NetworkGroupMetadata m_network_group_metadata;
+    AccumulatorPtr m_activation_time_accumulator;
+    AccumulatorPtr m_deactivation_time_accumulator;
+};
+
+} /* namespace hailort */
+
+#endif /* _HAILO_NETWORK_GROUP_INTERNAL_HPP_ */
diff --git a/hailort/libhailort/src/context_switch/resource_manager.cpp b/hailort/libhailort/src/context_switch/resource_manager.cpp
new file mode 100644 (file)
index 0000000..35b6c18
--- /dev/null
@@ -0,0 +1,592 @@
+#include "multi_context/resource_manager.hpp"
+#include "control.hpp"
+#include "hailort_defaults.hpp"
+#include <numeric>
+
+namespace hailort
+{
+
+
+ConfigResources::ConfigResources(HailoRTDriver &driver, VdmaBuffer &&buffer,
+    VdmaDescriptorList &&descriptor, uint16_t requested_desc_page_size, size_t total_buffer_size)
+    : m_buffer(std::move(buffer)), m_descriptor(std::move(descriptor)),
+      m_desc_page_size(driver.calc_desc_page_size(requested_desc_page_size)), 
+      m_total_buffer_size(total_buffer_size), m_acc_buffer_offset(0), m_acc_desc_count(0),
+      m_current_buffer_size(0)
+{}
+
+Expected<uint16_t> ConfigResources::program_descriptors()
+{
+    auto descriptors_count =
+        m_descriptor.program_descriptors(m_acc_buffer_offset,  VdmaInterruptsDomain::NONE, VdmaInterruptsDomain::DEVICE,
+        m_acc_desc_count, false);
+    CHECK_EXPECTED(descriptors_count);
+
+    /* TODO - remove static cast */
+    m_acc_desc_count = static_cast<uint16_t>(m_acc_desc_count + descriptors_count.value());
+    m_acc_buffer_offset = 0;
+
+    return descriptors_count;
+}
+
+hailo_status ConfigResources::write(const void *data, size_t data_size)
+{
+    size_t total_offset = (m_acc_desc_count * m_desc_page_size) + m_acc_buffer_offset;
+    auto status = m_buffer.write(data, data_size, total_offset);
+    CHECK_SUCCESS(status);
+
+    m_acc_buffer_offset += data_size;
+    m_current_buffer_size += data_size;
+    return HAILO_SUCCESS;
+}
+
+uint16_t ConfigResources::get_page_size()
+{
+    return m_desc_page_size;
+}
+
+size_t ConfigResources::get_total_cfg_size()
+{
+    return m_total_buffer_size;
+}
+
+size_t ConfigResources::get_current_buffer_size()
+{
+    return m_current_buffer_size;
+}
+
+Expected<ConfigResources> ResourcesManager::create_config_resources(uint8_t channel_index,
+    const std::vector<uint32_t> &cfg_sizes, HailoRTDriver &driver)
+{
+    auto desc_sizes_pair = VdmaDescriptorList::get_desc_buffer_sizes_for_multiple_transfers(driver, 1, cfg_sizes);
+    CHECK_EXPECTED(desc_sizes_pair);
+
+    auto page_size = desc_sizes_pair->first;
+    auto descs_count = desc_sizes_pair->second;
+
+    auto buffer_size = std::accumulate(cfg_sizes.begin(), cfg_sizes.end(), 0);
+
+    auto buffer = VdmaBuffer::create((page_size * descs_count), HailoRTDriver::DmaDirection::H2D, driver);
+    CHECK_EXPECTED(buffer);
+
+    auto desc_list = VdmaDescriptorList::create(descs_count, page_size, driver);
+    CHECK_EXPECTED(desc_list);
+
+    auto status = desc_list->configure_to_use_buffer(buffer.value(), channel_index);
+    CHECK_SUCCESS_AS_EXPECTED(status);
+
+    return ConfigResources(driver, buffer.release(), desc_list.release(), page_size, buffer_size);
+}
+
+static Expected<std::vector<std::string>> build_network_index_map(ProtoHEFNetworkGroupPtr network_group_proto,
+    const NetworkGroupSupportedFeatures &supported_features)
+{
+    std::vector<std::string> partial_network_name_vector;
+    if (supported_features.multi_network_support) {
+        auto network_count = network_group_proto.get()->networks_names_size();
+        CHECK_AS_EXPECTED((network_count > 0), HAILO_INTERNAL_FAILURE, 
+            "Hef support multiple networks, but no networks found in the proto");
+        partial_network_name_vector.reserve(network_count);
+        for (uint8_t network_index = 0; network_index < network_count; network_index++) {
+            auto partial_network_name = network_group_proto.get()->networks_names(network_index);
+            partial_network_name_vector.push_back(partial_network_name);
+        }
+    } else {
+        /* In case there is no defines networks, add single network with the same name as the network group */
+        partial_network_name_vector.reserve(1);
+        auto net_group_name = network_group_proto->network_group_metadata().network_group_name();
+        auto partial_network_name = HailoRTDefaults::get_partial_network_name();
+        partial_network_name_vector.push_back(partial_network_name);
+    }
+
+    return partial_network_name_vector;
+}
+
+Expected<ResourcesManager> ResourcesManager::create(VdmaDevice &vdma_device, HailoRTDriver &driver,
+    const ConfigureNetworkParams &config_params, ProtoHEFNetworkGroupPtr network_group_proto,
+    std::shared_ptr<NetworkGroupMetadata> network_group_metadata, const HefParsingInfo &parsing_info,
+    uint8_t net_group_index)
+{
+    CHECK_ARG_NOT_NULL_AS_EXPECTED(network_group_proto);
+
+    // Backwards compatibility for HEFs without this field
+    CHECK_AS_EXPECTED(IS_FIT_IN_UINT8(network_group_proto->network_group_metadata().cfg_channels_count()),
+        HAILO_INTERNAL_FAILURE, "Invalid cfg channels count");
+    uint8_t cfg_channels_count = (0 == network_group_proto->network_group_metadata().cfg_channels_count()) ?
+        1u : static_cast<uint8_t>(network_group_proto->network_group_metadata().cfg_channels_count());
+
+    std::vector<ConfigResources> preliminary_configs_vector;
+    auto cfg_count_preliminary = parsing_info.cfg_infos_preliminary_config.size();
+    CHECK_AS_EXPECTED(cfg_channels_count >= cfg_count_preliminary, HAILO_INTERNAL_FAILURE,
+        "preliminary cfg count ({}) is bigger than the size passed to the network_group ({})",
+        cfg_count_preliminary, cfg_channels_count);
+    preliminary_configs_vector.reserve(cfg_count_preliminary);
+    for (uint8_t cfg_index = MIN_H2D_CHANNEL_INDEX; cfg_index < cfg_count_preliminary; cfg_index++) {
+        CHECK_AS_EXPECTED(contains(parsing_info.cfg_infos_preliminary_config, cfg_index), HAILO_INTERNAL_FAILURE,
+            "Mismmatch for cfg index {}", cfg_index);
+        auto buffer_resource = ResourcesManager::create_config_resources(cfg_index,
+            parsing_info.cfg_infos_preliminary_config.at(cfg_index), driver);
+        CHECK_EXPECTED(buffer_resource);
+
+        preliminary_configs_vector.emplace_back(buffer_resource.release());
+    }
+
+    std::vector<std::vector<ConfigResources>> dynamic_cfg_vectors;
+    dynamic_cfg_vectors.reserve(network_group_proto->contexts_size());
+
+    for (int ctxt_index = 0; ctxt_index < network_group_proto->contexts_size(); ctxt_index++) {
+        std::vector<ConfigResources> dynamic_cfg_vector_per_context;
+        auto cfg_count_ctxt = parsing_info.cfg_infos_per_context[ctxt_index].size();
+
+        CHECK_AS_EXPECTED(cfg_channels_count >= cfg_count_ctxt, HAILO_INTERNAL_FAILURE,
+            "dynamic context cfg count ({}) (context {}) is bigger than the size passed to the network_group ({})",
+            cfg_count_ctxt, ctxt_index, cfg_channels_count);
+
+        dynamic_cfg_vector_per_context.reserve(cfg_count_ctxt);
+        for (uint8_t cfg_index = MIN_H2D_CHANNEL_INDEX; cfg_index < cfg_count_ctxt; cfg_index++) {
+            CHECK_AS_EXPECTED(contains(parsing_info.cfg_infos_per_context[ctxt_index], cfg_index),
+                HAILO_INTERNAL_FAILURE, "Mismmatch for cfg index {}", cfg_index);
+            auto buffer_resource = ResourcesManager::create_config_resources(cfg_index,
+                parsing_info.cfg_infos_per_context[ctxt_index].at(cfg_index), driver);
+            CHECK_EXPECTED(buffer_resource);
+
+            dynamic_cfg_vector_per_context.emplace_back(buffer_resource.release());
+        }
+        dynamic_cfg_vectors.emplace_back(std::move(dynamic_cfg_vector_per_context));
+    }
+
+    auto network_index_map = build_network_index_map(network_group_proto, network_group_metadata->supported_features());
+    CHECK_EXPECTED(network_index_map);
+    ResourcesManager resources_manager(vdma_device, driver, config_params,
+        std::move(preliminary_configs_vector), std::move(dynamic_cfg_vectors), std::move(network_group_metadata), net_group_index,
+        std::move(network_index_map.release()));
+
+    auto status = resources_manager.set_number_of_cfg_channels(cfg_channels_count);
+    CHECK_SUCCESS_AS_EXPECTED(status);
+
+    return resources_manager;
+}
+
+hailo_status ResourcesManager::fill_network_batch_size(CONTROL_PROTOCOL__application_header_t &app_header)
+{
+    app_header.networks_count = static_cast<uint8_t>(m_config_params.network_params_by_name.size());
+    for (const auto &network_pair : m_config_params.network_params_by_name) {
+        auto network_name = network_pair.first;
+        uint8_t network_index = 0;
+        for (network_index = 0; network_index < m_network_index_map.size(); network_index++) {
+            auto const partial_network_name = m_network_index_map[network_index];
+            auto found = network_name.find(HAILO_DEFAULT_NETWORK_NAME_QUALIFIER + partial_network_name);
+            if (found != std::string::npos) {
+                app_header.batch_size[network_index] = network_pair.second.batch_size;
+                break;
+            }
+        }
+        if (m_network_index_map.size() == network_index) {
+            LOGGER__ERROR("Failed to find network with network name {}", network_name);
+            return HAILO_NOT_FOUND;
+        }
+    }
+
+    return HAILO_SUCCESS;
+}
+
+hailo_status ResourcesManager::create_vdma_channels()
+{
+    std::vector<uint8_t> intermediate_channels_idx;
+    std::vector<uint8_t> cfg_channels_idx;
+    std::vector<uint8_t> ddr_channels_idx;
+
+    for (uint8_t i = 0; i < m_channels_info.max_size(); ++i) {
+        if (m_channels_info[i].is_type(ChannelInfo::Type::INTER_CONTEXT)) {
+            intermediate_channels_idx.push_back(i);
+        } else if (m_channels_info[i].is_type(ChannelInfo::Type::CFG)) {
+            cfg_channels_idx.push_back(i);
+        } else if (m_channels_info[i].is_type(ChannelInfo::Type::DDR)) {
+            ddr_channels_idx.push_back(i);
+        }
+    }
+
+    m_config_channels.reserve(cfg_channels_idx.size());
+    m_inter_context_channels.reserve(intermediate_channels_idx.size());
+    m_ddr_buffer_channels.reserve(ddr_channels_idx.size());
+
+    for (const auto &ch : cfg_channels_idx) {
+        auto config_channel = VdmaChannel::create(ch, VdmaChannel::Direction::H2D, m_driver,
+            m_vdma_device.get_default_desc_page_size());
+        CHECK_EXPECTED_AS_STATUS(config_channel);
+        m_config_channels.emplace_back(config_channel.release());
+    }
+
+    for (const auto &ch : intermediate_channels_idx) {
+        auto direction = (ch < MIN_D2H_CHANNEL_INDEX) ? VdmaChannel::Direction::H2D : VdmaChannel::Direction::D2H;
+        auto vdma_channel = VdmaChannel::create(ch, direction, m_driver, m_vdma_device.get_default_desc_page_size());
+        CHECK_EXPECTED_AS_STATUS(vdma_channel);
+        m_inter_context_channels.emplace_back(vdma_channel.release());
+    }
+
+    for (const auto &ch : ddr_channels_idx) {
+        auto direction = (ch < MIN_D2H_CHANNEL_INDEX) ? VdmaChannel::Direction::H2D : VdmaChannel::Direction::D2H;
+        auto vdma_channel = VdmaChannel::create(ch, direction, m_driver, m_vdma_device.get_default_desc_page_size());
+        CHECK_EXPECTED_AS_STATUS(vdma_channel);
+        m_ddr_buffer_channels.emplace_back(vdma_channel.release());
+    }
+
+    return HAILO_SUCCESS;
+}
+
+ExpectedRef<IntermediateBuffer> ResourcesManager::create_inter_context_buffer(uint32_t transfer_size,
+    uint8_t src_stream_index, uint8_t src_context_index, const std::string &partial_network_name)
+{
+    auto network_batch_size_exp = get_network_batch_size_from_partial_name(partial_network_name);
+    CHECK_EXPECTED(network_batch_size_exp);
+    auto network_batch_size = network_batch_size_exp.value();
+
+    const auto intermediate_buffer_key = std::make_pair(src_context_index, src_stream_index);
+    auto intermediate_buffer = create_intermediate_buffer(transfer_size, network_batch_size, intermediate_buffer_key);
+    CHECK_EXPECTED(intermediate_buffer);
+    auto intermediate_buffer_ref = intermediate_buffer.release();
+
+    auto status = intermediate_buffer_ref.get().program_inter_context();
+    CHECK_SUCCESS_AS_EXPECTED(status);
+
+    return intermediate_buffer_ref;
+}
+
+ExpectedRef<IntermediateBuffer> ResourcesManager::get_intermediate_buffer(const IntermediateBufferKey &key)
+{
+    auto intermediate_buffer_it = m_intermediate_buffers.find(key);
+    if (std::end(m_intermediate_buffers) == intermediate_buffer_it) {
+        return make_unexpected(HAILO_NOT_FOUND);
+    }
+
+    return std::ref(*intermediate_buffer_it->second);
+}
+
+Expected<std::pair<uint16_t, uint32_t>> ResourcesManager::get_desc_buffer_sizes_for_boundary_channel(
+    uint32_t transfer_size, const std::string &partial_network_name)
+{
+    auto network_batch_size = get_network_batch_size_from_partial_name(partial_network_name);
+    CHECK_EXPECTED(network_batch_size);
+    uint32_t min_active_trans = MIN_ACTIVE_TRANSFERS_SCALE * network_batch_size.value();
+    uint32_t max_active_trans = MAX_ACTIVE_TRANSFERS_SCALE * network_batch_size.value();
+
+    CHECK_AS_EXPECTED(IS_FIT_IN_UINT16(min_active_trans), HAILO_INVALID_ARGUMENT, 
+        "calculated min_active_trans for vdma descriptor list is out of UINT16 range");
+
+    CHECK_AS_EXPECTED(IS_FIT_IN_UINT16(max_active_trans), HAILO_INVALID_ARGUMENT, 
+        "calculated min_active_trans for vdma descriptor list is out of UINT16 range");
+
+    return VdmaDescriptorList::get_desc_buffer_sizes_for_single_transfer(m_driver,
+        static_cast<uint16_t>(min_active_trans), static_cast<uint16_t>(max_active_trans), transfer_size);
+}
+
+Expected<CONTROL_PROTOCOL__application_header_t> ResourcesManager::get_control_network_group_header()
+{
+    CONTROL_PROTOCOL__application_header_t  app_header = {};
+    app_header.dynamic_contexts_count = static_cast<uint8_t>(m_contexts.size() - 1);
+    app_header.host_boundary_channels_bitmap = 0;
+    app_header.host_ddr_channels_bitmap = 0;
+
+    /* Bitmask of all boundary and DDR channels*/
+    int host_boundary_channels_bitmap_local = 0;
+    int host_ddr_channels_bitmap_local = 0;
+    for (size_t i = MIN_H2D_CHANNEL_INDEX; i <= MAX_D2H_CHANNEL_INDEX; i++) {
+        /* Set boundary channels */
+        if (m_channels_info[i].is_type(ChannelInfo::Type::BOUNDARY) && m_channels_info[i].is_used()) {
+            host_boundary_channels_bitmap_local |= 1 << i;
+        }
+        /* DDR buffer channels are host controlled only if the HEF does not support padded ddr buffers */
+        else if ((m_channels_info[i].is_type(ChannelInfo::Type::DDR) && (!get_supported_features().padded_ddr_buffers)) &&
+            (m_channels_info[i].is_used())) {
+            host_ddr_channels_bitmap_local |= 1 << i;
+        }
+    }
+
+    app_header.host_boundary_channels_bitmap = static_cast<uint32_t>(host_boundary_channels_bitmap_local);
+    app_header.host_ddr_channels_bitmap = static_cast<uint32_t>(host_ddr_channels_bitmap_local);
+
+    uint8_t cfg_handle_idx = 0;
+    for (uint8_t ch_idx = MIN_H2D_CHANNEL_INDEX; ch_idx <= MAX_H2D_CHANNEL_INDEX; ch_idx++) {
+        if ((m_channels_info[ch_idx].is_type(ChannelInfo::Type::CFG)) &&
+            (m_channels_info[ch_idx].is_used())) {
+            assert(cfg_handle_idx < CONTROL_PROTOCOL__MAX_CFG_CHANNELS);
+            app_header.cfg_channel_numbers[cfg_handle_idx] = ch_idx;
+            cfg_handle_idx++;
+        }
+    }
+    app_header.cfg_channels_count = cfg_handle_idx;
+    app_header.power_mode = static_cast<uint8_t>(m_config_params.power_mode);
+    fill_network_batch_size(app_header);
+    return app_header;
+}
+
+Expected<uint8_t> ResourcesManager::get_available_channel_index(std::set<uint8_t> &blacklist,
+    ChannelInfo::Type required_type, VdmaChannel::Direction direction, const std::string &layer_name)
+{
+    uint8_t min_channel_index =
+        (direction == VdmaChannel::Direction::H2D) ? MIN_H2D_CHANNEL_INDEX : MIN_D2H_CHANNEL_INDEX;
+    uint8_t max_channel_index =
+        (direction == VdmaChannel::Direction::H2D) ? MAX_H2D_CHANNEL_INDEX : MAX_D2H_CHANNEL_INDEX;
+
+    for (uint8_t index = min_channel_index; index <= max_channel_index; ++index) {
+        // Skip index that are on the blacklist
+        if (contains(blacklist, index)) {
+            continue;
+        }
+
+        // Use the empty channel if available
+        if (!m_channels_info[index].is_used()) {
+            m_channels_info[index].set_type(required_type);
+            m_channels_info[index].set_layer_name(layer_name);
+            return index;
+        }
+
+        // If DDR is managed by the FW - allow reuse of channels for DDR / inter_context
+        if (get_supported_features().padded_ddr_buffers) {
+            if (((ChannelInfo::Type::BOUNDARY != required_type) && (ChannelInfo::Type::CFG != required_type)) &&
+                ((m_channels_info[index].is_type(ChannelInfo::Type::DDR)) || (m_channels_info[index].is_type(ChannelInfo::Type::INTER_CONTEXT)))) {
+                m_channels_info[index].set_type(required_type);
+                m_channels_info[index].set_layer_name(layer_name);
+                return index;
+            }
+        } else {
+            // If not - allow reuse of channels for within the same type
+            if (m_channels_info[index].is_type(required_type)) {
+                m_channels_info[index].set_type(required_type);
+                m_channels_info[index].set_layer_name(layer_name);
+                return index;
+            }
+        }
+    }
+
+    LOGGER__ERROR("Failed to get available channel_index");
+    return make_unexpected(HAILO_INTERNAL_FAILURE);
+}
+
+Expected<uint8_t> ResourcesManager::get_boundary_channel_index(uint8_t stream_index,
+    hailo_stream_direction_t direction, const std::string &layer_name)
+{
+    uint8_t min_channel_index =
+        (direction == HAILO_H2D_STREAM) ? MIN_H2D_CHANNEL_INDEX : MIN_D2H_CHANNEL_INDEX;
+    uint8_t max_channel_index =
+        (direction == HAILO_H2D_STREAM) ? MAX_H2D_CHANNEL_INDEX : MAX_D2H_CHANNEL_INDEX;
+
+    for (uint8_t channel_index = min_channel_index; channel_index <= max_channel_index; channel_index++) {
+        auto info = m_channels_info[channel_index];
+        if ((info.is_type(ChannelInfo::Type::BOUNDARY) && (stream_index == info.get_pcie_stream_index()) && 
+            layer_name == info.get_layer_name())) {
+            return channel_index;
+        }
+    }
+
+    return make_unexpected(HAILO_INVALID_ARGUMENT);
+}
+
+void ResourcesManager::update_preliminary_config_buffer_info()
+{
+    // Preliminary_config is the first 'context' m_contexts vector
+    assert(CONTROL_PROTOCOL__MAX_CFG_CHANNELS >= m_preliminary_config.size());
+    auto i = 0;
+    for (const auto &config : m_preliminary_config) {
+        m_contexts[0].context_cfg_base_address[i] = config.m_descriptor.dma_address();
+        m_contexts[0].context_total_descriptors[i] = config.m_acc_desc_count;
+        i++;
+    }
+}
+
+void ResourcesManager::update_dynamic_contexts_buffer_info()
+{
+    // Preliminary_config is the first 'context' m_contexts vector
+    assert((m_dynamic_config.size() + 1) == m_contexts.size());
+    int ctxt_index = 1;
+    for (const auto &cfg_context : m_dynamic_config) {
+        assert(CONTROL_PROTOCOL__MAX_CFG_CHANNELS >= cfg_context.size());
+        auto i = 0;
+        for (auto &cfg : cfg_context) {
+            m_contexts[ctxt_index].context_cfg_base_address[i] = cfg.m_descriptor.dma_address();
+            m_contexts[ctxt_index].context_total_descriptors[i] = cfg.m_acc_desc_count;
+            i++;
+        }
+        ctxt_index++;
+    }
+}
+
+ExpectedRef<IntermediateBuffer> ResourcesManager::create_ddr_buffer(DdrChannelsInfo &ddr_info, uint8_t context_index)
+{
+    const uint32_t number_of_transfers = ddr_info.min_buffered_rows * DDR_THREADS_MIN_BUFFERED_ROWS_INITIAL_SCALE;
+    CHECK(IS_FIT_IN_UINT16(number_of_transfers), make_unexpected(HAILO_INVALID_ARGUMENT), 
+        "calculated number of transfers for DDR buffer is out of UINT16_T range");
+
+    const uint32_t transfer_size = ddr_info.row_size * DDR_NUMBER_OF_ROWS_PER_INTERRUPT;
+
+    auto intermediate_buffer_key = std::make_pair(context_index, ddr_info.d2h_stream_index);
+    return create_intermediate_buffer(transfer_size, static_cast<uint16_t>(number_of_transfers),
+        intermediate_buffer_key);
+}
+
+hailo_status ResourcesManager::set_number_of_cfg_channels(const uint8_t number_of_cfg_channels)
+{
+    CHECK(number_of_cfg_channels <= CONTROL_PROTOCOL__MAX_CFG_CHANNELS, HAILO_INVALID_HEF, "Too many cfg channels");
+    size_t channels_count = 0;
+    for (uint8_t index = MIN_H2D_CHANNEL_INDEX; index <= MAX_H2D_CHANNEL_INDEX; ++index) {
+        // use the empty channel if avaialble
+        if (!m_channels_info[index].is_used()) {
+            m_channels_info[index].set_type(ChannelInfo::Type::CFG);
+            channels_count++;
+        }
+        if (number_of_cfg_channels == channels_count) {
+            return HAILO_SUCCESS;
+        }
+    }
+
+    LOGGER__ERROR("Failed to set cfg channels");
+    return HAILO_INTERNAL_FAILURE;
+}
+
+Expected<hailo_stream_interface_t> ResourcesManager::get_default_streams_interface()
+{
+    return m_vdma_device.get_default_streams_interface();
+}
+
+hailo_status ResourcesManager::register_fw_managed_vdma_channels()
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+
+    for (auto &ch : m_inter_context_channels) {
+        status = ch.register_fw_controlled_channel();
+        CHECK_SUCCESS(status);
+    }
+
+    for (auto &ch : m_config_channels) {
+        status = ch.register_fw_controlled_channel();
+        CHECK_SUCCESS(status);
+    }
+
+    /* If ddr supported padded buffers - DDR buffers are managed by the FW */
+    if (get_supported_features().padded_ddr_buffers) {
+        for (auto &ch : m_ddr_buffer_channels) {
+            status = ch.register_fw_controlled_channel();
+            CHECK_SUCCESS(status);
+        }
+    }
+
+    return HAILO_SUCCESS;
+}
+
+hailo_status ResourcesManager::unregister_fw_managed_vdma_channels()
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+
+    // TODO: Add one icotl to stop all channels at once (HRT-6097)
+    for (auto &ch : m_inter_context_channels) {
+        status = ch.unregister_fw_controlled_channel();
+        CHECK_SUCCESS(status);
+    }
+
+    for (auto &ch : m_config_channels) {
+        status = ch.unregister_fw_controlled_channel();
+        CHECK_SUCCESS(status);
+    }
+
+    /* If ddr supported padded buffers - DDR buffers are managed by the FW */
+    if (get_supported_features().padded_ddr_buffers) {
+        for (auto &ch : m_ddr_buffer_channels) {
+            status = ch.unregister_fw_controlled_channel();
+            CHECK_SUCCESS(status);
+        }
+    }
+
+    return HAILO_SUCCESS;
+}
+
+Expected<uint16_t> ResourcesManager::get_network_batch_size_from_partial_name(const std::string &partial_network_name) const
+{
+    for (auto const &network_map: m_config_params.network_params_by_name) {
+        auto const network_name = network_map.first;
+        auto found = network_name.find(HAILO_DEFAULT_NETWORK_NAME_QUALIFIER + partial_network_name);
+        if (found != std::string::npos) {
+            return Expected<uint16_t>(network_map.second.batch_size);
+        }
+    }
+
+    LOGGER__ERROR("Failed to find network with network name {}", partial_network_name);
+
+    return make_unexpected(HAILO_NOT_FOUND);
+}
+
+Expected<Buffer> ResourcesManager::read_intermediate_buffer(const IntermediateBufferKey &key)
+{
+    auto intermediate_buffer_it = m_intermediate_buffers.find(key);
+    if (std::end(m_intermediate_buffers) == intermediate_buffer_it) {
+        LOGGER__ERROR("Failed to find intermediate buffer for src_context {}, src_stream_index {}", key.first,
+            key.second);
+        return make_unexpected(HAILO_NOT_FOUND);
+    }
+
+    auto &intermediate_buffer = *intermediate_buffer_it->second;
+    return intermediate_buffer.read();
+}
+
+hailo_status ResourcesManager::enable_state_machine()
+{
+    if (Device::Type::CORE == m_vdma_device.get_type()) {
+        // On core device, the nn_manager is not responsible to reset the nn-core so
+        // we use the SCU control for that.
+        auto status = m_vdma_device.reset(HAILO_RESET_DEVICE_MODE_NN_CORE);
+        CHECK_SUCCESS(status);
+    }
+
+    return Control::enable_network_group(m_vdma_device, m_net_group_index);
+}
+
+hailo_status ResourcesManager::reset_state_machine()
+{
+    return Control::reset_context_switch_state_machine(m_vdma_device);
+}
+
+hailo_status ResourcesManager::open_ddr_channels()
+{
+    for (auto& ddr_info : m_ddr_infos) {
+        for (auto &ch : m_ddr_buffer_channels) {
+            if (ddr_info.d2h_channel_index == ch.channel_index) {
+                auto status = ch.start_channel(*ddr_info.intermediate_buffer->get_desc_list());
+                CHECK_SUCCESS(status);
+                ddr_info.d2h_ch = &ch;
+            }
+            if (ddr_info.h2d_channel_index == ch.channel_index) {
+                auto status = ch.start_channel(*ddr_info.intermediate_buffer->get_desc_list());
+                CHECK_SUCCESS(status);
+                ddr_info.h2d_ch = &ch;
+            }
+        }
+    }
+    return HAILO_SUCCESS;
+}
+
+void ResourcesManager::abort_ddr_channels() // Best effort func
+{
+    for (auto &ch : m_ddr_buffer_channels) {
+        auto status = ch.abort();
+        if (HAILO_SUCCESS != status) {
+            LOGGER__ERROR("Failed to abort DDR channel {}", ch.channel_index);
+        }
+    }
+}
+
+void ResourcesManager::close_ddr_channels() // Best effort func
+{
+    m_ddr_buffer_channels.clear();
+}
+
+ExpectedRef<IntermediateBuffer> ResourcesManager::create_intermediate_buffer(uint32_t transfer_size, uint16_t batch_size,
+    const IntermediateBufferKey &key)
+{
+    auto intermediate_buffer = IntermediateBuffer::create(IntermediateBuffer::Type::EXTERNAL_DESC, m_driver, transfer_size,
+        batch_size);
+    CHECK_EXPECTED(intermediate_buffer);
+
+    auto emplace_res = m_intermediate_buffers.emplace(key, intermediate_buffer.release());
+    return std::ref(*emplace_res.first->second);
+}
+
+} /* namespace hailort */
diff --git a/hailort/libhailort/src/context_switch/single_context/hcp_config_activated_network_group.hpp b/hailort/libhailort/src/context_switch/single_context/hcp_config_activated_network_group.hpp
new file mode 100644 (file)
index 0000000..05a90b0
--- /dev/null
@@ -0,0 +1,76 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file hcp_config_activated_network_group.hpp
+ * @brief Represent activated network_group from HEF. 
+ *
+ * This network_group can be used for control-cofigured network_groups only (for etherent or pcie)
+  **/
+
+#ifndef _HAILO_CONTEXT_SWITCH_HCP_CONFIG_ACTIVATED_NETWORK_GROUP_HPP_
+#define _HAILO_CONTEXT_SWITCH_HCP_CONFIG_ACTIVATED_NETWORK_GROUP_HPP_
+
+#include "hailo/device.hpp"
+#include "common/utils.hpp"
+#include "context_switch/network_group_internal.hpp"
+#include "context_switch/active_network_group_holder.hpp"
+
+#include <vector>
+#include <map>
+
+namespace hailort
+{
+
+struct WriteMemoryInfo
+{
+    uint32_t address;
+    Buffer data;
+};
+
+class HcpConfigActivatedNetworkGroup;
+using HcpConfigActiveAppHolder = ActiveNetworkGroupHolder<HcpConfigActivatedNetworkGroup>;
+
+class HcpConfigActivatedNetworkGroup : public ActivatedNetworkGroupBase
+{
+  public:
+    static Expected<HcpConfigActivatedNetworkGroup> create(Device &device, std::vector<WriteMemoryInfo> &config,
+        const hailo_activate_network_group_params_t &network_group_params,
+        std::map<std::string, std::unique_ptr<InputStream>> &input_streams,
+        std::map<std::string, std::unique_ptr<OutputStream>> &output_streams,        
+        HcpConfigActiveAppHolder &active_net_group_holder,
+        hailo_power_mode_t power_mode, EventPtr network_group_activated_event);
+
+    virtual ~HcpConfigActivatedNetworkGroup();
+    HcpConfigActivatedNetworkGroup(const HcpConfigActivatedNetworkGroup &) = delete;
+    HcpConfigActivatedNetworkGroup &operator=(const HcpConfigActivatedNetworkGroup &) = delete;
+    HcpConfigActivatedNetworkGroup &operator=(HcpConfigActivatedNetworkGroup &&) = delete;
+    HcpConfigActivatedNetworkGroup(HcpConfigActivatedNetworkGroup &&other) noexcept :
+      ActivatedNetworkGroupBase(std::move(other)), m_active_net_group_holder(other.m_active_net_group_holder),
+      m_is_active(std::exchange(other.m_is_active, false)), m_power_mode(other.m_power_mode),
+      m_device(other.m_device) {};
+
+    virtual Expected<Buffer> get_intermediate_buffer(const IntermediateBufferKey &/*key*/) override
+    {
+        LOGGER__ERROR("get_intermediate_buffer() is not supported on single_context network_groups");
+        return make_unexpected(HAILO_INVALID_OPERATION);
+    }
+
+
+  private:
+      HcpConfigActivatedNetworkGroup(Device &device, HcpConfigActiveAppHolder &active_net_group_holder,
+        const hailo_activate_network_group_params_t &network_group_params,
+        std::map<std::string, std::unique_ptr<InputStream>> &input_streams,
+        std::map<std::string, std::unique_ptr<OutputStream>> &output_streams,        
+        hailo_power_mode_t power_mode, EventPtr &&network_group_activated_event, hailo_status &status);
+
+    HcpConfigActiveAppHolder &m_active_net_group_holder;
+    bool m_is_active;
+    hailo_power_mode_t m_power_mode;
+    Device &m_device;
+};
+
+} /* namespace hailort */
+
+#endif /* _HAILO_CONTEXT_SWITCH_HCP_CONFIG_ACTIVATED_NETWORK_GROUP_HPP_ */
diff --git a/hailort/libhailort/src/context_switch/single_context/hcp_config_manager.hpp b/hailort/libhailort/src/context_switch/single_context/hcp_config_manager.hpp
new file mode 100644 (file)
index 0000000..5ccc4cb
--- /dev/null
@@ -0,0 +1,52 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file hcp_config_manager.hpp
+ * @brief Manager of HEF parsing for control-configured network groups (Pcie and Etherent support)
+ *
+ *
+ **/
+
+#ifndef HAILO_HCP_CONFIG_MANAGER_HPP_
+#define HAILO_HCP_CONFIG_MANAGER_HPP_
+
+#include "context_switch/config_manager.hpp"
+#include "context_switch/single_context/hcp_config_network_group.hpp"
+#include "hailo/hailort.h"
+#include "hailo/device.hpp"
+#include "hailo/expected.hpp"
+#include "common/utils.hpp"
+
+#include <vector>
+#include <map>
+#include <algorithm>
+
+namespace hailort
+{
+
+class HcpConfigManager : public ConfigManager {
+public:
+    HcpConfigManager(Device &device) : m_device(device) {}
+    virtual ~HcpConfigManager() = default;
+    virtual ConfigManagerType get_manager_type();
+
+    virtual Expected<ConfiguredNetworkGroupVector> add_hef(Hef &hef,
+        const NetworkGroupsParamsMap &configure_params={});
+
+    HcpConfigManager(const HcpConfigManager &other) = delete;
+    HcpConfigManager &operator=(const HcpConfigManager &other) = delete;
+    HcpConfigManager &operator=(HcpConfigManager &&other) = delete;
+    HcpConfigManager(HcpConfigManager &&other) noexcept = default;
+
+private:
+    // TODO: (SDK-16665) Dont need is_active flag for dtor?
+    std::vector<std::shared_ptr<HcpConfigNetworkGroup>> m_net_groups;
+    Device &m_device;
+    HcpConfigActiveAppHolder m_active_net_group_holder;
+};
+
+} /* namespace hailort */
+
+#endif /* HAILO_HCP_CONFIG_MANAGER_HPP_ */
diff --git a/hailort/libhailort/src/context_switch/single_context/hcp_config_network_group.hpp b/hailort/libhailort/src/context_switch/single_context/hcp_config_network_group.hpp
new file mode 100644 (file)
index 0000000..378d67e
--- /dev/null
@@ -0,0 +1,61 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file hcp_config_network_group.hpp
+ * @brief Represent network_group from HEF file that can be activated 
+ *
+ * This network_group can be used for control-configured network_groups (for etherent or pcie)
+  **/
+
+#ifndef _HAILO_CONTEXT_SWITCH_HCP_CONFIG_NETWORK_GROUP_HPP_
+#define _HAILO_CONTEXT_SWITCH_HCP_CONFIG_NETWORK_GROUP_HPP_
+
+#include "hailo/device.hpp"
+#include "common/utils.hpp"
+#include "context_switch/network_group_internal.hpp"
+#include "context_switch/active_network_group_holder.hpp"
+#include "hailort_defaults.hpp"
+#include "context_switch/single_context/hcp_config_activated_network_group.hpp"
+
+#include <vector>
+#include <map>
+
+namespace hailort
+{
+
+using HcpConfigActiveAppHolder = ActiveNetworkGroupHolder<HcpConfigActivatedNetworkGroup>;
+
+class HcpConfigNetworkGroup : public ConfiguredNetworkGroupBase
+{
+public:
+    HcpConfigNetworkGroup(
+        Device &device, HcpConfigActiveAppHolder &active_net_group_holder, std::vector<WriteMemoryInfo> &&config,
+        const ConfigureNetworkParams &config_params, uint8_t net_group_index,
+        NetworkGroupMetadata &&network_group_metadata, hailo_status &status);
+
+    virtual Expected<std::unique_ptr<ActivatedNetworkGroup>> activate(
+    const hailo_activate_network_group_params_t &network_group_params = HailoRTDefaults::get_network_group_params()) override;
+    virtual Expected<hailo_stream_interface_t> get_default_streams_interface() override;
+
+    virtual Expected<uint8_t> get_boundary_channel_index(uint8_t stream_index, hailo_stream_direction_t direction,
+        const std::string &layer_name) override;
+
+    virtual ~HcpConfigNetworkGroup() = default;
+    HcpConfigNetworkGroup(const HcpConfigNetworkGroup &other) = delete;
+    HcpConfigNetworkGroup &operator=(const HcpConfigNetworkGroup &other) = delete;
+    HcpConfigNetworkGroup &operator=(HcpConfigNetworkGroup &&other) = delete;
+    HcpConfigNetworkGroup(HcpConfigNetworkGroup &&other) noexcept : ConfiguredNetworkGroupBase(std::move(other)),
+        m_config(std::move(other.m_config)), m_active_net_group_holder(other.m_active_net_group_holder),
+        m_device(other.m_device) {}
+
+private:
+        std::vector<WriteMemoryInfo> m_config;
+        HcpConfigActiveAppHolder &m_active_net_group_holder;
+        Device &m_device;
+};
+
+} /* namespace hailort */
+
+#endif /* _HAILO_CONTEXT_SWITCH_HCP_CONFIG_NETWORK_GROUP_HPP_ */
diff --git a/hailort/libhailort/src/context_switch/vdma_config_activated_network_group.cpp b/hailort/libhailort/src/context_switch/vdma_config_activated_network_group.cpp
new file mode 100644 (file)
index 0000000..bbf47a8
--- /dev/null
@@ -0,0 +1,329 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file multi_context_activatedN_network_group.cpp
+ * @brief VdmaConfigActivatedNetworkGroup implementation
+ **/
+
+#include "context_switch/multi_context/vdma_config_activated_network_group.hpp"
+#include "control.hpp"
+#include <chrono>
+
+namespace hailort
+{
+
+Expected<VdmaConfigActivatedNetworkGroup> VdmaConfigActivatedNetworkGroup::create(
+    VdmaConfigActiveAppHolder &active_net_group_holder,
+    std::vector<std::shared_ptr<ResourcesManager>> resources_managers,
+    const hailo_activate_network_group_params_t &network_group_params,
+    std::map<std::string, std::unique_ptr<InputStream>> &input_streams,
+    std::map<std::string, std::unique_ptr<OutputStream>> &output_streams,         
+    EventPtr network_group_activated_event,
+    AccumulatorPtr deactivation_time_accumulator)
+{
+    CHECK(!active_net_group_holder.is_any_active(), make_unexpected(HAILO_INVALID_OPERATION),
+        "network group is currently active. You must deactivate before activating another network_group");
+
+    CHECK_ARG_NOT_NULL_AS_EXPECTED(deactivation_time_accumulator);
+
+    auto status = HAILO_UNINITIALIZED;
+    VdmaConfigActivatedNetworkGroup object(network_group_params, input_streams, output_streams,
+        std::move(resources_managers), active_net_group_holder, std::move(network_group_activated_event),
+        deactivation_time_accumulator, status);
+    CHECK_SUCCESS_AS_EXPECTED(status);
+
+    return object;
+}
+
+VdmaConfigActivatedNetworkGroup::VdmaConfigActivatedNetworkGroup(
+    const hailo_activate_network_group_params_t &network_group_params,
+    std::map<std::string, std::unique_ptr<InputStream>> &input_streams,
+    std::map<std::string, std::unique_ptr<OutputStream>> &output_streams,
+    std::vector<std::shared_ptr<ResourcesManager>> &&resources_managers,
+    VdmaConfigActiveAppHolder &active_net_group_holder,
+    EventPtr &&network_group_activated_event,
+    AccumulatorPtr deactivation_time_accumulator,
+    hailo_status &status) :
+        ActivatedNetworkGroupBase(network_group_params, input_streams, output_streams,
+            std::move(network_group_activated_event), status),
+        m_should_reset_state_machine(true),
+        m_active_net_group_holder(active_net_group_holder),
+        m_resources_managers(std::move(resources_managers)),
+        m_ddr_send_threads(),
+        m_ddr_recv_threads(),
+        m_deactivation_time_accumulator(deactivation_time_accumulator)
+{
+    // Validate ActivatedNetworkGroup status
+    if (HAILO_SUCCESS != status) {
+        return;
+    }
+    m_active_net_group_holder.set(*this);
+
+    for (auto &resources_manager : m_resources_managers) {
+        status = resources_manager->register_fw_managed_vdma_channels();
+        if (HAILO_SUCCESS != status) {
+            LOGGER__ERROR("Failed to start fw managed vdma channels.");
+            return;
+        }
+    }
+
+    /* If ddr buffers are not SDK padded, host should control the DDR buffering */
+    if (!m_resources_managers[0]->get_supported_features().padded_ddr_buffers) { // All ResourceManagers shares the same supported_features
+        status = init_ddr_resources();
+        if (HAILO_SUCCESS != status) {
+            LOGGER__ERROR("Failed to initialize DDR resources.");
+            return;
+        }
+    }
+
+    for (auto &resources_manager : m_resources_managers) {
+        status = resources_manager->enable_state_machine();
+        if (HAILO_SUCCESS != status) {
+            LOGGER__ERROR("Failed to activate state-machine");
+            return;
+        }
+    }
+}
+
+VdmaConfigActivatedNetworkGroup::VdmaConfigActivatedNetworkGroup(VdmaConfigActivatedNetworkGroup &&other) noexcept :
+    ActivatedNetworkGroupBase(std::move(other)),
+    m_should_reset_state_machine(std::exchange(other.m_should_reset_state_machine, false)),
+    m_active_net_group_holder(other.m_active_net_group_holder),
+    m_resources_managers(std::move(other.m_resources_managers)),
+    m_ddr_send_threads(std::move(other.m_ddr_send_threads)),
+    m_ddr_recv_threads(std::move(other.m_ddr_recv_threads)),
+    m_deactivation_time_accumulator(std::move(other.m_deactivation_time_accumulator))
+{}
+
+VdmaConfigActivatedNetworkGroup::~VdmaConfigActivatedNetworkGroup()
+{
+    if (!m_should_reset_state_machine) {
+        return;
+    }
+
+    auto status = HAILO_UNINITIALIZED;
+    const auto start_time = std::chrono::steady_clock::now();
+
+    // If ddr buffers are not SDK padded, host should control the DDR buffering
+    // Note: All ResourceManagers share the same supported_features
+    if (!m_resources_managers[0]->get_supported_features().padded_ddr_buffers) {
+        status = cleanup_ddr_resources();
+        if (HAILO_SUCCESS != status) {
+            LOGGER__ERROR("Failed to cleanup DDR resources.");
+        }
+    }
+
+    m_active_net_group_holder.clear();
+    deactivate_resources();
+
+    for (auto &resources_manager : m_resources_managers) {
+        status = resources_manager->reset_state_machine();
+        if (HAILO_SUCCESS != status) {
+            LOGGER__ERROR("Failed to reset context switch status");
+        }
+    }
+
+    for (auto &resources_manager : m_resources_managers) {
+        status = resources_manager->unregister_fw_managed_vdma_channels();
+        if (HAILO_SUCCESS != status) {
+            LOGGER__ERROR("Failed to stop fw managed vdma channels");
+        }
+    }
+
+    const auto elapsed_time_ms = std::chrono::duration<double, std::milli>(
+        std::chrono::steady_clock::now() - start_time).count();
+    LOGGER__INFO("Deactivating took {} ms", elapsed_time_ms);
+    m_deactivation_time_accumulator->add_data_point(elapsed_time_ms);
+}
+
+Expected<Buffer> VdmaConfigActivatedNetworkGroup::get_intermediate_buffer(const IntermediateBufferKey &key)
+{
+    CHECK_AS_EXPECTED(1 == m_resources_managers.size(), HAILO_INVALID_OPERATION,
+        "'get_intermediate_buffer' function works only when working with 1 physical device. number of physical devices: {}",
+        m_resources_managers.size());
+    return m_resources_managers[0]->read_intermediate_buffer(key);
+}
+
+hailo_status VdmaConfigActivatedNetworkGroup::init_ddr_resources()
+{
+    for (auto &resources_manager : m_resources_managers) {
+        auto status = resources_manager->open_ddr_channels();
+        CHECK_SUCCESS(status);
+
+        for (auto &ddr_info : resources_manager->ddr_infos()) {
+            auto num_ready = make_shared_nothrow<std::atomic<uint16_t>>(static_cast<uint16_t>(0));
+            CHECK_NOT_NULL(num_ready, HAILO_OUT_OF_HOST_MEMORY);
+
+            m_ddr_send_threads.push_back(std::thread(ddr_send_thread_main, ddr_info, num_ready));
+            m_ddr_recv_threads.push_back(std::thread(ddr_recv_thread_main, ddr_info, num_ready));
+        }
+    }
+    return HAILO_SUCCESS;
+}
+
+hailo_status VdmaConfigActivatedNetworkGroup::cleanup_ddr_resources()
+{
+    hailo_status status = HAILO_SUCCESS; // Success oriented
+
+    for (auto &resources_manager : m_resources_managers) {
+        resources_manager->abort_ddr_channels();
+    }
+
+    for (auto& thread : m_ddr_send_threads) {
+        thread.join();
+    }
+    m_ddr_send_threads.clear();
+
+    for (auto& thread : m_ddr_recv_threads) {
+        thread.join();
+    }
+    m_ddr_recv_threads.clear();
+
+    for (auto &resources_manager : m_resources_managers) {
+        resources_manager->close_ddr_channels();
+    }
+
+    return status;
+}
+
+void VdmaConfigActivatedNetworkGroup::ddr_send_thread_main(DdrChannelsInfo ddr_info,
+    std::shared_ptr<std::atomic<uint16_t>> desc_list_num_ready)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    const auto timeout = std::chrono::milliseconds(DDR_THREAD_DEFAULT_TIMEOUT_MS);
+    uint16_t last_num_proc = 0;
+    uint32_t acc_sent_descs = 0;
+    auto descs_per_interrupt = ddr_info.intermediate_buffer->descriptors_in_frame();
+    assert(0 == (ddr_info.min_buffered_rows % DDR_NUMBER_OF_ROWS_PER_INTERRUPT));
+    auto interrupts_per_min_buffered_rows = ddr_info.min_buffered_rows / DDR_NUMBER_OF_ROWS_PER_INTERRUPT;
+    uint32_t descs_per_min_buffered_rows = interrupts_per_min_buffered_rows * descs_per_interrupt;
+
+    if (nullptr == ddr_info.h2d_ch) {
+        LOGGER__ERROR("Failed to find DDR H2D channel {}.", ddr_info.h2d_channel_index);
+        return;
+    }
+    if (nullptr == ddr_info.d2h_ch) {
+        LOGGER__ERROR("Failed to find DDR D2H channel {}.", ddr_info.d2h_channel_index);
+        return;
+    }
+
+    while (true) {
+        /* H2D */
+        status = ddr_info.h2d_ch->wait_channel_interrupts_for_ddr(timeout);
+        if (HAILO_SUCCESS != status) {
+            goto l_exit;
+        }
+
+        /* D2H */
+        auto hw_num_proc = ddr_info.h2d_ch->get_hw_num_processed_ddr(ddr_info.desc_list_size_mask);
+        if (!hw_num_proc) {
+            LOGGER__ERROR("Failed to get DDR_H2D_Channel {} num_processed.", ddr_info.h2d_channel_index);
+            status = hw_num_proc.status();
+            goto l_exit;
+        }
+
+        auto transferred_descs = (last_num_proc <= hw_num_proc.value()) ?
+            static_cast<uint16_t>(hw_num_proc.value() - last_num_proc) :
+            static_cast<uint16_t>(ddr_info.intermediate_buffer->descs_count() - last_num_proc + hw_num_proc.value());
+        acc_sent_descs += transferred_descs;
+        last_num_proc = hw_num_proc.value();
+
+        auto sent_batches = acc_sent_descs / descs_per_min_buffered_rows;
+        if (0 < sent_batches) {
+            auto desc_count = ddr_info.intermediate_buffer->program_host_managed_ddr(ddr_info.row_size,
+                (ddr_info.min_buffered_rows * sent_batches), *desc_list_num_ready);
+            if (!desc_count) {
+                LOGGER__ERROR("Failed to program descs for DDR");
+                status = desc_count.status();
+                goto l_exit;
+            }
+            acc_sent_descs -= desc_count.value();
+            *desc_list_num_ready =
+                static_cast<uint16_t>((desc_count.value() + *desc_list_num_ready) & ddr_info.desc_list_size_mask);
+
+            status = ddr_info.d2h_ch->inc_num_available_for_ddr(desc_count.value(),
+                ddr_info.desc_list_size_mask);
+            if (HAILO_SUCCESS != status) {
+                goto l_exit;
+            }
+        }
+    }
+l_exit:
+    if (HAILO_SUCCESS != status) {
+        if ((HAILO_STREAM_INTERNAL_ABORT == status) || (HAILO_STREAM_NOT_ACTIVATED == status)) {
+            LOGGER__INFO("DDR thread main for channels {}:(h2d), {}:(d2h) exit with status {}",ddr_info.h2d_channel_index,
+                ddr_info.d2h_channel_index, status);
+        } else {
+            LOGGER__ERROR("DDR thread main for channels {}:(h2d), {}:(d2h) exit with status {}",ddr_info.h2d_channel_index,
+                ddr_info.d2h_channel_index, status);
+        }
+    }
+    return;
+}
+
+void VdmaConfigActivatedNetworkGroup::ddr_recv_thread_main(DdrChannelsInfo ddr_info,
+    std::shared_ptr<std::atomic<uint16_t>> desc_list_num_ready)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    uint16_t last_num_proc = 0;
+    uint16_t transferred_descs = 0;
+    const auto timeout = std::chrono::milliseconds(DDR_THREAD_DEFAULT_TIMEOUT_MS);
+
+    if (nullptr == ddr_info.h2d_ch) {
+        LOGGER__ERROR("Failed to find DDR H2D channel {}.", ddr_info.h2d_channel_index);
+        return;
+    }
+    if (nullptr == ddr_info.d2h_ch) {
+        LOGGER__ERROR("Failed to find DDR D2H channel {}.", ddr_info.d2h_channel_index);
+        return;
+    }
+
+    *desc_list_num_ready = ddr_info.initial_programed_descs;
+
+    status = ddr_info.d2h_ch->set_num_avail_value(ddr_info.initial_programed_descs);
+    if (HAILO_SUCCESS != status) {
+        goto l_exit;
+    }
+
+    while (true) {
+        /* D2H */
+        status = ddr_info.d2h_ch->wait_channel_interrupts_for_ddr(timeout);
+        if (HAILO_SUCCESS != status) {
+            goto l_exit;
+        }
+
+        /* H2D */
+        auto hw_num_proc = ddr_info.d2h_ch->get_hw_num_processed_ddr(ddr_info.desc_list_size_mask);
+        if (!hw_num_proc) {
+            LOGGER__ERROR("Failed to get DDR_D2H_Channel {} num_processed.", ddr_info.d2h_channel_index);
+            status = hw_num_proc.status();
+            goto l_exit;
+        }
+
+        transferred_descs = (last_num_proc <= hw_num_proc.value()) ?
+            static_cast<uint16_t>(hw_num_proc.value() - last_num_proc) :
+            static_cast<uint16_t>(ddr_info.intermediate_buffer->descs_count() - last_num_proc + hw_num_proc.value());
+        last_num_proc = hw_num_proc.value();
+
+        status = ddr_info.h2d_ch->inc_num_available_for_ddr(transferred_descs,
+            ddr_info.desc_list_size_mask);
+        if (HAILO_SUCCESS != status) {
+            goto l_exit;
+        }
+    }
+l_exit:
+    if (HAILO_SUCCESS != status) {
+        if ((HAILO_STREAM_INTERNAL_ABORT == status) || (HAILO_STREAM_NOT_ACTIVATED == status)) {
+            LOGGER__INFO("DDR thread main for channels {}:(h2d), {}:(d2h) exit with status {}",ddr_info.h2d_channel_index,
+                ddr_info.d2h_channel_index, status);
+        } else {
+            LOGGER__ERROR("DDR thread main for channels {}:(h2d), {}:(d2h) exit with status {}",ddr_info.h2d_channel_index,
+                ddr_info.d2h_channel_index, status);
+        }
+    }
+    return;
+}
+
+} /* namespace hailort */
diff --git a/hailort/libhailort/src/context_switch/vdma_config_manager.cpp b/hailort/libhailort/src/context_switch/vdma_config_manager.cpp
new file mode 100644 (file)
index 0000000..0a3bc7e
--- /dev/null
@@ -0,0 +1,236 @@
+
+// https://github.com/protocolbuffers/protobuf/tree/master/cmake#notes-on-compiler-warnings
+#if defined(_MSC_VER)
+#pragma warning(push)
+#pragma warning(disable: 4244 4267 4127)
+#else
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wconversion"
+#endif
+#include "hef.pb.h"
+#if defined(_MSC_VER)
+#pragma warning( pop ) 
+#else
+#pragma GCC diagnostic pop
+#endif
+
+#include "context_switch/multi_context/vdma_config_manager.hpp"
+#include "network_group_internal.hpp"
+#include "hailo/hailort.h"
+#include "common/utils.hpp"
+#include "hailo/device.hpp"
+#include "hailo/vdevice.hpp"
+#include "hailo/hef.hpp"
+#include "control.hpp"
+#include "hailort_defaults.hpp"
+
+#include "pcie_device.hpp"
+#include "hlpcie.hpp"
+
+#include <new>
+#include <algorithm>
+
+namespace hailort
+{
+
+Expected<VdmaConfigManager> VdmaConfigManager::create(VdmaDevice &device)
+{
+    const bool is_vdevice = false;
+    std::vector<std::reference_wrapper<VdmaDevice>> devices;
+    devices.push_back(device);
+    VdmaConfigManager manager(std::move(devices), is_vdevice);
+    return manager;
+}
+
+Expected<VdmaConfigManager> VdmaConfigManager::create(VDevice &vdevice)
+{
+    const bool is_vdevice = true;
+    auto devices = vdevice.get_physical_devices();
+    CHECK_EXPECTED(devices);
+
+    // Down casting Device to PcieDevice
+    std::vector<std::reference_wrapper<VdmaDevice>> pcie_devices;
+    for (auto &dev : devices.release()) {
+        CHECK_AS_EXPECTED(Device::Type::PCIE == dev.get().get_type(), HAILO_INTERNAL_FAILURE,
+            "vDevice is supported only with PCIe devices");
+        pcie_devices.emplace_back(static_cast<PcieDevice&>(dev.get()));
+    }
+
+
+    VdmaConfigManager manager(std::move(pcie_devices), is_vdevice);
+    return manager;
+}
+
+VdmaConfigManager::VdmaConfigManager(std::vector<std::reference_wrapper<VdmaDevice>> &&devices, bool is_vdevice)
+    : m_devices(std::move(devices)), m_net_groups(), m_is_vdevice(is_vdevice) {}
+
+Expected<ConfiguredNetworkGroupVector> VdmaConfigManager::add_hef(Hef &hef,
+    const NetworkGroupsParamsMap &configure_params)
+{
+    auto &hef_network_groups = hef.pimpl->network_groups();
+    auto current_net_group_index = static_cast<uint8_t>(m_net_groups.size());
+    CHECK(CONTROL_PROTOCOL__MAX_CONTEXT_SWITCH_APPLICATIONS >= m_net_groups.size() + hef_network_groups.size(),
+        make_unexpected(HAILO_INVALID_OPERATION),
+        "Can't add network_groups from HEF because of too many network_groups. maximum allowed network_groups are: {}",
+        CONTROL_PROTOCOL__MAX_CONTEXT_SWITCH_APPLICATIONS);
+
+    ConfiguredNetworkGroupVector added_network_groups;
+    added_network_groups.reserve(hef_network_groups.size());
+
+    auto configure_params_copy = configure_params;
+    for (const auto &network_group_proto : hef_network_groups) {
+        CHECK_NOT_NULL_AS_EXPECTED(network_group_proto, HAILO_INTERNAL_FAILURE);
+        auto status = Hef::Impl::validate_net_group_unique_layer_names(network_group_proto);
+        CHECK_SUCCESS_AS_EXPECTED(status);
+
+        static_assert(HAILO_DEFAULT_BATCH_SIZE <= std::numeric_limits<uint16_t>::max(),
+            "Invalid HAILO_DEFAULT_BATCH_SIZE");
+
+        ConfigureNetworkParams config_params = {};
+        if (contains(configure_params, network_group_proto->network_group_metadata().network_group_name())) {
+            config_params = configure_params_copy.at(network_group_proto->network_group_metadata().network_group_name());
+            configure_params_copy.erase(network_group_proto->network_group_metadata().network_group_name());
+        } else {
+            auto first_streams_interface = m_devices[0].get().get_default_streams_interface();
+            CHECK_EXPECTED(first_streams_interface);
+#ifndef NDEBUG
+            // Check that all physicall devices has the same interface
+            for (auto &device : m_devices) {
+                auto interface = device.get().get_default_streams_interface();
+                CHECK_EXPECTED(interface);
+                CHECK_AS_EXPECTED(interface.value() == first_streams_interface.value(), HAILO_INTERNAL_FAILURE,
+                    "Not all default stream interfaces are the same");
+            }
+#endif
+            auto config_params_exp = hef.create_configure_params(first_streams_interface.value(),
+                network_group_proto->network_group_metadata().network_group_name());
+            CHECK_EXPECTED(config_params_exp);
+            config_params = config_params_exp.release();
+        }
+
+        /* Validate batch size (network group batch size vs network batch size) */
+        status = update_network_batch_size(config_params);
+        CHECK_SUCCESS_AS_EXPECTED(status);
+
+        auto network_group_metadata = hef.pimpl->get_network_group_metadata(network_group_proto->network_group_metadata().network_group_name());
+        CHECK_EXPECTED(network_group_metadata);
+
+        auto network_group_metadata_ptr = make_shared_nothrow<NetworkGroupMetadata>(network_group_metadata.release());
+        CHECK_AS_EXPECTED(nullptr != network_group_metadata_ptr, HAILO_OUT_OF_HOST_MEMORY);
+
+        /* build HEF supported features */
+        std::vector<std::shared_ptr<ResourcesManager>> resources_managers;
+        for (auto device : m_devices) {
+            auto resource_manager = Hef::Impl::create_resources_manager(network_group_proto, current_net_group_index,
+                device.get(), device.get().get_driver(), config_params, network_group_metadata_ptr, hef.pimpl->get_device_arch());
+            CHECK_EXPECTED(resource_manager);
+            resources_managers.push_back(resource_manager.release());
+        }
+
+        auto net_group = VdmaConfigNetworkGroup::create(m_active_net_group_holder, config_params,
+            resources_managers, network_group_metadata_ptr);
+        current_net_group_index++;
+
+        auto net_group_ptr = make_shared_nothrow<VdmaConfigNetworkGroup>(net_group.release());
+        CHECK_AS_EXPECTED(nullptr != net_group_ptr, HAILO_OUT_OF_HOST_MEMORY);
+        m_net_groups.emplace_back(net_group_ptr);
+
+        // TODO: move this func into VdmaConfigNetworkGroup c'tor
+        if (m_is_vdevice) {
+            status = net_group_ptr->create_vdevice_streams_from_config_params();
+            CHECK_SUCCESS_AS_EXPECTED(status);
+        } else {
+            status = net_group_ptr->create_streams_from_config_params(net_group_ptr->get_resources_managers()[0]->get_device());
+            CHECK_SUCCESS_AS_EXPECTED(status);
+        }
+    
+        // Check that all boundary streams were created
+        status = validate_boundary_streams_were_created(hef, network_group_proto->network_group_metadata().network_group_name(), *net_group_ptr);
+        CHECK_SUCCESS_AS_EXPECTED(status);
+
+        added_network_groups.emplace_back(std::static_pointer_cast<ConfiguredNetworkGroup>(net_group_ptr));
+    }
+    std::string unmatched_keys = "";
+    for (const auto &pair : configure_params_copy) {
+        unmatched_keys.append(" ");
+        unmatched_keys.append(pair.first);
+    }
+    CHECK_AS_EXPECTED(unmatched_keys.size() == 0, HAILO_INVALID_ARGUMENT,
+        "Some network group names in the configuration are not found in the hef file:{}", unmatched_keys);
+
+    for (auto device : m_devices) {
+        CONTROL_PROTOCOL__context_switch_info_t context_switch_info = {};
+        context_switch_info.context_switch_main_header.context_switch_version = CONTROL_PROTOCOL__CONTEXT_SWITCH_VER_V1_0_0;
+        context_switch_info.context_switch_main_header.application_count = static_cast<uint8_t>(m_net_groups.size());
+
+        auto is_abbale_supported = false;
+        // TODO: fix is_abbale_supported
+        // auto proto_message = hef.pimpl.proto_message();
+        // auto has_included_features = proto_message->has_included_features();
+        // if (has_included_features) {
+        //     is_abbale_supported = proto_message->included_features().abbale();
+        // }
+
+        context_switch_info.context_switch_main_header.validation_features.is_abbale_supported = is_abbale_supported;
+        for (size_t i = 0, contexts = 0; i < m_net_groups.size(); ++i) {
+            for (auto &resource_manager : m_net_groups[i]->get_resources_managers()) {
+                if (0 == strcmp(device.get().get_dev_id(), resource_manager->get_dev_id())) {
+                    auto net_group_header_exp = resource_manager->get_control_network_group_header();
+                    CHECK_EXPECTED(net_group_header_exp);
+                    context_switch_info.context_switch_main_header.application_header[i] = net_group_header_exp.value();
+                    auto net_group_contexts = resource_manager->get_contexts();
+                    CHECK_AS_EXPECTED((contexts + net_group_contexts.size()) <= CONTROL_PROTOCOL__MAX_TOTAL_CONTEXTS,
+                        HAILO_INVALID_ARGUMENT,
+                        "There are too many contexts in the configured network groups. Max allowed for all network groups together: {}",
+                        CONTROL_PROTOCOL__MAX_TOTAL_CONTEXTS);
+                    std::memcpy(&context_switch_info.context[contexts], net_group_contexts.data(),
+                        net_group_contexts.size() * sizeof(context_switch_info.context[0]));
+                    contexts += net_group_contexts.size();
+                }
+            }
+        }
+
+        // Reset context_switch status
+        auto status = Control::reset_context_switch_state_machine(device.get());
+        CHECK_SUCCESS_AS_EXPECTED(status);
+
+        // Write context_switch info
+        status = Control::write_context_switch_info(device.get(), &context_switch_info);
+        CHECK_SUCCESS_AS_EXPECTED(status);
+    }
+
+    return added_network_groups;
+}
+
+hailo_status VdmaConfigManager::update_network_batch_size(ConfigureNetworkParams &config_params)
+{
+    auto single_network_default_batch = (HAILO_DEFAULT_BATCH_SIZE == config_params.batch_size);
+    auto multi_network_default_batch = true;
+    /* Batch size overide logic - if user modifies network group batch size
+    and not the network batch size,  */
+
+    for (auto const &network_params : config_params.network_params_by_name) {
+        if (HAILO_DEFAULT_BATCH_SIZE != network_params.second.batch_size) {
+            multi_network_default_batch = false;
+        }
+    }
+
+    CHECK((single_network_default_batch || multi_network_default_batch), HAILO_INVALID_OPERATION, 
+        "User provided non batch size for network group and for network as well. User is adviced to work with network's batch size only");
+
+    /* In case user works with network group, overide the network batch size.*/
+    if (!single_network_default_batch && multi_network_default_batch) {
+        for (auto &network_params : config_params.network_params_by_name) {
+            network_params.second.batch_size = config_params.batch_size;
+        }
+    }
+
+    return HAILO_SUCCESS;
+}
+
+ConfigManagerType VdmaConfigManager::get_manager_type()
+{
+    return ConfigManagerType::VdmaConfigManager;
+}
+
+} /* namespace hailort */
diff --git a/hailort/libhailort/src/context_switch/vdma_config_network_group.cpp b/hailort/libhailort/src/context_switch/vdma_config_network_group.cpp
new file mode 100644 (file)
index 0000000..fd5610d
--- /dev/null
@@ -0,0 +1,164 @@
+#include "context_switch/multi_context/vdma_config_network_group.hpp"
+#include "network_group_internal.hpp"
+#include "eth_stream.hpp"
+#include "pcie_stream.hpp"
+#include "mipi_stream.hpp"
+#include "vdevice_stream.hpp"
+
+namespace hailort
+{
+
+Expected<VdmaConfigNetworkGroup> VdmaConfigNetworkGroup::create(VdmaConfigActiveAppHolder &active_net_group_holder,
+        const ConfigureNetworkParams &config_params, 
+        std::vector<std::shared_ptr<ResourcesManager>> resources_managers,
+        std::shared_ptr<NetworkGroupMetadata> network_group_metadata)
+{
+    auto status = HAILO_UNINITIALIZED;
+
+    VdmaConfigNetworkGroup object(active_net_group_holder, config_params,
+        std::move(resources_managers), *network_group_metadata, status);
+    CHECK_SUCCESS_AS_EXPECTED(status);
+
+    return object;
+}
+
+VdmaConfigNetworkGroup::VdmaConfigNetworkGroup(VdmaConfigActiveAppHolder &active_net_group_holder,
+    const ConfigureNetworkParams &config_params,
+    std::vector<std::shared_ptr<ResourcesManager>> &&resources_managers,
+    const NetworkGroupMetadata &network_group_metadata, hailo_status &status) :
+        ConfiguredNetworkGroupBase(config_params,
+        resources_managers[0]->get_network_group_index(),
+        network_group_metadata, status), // All ResourceManagers shares the same net_group_index
+        m_active_net_group_holder(active_net_group_holder),
+        m_resources_managers(std::move(resources_managers)) {}
+
+Expected<std::unique_ptr<ActivatedNetworkGroup>> VdmaConfigNetworkGroup::activate(
+      const hailo_activate_network_group_params_t &network_group_params)
+{
+    auto start_time = std::chrono::steady_clock::now();
+    auto activated_net_group = VdmaConfigActivatedNetworkGroup::create(
+        m_active_net_group_holder, m_resources_managers, network_group_params,
+        m_input_streams, m_output_streams, m_network_group_activated_event, m_deactivation_time_accumulator);
+    const auto elapsed_time_ms = std::chrono::duration<double, std::milli>(
+        std::chrono::steady_clock::now() - start_time).count();
+    CHECK_EXPECTED(activated_net_group);
+
+    LOGGER__INFO("Activating {} took {} milliseconds. Note that the function is asynchronous and"
+                 " thus the network is not fully activated yet.", get_network_group_name(), elapsed_time_ms);
+    m_activation_time_accumulator->add_data_point(elapsed_time_ms);
+
+    std::unique_ptr<ActivatedNetworkGroup> activated_net_group_ptr =
+        make_unique_nothrow<VdmaConfigActivatedNetworkGroup>(activated_net_group.release());
+    CHECK_AS_EXPECTED(nullptr != activated_net_group_ptr, HAILO_OUT_OF_HOST_MEMORY);
+    
+    return activated_net_group_ptr;
+}
+
+Expected<hailo_stream_interface_t> VdmaConfigNetworkGroup::get_default_streams_interface()
+{
+    auto first_streams_interface = m_resources_managers[0]->get_default_streams_interface();
+    CHECK_EXPECTED(first_streams_interface);
+#ifndef NDEBUG
+    // Check that all physicall devices has the same interface
+    for (auto &resoucres_manager : m_resources_managers) {
+        auto iface = resoucres_manager->get_default_streams_interface();
+        CHECK_EXPECTED(iface);
+        CHECK_AS_EXPECTED(iface.value() == first_streams_interface.value(), HAILO_INTERNAL_FAILURE,
+            "Not all default stream interfaces are the same");
+    }
+#endif
+    return first_streams_interface;
+}
+
+Expected<uint8_t> VdmaConfigNetworkGroup::get_boundary_channel_index(uint8_t stream_index,
+    hailo_stream_direction_t direction, const std::string &layer_name)
+{
+    // All ResourceManagers shares the same metadata and channels info
+    return m_resources_managers[0]->get_boundary_channel_index(stream_index, direction, layer_name);
+}
+
+hailo_status VdmaConfigNetworkGroup::create_vdevice_streams_from_config_params()
+{
+    if ((m_config_params.latency & HAILO_LATENCY_MEASURE) == HAILO_LATENCY_MEASURE) {
+        if (1 == m_resources_managers.size()) {
+            // Best affort for starting latency meter.
+            auto networks_names = m_network_group_metadata.get_partial_network_names();
+            for (auto &network_name : networks_names) {
+                auto layer_infos = m_network_group_metadata.get_all_layer_infos(network_name);
+                CHECK_EXPECTED_AS_STATUS(layer_infos);
+                auto latency_meter = ConfiguredNetworkGroupBase::create_hw_latency_meter(m_resources_managers[0]->get_device(),
+                    layer_infos.value());
+                if (latency_meter) {
+                    m_latency_meter.emplace(network_name, latency_meter.release());
+                    LOGGER__DEBUG("Starting hw latency measurement for network {}", network_name);
+                }
+            }
+        } else {
+            LOGGER__WARNING("Latency measurement is not supported on more than 1 physical device.");
+        }
+    }
+
+    for (const auto &stream_parameters_pair : m_config_params.stream_params_by_name) {
+        switch (stream_parameters_pair.second.direction) {
+            case HAILO_H2D_STREAM:
+                {
+                    auto status = create_input_vdevice_stream_from_config_params(stream_parameters_pair.second,
+                        stream_parameters_pair.first);
+                    CHECK_SUCCESS(status);
+                }
+                break;
+            case HAILO_D2H_STREAM:
+                {
+                    auto status = create_output_vdevice_stream_from_config_params(stream_parameters_pair.second,
+                        stream_parameters_pair.first);
+                    CHECK_SUCCESS(status);
+                }
+                break;
+            default:
+                LOGGER__ERROR("stream name {} direction is invalid.", stream_parameters_pair.first);
+                return HAILO_INVALID_ARGUMENT;
+        }
+    }
+
+    return HAILO_SUCCESS;
+}
+
+hailo_status VdmaConfigNetworkGroup::create_input_vdevice_stream_from_config_params(const hailo_stream_parameters_t &stream_params,
+    const std::string &stream_name)
+{
+    auto edge_layer = get_layer_info(stream_name);
+    CHECK_EXPECTED_AS_STATUS(edge_layer);
+
+    auto partial_network_name = edge_layer->partial_network_name;
+    auto latency_meter = (contains(m_latency_meter, partial_network_name)) ? m_latency_meter.at(partial_network_name) : nullptr;
+
+    CHECK(HAILO_STREAM_INTERFACE_PCIE == stream_params.stream_interface, HAILO_INVALID_OPERATION,
+        "Only PCIe streams are supported on VDevice usage. {} has {} interface.", stream_name, stream_params.stream_interface);
+    auto input_stream = VDeviceInputStream::create(m_resources_managers, edge_layer.value(),
+        stream_name, m_network_group_activated_event, latency_meter);
+    CHECK_EXPECTED_AS_STATUS(input_stream);
+    m_input_streams.insert(make_pair(stream_name, input_stream.release()));
+
+    return HAILO_SUCCESS;
+}
+
+hailo_status VdmaConfigNetworkGroup::create_output_vdevice_stream_from_config_params(const hailo_stream_parameters_t &stream_params,
+    const std::string &stream_name)
+{
+    auto edge_layer = get_layer_info(stream_name);
+    CHECK_EXPECTED_AS_STATUS(edge_layer);
+
+    auto partial_network_name = edge_layer->partial_network_name;
+    auto latency_meter = (contains(m_latency_meter, partial_network_name)) ? m_latency_meter.at(partial_network_name) : nullptr;
+
+    CHECK(HAILO_STREAM_INTERFACE_PCIE == stream_params.stream_interface, HAILO_INVALID_OPERATION,
+        "Only PCIe streams are supported on VDevice usage. {} has {} interface.", stream_name, stream_params.stream_interface);
+    auto output_stream = VDeviceOutputStream::create(m_resources_managers, edge_layer.value(),
+        stream_name, m_network_group_activated_event, latency_meter);
+    CHECK_EXPECTED_AS_STATUS(output_stream);
+    m_output_streams.insert(make_pair(stream_name, output_stream.release()));
+
+    return HAILO_SUCCESS;
+}
+
+} /* namespace hailort */
diff --git a/hailort/libhailort/src/control.cpp b/hailort/libhailort/src/control.cpp
new file mode 100644 (file)
index 0000000..12cb00f
--- /dev/null
@@ -0,0 +1,3378 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file control.cpp
+ * @brief Implements module which allows controling Hailo chip.
+ **/
+
+#include "common/utils.hpp"
+#include "control.hpp"
+#include "common/logger_macros.hpp"
+#include "control_protocol.h"
+#include "byte_order.h"
+#include "firmware_status.h"
+#include "firmware_header_utils.h"
+#include "d2h_events.h"
+#include "hw_consts.hpp"
+#include <array>
+
+namespace hailort
+{
+
+#ifndef MIN
+#define MIN(x, y) (((x) < (y)) ? (x) : (y))
+#endif
+
+#define OVERCURRENT_PROTECTION_WARNING ( \
+        "Using the overcurrent protection dvm for power measurement will disable the ovecurrent protection.\n" \
+        "If only taking one measurement, the protection will resume automatically.\n" \
+        "If doing continuous measurement, to enable overcurrent protection again you have to stop the power measurement on this dvm." \
+    )
+
+typedef std::array<std::array<float64_t, CONTROL_PROTOCOL__POWER_MEASUREMENT_TYPES__COUNT>, CONTROL_PROTOCOL__DVM_OPTIONS_COUNT> power_conversion_multiplier_t;
+
+
+Expected<hailo_device_identity_t> control__parse_identify_results(CONTROL_PROTOCOL_identify_response_t *identify_response)
+{
+    hailo_device_identity_t board_info;
+
+    CHECK_AS_EXPECTED(nullptr != identify_response, HAILO_INVALID_ARGUMENT);
+
+    /* Store identify response inside control */
+    board_info.protocol_version = BYTE_ORDER__ntohl(identify_response->protocol_version);
+    board_info.logger_version = BYTE_ORDER__ntohl(identify_response->logger_version);
+    (void)memcpy(&(board_info.fw_version),
+            &(identify_response->fw_version),
+            sizeof(board_info.fw_version));
+    board_info.board_name_length = (uint8_t)BYTE_ORDER__ntohl(identify_response->board_name_length);
+    (void)memcpy(&(board_info.board_name),
+            &(identify_response->board_name),
+            BYTE_ORDER__ntohl(identify_response->board_name_length));
+    board_info.serial_number_length = (uint8_t)BYTE_ORDER__ntohl(identify_response->serial_number_length);
+    (void)memcpy(&(board_info.serial_number),
+            &(identify_response->serial_number),
+            BYTE_ORDER__ntohl(identify_response->serial_number_length));
+    board_info.part_number_length = (uint8_t)BYTE_ORDER__ntohl(identify_response->part_number_length);
+    (void)memcpy(&(board_info.part_number),
+        &(identify_response->part_number),
+        BYTE_ORDER__ntohl(identify_response->part_number_length));
+    board_info.product_name_length = (uint8_t)BYTE_ORDER__ntohl(identify_response->product_name_length);
+    (void)memcpy(&(board_info.product_name),
+        &(identify_response->product_name),
+        BYTE_ORDER__ntohl(identify_response->product_name_length));
+
+    /* Check if firmware is at debug/release */
+    board_info.is_release = (!IS_REVISION_DEV(board_info.fw_version.revision));
+
+    // Make sure response was from app CPU
+    CHECK_AS_EXPECTED((0 == (board_info.fw_version.revision & REVISION_APP_CORE_FLAG_BIT_MASK)), HAILO_INVALID_FIRMWARE,
+     "Got invalid app FW type, which means the FW was not marked correctly. unmaked FW revision {}", board_info.fw_version.revision);
+
+    /* Clear debug/release bit */
+    board_info.fw_version.revision = GET_REVISION_NUMBER_VALUE(board_info.fw_version.revision);
+
+    // TODO: Fix static cast, we are assuming they are the same (HRT-3177)
+    board_info.device_architecture = static_cast<hailo_device_architecture_t>(
+        BYTE_ORDER__ntohl(identify_response->device_architecture));
+
+    /* Write identify results to log */
+    LOGGER__INFO("firmware_version is: {}.{}.{}",
+            board_info.fw_version.major,
+            board_info.fw_version.minor,
+            board_info.fw_version.revision
+            );
+    LOGGER__DEBUG("Protocol version: {}", board_info.protocol_version);
+    LOGGER__DEBUG("Logger version: {}", board_info.logger_version);
+    LOGGER__DEBUG("Device architecture code: {}", board_info.device_architecture);
+
+    return board_info;
+}
+
+Expected<hailo_extended_device_information_t> control__parse_get_extended_device_information_results
+        (CONTROL_PROTOCOL__get_extended_device_information_response_t *get_extended_device_information_response)
+{
+    uint8_t local_supported_features;
+    hailo_extended_device_information_t device_info;
+
+    CHECK_AS_EXPECTED(nullptr != get_extended_device_information_response, HAILO_INVALID_ARGUMENT);
+
+    local_supported_features = (uint8_t)BYTE_ORDER__ntohl(get_extended_device_information_response->supported_features);
+
+    device_info.supported_features.ethernet = (local_supported_features &
+                                               (1 << CONTROL_PROTOCOL__SUPPORTED_FEATURES_ETHERNET_BIT_OFFSET)) != 0;
+    device_info.supported_features.pcie = (local_supported_features &
+                                           (1 << CONTROL_PROTOCOL__SUPPORTED_FEATURES_PCIE_BIT_OFFSET)) != 0;
+    device_info.supported_features.mipi = (local_supported_features &
+                                           (1 << CONTROL_PROTOCOL__SUPPORTED_FEATURES_MIPI_BIT_OFFSET)) != 0;
+    device_info.supported_features.current_monitoring = (local_supported_features &
+                                                         (1 << CONTROL_PROTOCOL__SUPPORTED_FEATURES_CURRENT_MONITORING_BIT_OFFSET)) != 0;
+    device_info.supported_features.mdio = (local_supported_features &
+                                           (1 << CONTROL_PROTOCOL__SUPPORTED_FEATURES_MDIO_BIT_OFFSET)) != 0;
+    device_info.neural_network_core_clock_rate = BYTE_ORDER__ntohl(get_extended_device_information_response->neural_network_core_clock_rate);
+
+    LOGGER__DEBUG("Max Neural Network Core Clock Rate: {}", device_info.neural_network_core_clock_rate);
+
+    device_info.boot_source = static_cast<hailo_device_boot_source_t>(
+        BYTE_ORDER__ntohl(get_extended_device_information_response->boot_source));
+
+    (void)memcpy(device_info.soc_id,
+                 get_extended_device_information_response->soc_id,
+                 BYTE_ORDER__ntohl(get_extended_device_information_response->soc_id_length));
+
+    device_info.lcs = get_extended_device_information_response->lcs;
+
+    memcpy(&device_info.unit_level_tracking_id[0], &get_extended_device_information_response->fuse_info, sizeof(device_info.unit_level_tracking_id));
+    memcpy(&device_info.eth_mac_address[0], &get_extended_device_information_response->eth_mac_address[0], BYTE_ORDER__ntohl(get_extended_device_information_response->eth_mac_length));
+    memcpy(&device_info.soc_pm_values, &get_extended_device_information_response->pd_info, sizeof(device_info.soc_pm_values));
+
+    return device_info;
+}
+
+Expected<hailo_health_info_t> control__parse_get_health_information_results
+        (CONTROL_PROTOCOL__get_health_information_response_t *get_health_information_response)
+{
+    hailo_health_info_t health_info;
+
+    CHECK_AS_EXPECTED(nullptr != get_health_information_response, HAILO_INVALID_ARGUMENT);
+
+    health_info.overcurrent_protection_active = get_health_information_response->overcurrent_protection_active;
+    health_info.current_overcurrent_zone = get_health_information_response->current_overcurrent_zone;
+    // Re-convertion to floats after
+    health_info.red_overcurrent_threshold = float32_t(BYTE_ORDER__ntohl(get_health_information_response->red_overcurrent_threshold));
+    health_info.orange_overcurrent_threshold = float32_t(BYTE_ORDER__ntohl(get_health_information_response->orange_overcurrent_threshold));
+    health_info.temperature_throttling_active = get_health_information_response->temperature_throttling_active;
+    health_info.current_temperature_zone = get_health_information_response->current_temperature_zone;
+    health_info.current_temperature_throttling_level = get_health_information_response->current_temperature_throttling_level;
+    memcpy(&health_info.temperature_throttling_levels[0], &get_health_information_response->temperature_throttling_levels[0],
+            BYTE_ORDER__ntohl(get_health_information_response->temperature_throttling_levels_length));
+    health_info.orange_temperature_threshold = BYTE_ORDER__ntohl(get_health_information_response->orange_temperature_threshold);
+    health_info.orange_hysteresis_temperature_threshold = BYTE_ORDER__ntohl(get_health_information_response->orange_hysteresis_temperature_threshold);
+    health_info.red_temperature_threshold = BYTE_ORDER__ntohl(get_health_information_response->red_temperature_threshold);
+    health_info.red_hysteresis_temperature_threshold = BYTE_ORDER__ntohl(get_health_information_response->red_hysteresis_temperature_threshold);
+    return health_info;
+}
+
+
+hailo_status control__parse_core_identify_results(CONTROL_PROTOCOL__core_identify_response_t *identify_response,
+        hailo_core_information_t *core_info)
+{
+    CHECK_ARG_NOT_NULL(core_info);
+    CHECK_ARG_NOT_NULL(identify_response);
+
+    /* Store identify response inside control */
+    (void)memcpy(&(core_info->fw_version),
+            &(identify_response->fw_version),
+            sizeof(core_info->fw_version));
+
+    /* Check if firmware is at debug/release */
+    core_info->is_release = !(IS_REVISION_DEV(core_info->fw_version.revision));
+
+    /* Make sure response was from core CPU */
+    CHECK((REVISION_APP_CORE_FLAG_BIT_MASK == (core_info->fw_version.revision & REVISION_APP_CORE_FLAG_BIT_MASK)), HAILO_INVALID_FIRMWARE,
+        "Got invalid core FW type, which means the FW was not marked correctly. unmaked FW revision {}", core_info->fw_version.revision);
+
+    /* Clear debug/release bit */
+    core_info->fw_version.revision = GET_REVISION_NUMBER_VALUE(core_info->fw_version.revision);
+
+    /* Write identify results to log */
+    LOGGER__INFO("core firmware_version is: {}.{}.{}",
+            core_info->fw_version.major,
+            core_info->fw_version.minor,
+            core_info->fw_version.revision
+            );
+
+    return HAILO_SUCCESS;
+}
+
+
+hailo_status Control::parse_and_validate_response(uint8_t *message, uint32_t message_size,
+    CONTROL_PROTOCOL__response_header_t **header, CONTROL_PROTOCOL__payload_t **payload,
+    CONTROL_PROTOCOL__request_t *request)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    HAILO_COMMON_STATUS_t common_status = HAILO_COMMON_STATUS__UNINITIALIZED;
+    CONTROL_PROTOCOL__status_t fw_status = {};
+    const char *firmware_status_text = NULL;
+
+    /* Parse the response */
+    common_status = CONTROL_PROTOCOL__parse_response(message, message_size, header, payload, &fw_status);
+    if (HAILO_STATUS__CONTROL_PROTOCOL__INVALID_VERSION == common_status) {
+        status = HAILO_UNSUPPORTED_CONTROL_PROTOCOL_VERSION;
+    }
+    else {
+        status = (HAILO_COMMON_STATUS__SUCCESS == common_status) ? HAILO_SUCCESS : HAILO_INTERNAL_FAILURE;
+    }
+    if (HAILO_SUCCESS != status) {
+        goto exit;
+    }
+    /* Valdiate response was succesfull - both major and minor should be error free */
+    if (0 != fw_status.major_status) {
+        status = HAILO_FW_CONTROL_FAILURE;
+        LOGGER__ERROR("Firmware control has failed. Major status: {:#x}, Minor status: {:#x}",
+                fw_status.major_status,
+                fw_status.minor_status);
+        common_status = FIRMWARE_STATUS__get_textual((FIRMWARE_STATUS_t)fw_status.major_status, &firmware_status_text);
+        if (HAILO_COMMON_STATUS__SUCCESS == common_status) {
+            LOGGER__ERROR("Firmware major status: {}", firmware_status_text);
+        } else {
+            LOGGER__ERROR("Cannot find textual address for firmware status {:#x}, common_status = {}",
+                (FIRMWARE_STATUS_t)fw_status.major_status, common_status);
+        }
+        common_status = FIRMWARE_STATUS__get_textual((FIRMWARE_STATUS_t)fw_status.minor_status, &firmware_status_text);
+        if (HAILO_COMMON_STATUS__SUCCESS == common_status) {
+            LOGGER__ERROR("Firmware minor status: {}", firmware_status_text);
+        } else {
+            LOGGER__ERROR("Cannot find textual address for firmware status {:#x}, common_status = {}",
+                (FIRMWARE_STATUS_t)fw_status.minor_status, common_status);
+        }
+
+        if ((HAILO_CONTROL_STATUS_UNSUPPORTED_OPCODE == fw_status.minor_status) ||
+            (HAILO_CONTROL_STATUS_UNSUPPORTED_OPCODE == fw_status.major_status)) {
+            status = HAILO_UNSUPPORTED_OPCODE;
+            LOGGER__ERROR("Opcode {} is not supported",
+                CONTROL_PROTOCOL__get_textual_opcode((CONTROL_PROTOCOL__OPCODE_t)BYTE_ORDER__ntohl(request->header.common_header.opcode)));
+        }
+        goto exit;
+    }
+
+    /* Validate response opcode is same as request */
+    if (request->header.common_header.opcode != (*header)->common_header.opcode) {
+        status = HAILO_INVALID_CONTROL_RESPONSE;
+        LOGGER__ERROR("Invalid opcode received from FW");
+        goto exit;
+    }
+
+    /* Validate response version is same as request */
+    if (request->header.common_header.version != (*header)->common_header.version) {
+        status = HAILO_INVALID_CONTROL_RESPONSE;
+        LOGGER__ERROR("Invalid protocol version received from FW");
+        goto exit;
+    }
+
+    status = HAILO_SUCCESS;
+exit:
+    return status;
+}
+
+Expected<hailo_device_identity_t> Control::identify(Device &device)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    HAILO_COMMON_STATUS_t common_status = HAILO_COMMON_STATUS__UNINITIALIZED;
+    CONTROL_PROTOCOL__request_t request = {};
+    size_t request_size = 0;
+    uint8_t response_buffer[RESPONSE_MAX_BUFFER_SIZE] = {};
+    size_t response_size = RESPONSE_MAX_BUFFER_SIZE;
+    CONTROL_PROTOCOL__response_header_t *header = NULL;
+    CONTROL_PROTOCOL__payload_t *payload = NULL;
+    CONTROL_PROTOCOL_identify_response_t *identify_response = NULL;
+
+    /* Validate arguments */
+    common_status = CONTROL_PROTOCOL__pack_identify_request(&request, &request_size, device.get_control_sequence());
+    status = (HAILO_COMMON_STATUS__SUCCESS == common_status) ? HAILO_SUCCESS : HAILO_INTERNAL_FAILURE;
+    CHECK_SUCCESS_AS_EXPECTED(status);
+
+    status = device.fw_interact((uint8_t*)(&request), request_size, (uint8_t*)&response_buffer, &response_size);
+    CHECK_SUCCESS_AS_EXPECTED(status);
+
+    /* Parse response */
+    status = parse_and_validate_response(response_buffer, (uint32_t)(response_size), &header, &payload,
+            &request);
+    CHECK_SUCCESS_AS_EXPECTED(status);
+    identify_response = (CONTROL_PROTOCOL_identify_response_t *)(payload->parameters);
+
+    return control__parse_identify_results(identify_response);
+}
+
+hailo_status Control::core_identify(Device &device, hailo_core_information_t *core_info)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    HAILO_COMMON_STATUS_t common_status = HAILO_COMMON_STATUS__UNINITIALIZED;
+    CONTROL_PROTOCOL__request_t request = {};
+    size_t request_size = 0;
+    uint8_t response_buffer[RESPONSE_MAX_BUFFER_SIZE] = {};
+    size_t response_size = RESPONSE_MAX_BUFFER_SIZE;
+    CONTROL_PROTOCOL__response_header_t *header = NULL;
+    CONTROL_PROTOCOL__payload_t *payload = NULL;
+    CONTROL_PROTOCOL__core_identify_response_t *identify_response = NULL;
+
+    /* Validate arguments */
+    CHECK_ARG_NOT_NULL(core_info);
+
+    common_status = CONTROL_PROTOCOL__pack_core_identify_request(&request, &request_size, device.get_control_sequence());
+    status = (HAILO_COMMON_STATUS__SUCCESS == common_status) ? HAILO_SUCCESS : HAILO_INTERNAL_FAILURE;
+    if (HAILO_SUCCESS != status) {
+        goto exit;
+    }
+
+    status = device.fw_interact((uint8_t*)(&request), request_size, (uint8_t*)&response_buffer, &response_size);
+    if (HAILO_SUCCESS != status) {
+        goto exit;
+    }
+
+    /* Parse response */
+    status = parse_and_validate_response(response_buffer, (uint32_t)(response_size), &header, &payload,
+            &request);
+    if (HAILO_SUCCESS != status) {
+        goto exit;
+    }
+    identify_response = (CONTROL_PROTOCOL__core_identify_response_t *)(payload->parameters);
+
+    /* Store results inside contol object */
+    status = control__parse_core_identify_results(identify_response, core_info);
+    if (HAILO_SUCCESS != status) {
+        goto exit;
+    }
+
+    status = HAILO_SUCCESS;
+exit:
+    return status;
+}
+
+
+hailo_status Control::set_fw_logger(Device &device, hailo_fw_logger_level_t level, uint32_t interface_mask)
+{
+    CONTROL_PROTOCOL__request_t request = {};
+    size_t request_size = 0;
+
+    auto common_status = CONTROL_PROTOCOL__pack_set_fw_logger_request(&request, &request_size, device.get_control_sequence(), level,
+        static_cast<uint8_t>(interface_mask));
+
+    auto status = (HAILO_COMMON_STATUS__SUCCESS == common_status) ? HAILO_SUCCESS : HAILO_INTERNAL_FAILURE;
+    CHECK_SUCCESS(status);
+
+    uint8_t response_buffer[RESPONSE_MAX_BUFFER_SIZE] = {};
+    size_t response_size = sizeof(response_buffer);
+    status = device.fw_interact((uint8_t*)(&request), request_size, (uint8_t*)&response_buffer, &response_size);
+    CHECK_SUCCESS(status);
+
+    /* Parse response */
+    CONTROL_PROTOCOL__response_header_t *header = NULL;
+    CONTROL_PROTOCOL__payload_t *payload = NULL;
+    status = parse_and_validate_response(response_buffer, (uint32_t)(response_size), &header, &payload,
+            &request);
+    CHECK_SUCCESS(status);
+
+    return HAILO_SUCCESS;
+}
+
+hailo_status Control::set_clock_freq(Device &device, uint32_t clock_freq)
+{
+    CONTROL_PROTOCOL__request_t request = {};
+    size_t request_size = 0;
+
+    auto common_status = CONTROL_PROTOCOL__pack_set_clock_freq_request(&request, &request_size, device.get_control_sequence(), clock_freq);
+
+    auto status = (HAILO_COMMON_STATUS__SUCCESS == common_status) ? HAILO_SUCCESS : HAILO_INTERNAL_FAILURE;
+    CHECK_SUCCESS(status);
+
+    uint8_t response_buffer[RESPONSE_MAX_BUFFER_SIZE] = {};
+    size_t response_size = sizeof(response_buffer);
+    status = device.fw_interact((uint8_t*)(&request), request_size, (uint8_t*)&response_buffer, &response_size);
+    CHECK_SUCCESS(status);
+
+    /* Parse response */
+    CONTROL_PROTOCOL__response_header_t *header = NULL;
+    CONTROL_PROTOCOL__payload_t *payload = NULL;
+    status = parse_and_validate_response(response_buffer, (uint32_t)(response_size), &header, &payload,
+            &request);
+    CHECK_SUCCESS(status);
+
+    return HAILO_SUCCESS;
+}
+
+hailo_status Control::set_throttling_state(Device &device, bool should_activate)
+{
+    CONTROL_PROTOCOL__request_t request = {};
+    size_t request_size = 0;
+
+    auto common_status = CONTROL_PROTOCOL__pack_set_throttling_state_request(&request, &request_size, device.get_control_sequence(), should_activate);
+
+    auto status = (HAILO_COMMON_STATUS__SUCCESS == common_status) ? HAILO_SUCCESS : HAILO_INTERNAL_FAILURE;
+    CHECK_SUCCESS(status);
+
+    uint8_t response_buffer[RESPONSE_MAX_BUFFER_SIZE] = {};
+    size_t response_size = sizeof(response_buffer);
+    status = device.fw_interact((uint8_t*)(&request), request_size, (uint8_t*)&response_buffer, &response_size);
+    CHECK_SUCCESS(status);
+
+    /* Parse response */
+    CONTROL_PROTOCOL__response_header_t *header = NULL;
+    CONTROL_PROTOCOL__payload_t *payload = NULL;
+    status = parse_and_validate_response(response_buffer, (uint32_t)(response_size), &header, &payload,
+            &request);
+    CHECK_SUCCESS(status);
+
+    return HAILO_SUCCESS;
+}
+
+Expected<bool> Control::get_throttling_state(Device &device)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    HAILO_COMMON_STATUS_t common_status = HAILO_COMMON_STATUS__UNINITIALIZED;
+    CONTROL_PROTOCOL__request_t request = {};
+    size_t request_size = 0;
+    uint8_t response_buffer[RESPONSE_MAX_BUFFER_SIZE] = {};
+    size_t response_size = RESPONSE_MAX_BUFFER_SIZE;
+    CONTROL_PROTOCOL__response_header_t *header = NULL;
+    CONTROL_PROTOCOL__payload_t *payload = NULL;
+    CONTROL_PROTOCOL__get_throttling_state_response_t *get_throttling_state_response = NULL;
+
+    common_status = CONTROL_PROTOCOL__pack_get_throttling_state_request(&request, &request_size, device.get_control_sequence());
+
+    status = (HAILO_COMMON_STATUS__SUCCESS == common_status) ? HAILO_SUCCESS : HAILO_INTERNAL_FAILURE;
+    CHECK_SUCCESS_AS_EXPECTED(status);
+
+    status = device.fw_interact((uint8_t*)(&request), request_size, (uint8_t*)&response_buffer, &response_size);
+    CHECK_SUCCESS_AS_EXPECTED(status);
+
+    /* Parse response */
+    status = parse_and_validate_response(response_buffer, (uint32_t)(response_size), &header, &payload, &request);
+    CHECK_SUCCESS_AS_EXPECTED(status);
+
+    get_throttling_state_response = (CONTROL_PROTOCOL__get_throttling_state_response_t *)(payload->parameters);
+    return std::move(get_throttling_state_response->is_active);
+}
+
+hailo_status Control::set_overcurrent_state(Device &device, bool should_activate)
+{
+    CONTROL_PROTOCOL__request_t request = {};
+    size_t request_size = 0;
+
+    auto common_status = CONTROL_PROTOCOL__pack_set_overcurrent_state_request(&request, &request_size, device.get_control_sequence(), should_activate);
+
+    auto status = (HAILO_COMMON_STATUS__SUCCESS == common_status) ? HAILO_SUCCESS : HAILO_INTERNAL_FAILURE;
+    CHECK_SUCCESS(status);
+
+    uint8_t response_buffer[RESPONSE_MAX_BUFFER_SIZE] = {};
+    size_t response_size = sizeof(response_buffer);
+    status = device.fw_interact((uint8_t*)(&request), request_size, (uint8_t*)&response_buffer, &response_size);
+    CHECK_SUCCESS(status);
+
+    /* Parse response */
+    CONTROL_PROTOCOL__response_header_t *header = NULL;
+    CONTROL_PROTOCOL__payload_t *payload = NULL;
+    status = parse_and_validate_response(response_buffer, (uint32_t)(response_size), &header, &payload, &request);
+    CHECK_SUCCESS(status);
+
+    return HAILO_SUCCESS;
+}
+
+Expected<bool> Control::get_overcurrent_state(Device &device)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    HAILO_COMMON_STATUS_t common_status = HAILO_COMMON_STATUS__UNINITIALIZED;
+    CONTROL_PROTOCOL__request_t request = {};
+    size_t request_size = 0;
+    uint8_t response_buffer[RESPONSE_MAX_BUFFER_SIZE] = {};
+    size_t response_size = RESPONSE_MAX_BUFFER_SIZE;
+    CONTROL_PROTOCOL__response_header_t *header = NULL;
+    CONTROL_PROTOCOL__payload_t *payload = NULL;
+    CONTROL_PROTOCOL__get_overcurrent_state_response_t *get_overcurrent_state_response = NULL;
+
+    common_status = CONTROL_PROTOCOL__pack_get_overcurrent_state_request(&request, &request_size, device.get_control_sequence());
+
+    status = (HAILO_COMMON_STATUS__SUCCESS == common_status) ? HAILO_SUCCESS : HAILO_INTERNAL_FAILURE;
+    CHECK_SUCCESS_AS_EXPECTED(status);
+
+    status = device.fw_interact((uint8_t*)(&request), request_size, (uint8_t*)&response_buffer, &response_size);
+    CHECK_SUCCESS_AS_EXPECTED(status);
+
+    /* Parse response */
+    status = parse_and_validate_response(response_buffer, (uint32_t)(response_size), &header, &payload, &request);
+    CHECK_SUCCESS_AS_EXPECTED(status);
+
+    get_overcurrent_state_response = (CONTROL_PROTOCOL__get_overcurrent_state_response_t *)(payload->parameters);
+    return std::move(get_overcurrent_state_response->is_required);
+}
+
+hailo_status Control::write_memory_chunk(Device &device, uint32_t address, const uint8_t *data, uint32_t chunk_size)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    HAILO_COMMON_STATUS_t common_status = HAILO_COMMON_STATUS__UNINITIALIZED;
+    CONTROL_PROTOCOL__request_t request = {};
+    size_t request_size = 0;
+    uint8_t response_buffer[RESPONSE_MAX_BUFFER_SIZE] = {};
+    size_t response_size = RESPONSE_MAX_BUFFER_SIZE;
+    CONTROL_PROTOCOL__response_header_t *header = NULL;
+    CONTROL_PROTOCOL__payload_t *payload = NULL;
+
+
+    /* Validate arguments */
+    ASSERT(NULL != data);
+
+    /* Validate chunk size is valid */
+    ASSERT(CONTROL__MAX_WRITE_MEMORY_CHUNK_SIZE >= chunk_size);
+    ASSERT(0 != chunk_size);
+
+    common_status = CONTROL_PROTOCOL__pack_write_memory_request(&request, &request_size, device.get_control_sequence(), address, data, chunk_size);
+    status = (HAILO_COMMON_STATUS__SUCCESS == common_status) ? HAILO_SUCCESS : HAILO_INTERNAL_FAILURE;
+    if (HAILO_SUCCESS != status) {
+        goto exit;
+    }
+
+    status = device.fw_interact((uint8_t*)(&request), request_size, (uint8_t*)&response_buffer, &response_size);
+    if (HAILO_SUCCESS != status) {
+        goto exit;
+    }
+
+    /* Parse response */
+    status = parse_and_validate_response(response_buffer, (uint32_t)(response_size), &header, &payload,
+            &request);
+    if (HAILO_SUCCESS != status) {
+        goto exit;
+    }
+
+    status = HAILO_SUCCESS;
+exit:
+    return status;
+}
+
+hailo_status Control::write_memory(Device &device, uint32_t address, const uint8_t *data, uint32_t data_length)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+
+    uint32_t current_write_address = address;
+    const uint8_t* current_data_address = data;
+    uint32_t chunk_size = CONTROL__MAX_WRITE_MEMORY_CHUNK_SIZE;
+    uint32_t number_of_chunks = data_length / chunk_size;
+    uint32_t data_chunk_leftover = data_length % chunk_size;
+
+    /* Validate arguments */
+    CHECK_ARG_NOT_NULL(data);
+
+    if (data_length >= chunk_size) {
+        for (size_t i = 0; i < number_of_chunks; i++ ) {
+            /* Write current memory chunk */
+            status = write_memory_chunk(device, current_write_address, current_data_address, chunk_size);
+            CHECK_SUCCESS(status);
+
+            current_write_address += chunk_size;
+            current_data_address += chunk_size;
+        }
+    }
+
+    if (data_chunk_leftover > 0) {
+        /* Write leftover */
+        status = write_memory_chunk(device, current_write_address, current_data_address, data_chunk_leftover);
+        CHECK_SUCCESS(status);
+    }
+
+    return HAILO_SUCCESS;
+}
+
+hailo_status Control::read_memory_chunk(Device &device, uint32_t address, uint8_t *data, uint32_t chunk_size)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    HAILO_COMMON_STATUS_t common_status = HAILO_COMMON_STATUS__UNINITIALIZED;
+    CONTROL_PROTOCOL__request_t request = {};
+    size_t request_size = 0;
+    uint8_t response_buffer[RESPONSE_MAX_BUFFER_SIZE] = {};
+    size_t response_size = RESPONSE_MAX_BUFFER_SIZE;
+    uint32_t actual_read_data_length = 0;
+    CONTROL_PROTOCOL__response_header_t *header = NULL;
+    CONTROL_PROTOCOL__payload_t *payload = NULL;
+    CONTROL_PROTOCOL__read_memory_response_t *read_memory_response = NULL;
+
+
+    /* Validate arguments */
+    ASSERT(NULL != data);
+
+
+    /* Validate chunk size is valid */
+    ASSERT(CONTROL__MAX_WRITE_MEMORY_CHUNK_SIZE >= chunk_size);
+    ASSERT(0 != chunk_size);
+
+    common_status = CONTROL_PROTOCOL__pack_read_memory_request(&request, &request_size, device.get_control_sequence(), address, chunk_size);
+    status = (HAILO_COMMON_STATUS__SUCCESS == common_status) ? HAILO_SUCCESS : HAILO_INTERNAL_FAILURE;
+    if (HAILO_SUCCESS != status) {
+        goto exit;
+    }
+
+    status = device.fw_interact((uint8_t*)(&request), request_size, (uint8_t*)&response_buffer, &response_size);
+    if (HAILO_SUCCESS != status) {
+        goto exit;
+    }
+
+    /* Parse response */
+    status = parse_and_validate_response(response_buffer, (uint32_t)(response_size), &header, &payload,
+            &request);
+    if (HAILO_SUCCESS != status) {
+        goto exit;
+    }
+
+    read_memory_response = (CONTROL_PROTOCOL__read_memory_response_t *)(payload->parameters);
+    actual_read_data_length = BYTE_ORDER__ntohl(read_memory_response->data_length);
+    if (chunk_size != actual_read_data_length) {
+        status = HAILO_INVALID_CONTROL_RESPONSE;
+        LOGGER__ERROR("Did not read all data from control response");
+        goto exit;
+    }
+    (void)memcpy(data, &read_memory_response->data[0], actual_read_data_length);
+
+    status = HAILO_SUCCESS;
+exit:
+    return status;
+}
+
+hailo_status Control::read_memory(Device &device, uint32_t address, uint8_t *data, uint32_t data_length)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+
+    uint32_t current_read_address = address;
+    uint8_t* current_data_address = data;
+    uint32_t chunk_size = CONTROL__MAX_WRITE_MEMORY_CHUNK_SIZE;
+    uint32_t number_of_chunks = data_length / chunk_size;
+    uint32_t data_chunk_leftover = data_length % chunk_size;
+
+    /* Validate arguments */
+    CHECK_ARG_NOT_NULL(data);
+
+    if (data_length >= chunk_size) {
+        for (size_t i = 0; i < number_of_chunks; i++ ) {
+            /* Read current memory chunk */
+            status = read_memory_chunk(device, current_read_address, current_data_address, chunk_size);
+            CHECK_SUCCESS(status);
+
+            current_read_address += chunk_size;
+            current_data_address += chunk_size;
+        }
+    }
+
+    if (data_chunk_leftover > 0) {
+        /* Read leftover */
+        status = read_memory_chunk(device, current_read_address, current_data_address, data_chunk_leftover);
+        CHECK_SUCCESS(status);
+    }
+
+    return HAILO_SUCCESS;
+}
+
+hailo_status Control::open_stream(Device &device, uint8_t dataflow_manager_id, bool is_input)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    HAILO_COMMON_STATUS_t common_status = HAILO_COMMON_STATUS__UNINITIALIZED;
+    CONTROL_PROTOCOL__request_t request = {};
+    size_t request_size = 0;
+    uint8_t response_buffer[RESPONSE_MAX_BUFFER_SIZE] = {};
+    size_t response_size = RESPONSE_MAX_BUFFER_SIZE;
+    CONTROL_PROTOCOL__response_header_t *header = NULL;
+    CONTROL_PROTOCOL__payload_t *payload = NULL;
+
+    common_status = CONTROL_PROTOCOL__pack_open_stream_request(&request, &request_size, device.get_control_sequence(),
+        dataflow_manager_id, is_input);
+    status = (HAILO_COMMON_STATUS__SUCCESS == common_status) ? HAILO_SUCCESS : HAILO_INTERNAL_FAILURE;
+    if (HAILO_SUCCESS != status) {
+        goto exit;
+    }
+
+    status = device.fw_interact((uint8_t*)(&request), request_size, (uint8_t*)&response_buffer, &response_size);
+    if (HAILO_SUCCESS != status) {
+        goto exit;
+    }
+
+    /* Parse response */
+    status = parse_and_validate_response(response_buffer, (uint32_t)(response_size), &header, &payload,
+            &request);
+    if (HAILO_SUCCESS != status) {
+        goto exit;
+    }
+
+    status = HAILO_SUCCESS;
+exit:
+    return status;
+}
+
+hailo_status Control::close_stream(Device &device, uint8_t dataflow_manager_id, bool is_input)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    HAILO_COMMON_STATUS_t common_status = HAILO_COMMON_STATUS__UNINITIALIZED;
+    CONTROL_PROTOCOL__request_t request = {};
+    size_t request_size = 0;
+    uint8_t response_buffer[RESPONSE_MAX_BUFFER_SIZE] = {};
+    size_t response_size = RESPONSE_MAX_BUFFER_SIZE;
+    CONTROL_PROTOCOL__response_header_t *header = NULL;
+    CONTROL_PROTOCOL__payload_t *payload = NULL;
+
+    common_status = CONTROL_PROTOCOL__pack_close_stream_request(&request, &request_size, device.get_control_sequence(),
+        dataflow_manager_id, is_input);
+    status = (HAILO_COMMON_STATUS__SUCCESS == common_status) ? HAILO_SUCCESS : HAILO_INTERNAL_FAILURE;
+    if (HAILO_SUCCESS != status) {
+        goto exit;
+    }
+
+    status = device.fw_interact((uint8_t*)(&request), request_size, (uint8_t*)&response_buffer, &response_size);
+    if (HAILO_SUCCESS != status) {
+        goto exit;
+    }
+
+    /* Parse response */
+    status = parse_and_validate_response(response_buffer, (uint32_t)(response_size), &header, &payload,
+            &request);
+    if (HAILO_SUCCESS != status) {
+        goto exit;
+    }
+
+    status = HAILO_SUCCESS;
+exit:
+    return status;
+}
+
+hailo_status Control::close_all_streams(Device &device)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+
+    /* Close all input streams */
+    status = close_stream(device, CONTROL_PROTOCOL__ALL_DATAFLOW_MANAGERS, true);
+    if (HAILO_SUCCESS != status) {
+        goto exit;
+    }
+
+    /* Close all output streams */
+    status = close_stream(device, CONTROL_PROTOCOL__ALL_DATAFLOW_MANAGERS, false);
+    if (HAILO_SUCCESS != status) {
+        goto exit;
+    }
+
+    status = HAILO_SUCCESS;
+exit:
+    return status;
+}
+
+hailo_status Control::config_stream_udp_input(Device &device, CONTROL_PROTOCOL__config_stream_params_t *params, uint8_t &dataflow_manager_id)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    HAILO_COMMON_STATUS_t common_status = HAILO_COMMON_STATUS__UNINITIALIZED;
+    CONTROL_PROTOCOL__request_t request = {};
+    size_t request_size = 0;
+    uint8_t response_buffer[RESPONSE_MAX_BUFFER_SIZE] = {};
+    size_t response_size = RESPONSE_MAX_BUFFER_SIZE;
+    CONTROL_PROTOCOL__response_header_t *header = NULL;
+    CONTROL_PROTOCOL__payload_t *payload = NULL;
+    CONTROL_PROTOCOL__config_stream_response_t *response = NULL;
+    uint32_t dataflow_manager_id_length = 0;
+
+    /* Validate arguments */
+    CHECK_ARG_NOT_NULL(params);
+
+    common_status = CONTROL_PROTOCOL__pack_config_stream_udp_input_request(&request, &request_size,
+        device.get_control_sequence(), params);
+    status = (HAILO_COMMON_STATUS__SUCCESS == common_status) ? HAILO_SUCCESS : HAILO_INTERNAL_FAILURE;
+    if (HAILO_SUCCESS != status) {
+        goto exit;
+    }
+
+    status = device.fw_interact((uint8_t*)(&request), request_size, (uint8_t*)&response_buffer, &response_size);
+    if (HAILO_SUCCESS != status) {
+        goto exit;
+    }
+
+    /* Parse response */
+    status = parse_and_validate_response(response_buffer, (uint32_t)(response_size), &header, &payload,
+            &request);
+    if (HAILO_SUCCESS != status) {
+        goto exit;
+    }
+
+    response = (CONTROL_PROTOCOL__config_stream_response_t *)(payload->parameters);
+    dataflow_manager_id_length = BYTE_ORDER__ntohl(response->dataflow_manager_id_length);
+
+    /* Validate read data is data size */
+    if (dataflow_manager_id_length != sizeof(response->dataflow_manager_id)) {
+        status = HAILO_INVALID_CONTROL_RESPONSE;
+        goto exit;
+    }
+
+    dataflow_manager_id = response->dataflow_manager_id;
+
+    status = HAILO_SUCCESS;
+exit:
+    return status;
+}
+
+hailo_status Control::config_stream_udp_output(Device &device, CONTROL_PROTOCOL__config_stream_params_t *params, uint8_t &dataflow_manager_id)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    HAILO_COMMON_STATUS_t common_status = HAILO_COMMON_STATUS__UNINITIALIZED;
+    CONTROL_PROTOCOL__request_t request = {};
+    size_t request_size = 0;
+    uint8_t response_buffer[RESPONSE_MAX_BUFFER_SIZE] = {};
+    size_t response_size = RESPONSE_MAX_BUFFER_SIZE;
+    CONTROL_PROTOCOL__response_header_t *header = NULL;
+    CONTROL_PROTOCOL__payload_t *payload = NULL;
+    CONTROL_PROTOCOL__config_stream_response_t *response = NULL;
+    uint32_t dataflow_manager_id_length = 0;
+
+    /* Validate arguments */
+    CHECK_ARG_NOT_NULL(params);
+
+    common_status = CONTROL_PROTOCOL__pack_config_stream_udp_output_request(&request, &request_size,
+        device.get_control_sequence(), params);
+    status = (HAILO_COMMON_STATUS__SUCCESS == common_status) ? HAILO_SUCCESS : HAILO_INTERNAL_FAILURE;
+    if (HAILO_SUCCESS != status) {
+        goto exit;
+    }
+
+    status = device.fw_interact((uint8_t*)(&request), request_size, (uint8_t*)&response_buffer, &response_size);
+    if (HAILO_SUCCESS != status) {
+        goto exit;
+    }
+
+    /* Parse response */
+    status = parse_and_validate_response(response_buffer, (uint32_t)(response_size), &header, &payload,
+            &request);
+    if (HAILO_SUCCESS != status) {
+        goto exit;
+    }
+
+    response = (CONTROL_PROTOCOL__config_stream_response_t *)(payload->parameters);
+    dataflow_manager_id_length = BYTE_ORDER__ntohl(response->dataflow_manager_id_length);
+
+    /* Validate read data is data size */
+    if (dataflow_manager_id_length != sizeof(response->dataflow_manager_id)) {
+        status = HAILO_INVALID_CONTROL_RESPONSE;
+        goto exit;
+    }
+
+    dataflow_manager_id = response->dataflow_manager_id;
+
+    status = HAILO_SUCCESS;
+exit:
+    return status;
+}
+
+hailo_status Control::config_stream_mipi_input(Device &device, CONTROL_PROTOCOL__config_stream_params_t *params, uint8_t &dataflow_manager_id)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    HAILO_COMMON_STATUS_t common_status = HAILO_COMMON_STATUS__UNINITIALIZED;
+    CONTROL_PROTOCOL__request_t request = {};
+    size_t request_size = 0;
+    uint8_t response_buffer[RESPONSE_MAX_BUFFER_SIZE] = {};
+    size_t response_size = RESPONSE_MAX_BUFFER_SIZE;
+    CONTROL_PROTOCOL__response_header_t *header = NULL;
+    CONTROL_PROTOCOL__payload_t *payload = NULL;
+    CONTROL_PROTOCOL__config_stream_response_t *response = NULL;
+    uint32_t dataflow_manager_id_length = 0;
+
+    /* Validate arguments */
+    CHECK_ARG_NOT_NULL(params);
+
+    common_status = CONTROL_PROTOCOL__pack_config_stream_mipi_input_request(&request, &request_size,
+        device.get_control_sequence(), params);
+    status = (HAILO_COMMON_STATUS__SUCCESS == common_status) ? HAILO_SUCCESS : HAILO_INTERNAL_FAILURE;
+    if (HAILO_SUCCESS != status) {
+        goto exit;
+    }
+
+    status = device.fw_interact((uint8_t*)(&request), request_size, (uint8_t*)&response_buffer, &response_size);
+    if (HAILO_SUCCESS != status) {
+        goto exit;
+    }
+
+    /* Parse response */
+    status = parse_and_validate_response(response_buffer, (uint32_t)(response_size), &header, &payload,
+            &request);
+    if (HAILO_SUCCESS != status) {
+        goto exit;
+    }
+
+    response = (CONTROL_PROTOCOL__config_stream_response_t *)(payload->parameters);
+    dataflow_manager_id_length = BYTE_ORDER__ntohl(response->dataflow_manager_id_length);
+
+    /* Validate read data is data size */
+    if (dataflow_manager_id_length != sizeof(response->dataflow_manager_id)) {
+        status = HAILO_INVALID_CONTROL_RESPONSE;
+        goto exit;
+    }
+
+    dataflow_manager_id = response->dataflow_manager_id;
+
+    status = HAILO_SUCCESS;
+exit:
+    return status;
+}
+
+hailo_status Control::config_stream_mipi_output(Device &device, CONTROL_PROTOCOL__config_stream_params_t *params, uint8_t &dataflow_manager_id)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    HAILO_COMMON_STATUS_t common_status = HAILO_COMMON_STATUS__UNINITIALIZED;
+    CONTROL_PROTOCOL__request_t request = {};
+    size_t request_size = 0;
+    uint8_t response_buffer[RESPONSE_MAX_BUFFER_SIZE] = {};
+    size_t response_size = RESPONSE_MAX_BUFFER_SIZE;
+    CONTROL_PROTOCOL__response_header_t *header = NULL;
+    CONTROL_PROTOCOL__payload_t *payload = NULL;
+    CONTROL_PROTOCOL__config_stream_response_t *response = NULL;
+    uint32_t dataflow_manager_id_length = 0;
+
+    /* Validate arguments */
+    CHECK_ARG_NOT_NULL(params);
+
+    common_status = CONTROL_PROTOCOL__pack_config_stream_mipi_output_request(&request, &request_size,
+        device.get_control_sequence(), params);
+    status = (HAILO_COMMON_STATUS__SUCCESS == common_status) ? HAILO_SUCCESS : HAILO_INTERNAL_FAILURE;
+    if (HAILO_SUCCESS != status) {
+        goto exit;
+    }
+
+    status = device.fw_interact((uint8_t*)(&request), request_size, (uint8_t*)&response_buffer, &response_size);
+    if (HAILO_SUCCESS != status) {
+        goto exit;
+    }
+
+    /* Parse response */
+    status = parse_and_validate_response(response_buffer, (uint32_t)(response_size), &header, &payload,
+            &request);
+    if (HAILO_SUCCESS != status) {
+        goto exit;
+    }
+
+    response = (CONTROL_PROTOCOL__config_stream_response_t *)(payload->parameters);
+    dataflow_manager_id_length = BYTE_ORDER__ntohl(response->dataflow_manager_id_length);
+
+    /* Validate read data is data size */
+    if (dataflow_manager_id_length != sizeof(response->dataflow_manager_id)) {
+        status = HAILO_INVALID_CONTROL_RESPONSE;
+        goto exit;
+    }
+
+    dataflow_manager_id = response->dataflow_manager_id;
+
+    status = HAILO_SUCCESS;
+exit:
+    return status;
+}
+
+hailo_status Control::config_stream_pcie_input(Device &device, CONTROL_PROTOCOL__config_stream_params_t *params, uint8_t &dataflow_manager_id)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    HAILO_COMMON_STATUS_t common_status = HAILO_COMMON_STATUS__UNINITIALIZED;
+    CONTROL_PROTOCOL__request_t request = {};
+    size_t request_size = 0;
+    uint8_t response_buffer[RESPONSE_MAX_BUFFER_SIZE] = {};
+    size_t response_size = RESPONSE_MAX_BUFFER_SIZE;
+    CONTROL_PROTOCOL__response_header_t *header = NULL;
+    CONTROL_PROTOCOL__payload_t *payload = NULL;
+    CONTROL_PROTOCOL__config_stream_response_t *response = NULL;
+    uint32_t dataflow_manager_id_length = 0;
+
+    /* Validate arguments */
+    CHECK_ARG_NOT_NULL(params);
+
+    common_status = CONTROL_PROTOCOL__pack_config_stream_pcie_input_request(&request, &request_size,
+        device.get_control_sequence(), params);
+    status = (HAILO_COMMON_STATUS__SUCCESS == common_status) ? HAILO_SUCCESS : HAILO_INTERNAL_FAILURE;
+    if (HAILO_SUCCESS != status) {
+        goto exit;
+    }
+
+    status = device.fw_interact((uint8_t*)(&request), request_size, (uint8_t*)&response_buffer, &response_size);
+    if (HAILO_SUCCESS != status) {
+        goto exit;
+    }
+
+    /* Parse response */
+    status = parse_and_validate_response(response_buffer, (uint32_t)(response_size), &header, &payload,
+            &request);
+    if (HAILO_SUCCESS != status) {
+        goto exit;
+    }
+
+    response = (CONTROL_PROTOCOL__config_stream_response_t *)(payload->parameters);
+    dataflow_manager_id_length = BYTE_ORDER__ntohl(response->dataflow_manager_id_length);
+
+    /* Validate read data is data size */
+    if (dataflow_manager_id_length != sizeof(response->dataflow_manager_id)) {
+        status = HAILO_INVALID_CONTROL_RESPONSE;
+        goto exit;
+    }
+
+    dataflow_manager_id = response->dataflow_manager_id;
+
+    status = HAILO_SUCCESS;
+exit:
+    return status;
+}
+
+hailo_status Control::config_stream_pcie_output(Device &device, CONTROL_PROTOCOL__config_stream_params_t *params, uint8_t &dataflow_manager_id)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    HAILO_COMMON_STATUS_t common_status = HAILO_COMMON_STATUS__UNINITIALIZED;
+    CONTROL_PROTOCOL__request_t request = {};
+    size_t request_size = 0;
+    uint8_t response_buffer[RESPONSE_MAX_BUFFER_SIZE] = {};
+    size_t response_size = RESPONSE_MAX_BUFFER_SIZE;
+    CONTROL_PROTOCOL__response_header_t *header = NULL;
+    CONTROL_PROTOCOL__payload_t *payload = NULL;
+    CONTROL_PROTOCOL__config_stream_response_t *response = NULL;
+    uint32_t dataflow_manager_id_length = 0;
+
+    /* Validate arguments */
+    CHECK_ARG_NOT_NULL(params);
+
+    common_status = CONTROL_PROTOCOL__pack_config_stream_pcie_output_request(&request, &request_size,
+        device.get_control_sequence(), params);
+    status = (HAILO_COMMON_STATUS__SUCCESS == common_status) ? HAILO_SUCCESS : HAILO_INTERNAL_FAILURE;
+    if (HAILO_SUCCESS != status) {
+        goto exit;
+    }
+
+    status = device.fw_interact((uint8_t*)(&request), request_size, (uint8_t*)&response_buffer, &response_size);
+    if (HAILO_SUCCESS != status) {
+        goto exit;
+    }
+
+    /* Parse response */
+    status = parse_and_validate_response(response_buffer, (uint32_t)(response_size), &header, &payload,
+            &request);
+    if (HAILO_SUCCESS != status) {
+        goto exit;
+    }
+
+    response = (CONTROL_PROTOCOL__config_stream_response_t *)(payload->parameters);
+    dataflow_manager_id_length = BYTE_ORDER__ntohl(response->dataflow_manager_id_length);
+
+    /* Validate read data is data size */
+    if (dataflow_manager_id_length != sizeof(response->dataflow_manager_id)) {
+        status = HAILO_INVALID_CONTROL_RESPONSE;
+        goto exit;
+    }
+
+    dataflow_manager_id = response->dataflow_manager_id;
+
+
+    status = HAILO_SUCCESS;
+exit:
+    return status;
+}
+
+// TODO: needed?
+hailo_status Control::power_measurement(Device &device, CONTROL_PROTOCOL__dvm_options_t dvm,
+    CONTROL_PROTOCOL__power_measurement_types_t measurement_type, float32_t *measurement)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    HAILO_COMMON_STATUS_t common_status = HAILO_COMMON_STATUS__UNINITIALIZED;
+    CONTROL_PROTOCOL__request_t request = {};
+    size_t request_size = 0;
+    uint8_t response_buffer[RESPONSE_MAX_BUFFER_SIZE] = {};
+    size_t response_size = RESPONSE_MAX_BUFFER_SIZE;
+    CONTROL_PROTOCOL__response_header_t *header = NULL;
+    CONTROL_PROTOCOL__payload_t *payload = NULL;
+    CONTROL_PROTOCOL__power_measurement_response_t *response = NULL;
+
+    /* Validate arguments */
+    CHECK_ARG_NOT_NULL(measurement);
+
+    common_status = CONTROL_PROTOCOL__pack_power_measurement_request(&request, &request_size, device.get_control_sequence(),
+            dvm, measurement_type);
+    status = (HAILO_COMMON_STATUS__SUCCESS == common_status) ? HAILO_SUCCESS : HAILO_INTERNAL_FAILURE;
+    if (HAILO_SUCCESS != status) {
+        goto exit;
+    }
+
+    status = device.fw_interact((uint8_t*)(&request), request_size, (uint8_t*)&response_buffer, &response_size);
+    if (HAILO_SUCCESS != status) {
+        goto exit;
+    }
+
+    /* Parse response */
+    status = parse_and_validate_response(response_buffer, (uint32_t)(response_size), &header, &payload,
+            &request);
+    if (HAILO_SUCCESS != status) {
+        goto exit;
+    }
+    response = (CONTROL_PROTOCOL__power_measurement_response_t*)(payload->parameters);
+
+    LOGGER__INFO("The chosen dvm type is: {}, and measurement type: {}", response->dvm,
+        response->measurement_type);
+    if (CONTROL_PROTOCOL__DVM_OPTIONS_OVERCURRENT_PROTECTION == response->dvm) {
+        LOGGER__WARN(OVERCURRENT_PROTECTION_WARNING);
+    }
+
+    *measurement = response->power_measurement;
+
+    status = HAILO_SUCCESS;
+exit:
+    return status;
+}
+
+hailo_status Control::set_power_measurement(Device &device, uint32_t index, CONTROL_PROTOCOL__dvm_options_t dvm,
+    CONTROL_PROTOCOL__power_measurement_types_t measurement_type)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    HAILO_COMMON_STATUS_t common_status = HAILO_COMMON_STATUS__UNINITIALIZED;
+    CONTROL_PROTOCOL__request_t request = {};
+    size_t request_size = 0;
+    uint8_t response_buffer[RESPONSE_MAX_BUFFER_SIZE] = {};
+    size_t response_size = RESPONSE_MAX_BUFFER_SIZE;
+    CONTROL_PROTOCOL__response_header_t *header = NULL;
+    CONTROL_PROTOCOL__payload_t *payload = NULL;
+    CONTROL_PROTOCOL__set_power_measurement_response_t *response = NULL;
+
+    CHECK(CONTROL_PROTOCOL__MAX_NUMBER_OF_POWER_MEASUREMETS > index,
+        HAILO_INVALID_ARGUMENT, "Invalid power measurement index {}", index);
+
+    common_status = CONTROL_PROTOCOL__pack_set_power_measurement_request(&request, &request_size, device.get_control_sequence(),
+            index, dvm, measurement_type);
+    status = (HAILO_COMMON_STATUS__SUCCESS == common_status) ? HAILO_SUCCESS : HAILO_INTERNAL_FAILURE;
+    if (HAILO_SUCCESS != status) {
+        goto exit;
+    }
+
+    status = device.fw_interact((uint8_t*)(&request), request_size, (uint8_t*)&response_buffer, &response_size);
+    if (HAILO_SUCCESS != status) {
+        goto exit;
+    }
+
+    /* Parse response */
+    status = parse_and_validate_response(response_buffer, (uint32_t)(response_size), &header, &payload,
+            &request);
+    if (HAILO_SUCCESS != status) {
+        goto exit;
+    }
+    response = (CONTROL_PROTOCOL__set_power_measurement_response_t*)(payload->parameters);
+
+    LOGGER__INFO("The chosen dvm type is: {}, and measurement type: {}", response->dvm,
+        response->measurement_type);
+    if (CONTROL_PROTOCOL__DVM_OPTIONS_OVERCURRENT_PROTECTION == response->dvm) {
+        LOGGER__WARN(OVERCURRENT_PROTECTION_WARNING);
+    }
+
+    status = HAILO_SUCCESS;
+exit:
+    return status;
+}
+
+hailo_status Control::get_power_measurement(Device &device, uint32_t index, bool should_clear,
+    hailo_power_measurement_data_t *measurement_data)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    HAILO_COMMON_STATUS_t common_status = HAILO_COMMON_STATUS__UNINITIALIZED;
+    CONTROL_PROTOCOL__request_t request = {};
+    size_t request_size = 0;
+    uint8_t response_buffer[RESPONSE_MAX_BUFFER_SIZE] = {};
+    size_t response_size = RESPONSE_MAX_BUFFER_SIZE;
+    CONTROL_PROTOCOL__response_header_t *header = NULL;
+    CONTROL_PROTOCOL__payload_t *payload = NULL;
+    CONTROL_PROTOCOL__get_power_measurement_response_t *get_power_response = NULL;
+
+    /* Validate arguments */
+    CHECK(CONTROL_PROTOCOL__MAX_NUMBER_OF_POWER_MEASUREMETS > index,
+        HAILO_INVALID_ARGUMENT, "Invalid power measurement index {}", index);
+    CHECK_ARG_NOT_NULL(measurement_data);
+    common_status = CONTROL_PROTOCOL__pack_get_power_measurement_request(&request, &request_size, device.get_control_sequence(),
+            index, should_clear);
+    status = (HAILO_COMMON_STATUS__SUCCESS == common_status) ? HAILO_SUCCESS : HAILO_INTERNAL_FAILURE;
+    if (HAILO_SUCCESS != status) {
+        goto exit;
+    }
+    status = device.fw_interact((uint8_t*)(&request), request_size, (uint8_t*)&response_buffer, &response_size);
+    if (HAILO_SUCCESS != status) {
+        goto exit;
+    }
+    /* Parse response */
+    status = parse_and_validate_response(response_buffer, (uint32_t)(response_size), &header, &payload,
+            &request);
+    if (HAILO_SUCCESS != status) {
+        goto exit;
+    }
+    get_power_response = (CONTROL_PROTOCOL__get_power_measurement_response_t *)(payload->parameters);
+
+    /* Copy measurement data from response to the exported measurement data */
+    measurement_data->average_time_value_milliseconds = get_power_response->average_time_value_milliseconds;
+    measurement_data->average_value = get_power_response->average_value;
+    measurement_data->min_value = get_power_response->min_value;
+    measurement_data->max_value = get_power_response->max_value;
+    measurement_data->total_number_of_samples = BYTE_ORDER__ntohl(get_power_response->total_number_of_samples);
+    LOGGER__DEBUG("avg: {:f}, min: {:f}, max: {:f}",
+            measurement_data->average_value,
+            measurement_data->min_value,
+            measurement_data->max_value);
+    status = HAILO_SUCCESS;
+exit:
+    return status;
+}
+
+hailo_status Control::start_power_measurement(Device &device, uint32_t delay_milliseconds,
+    CONTROL_PROTOCOL__averaging_factor_t averaging_factor , CONTROL_PROTOCOL__sampling_period_t sampling_period)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    HAILO_COMMON_STATUS_t common_status = HAILO_COMMON_STATUS__UNINITIALIZED;
+    CONTROL_PROTOCOL__request_t request = {};
+    size_t request_size = 0;
+    uint8_t response_buffer[RESPONSE_MAX_BUFFER_SIZE] = {};
+    size_t response_size = RESPONSE_MAX_BUFFER_SIZE;
+    CONTROL_PROTOCOL__response_header_t *header = NULL;
+    CONTROL_PROTOCOL__payload_t *payload = NULL;
+
+    common_status = CONTROL_PROTOCOL__pack_start_power_measurement_request(&request, &request_size, device.get_control_sequence(),
+            delay_milliseconds, averaging_factor, sampling_period);
+    status = (HAILO_COMMON_STATUS__SUCCESS == common_status) ? HAILO_SUCCESS : HAILO_INTERNAL_FAILURE;
+    if (HAILO_SUCCESS != status) {
+        goto exit;
+    }
+
+    status = device.fw_interact((uint8_t*)(&request), request_size, (uint8_t*)&response_buffer, &response_size);
+    if (HAILO_SUCCESS != status) {
+        goto exit;
+    }
+
+    /* Parse response */
+    status = parse_and_validate_response(response_buffer, (uint32_t)(response_size), &header, &payload,
+            &request);
+    if (HAILO_SUCCESS != status) {
+        goto exit;
+    }
+
+    status = HAILO_SUCCESS;
+exit:
+    return status;
+}
+
+hailo_status Control::stop_power_measurement(Device &device)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    HAILO_COMMON_STATUS_t common_status = HAILO_COMMON_STATUS__UNINITIALIZED;
+    CONTROL_PROTOCOL__request_t request = {};
+    size_t request_size = 0;
+    uint8_t response_buffer[RESPONSE_MAX_BUFFER_SIZE] = {};
+    size_t response_size = RESPONSE_MAX_BUFFER_SIZE;
+    CONTROL_PROTOCOL__response_header_t *header = NULL;
+    CONTROL_PROTOCOL__payload_t *payload = NULL;
+
+    common_status = CONTROL_PROTOCOL__pack_stop_power_measurement_request(&request, &request_size, device.get_control_sequence());
+    status = (HAILO_COMMON_STATUS__SUCCESS == common_status) ? HAILO_SUCCESS : HAILO_INTERNAL_FAILURE;
+    if (HAILO_SUCCESS != status) {
+        goto exit;
+    }
+
+    status = device.fw_interact((uint8_t*)(&request), request_size, (uint8_t*)&response_buffer, &response_size);
+    if (HAILO_SUCCESS != status) {
+        goto exit;
+    }
+
+    /* Parse response */
+    status = parse_and_validate_response(response_buffer, (uint32_t)(response_size), &header, &payload,
+            &request);
+    if (HAILO_SUCCESS != status) {
+        goto exit;
+    }
+
+    status = HAILO_SUCCESS;
+exit:
+    return status;
+}
+
+hailo_status Control::i2c_write(Device &device, const hailo_i2c_slave_config_t *slave_config, uint32_t register_address,
+        const uint8_t *data, uint32_t length)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    HAILO_COMMON_STATUS_t common_status = HAILO_COMMON_STATUS__UNINITIALIZED;
+    CONTROL_PROTOCOL__request_t request = {};
+    size_t request_size = 0;
+    uint8_t response_buffer[RESPONSE_MAX_BUFFER_SIZE] = {};
+    size_t response_size = RESPONSE_MAX_BUFFER_SIZE;
+    CONTROL_PROTOCOL__response_header_t *header = NULL;
+    CONTROL_PROTOCOL__payload_t *payload = NULL;
+
+    /* Validate arguments */
+    CHECK_ARG_NOT_NULL(slave_config);
+    CHECK_ARG_NOT_NULL(data);
+
+    /* Pack request */
+    common_status = CONTROL_PROTOCOL__pack_i2c_write_request(&request, &request_size, device.get_control_sequence(),
+            register_address, static_cast<uint8_t>(slave_config->endianness),
+            slave_config->slave_address, slave_config->register_address_size, slave_config->bus_index, data, length);
+    status = (HAILO_COMMON_STATUS__SUCCESS == common_status) ? HAILO_SUCCESS : HAILO_INTERNAL_FAILURE;
+    if (HAILO_SUCCESS != status) {
+        goto exit;
+    }
+
+    status = device.fw_interact((uint8_t*)(&request), request_size, (uint8_t*)&response_buffer,
+            &response_size);
+    if (HAILO_SUCCESS != status) {
+        goto exit;
+    }
+
+    /* Parse response */
+    status = parse_and_validate_response(response_buffer, (uint32_t)(response_size), &header, &payload,
+            &request);
+    if (HAILO_SUCCESS != status) {
+        goto exit;
+    }
+
+    status = HAILO_SUCCESS;
+exit:
+    return status;
+}
+
+hailo_status Control::i2c_read(Device &device, const hailo_i2c_slave_config_t *slave_config, uint32_t register_address,
+        uint8_t *data, uint32_t length)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    HAILO_COMMON_STATUS_t common_status = HAILO_COMMON_STATUS__UNINITIALIZED;
+    CONTROL_PROTOCOL__request_t request = {};
+    size_t request_size = 0;
+    uint8_t response_buffer[RESPONSE_MAX_BUFFER_SIZE] = {};
+    size_t response_size = RESPONSE_MAX_BUFFER_SIZE;
+    CONTROL_PROTOCOL__response_header_t *header = NULL;
+    CONTROL_PROTOCOL__payload_t *payload = NULL;
+    CONTROL_PROTOCOL__i2c_read_response_t *response = NULL;
+    uint32_t local_data_length = 0;
+
+    /* Validate arguments */
+    CHECK_ARG_NOT_NULL(slave_config);
+    CHECK_ARG_NOT_NULL(data);
+
+    /* Pack request */
+    common_status = CONTROL_PROTOCOL__pack_i2c_read_request(&request, &request_size, device.get_control_sequence(),
+            register_address, static_cast<uint8_t>(slave_config->endianness),
+            slave_config->slave_address, slave_config->register_address_size, slave_config->bus_index, length,
+            slave_config->should_hold_bus);
+    status = (HAILO_COMMON_STATUS__SUCCESS == common_status) ? HAILO_SUCCESS : HAILO_INTERNAL_FAILURE;
+    if (HAILO_SUCCESS != status) {
+        goto exit;
+    }
+
+    status = device.fw_interact((uint8_t*)(&request), request_size, (uint8_t*)&response_buffer,
+            &response_size);
+    if (HAILO_SUCCESS != status) {
+        goto exit;
+    }
+
+    /* Parse response */
+    status = parse_and_validate_response(response_buffer, (uint32_t)(response_size), &header, &payload,
+            &request);
+    if (HAILO_SUCCESS != status) {
+        goto exit;
+    }
+
+    response = (CONTROL_PROTOCOL__i2c_read_response_t *)(payload->parameters);
+    local_data_length = BYTE_ORDER__ntohl(response->data_length);
+
+    /* Validate read data is data size */
+    if (local_data_length != length) {
+        status = HAILO_INVALID_CONTROL_RESPONSE;
+        LOGGER__ERROR("Read data size from I2C does not match register size. ({} != {})",
+                local_data_length, length);
+        goto exit;
+    }
+
+    /* Copy the returned results back to the user */
+    (void)memcpy(data, response->data, local_data_length);
+
+    status = HAILO_SUCCESS;
+exit:
+    return status;
+}
+
+hailo_status Control::config_core_top(Device &device, CONTROL_PROTOCOL__config_core_top_type_t config_type,
+    CONTROL_PROTOCOL__config_core_top_params_t *params)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    HAILO_COMMON_STATUS_t common_status = HAILO_COMMON_STATUS__UNINITIALIZED;
+    CONTROL_PROTOCOL__request_t request = {};
+    size_t request_size = 0;
+    uint8_t response_buffer[RESPONSE_MAX_BUFFER_SIZE] = {};
+    size_t response_size = RESPONSE_MAX_BUFFER_SIZE;
+    CONTROL_PROTOCOL__response_header_t *header = NULL;
+    CONTROL_PROTOCOL__payload_t *payload = NULL;
+
+    /* Validate arguments */
+    CHECK_ARG_NOT_NULL(params);
+
+    common_status = CONTROL_PROTOCOL__pack_config_core_top_request(&request, &request_size, device.get_control_sequence(), config_type, params);
+    status = (HAILO_COMMON_STATUS__SUCCESS == common_status) ? HAILO_SUCCESS : HAILO_INTERNAL_FAILURE;
+    if (HAILO_SUCCESS != status) {
+        goto exit;
+    }
+
+    status = device.fw_interact((uint8_t*)(&request), request_size, (uint8_t*)&response_buffer, &response_size);
+    if (HAILO_SUCCESS != status) {
+        goto exit;
+    }
+
+    /* Parse response */
+    status = parse_and_validate_response(response_buffer, (uint32_t)(response_size), &header, &payload,
+            &request);
+    if (HAILO_SUCCESS != status) {
+        goto exit;
+    }
+
+    status = HAILO_SUCCESS;
+exit:
+    return status;
+}
+
+hailo_status Control::phy_operation(Device &device, CONTROL_PROTOCOL__phy_operation_t operation_type)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    HAILO_COMMON_STATUS_t common_status = HAILO_COMMON_STATUS__UNINITIALIZED;
+    CONTROL_PROTOCOL__request_t request = {};
+    size_t request_size = 0;
+    uint8_t response_buffer[RESPONSE_MAX_BUFFER_SIZE] = {};
+    size_t response_size = RESPONSE_MAX_BUFFER_SIZE;
+    CONTROL_PROTOCOL__response_header_t *header = NULL;
+    CONTROL_PROTOCOL__payload_t *payload = NULL;
+
+    common_status = CONTROL_PROTOCOL__pack_phy_operation_request(&request, &request_size, device.get_control_sequence(), operation_type);
+    status = (HAILO_COMMON_STATUS__SUCCESS == common_status) ? HAILO_SUCCESS : HAILO_INTERNAL_FAILURE;
+    if (HAILO_SUCCESS != status) {
+        goto exit;
+    }
+
+    status = device.fw_interact((uint8_t*)(&request), request_size, (uint8_t*)&response_buffer, &response_size);
+    if (HAILO_SUCCESS != status) {
+        goto exit;
+    }
+
+    /* Parse response */
+    status = parse_and_validate_response(response_buffer, (uint32_t)(response_size), &header, &payload,
+            &request);
+    if (HAILO_SUCCESS != status) {
+        goto exit;
+    }
+
+    status = HAILO_SUCCESS;
+exit:
+    return status;
+}
+
+hailo_status Control::examine_user_config(Device &device, hailo_fw_user_config_information_t *info)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    HAILO_COMMON_STATUS_t common_status = HAILO_COMMON_STATUS__UNINITIALIZED;
+    CONTROL_PROTOCOL__request_t request = {};
+    size_t request_size = 0;
+    uint8_t response_buffer[RESPONSE_MAX_BUFFER_SIZE] = {};
+    size_t response_size = RESPONSE_MAX_BUFFER_SIZE;
+    CONTROL_PROTOCOL__response_header_t *header = NULL;
+    CONTROL_PROTOCOL__payload_t *payload = NULL;
+    CONTROL_PROTOCOL__examine_user_config_response_t *response = NULL;
+
+    /* Validate arguments */
+    CHECK_ARG_NOT_NULL(info);
+
+    common_status = CONTROL_PROTOCOL__pack_examine_user_config(&request, &request_size, device.get_control_sequence());
+    status = (HAILO_COMMON_STATUS__SUCCESS == common_status) ? HAILO_SUCCESS : HAILO_INTERNAL_FAILURE;
+    if (HAILO_SUCCESS != status) {
+        goto exit;
+    }
+
+    status = device.fw_interact((uint8_t*)(&request), request_size, (uint8_t*)&response_buffer, &response_size);
+    if (HAILO_SUCCESS != status) {
+        goto exit;
+    }
+
+    /* Parse response */
+    status = parse_and_validate_response(response_buffer, (uint32_t)(response_size), &header, &payload,
+            &request);
+    if (HAILO_SUCCESS != status) {
+        goto exit;
+    }
+
+    /* Save response information into exported struct */
+    response = ((CONTROL_PROTOCOL__examine_user_config_response_t *)(payload->parameters));
+    info->version = BYTE_ORDER__ntohl(response->version);
+    info->entry_count = BYTE_ORDER__ntohl(response->entry_count);
+    info->total_size = BYTE_ORDER__ntohl(response->total_size);
+
+    status = HAILO_SUCCESS;
+exit:
+    return status;
+}
+
+hailo_status Control::read_user_config_chunk(Device &device, uint32_t read_offset, uint32_t read_length,
+    uint8_t *buffer, uint32_t *actual_read_data_length)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    HAILO_COMMON_STATUS_t common_status = HAILO_COMMON_STATUS__UNINITIALIZED;
+    CONTROL_PROTOCOL__request_t request = {};
+    size_t request_size = 0;
+    uint8_t response_buffer[RESPONSE_MAX_BUFFER_SIZE] = {};
+    size_t response_size = RESPONSE_MAX_BUFFER_SIZE;
+    CONTROL_PROTOCOL__response_header_t *header = NULL;
+    CONTROL_PROTOCOL__payload_t *payload = NULL;
+    CONTROL_PROTOCOL__read_user_config_response_t *response = NULL;
+
+    common_status = CONTROL_PROTOCOL__pack_read_user_config(&request, &request_size, device.get_control_sequence(),
+        read_offset, read_length);
+    status = (HAILO_COMMON_STATUS__SUCCESS == common_status) ? HAILO_SUCCESS : HAILO_INTERNAL_FAILURE;
+    CHECK_SUCCESS(status);
+
+    status = device.fw_interact((uint8_t*)(&request), request_size, (uint8_t*)&response_buffer,
+        &response_size);
+    CHECK_SUCCESS(status);
+
+    /* Parse response */
+    status = parse_and_validate_response(response_buffer, (uint32_t)(response_size), &header, &payload,
+            &request);
+    CHECK_SUCCESS(status);
+
+    response = (CONTROL_PROTOCOL__read_user_config_response_t *)(payload->parameters);
+    *actual_read_data_length = BYTE_ORDER__ntohl(response->data_length);
+    (void) memcpy(buffer, response->data, *actual_read_data_length);
+
+    return HAILO_SUCCESS;
+}
+
+hailo_status Control::read_user_config(Device &device, uint8_t *buffer, uint32_t buffer_length)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    uint32_t actual_read_data_length = 0;
+    uint32_t read_offset = 0;
+    hailo_fw_user_config_information_t user_config_info = {};
+
+    /* Validate arguments */
+    CHECK_ARG_NOT_NULL(buffer);
+
+    status = examine_user_config(device, &user_config_info);
+    CHECK_SUCCESS(status);
+
+    CHECK(buffer_length >= user_config_info.total_size, HAILO_INSUFFICIENT_BUFFER,
+        "read buffer is too small. provided buffer size: {} bytes, user config size: {} bytes", buffer_length,
+        user_config_info.total_size);
+
+    LOGGER__INFO("Preparing to read user configuration. Version: {}, Entry Count: {}, Total Size (bytes): {}",
+        user_config_info.version, user_config_info.entry_count, user_config_info.total_size);
+
+    while (read_offset < user_config_info.total_size) {
+        read_user_config_chunk(device, read_offset, user_config_info.total_size - read_offset,
+            buffer + read_offset, &actual_read_data_length);
+        read_offset += actual_read_data_length;
+    }
+
+    return HAILO_SUCCESS;
+}
+
+hailo_status Control::write_user_config_chunk(Device &device, uint32_t offset, const uint8_t *data, uint32_t chunk_size)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    HAILO_COMMON_STATUS_t common_status = HAILO_COMMON_STATUS__UNINITIALIZED;
+    CONTROL_PROTOCOL__request_t request = {};
+    size_t request_size = 0;
+    uint8_t response_buffer[RESPONSE_MAX_BUFFER_SIZE] = {};
+    size_t response_size = RESPONSE_MAX_BUFFER_SIZE;
+    CONTROL_PROTOCOL__response_header_t *header = NULL;
+    CONTROL_PROTOCOL__payload_t *payload = NULL;
+
+    common_status = CONTROL_PROTOCOL__pack_write_user_config_request(&request, &request_size,
+        device.get_control_sequence(), offset, data + offset, chunk_size);
+    status = (HAILO_COMMON_STATUS__SUCCESS == common_status) ? HAILO_SUCCESS : HAILO_INTERNAL_FAILURE;
+    CHECK_SUCCESS(status);
+
+    status = device.fw_interact((uint8_t*)(&request), request_size, (uint8_t*)&response_buffer,
+        &response_size);
+    CHECK_SUCCESS(status);
+
+    /* Parse response */
+    status = parse_and_validate_response(response_buffer, (uint32_t)(response_size), &header, &payload,
+            &request);
+    CHECK_SUCCESS(status);
+
+    return HAILO_SUCCESS;
+}
+
+hailo_status Control::write_user_config(Device &device, const uint8_t *data, uint32_t data_length)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    uint32_t offset = 0;
+    uint32_t chunk_size = 0;
+
+    /* Validate arguments */
+    CHECK_ARG_NOT_NULL(data);
+
+    while (offset < data_length) {
+        chunk_size = MIN(WRITE_CHUNK_SIZE, (data_length - offset));
+        status = write_user_config_chunk(device, offset, data, chunk_size);
+        CHECK_SUCCESS(status);
+        offset += chunk_size;
+    }
+
+    return HAILO_SUCCESS;
+}
+
+hailo_status Control::erase_user_config(Device &device)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    HAILO_COMMON_STATUS_t common_status = HAILO_COMMON_STATUS__UNINITIALIZED;
+    CONTROL_PROTOCOL__request_t request = {};
+    size_t request_size = 0;
+    uint8_t response_buffer[RESPONSE_MAX_BUFFER_SIZE] = {};
+    size_t response_size = RESPONSE_MAX_BUFFER_SIZE;
+    CONTROL_PROTOCOL__response_header_t *header = NULL;
+    CONTROL_PROTOCOL__payload_t *payload = NULL;
+
+    common_status = CONTROL_PROTOCOL__pack_erase_user_config_request(&request, &request_size, device.get_control_sequence());
+    status = (HAILO_COMMON_STATUS__SUCCESS == common_status) ? HAILO_SUCCESS : HAILO_INTERNAL_FAILURE;
+    if (HAILO_SUCCESS != status) {
+        goto exit;
+    }
+
+    status = device.fw_interact((uint8_t*)(&request), request_size, (uint8_t*)&response_buffer, &response_size);
+    if (HAILO_SUCCESS != status) {
+        goto exit;
+    }
+
+    /* Parse response */
+    status = parse_and_validate_response(response_buffer, (uint32_t)(response_size), &header, &payload,
+            &request);
+    if (HAILO_SUCCESS != status) {
+        goto exit;
+    }
+
+    status = HAILO_SUCCESS;
+exit:
+    return status;
+}
+
+
+hailo_status Control::read_board_config(Device &device, uint8_t *buffer, uint32_t buffer_length)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    HAILO_COMMON_STATUS_t common_status = HAILO_COMMON_STATUS__UNINITIALIZED;
+    CONTROL_PROTOCOL__request_t request = {};
+    uint32_t actual_read_data_length = 0;
+    uint32_t read_offset = 0;
+    size_t request_size = 0;
+    uint8_t response_buffer[RESPONSE_MAX_BUFFER_SIZE] = {};
+    size_t response_size = RESPONSE_MAX_BUFFER_SIZE;
+    CONTROL_PROTOCOL__response_header_t *header = NULL;
+    CONTROL_PROTOCOL__payload_t *payload = NULL;
+    CONTROL_PROTOCOL__read_user_config_response_t *response = NULL;
+
+    /* Validate arguments */
+    CHECK_ARG_NOT_NULL(buffer);
+
+    CHECK(buffer_length >= BOARD_CONFIG_SIZE, HAILO_INSUFFICIENT_BUFFER,
+        "read buffer is too small. provided buffer size: {} bytes, board config size: {} bytes", buffer_length,
+        BOARD_CONFIG_SIZE);
+
+    LOGGER__INFO("Preparing to read board configuration");
+    common_status = CONTROL_PROTOCOL__pack_read_board_config(&request, &request_size, device.get_control_sequence(),
+        read_offset, BOARD_CONFIG_SIZE);
+
+    status = (HAILO_COMMON_STATUS__SUCCESS == common_status) ? HAILO_SUCCESS : HAILO_INTERNAL_FAILURE;
+    CHECK_SUCCESS(status);
+
+    status = device.fw_interact((uint8_t*)(&request), request_size, (uint8_t*)&response_buffer,
+        &response_size);
+    CHECK_SUCCESS(status);
+
+    /* Parse response */
+    status = parse_and_validate_response(response_buffer, (uint32_t)(response_size), &header, &payload,
+            &request);
+    CHECK_SUCCESS(status);
+    response = (CONTROL_PROTOCOL__read_board_config_response_t *)(payload->parameters);
+    actual_read_data_length = BYTE_ORDER__ntohl(response->data_length);
+    (void) memcpy(buffer, response->data, actual_read_data_length);
+
+    return HAILO_SUCCESS;
+}
+
+
+
+hailo_status Control::write_board_config(Device &device, const uint8_t *data, uint32_t data_length)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    HAILO_COMMON_STATUS_t common_status = HAILO_COMMON_STATUS__UNINITIALIZED;
+    CONTROL_PROTOCOL__request_t request = {};
+    size_t request_size = 0;
+    uint8_t response_buffer[RESPONSE_MAX_BUFFER_SIZE] = {};
+    size_t response_size = RESPONSE_MAX_BUFFER_SIZE;
+    CONTROL_PROTOCOL__response_header_t *header = NULL;
+    CONTROL_PROTOCOL__payload_t *payload = NULL;
+    uint32_t write_offset = 0;
+
+    /* Validate arguments */
+    CHECK_ARG_NOT_NULL(data);
+
+    CHECK(BOARD_CONFIG_SIZE >= data_length, HAILO_INVALID_OPERATION,
+        "Invalid size of board config. data_length={},  max_size={}" , data_length, BOARD_CONFIG_SIZE);
+
+    common_status = CONTROL_PROTOCOL__pack_write_board_config_request(&request, &request_size,
+        device.get_control_sequence(), write_offset, data + write_offset, data_length);
+    status = (HAILO_COMMON_STATUS__SUCCESS == common_status) ? HAILO_SUCCESS : HAILO_INTERNAL_FAILURE;
+    CHECK_SUCCESS(status);
+
+    status = device.fw_interact((uint8_t*)(&request), request_size, (uint8_t*)&response_buffer,
+        &response_size);
+    CHECK_SUCCESS(status);
+
+    /* Parse response */
+    status = parse_and_validate_response(response_buffer, (uint32_t)(response_size), &header, &payload,
+            &request);
+    CHECK_SUCCESS(status);
+
+    return HAILO_SUCCESS;
+}
+
+hailo_status Control::write_second_stage_to_internal_memory(Device &device, uint32_t offset, uint8_t *data, uint32_t data_length)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    HAILO_COMMON_STATUS_t common_status = HAILO_COMMON_STATUS__UNINITIALIZED;
+    CONTROL_PROTOCOL__request_t request = {};
+    size_t request_size = 0;
+    uint8_t response_buffer[RESPONSE_MAX_BUFFER_SIZE] = {};
+    size_t response_size = RESPONSE_MAX_BUFFER_SIZE;
+    CONTROL_PROTOCOL__response_header_t *header = NULL;
+    CONTROL_PROTOCOL__payload_t *payload = NULL;
+
+    /* Validate arguments */
+    CHECK_ARG_NOT_NULL(data);
+
+    common_status = CONTROL_PROTOCOL__write_second_stage_to_internal_memory_request(&request, &request_size, device.get_control_sequence(), offset,
+            data, data_length);
+    status = (HAILO_COMMON_STATUS__SUCCESS == common_status) ? HAILO_SUCCESS : HAILO_INTERNAL_FAILURE;
+    if (HAILO_SUCCESS != status) {
+        goto exit;
+    }
+
+    status = device.fw_interact((uint8_t*)(&request), request_size, (uint8_t*)&response_buffer, &response_size);
+    if (HAILO_SUCCESS != status) {
+        goto exit;
+    }
+
+    /* Parse response */
+    status = parse_and_validate_response(response_buffer, (uint32_t)(response_size), &header, &payload,
+            &request);
+    if (HAILO_SUCCESS != status) {
+        goto exit;
+    }
+
+    status = HAILO_SUCCESS;
+exit:
+    return status;
+}
+
+
+hailo_status Control::copy_second_stage_to_flash(Device &device, MD5_SUM_t *expected_md5, uint32_t second_stage_size)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    HAILO_COMMON_STATUS_t common_status = HAILO_COMMON_STATUS__UNINITIALIZED;
+    CONTROL_PROTOCOL__request_t request = {};
+    size_t request_size = 0;
+    uint8_t response_buffer[RESPONSE_MAX_BUFFER_SIZE] = {};
+    size_t response_size = RESPONSE_MAX_BUFFER_SIZE;
+    CONTROL_PROTOCOL__response_header_t *header = NULL;
+    CONTROL_PROTOCOL__payload_t *payload = NULL;
+
+    /* Validate arguments */
+    CHECK_ARG_NOT_NULL(expected_md5);
+
+    common_status = CONTROL_PROTOCOL__copy_second_stage_to_flash_request(&request, &request_size, device.get_control_sequence(), expected_md5, second_stage_size);
+    status = (HAILO_COMMON_STATUS__SUCCESS == common_status) ? HAILO_SUCCESS : HAILO_INTERNAL_FAILURE;
+    if (HAILO_SUCCESS != status) {
+        goto exit;
+    }
+
+    status = device.fw_interact((uint8_t*)(&request), request_size, (uint8_t*)&response_buffer, &response_size);
+    if (HAILO_SUCCESS != status) {
+        goto exit;
+    }
+
+    /* Parse response */
+    status = parse_and_validate_response(response_buffer, (uint32_t)(response_size), &header, &payload,
+            &request);
+    if (HAILO_SUCCESS != status) {
+        goto exit;
+    }
+
+    status = HAILO_SUCCESS;
+exit:
+    return status;
+}
+
+hailo_status Control::start_firmware_update(Device &device)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    HAILO_COMMON_STATUS_t common_status = HAILO_COMMON_STATUS__UNINITIALIZED;
+    CONTROL_PROTOCOL__request_t request = {};
+    size_t request_size = 0;
+    uint8_t response_buffer[RESPONSE_MAX_BUFFER_SIZE] = {};
+    size_t response_size = RESPONSE_MAX_BUFFER_SIZE;
+    CONTROL_PROTOCOL__response_header_t *header = NULL;
+    CONTROL_PROTOCOL__payload_t *payload = NULL;
+
+    common_status = CONTROL_PROTOCOL__pack_start_firmware_update_request(&request, &request_size, device.get_control_sequence());
+    status = (HAILO_COMMON_STATUS__SUCCESS == common_status) ? HAILO_SUCCESS : HAILO_INTERNAL_FAILURE;
+    if (HAILO_SUCCESS != status) {
+        goto exit;
+    }
+
+    status = device.fw_interact((uint8_t*)(&request), request_size, (uint8_t*)&response_buffer, &response_size);
+    if (HAILO_SUCCESS != status) {
+        goto exit;
+    }
+
+    /* Parse response */
+    status = parse_and_validate_response(response_buffer, (uint32_t)(response_size), &header, &payload,
+            &request);
+    if (HAILO_SUCCESS != status) {
+        goto exit;
+    }
+
+    status = HAILO_SUCCESS;
+exit:
+    return status;
+}
+
+hailo_status Control::finish_firmware_update(Device &device)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    HAILO_COMMON_STATUS_t common_status = HAILO_COMMON_STATUS__UNINITIALIZED;
+    CONTROL_PROTOCOL__request_t request = {};
+    size_t request_size = 0;
+    uint8_t response_buffer[RESPONSE_MAX_BUFFER_SIZE] = {};
+    size_t response_size = RESPONSE_MAX_BUFFER_SIZE;
+    CONTROL_PROTOCOL__response_header_t *header = NULL;
+    CONTROL_PROTOCOL__payload_t *payload = NULL;
+
+    common_status = CONTROL_PROTOCOL__pack_finish_firmware_update_request(&request, &request_size, device.get_control_sequence());
+    status = (HAILO_COMMON_STATUS__SUCCESS == common_status) ? HAILO_SUCCESS : HAILO_INTERNAL_FAILURE;
+    if (HAILO_SUCCESS != status) {
+        goto exit;
+    }
+
+    status = device.fw_interact((uint8_t*)(&request), request_size, (uint8_t*)&response_buffer, &response_size);
+    if (HAILO_SUCCESS != status) {
+        goto exit;
+    }
+
+    /* Parse response */
+    status = parse_and_validate_response(response_buffer, (uint32_t)(response_size), &header, &payload,
+            &request);
+    if (HAILO_SUCCESS != status) {
+        goto exit;
+    }
+
+    status = HAILO_SUCCESS;
+exit:
+    return status;
+}
+
+hailo_status Control::write_firmware_update(Device &device, uint32_t offset, const uint8_t *data, uint32_t data_length)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    HAILO_COMMON_STATUS_t common_status = HAILO_COMMON_STATUS__UNINITIALIZED;
+    CONTROL_PROTOCOL__request_t request = {};
+    size_t request_size = 0;
+    uint8_t response_buffer[RESPONSE_MAX_BUFFER_SIZE] = {};
+    size_t response_size = RESPONSE_MAX_BUFFER_SIZE;
+    CONTROL_PROTOCOL__response_header_t *header = NULL;
+    CONTROL_PROTOCOL__payload_t *payload = NULL;
+
+    /* Validate arguments */
+    CHECK_ARG_NOT_NULL(data);
+
+    common_status = CONTROL_PROTOCOL__write_firmware_update_request(&request, &request_size, device.get_control_sequence(), offset,
+            data, data_length);
+    status = (HAILO_COMMON_STATUS__SUCCESS == common_status) ? HAILO_SUCCESS : HAILO_INTERNAL_FAILURE;
+    if (HAILO_SUCCESS != status) {
+        goto exit;
+    }
+
+    status = device.fw_interact((uint8_t*)(&request), request_size, (uint8_t*)&response_buffer, &response_size);
+    if (HAILO_SUCCESS != status) {
+        goto exit;
+    }
+
+    /* Parse response */
+    status = parse_and_validate_response(response_buffer, (uint32_t)(response_size), &header, &payload,
+            &request);
+    if (HAILO_SUCCESS != status) {
+        goto exit;
+    }
+
+    status = HAILO_SUCCESS;
+exit:
+    return status;
+}
+
+hailo_status Control::validate_firmware_update(Device &device, MD5_SUM_t *expected_md5, uint32_t firmware_size)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    HAILO_COMMON_STATUS_t common_status = HAILO_COMMON_STATUS__UNINITIALIZED;
+    CONTROL_PROTOCOL__request_t request = {};
+    size_t request_size = 0;
+    uint8_t response_buffer[RESPONSE_MAX_BUFFER_SIZE] = {};
+    size_t response_size = RESPONSE_MAX_BUFFER_SIZE;
+    CONTROL_PROTOCOL__response_header_t *header = NULL;
+    CONTROL_PROTOCOL__payload_t *payload = NULL;
+
+    /* Validate arguments */
+    CHECK_ARG_NOT_NULL(expected_md5);
+
+    common_status = CONTROL_PROTOCOL__pack_validate_firmware_update_request(&request, &request_size, device.get_control_sequence(),
+            expected_md5, firmware_size);
+    status = (HAILO_COMMON_STATUS__SUCCESS == common_status) ? HAILO_SUCCESS : HAILO_INTERNAL_FAILURE;
+    if (HAILO_SUCCESS != status) {
+        goto exit;
+    }
+
+    status = device.fw_interact((uint8_t*)(&request), request_size, (uint8_t*)&response_buffer, &response_size);
+    if (HAILO_SUCCESS != status) {
+        goto exit;
+    }
+
+    /* Parse response */
+    status = parse_and_validate_response(response_buffer, (uint32_t)(response_size), &header, &payload,
+            &request);
+    if (HAILO_SUCCESS != status) {
+        goto exit;
+    }
+
+    status = HAILO_SUCCESS;
+exit:
+    return status;
+}
+
+hailo_status Control::latency_measurement_read(Device &device, uint32_t *inbound_to_outbound_latency_nsec)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    HAILO_COMMON_STATUS_t common_status = HAILO_COMMON_STATUS__UNINITIALIZED;
+    CONTROL_PROTOCOL__request_t request = {};
+    size_t request_size = 0;
+    uint8_t response_buffer[RESPONSE_MAX_BUFFER_SIZE] = {};
+    size_t response_size = RESPONSE_MAX_BUFFER_SIZE;
+    CONTROL_PROTOCOL__response_header_t *header = NULL;
+    CONTROL_PROTOCOL__payload_t *payload = NULL;
+    CONTROL_PROTOCOL__latency_read_response_t *response = NULL;
+
+    /* Validate arguments */
+    CHECK_ARG_NOT_NULL(inbound_to_outbound_latency_nsec);
+
+    common_status = CONTROL_PROTOCOL__pack_latency_measurement_read_request(&request, &request_size, device.get_control_sequence());
+    status = (HAILO_COMMON_STATUS__SUCCESS == common_status) ? HAILO_SUCCESS : HAILO_INTERNAL_FAILURE;
+    if (HAILO_SUCCESS != status) {
+        goto exit;
+    }
+
+    status = device.fw_interact((uint8_t*)(&request), request_size, (uint8_t*)&response_buffer, &response_size);
+    if (HAILO_SUCCESS != status) {
+        goto exit;
+    }
+
+    /* Parse response */
+    status = parse_and_validate_response(response_buffer, (uint32_t)(response_size), &header, &payload,
+            &request);
+    if (HAILO_SUCCESS != status) {
+        goto exit;
+    }
+
+    response = (CONTROL_PROTOCOL__latency_read_response_t*)(payload->parameters);
+    *inbound_to_outbound_latency_nsec = BYTE_ORDER__ntohl(response->inbound_to_outbound_latency_nsec);
+
+    status = HAILO_SUCCESS;
+exit:
+    return status;
+}
+
+hailo_status Control::latency_measurement_config(Device &device, uint8_t latency_measurement_en,
+    uint32_t inbound_start_buffer_number, uint32_t outbound_stop_buffer_number, uint32_t inbound_stream_index,
+    uint32_t outbound_stream_index)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    HAILO_COMMON_STATUS_t common_status = HAILO_COMMON_STATUS__UNINITIALIZED;
+    CONTROL_PROTOCOL__request_t request = {};
+    size_t request_size = 0;
+    uint8_t response_buffer[RESPONSE_MAX_BUFFER_SIZE] = {};
+    size_t response_size = RESPONSE_MAX_BUFFER_SIZE;
+    CONTROL_PROTOCOL__response_header_t *header = NULL;
+    CONTROL_PROTOCOL__payload_t *payload = NULL;
+
+    common_status = CONTROL_PROTOCOL__pack_latency_measurement_config_request(&request, &request_size, device.get_control_sequence(),
+            latency_measurement_en, inbound_start_buffer_number, outbound_stop_buffer_number,
+            inbound_stream_index, outbound_stream_index);
+    status = (HAILO_COMMON_STATUS__SUCCESS == common_status) ? HAILO_SUCCESS : HAILO_INTERNAL_FAILURE;
+    if (HAILO_SUCCESS != status) {
+        goto exit;
+    }
+
+    status = device.fw_interact((uint8_t*)(&request), request_size, (uint8_t*)&response_buffer, &response_size);
+    if (HAILO_SUCCESS != status) {
+        goto exit;
+    }
+
+    /* Parse response */
+    status = parse_and_validate_response(response_buffer, (uint32_t)(response_size), &header, &payload,
+            &request);
+    if (HAILO_SUCCESS != status) {
+        goto exit;
+    }
+
+    status = HAILO_SUCCESS;
+exit:
+    return status;
+}
+
+
+hailo_status Control::sensor_store_config(Device &device, uint32_t is_first, uint32_t section_index,
+    uint32_t start_offset, uint32_t reset_data_size, uint32_t sensor_type, uint32_t total_data_size, uint8_t *data,
+    uint32_t data_length,uint16_t config_height, uint16_t config_width, uint16_t config_fps,
+    uint32_t config_name_length, uint8_t *config_name)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    HAILO_COMMON_STATUS_t common_status = HAILO_COMMON_STATUS__UNINITIALIZED;
+    CONTROL_PROTOCOL__request_t request = {};
+    size_t request_size = 0;
+    uint8_t response_buffer[RESPONSE_MAX_BUFFER_SIZE] = {};
+    size_t response_size = RESPONSE_MAX_BUFFER_SIZE;
+    CONTROL_PROTOCOL__response_header_t *header = NULL;
+    CONTROL_PROTOCOL__payload_t *payload = NULL;
+
+
+    /* Validate arguments */
+    CHECK_ARG_NOT_NULL(data);
+    CHECK_ARG_NOT_NULL(config_name);
+
+
+    common_status =  CONTROL_PROTOCOL__pack_sensor_store_config_request(&request, &request_size, device.get_control_sequence(), is_first, section_index, start_offset,
+                                                                        reset_data_size, sensor_type, total_data_size, data, data_length, config_height,
+                                                                        config_width, config_fps, config_name_length, config_name);
+
+    status = (HAILO_COMMON_STATUS__SUCCESS == common_status) ? HAILO_SUCCESS : HAILO_INTERNAL_FAILURE;
+    if (HAILO_SUCCESS != status) {
+        goto exit;
+    }
+
+    status = device.fw_interact((uint8_t*)(&request), request_size, (uint8_t*)&response_buffer, &response_size);
+    if (HAILO_SUCCESS != status) {
+        goto exit;
+    }
+
+    /* Parse response */
+    status = parse_and_validate_response(response_buffer, (uint32_t)(response_size), &header, &payload,
+            &request);
+    if (HAILO_SUCCESS != status) {
+        goto exit;
+    }
+
+    status = HAILO_SUCCESS;
+exit:
+    return status;
+}
+
+hailo_status Control::sensor_set_i2c_bus_index(Device &device, uint32_t sensor_type, uint32_t bus_index)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    CONTROL_PROTOCOL__request_t request = {};
+    size_t request_size = 0;
+    uint8_t response_buffer[RESPONSE_MAX_BUFFER_SIZE] = {};
+    size_t response_size = RESPONSE_MAX_BUFFER_SIZE;
+    CONTROL_PROTOCOL__response_header_t *header = NULL;
+    CONTROL_PROTOCOL__payload_t *payload = NULL;
+
+    status = CONTROL_PROTOCOL__pack_sensor_set_i2c_bus_index_request(&request, &request_size, device.get_control_sequence(), sensor_type, bus_index);
+    CHECK_SUCCESS(status);
+
+    status = device.fw_interact((uint8_t*)(&request), request_size, (uint8_t*)&response_buffer, &response_size);
+    CHECK_SUCCESS(status);
+
+    /* Parse response */
+    status = parse_and_validate_response(response_buffer, (uint32_t)(response_size), &header, &payload, &request);
+    CHECK_SUCCESS(status);
+
+    return HAILO_SUCCESS;
+}
+
+hailo_status Control::sensor_load_and_start_config(Device &device, uint32_t section_index)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    HAILO_COMMON_STATUS_t common_status = HAILO_COMMON_STATUS__UNINITIALIZED;
+    CONTROL_PROTOCOL__request_t request = {};
+    size_t request_size = 0;
+    uint8_t response_buffer[RESPONSE_MAX_BUFFER_SIZE] = {};
+    size_t response_size = RESPONSE_MAX_BUFFER_SIZE;
+    CONTROL_PROTOCOL__response_header_t *header = NULL;
+    CONTROL_PROTOCOL__payload_t *payload = NULL;
+
+    common_status = CONTROL_PROTOCOL__pack_sensor_load_and_start_config_request(&request, &request_size, device.get_control_sequence(), section_index);
+    status = (HAILO_COMMON_STATUS__SUCCESS == common_status) ? HAILO_SUCCESS : HAILO_INTERNAL_FAILURE;
+    if (HAILO_SUCCESS != status) {
+        goto exit;
+    }
+
+    status = device.fw_interact((uint8_t*)(&request), request_size, (uint8_t*)&response_buffer, &response_size);
+    if (HAILO_SUCCESS != status) {
+        goto exit;
+    }
+
+    /* Parse response */
+    status = parse_and_validate_response(response_buffer, (uint32_t)(response_size), &header, &payload,
+            &request);
+    if (HAILO_SUCCESS != status) {
+        goto exit;
+    }
+
+    status = HAILO_SUCCESS;
+exit:
+    return status;
+}
+
+hailo_status Control::sensor_reset(Device &device, uint32_t section_index)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    HAILO_COMMON_STATUS_t common_status = HAILO_COMMON_STATUS__UNINITIALIZED;
+    CONTROL_PROTOCOL__request_t request = {};
+    size_t request_size = 0;
+    uint8_t response_buffer[RESPONSE_MAX_BUFFER_SIZE] = {};
+    size_t response_size = RESPONSE_MAX_BUFFER_SIZE;
+    CONTROL_PROTOCOL__response_header_t *header = NULL;
+    CONTROL_PROTOCOL__payload_t *payload = NULL;
+
+    common_status = CONTROL_PROTOCOL__pack_sensor_reset_request(&request, &request_size, device.get_control_sequence(), section_index);
+    status = (HAILO_COMMON_STATUS__SUCCESS == common_status) ? HAILO_SUCCESS : HAILO_INTERNAL_FAILURE;
+    if (HAILO_SUCCESS != status) {
+        goto exit;
+    }
+
+    status = device.fw_interact((uint8_t*)(&request), request_size, (uint8_t*)&response_buffer, &response_size);
+    if (HAILO_SUCCESS != status) {
+        goto exit;
+    }
+
+    /* Parse response */
+    status = parse_and_validate_response(response_buffer, (uint32_t)(response_size), &header, &payload,
+            &request);
+    if (HAILO_SUCCESS != status) {
+        goto exit;
+    }
+
+    status = HAILO_SUCCESS;
+exit:
+    return status;
+}
+
+hailo_status Control::sensor_set_generic_i2c_slave(Device &device, uint16_t slave_address,
+    uint8_t register_address_size, uint8_t bus_index, uint8_t should_hold_bus, uint8_t endianness)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    HAILO_COMMON_STATUS_t common_status = HAILO_COMMON_STATUS__UNINITIALIZED;
+    CONTROL_PROTOCOL__request_t request = {};
+    size_t request_size = 0;
+    uint8_t response_buffer[RESPONSE_MAX_BUFFER_SIZE] = {};
+    size_t response_size = RESPONSE_MAX_BUFFER_SIZE;
+    CONTROL_PROTOCOL__response_header_t *header = NULL;
+    CONTROL_PROTOCOL__payload_t *payload = NULL;
+
+    common_status = CONTROL_PROTOCOL__pack_sensor_set_generic_i2c_slave_request(&request, &request_size, device.get_control_sequence(), slave_address, register_address_size, bus_index, should_hold_bus, endianness);
+    status = (HAILO_COMMON_STATUS__SUCCESS == common_status) ? HAILO_SUCCESS : HAILO_INTERNAL_FAILURE;
+    if (HAILO_SUCCESS != status) {
+        goto exit;
+    }
+
+    status = device.fw_interact((uint8_t*)(&request), request_size, (uint8_t*)&response_buffer, &response_size);
+    if (HAILO_SUCCESS != status) {
+        goto exit;
+    }
+
+    /* Parse response */
+    status = parse_and_validate_response(response_buffer, (uint32_t)(response_size), &header, &payload,
+            &request);
+    if (HAILO_SUCCESS != status) {
+        goto exit;
+    }
+
+    status = HAILO_SUCCESS;
+exit:
+    return status;
+}
+
+
+hailo_status Control::sensor_get_config(Device &device, uint32_t section_index, uint32_t offset, uint32_t data_length,
+    uint8_t *data)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    HAILO_COMMON_STATUS_t common_status = HAILO_COMMON_STATUS__UNINITIALIZED;
+    CONTROL_PROTOCOL__request_t request = {};
+    size_t request_size = 0;
+    uint8_t response_buffer[RESPONSE_MAX_BUFFER_SIZE] = {};
+    size_t response_size = RESPONSE_MAX_BUFFER_SIZE;
+    uint32_t actual_read_data_length = 0;
+    CONTROL_PROTOCOL__response_header_t *header = NULL;
+    CONTROL_PROTOCOL__payload_t *payload = NULL;
+    CONTROL_PROTOCOL__sensor_get_config_response_t *sensor_get_config_response = NULL;
+
+    /* Validate arguments */
+    CHECK_ARG_NOT_NULL(data);
+
+    common_status = CONTROL_PROTOCOL__pack_sensor_get_config_request(&request, &request_size, device.get_control_sequence(), section_index, offset, data_length);
+
+    status = (HAILO_COMMON_STATUS__SUCCESS == common_status) ? HAILO_SUCCESS : HAILO_INTERNAL_FAILURE;
+    if (HAILO_SUCCESS != status) {
+        goto exit;
+    }
+
+    status = device.fw_interact((uint8_t*)(&request), request_size, (uint8_t*)&response_buffer, &response_size);
+    if (HAILO_SUCCESS != status) {
+        goto exit;
+    }
+
+    /* Parse response */
+    status = parse_and_validate_response(response_buffer, (uint32_t)(response_size), &header, &payload,
+            &request);
+    if (HAILO_SUCCESS != status) {
+        goto exit;
+    }
+
+    sensor_get_config_response = (CONTROL_PROTOCOL__sensor_get_config_response_t *)(payload->parameters);
+    actual_read_data_length = BYTE_ORDER__ntohl(sensor_get_config_response->data_length);
+    if (data_length != actual_read_data_length) {
+        status = HAILO_INVALID_CONTROL_RESPONSE;
+        LOGGER__ERROR("Did not read all data from control response");
+        goto exit;
+    }
+    (void)memcpy(data, &sensor_get_config_response->data[0], actual_read_data_length);
+
+    status = HAILO_SUCCESS;
+exit:
+    return status;
+}
+
+hailo_status Control::sensor_get_sections_info(Device &device, uint8_t *data)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    HAILO_COMMON_STATUS_t common_status = HAILO_COMMON_STATUS__UNINITIALIZED;
+    CONTROL_PROTOCOL__request_t request = {};
+    size_t request_size = 0;
+    uint8_t response_buffer[RESPONSE_MAX_BUFFER_SIZE] = {};
+    size_t response_size = RESPONSE_MAX_BUFFER_SIZE;
+    uint32_t actual_read_data_length = 0;
+    CONTROL_PROTOCOL__response_header_t *header = NULL;
+    CONTROL_PROTOCOL__payload_t *payload = NULL;
+    CONTROL_PROTOCOL__sensor_get_sections_info_response_t *get_sections_info_response = NULL;
+
+    /* Validate arguments */
+    CHECK_ARG_NOT_NULL(data);
+
+    common_status = CONTROL_PROTOCOL__pack_sensor_get_sections_info_request(&request, &request_size, device.get_control_sequence());
+
+    status = (HAILO_COMMON_STATUS__SUCCESS == common_status) ? HAILO_SUCCESS : HAILO_INTERNAL_FAILURE;
+    if (HAILO_SUCCESS != status) {
+        goto exit;
+    }
+
+    status = device.fw_interact((uint8_t*)(&request), request_size, (uint8_t*)&response_buffer, &response_size);
+    if (HAILO_SUCCESS != status) {
+        goto exit;
+    }
+
+    /* Parse response */
+    status = parse_and_validate_response(response_buffer, (uint32_t)(response_size), &header, &payload,
+            &request);
+    if (HAILO_SUCCESS != status) {
+        goto exit;
+    }
+
+    get_sections_info_response = (CONTROL_PROTOCOL__sensor_get_sections_info_response_t *)(payload->parameters);
+
+    actual_read_data_length = BYTE_ORDER__ntohl(get_sections_info_response->data_length);
+    if (0 == actual_read_data_length) {
+        status = HAILO_INVALID_CONTROL_RESPONSE;
+        LOGGER__ERROR("Did not read all data from control response");
+        goto exit;
+    }
+    (void)memcpy(data, &get_sections_info_response->data[0], actual_read_data_length);
+    status = HAILO_SUCCESS;
+exit:
+    return status;
+}
+
+hailo_status Control::context_switch_set_main_header(Device &device,
+    CONTROL_PROTOCOL__context_switch_main_header_t *context_switch_header)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    HAILO_COMMON_STATUS_t common_status = HAILO_COMMON_STATUS__UNINITIALIZED;
+    CONTROL_PROTOCOL__request_t request = {};
+    size_t request_size = 0;
+    uint8_t response_buffer[RESPONSE_MAX_BUFFER_SIZE] = {};
+    size_t response_size = RESPONSE_MAX_BUFFER_SIZE;
+    CONTROL_PROTOCOL__response_header_t *header = NULL;
+    CONTROL_PROTOCOL__payload_t *payload = NULL;
+
+    /* Validate arguments */
+    CHECK_ARG_NOT_NULL(context_switch_header);
+
+    common_status = CONTROL_PROTOCOL__pack_context_switch_set_main_header_request(&request, &request_size, device.get_control_sequence(),
+            context_switch_header);
+    status = (HAILO_COMMON_STATUS__SUCCESS == common_status) ? HAILO_SUCCESS : HAILO_INTERNAL_FAILURE;
+    if (HAILO_SUCCESS != status) {
+        goto exit;
+    }
+
+    status = device.fw_interact((uint8_t*)(&request), request_size, (uint8_t*)&response_buffer, &response_size);
+    if (HAILO_SUCCESS != status) {
+        goto exit;
+    }
+
+    /* Parse response */
+    status = parse_and_validate_response(response_buffer, (uint32_t)(response_size), &header, &payload,
+            &request);
+    if (HAILO_SUCCESS != status) {
+        goto exit;
+    }
+
+    status = HAILO_SUCCESS;
+exit:
+    return status;
+}
+
+hailo_status Control::context_switch_set_context_info_chunk(Device &device,
+    CONTROL_PROTOCOL__context_switch_context_info_single_control_t *context_info)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    HAILO_COMMON_STATUS_t common_status = HAILO_COMMON_STATUS__UNINITIALIZED;
+    CONTROL_PROTOCOL__request_t request = {};
+    size_t request_size = 0;
+    uint8_t response_buffer[RESPONSE_MAX_BUFFER_SIZE] = {};
+    size_t response_size = RESPONSE_MAX_BUFFER_SIZE;
+    CONTROL_PROTOCOL__response_header_t *header = NULL;
+    CONTROL_PROTOCOL__payload_t *payload = NULL;
+
+    /* Validate arguments */
+    CHECK_ARG_NOT_NULL(context_info);
+
+    common_status = CONTROL_PROTOCOL__pack_context_switch_set_context_info_request(&request, &request_size, device.get_control_sequence(),
+            context_info);
+    status = (HAILO_COMMON_STATUS__SUCCESS == common_status) ? HAILO_SUCCESS : HAILO_INTERNAL_FAILURE;
+    if (HAILO_SUCCESS != status) {
+        goto exit;
+    }
+
+    status = device.fw_interact((uint8_t*)(&request), request_size, (uint8_t*)&response_buffer, &response_size);
+    if (HAILO_SUCCESS != status) {
+        goto exit;
+    }
+
+    /* Parse response */
+    status = parse_and_validate_response(response_buffer, (uint32_t)(response_size), &header, &payload,
+            &request);
+    if (HAILO_SUCCESS != status) {
+        /* In case of max memory error, add LOGGER ERROR, and set indicative error to the user */
+        CHECK((CONTEXT_SWITCH_TASK_STATUS_ADD_TRIGGER_FUNCTION_REACHED_FORBIDDEN_MEMORY_SPACE != header->status.major_status),
+            HAILO_OUT_OF_FW_MEMORY,
+            "Configfured network groups Reached maximum device internal memory. please consider using less network groups.");
+        goto exit;
+    }
+
+    status = HAILO_SUCCESS;
+exit:
+    return status;
+}
+
+hailo_status Control::context_switch_set_context_info(Device &device,
+    CONTROL_PROTOCOL__context_switch_context_info_t *context_info)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    CONTROL_PROTOCOL__context_switch_context_info_single_control_t context_info_single_control = {};
+    uint8_t slice_index = 0;
+    bool is_first_control_per_context = false;
+    bool is_last_control_per_context = false;
+    uint16_t slice_start_offset = 0;
+    uint16_t slice_end_offset = 0;
+
+    /* Validate arguments */
+    CHECK_ARG_NOT_NULL(context_info);
+
+    do {
+        if (0 == slice_index) {
+            is_first_control_per_context = true;
+        } else {
+            is_first_control_per_context = false;
+        }
+        if (0 == context_info->control_slicing_data.control_slicing_offsets[slice_index]) {
+            is_last_control_per_context = true;
+        } else {
+            is_last_control_per_context = false;
+        }
+
+        /* Build single control struct from context info */
+
+        context_info_single_control.is_first_control_per_context = is_first_control_per_context;
+        context_info_single_control.is_last_control_per_context = is_last_control_per_context;
+        static_assert(sizeof(context_info_single_control.context_cfg_base_address) == sizeof(context_info->context_cfg_base_address),
+            "mismatch in sizes of context_cfg_base_address");
+        static_assert(sizeof(context_info_single_control.context_cfg_total_descriptors) == sizeof(context_info->context_total_descriptors),
+            "mismatch in sizes of context_cfg_total_descriptors");
+        static_assert(sizeof(context_info_single_control.context_stream_remap_data) == sizeof(context_info->context_stream_remap_data),
+            "mismatch in sizes of context_stream_remap_data");
+        memcpy(context_info_single_control.context_cfg_base_address,
+                context_info->context_cfg_base_address,
+                sizeof(context_info_single_control.context_cfg_base_address));
+        memcpy(context_info_single_control.context_cfg_total_descriptors,
+                context_info->context_total_descriptors,
+                sizeof(context_info_single_control.context_cfg_total_descriptors));
+
+        memcpy(&(context_info_single_control.context_stream_remap_data),
+                &(context_info->context_stream_remap_data),
+                sizeof(context_info_single_control.context_stream_remap_data));
+
+        context_info_single_control.number_of_edge_layers =
+            context_info->control_slicing_data.slice_edge_layers[slice_index];
+        context_info_single_control.number_of_trigger_groups =
+            context_info->control_slicing_data.slice_triggers[slice_index];
+
+        if (is_first_control_per_context) {
+            slice_start_offset = 0;
+        } else {
+            slice_start_offset = context_info->control_slicing_data.control_slicing_offsets[slice_index-1];
+        }
+        if (is_last_control_per_context) {
+            slice_end_offset = (uint16_t)context_info->context_network_data_length;
+        } else {
+            slice_end_offset = context_info->control_slicing_data.control_slicing_offsets[slice_index];
+        }
+
+        /* Validation on the memory before memcpy. Should not fail */
+        if ((CONTROL_PROTOCOL__CONTEXT_NETWORK_DATA_MAX_SIZE < slice_end_offset) ||
+                (CONTROL_PROTOCOL__CONTEXT_NETWORK_DATA_SINGLE_CONTROL_MAX_SIZE <
+                 (uint16_t)(slice_end_offset - slice_start_offset))) {
+            status = HAILO_OUT_OF_HOST_MEMORY;
+            goto exit;
+        }
+
+        context_info_single_control.context_network_data_length = (slice_end_offset - slice_start_offset);
+        memcpy(context_info_single_control.context_network_data,
+                (uint8_t *)((uintptr_t)context_info->context_network_data + slice_start_offset),
+                context_info_single_control.context_network_data_length);
+
+        status = Control::context_switch_set_context_info_chunk(device, &context_info_single_control);
+        CHECK_SUCCESS(status);
+
+        /* Advance control slice index */
+        slice_index++;
+    } while (!is_last_control_per_context);
+
+    status = HAILO_SUCCESS;
+exit:
+    return status;
+}
+
+hailo_status Control::write_context_switch_info(Device &device,
+    CONTROL_PROTOCOL__context_switch_info_t *context_switch_info)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    uint8_t network_group_index = 0;
+    uint8_t total_context_counter = 0;
+    uint8_t dynamic_contexts_index = 0;
+
+    /* Validate arguments */
+    CHECK_ARG_NOT_NULL(context_switch_info);
+
+    status = Control::context_switch_set_main_header(device, &(context_switch_info->context_switch_main_header));
+    CHECK_SUCCESS(status);
+
+    /* For each network_group */
+    for (network_group_index = 0; network_group_index < context_switch_info->context_switch_main_header.application_count;
+            network_group_index++) {
+        /* number of contexts in network_group - all dynamic contexts +1 for the additional preliminary context */
+        uint8_t total_contexts_in_app = (uint8_t)(
+                context_switch_info->context_switch_main_header.application_header[network_group_index].dynamic_contexts_count + 1);
+        /* For each context in network_group */
+        for (dynamic_contexts_index = 0; dynamic_contexts_index < total_contexts_in_app; dynamic_contexts_index++) {
+            if (CONTROL_PROTOCOL__MAX_TOTAL_CONTEXTS <= total_context_counter) {
+                status = HAILO_INVALID_CONTEXT_COUNT;
+                goto exit;
+            }
+
+            /* Send context info */
+            status = context_switch_set_context_info(device, &(context_switch_info->context[total_context_counter]));
+            CHECK_SUCCESS(status);
+            total_context_counter++;
+        }
+    }
+
+    status = HAILO_SUCCESS;
+exit:
+    return status;
+}
+
+hailo_status Control::idle_time_get_measurement(Device &device, uint64_t *measurement)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    HAILO_COMMON_STATUS_t common_status = HAILO_COMMON_STATUS__UNINITIALIZED;
+    CONTROL_PROTOCOL__request_t request = {};
+    size_t request_size = 0;
+    uint8_t response_buffer[RESPONSE_MAX_BUFFER_SIZE] = {};
+    size_t response_size = RESPONSE_MAX_BUFFER_SIZE;
+    CONTROL_PROTOCOL__response_header_t *header = NULL;
+    CONTROL_PROTOCOL__payload_t *payload = NULL;
+    CONTROL_PROTOCOL__idle_time_get_measurement_response_t *idle_time_get_measurement_response = NULL;
+
+    /* Validate arguments */
+    CHECK_ARG_NOT_NULL(measurement);
+
+    common_status = CONTROL_PROTOCOL__pack_idle_time_get_measuremment_request(&request, &request_size, device.get_control_sequence());
+    status = (HAILO_COMMON_STATUS__SUCCESS == common_status) ? HAILO_SUCCESS : HAILO_INTERNAL_FAILURE;
+    if (HAILO_SUCCESS != status) {
+        LOGGER__ERROR("failed CONTROL_PROTOCOL__pack_idle_time_get_measuremment_request with status {:#X}", common_status);
+        goto exit;
+    }
+
+    status = device.fw_interact((uint8_t*)(&request), request_size, (uint8_t*)&response_buffer, &response_size);
+    if (HAILO_SUCCESS != status) {
+        LOGGER__ERROR("failed idle_time_get_measurement control with status {}", status);
+        goto exit;
+    }
+
+    /* Parse response */
+    status = parse_and_validate_response(response_buffer, (uint32_t)(response_size), &header, &payload,
+            &request);
+    if (HAILO_SUCCESS != status) {
+        LOGGER__ERROR("failed validating idle_time_get_measurement control response with status {}", status);
+        goto exit;
+    }
+
+    idle_time_get_measurement_response = (CONTROL_PROTOCOL__idle_time_get_measurement_response_t *)(payload->parameters);
+
+    /*copy the measurement*/
+    *measurement = BYTE_ORDER__ntohll(idle_time_get_measurement_response->idle_time_ns);
+
+    LOGGER__DEBUG("Received idle measurement low: {:#X} ns",
+        *((uint32_t *) measurement));
+    LOGGER__DEBUG("Received idle measurement high: {:#X} ns",
+        *(((uint32_t *) measurement) + 1));
+
+    status = HAILO_SUCCESS;
+exit:
+    return status;
+}
+
+hailo_status Control::idle_time_set_measurement(Device &device, uint8_t measurement_enable)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    HAILO_COMMON_STATUS_t common_status = HAILO_COMMON_STATUS__UNINITIALIZED;
+    CONTROL_PROTOCOL__request_t request = {};
+    size_t request_size = 0;
+    uint8_t response_buffer[RESPONSE_MAX_BUFFER_SIZE] = {};
+    size_t response_size = RESPONSE_MAX_BUFFER_SIZE;
+    CONTROL_PROTOCOL__response_header_t *header = NULL;
+    CONTROL_PROTOCOL__payload_t *payload = NULL;
+
+    LOGGER__DEBUG("Sending idle_time_set_measurement with parameter {}", measurement_enable);
+
+    common_status = CONTROL_PROTOCOL__pack_idle_time_set_measuremment_request(&request, &request_size, device.get_control_sequence(), measurement_enable);
+    status = (HAILO_COMMON_STATUS__SUCCESS == common_status) ? HAILO_SUCCESS : HAILO_INTERNAL_FAILURE;
+    if (HAILO_SUCCESS != status) {
+        LOGGER__ERROR("failed CONTROL_PROTOCOL__pack_idle_time_set_measuremment_request with status {:#X}", common_status);
+        goto exit;
+    }
+
+    status = device.fw_interact((uint8_t*)(&request), request_size, (uint8_t*)&response_buffer, &response_size);
+    if (HAILO_SUCCESS != status) {
+        goto exit;
+    }
+
+    /* Parse response */
+    status = parse_and_validate_response(response_buffer, (uint32_t)(response_size), &header, &payload,
+            &request);
+    if (HAILO_SUCCESS != status) {
+        LOGGER__ERROR("failed idle_time_set_measurement control with status {}", status);
+        goto exit;
+    }
+    status = HAILO_SUCCESS;
+exit:
+    return status;
+}
+
+hailo_status Control::set_pause_frames(Device &device, uint8_t rx_pause_frames_enable)
+{
+
+    LOGGER__DEBUG("Sending set_pause_frames with parameter {}", rx_pause_frames_enable);
+
+    CONTROL_PROTOCOL__request_t request = {};
+    size_t request_size = 0;
+    HAILO_COMMON_STATUS_t common_status = CONTROL_PROTOCOL__pack_set_pause_frames_request(&request, &request_size,
+                                             device.get_control_sequence(), rx_pause_frames_enable);
+    hailo_status status = (HAILO_COMMON_STATUS__SUCCESS == common_status) ? HAILO_SUCCESS : HAILO_INTERNAL_FAILURE;
+    CHECK_SUCCESS(status);
+
+    uint8_t response_buffer[RESPONSE_MAX_BUFFER_SIZE] = {};
+    size_t response_size = RESPONSE_MAX_BUFFER_SIZE;
+    status = device.fw_interact((uint8_t*)(&request), request_size, (uint8_t*)&response_buffer, &response_size);
+    CHECK_SUCCESS(status);
+
+    /* Parse response */
+    CONTROL_PROTOCOL__response_header_t *header = NULL;
+    CONTROL_PROTOCOL__payload_t *payload = NULL;
+    status = parse_and_validate_response(response_buffer, (uint32_t)(response_size), &header, &payload,
+            &request);
+    CHECK_SUCCESS(status);
+
+    return HAILO_SUCCESS;
+}
+
+hailo_status Control::download_context_action_list_chunk(Device &device, uint8_t context_index, uint16_t action_list_offset,
+        size_t action_list_max_size, uint32_t *base_address, uint8_t *action_list, uint16_t *action_list_length,
+        bool *is_action_list_end, uint32_t *batch_counter)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    HAILO_COMMON_STATUS_t common_status = HAILO_COMMON_STATUS__UNINITIALIZED;
+    CONTROL_PROTOCOL__request_t request = {};
+    size_t request_size = 0;
+    uint8_t response_buffer[RESPONSE_MAX_BUFFER_SIZE] = {};
+    size_t response_size = sizeof(response_buffer);
+    CONTROL_PROTOCOL__response_header_t *header = NULL;
+    CONTROL_PROTOCOL__payload_t *payload = NULL;
+    CONTROL_PROTOCOL__download_context_action_list_response_t *context_action_list_response = NULL;
+
+
+    /* Validate arguments */
+    CHECK_ARG_NOT_NULL(base_address);
+    CHECK_ARG_NOT_NULL(action_list);
+    CHECK_ARG_NOT_NULL(action_list_length);
+
+    common_status = CONTROL_PROTOCOL__pack_download_context_action_list_request(&request, &request_size, device.get_control_sequence(),
+            context_index, action_list_offset);
+
+    status = (HAILO_COMMON_STATUS__SUCCESS == common_status) ? HAILO_SUCCESS : HAILO_INTERNAL_FAILURE;
+    if (HAILO_SUCCESS != status) {
+        goto exit;
+    }
+
+    status = device.fw_interact((uint8_t*)(&request), request_size, (uint8_t*)&response_buffer, &response_size);
+    if (HAILO_SUCCESS != status) {
+        goto exit;
+    }
+
+    /* Parse response */
+    status = parse_and_validate_response(response_buffer, (uint32_t)(response_size), &header, &payload,
+            &request);
+    if (HAILO_SUCCESS != status) {
+        goto exit;
+    }
+
+    context_action_list_response = (CONTROL_PROTOCOL__download_context_action_list_response_t *)(payload->parameters);
+
+    if (0 == BYTE_ORDER__ntohl(context_action_list_response->action_list_length)) {
+        status = HAILO_INVALID_CONTROL_RESPONSE;
+        LOGGER__ERROR("Received empty action list");
+        goto exit;
+    }
+    if (0 == BYTE_ORDER__ntohl(context_action_list_response->base_address)) {
+        status = HAILO_INVALID_CONTROL_RESPONSE;
+        LOGGER__ERROR("Received NULL pointer to base address");
+        goto exit;
+    }
+
+    if (action_list_max_size < BYTE_ORDER__ntohl(context_action_list_response->action_list_length)) {
+        status = HAILO_INVALID_CONTROL_RESPONSE;
+        LOGGER__ERROR("Received action list bigger than allocated user buffer");
+    }
+
+    (void)memcpy(action_list, context_action_list_response->action_list
+            ,BYTE_ORDER__ntohl(context_action_list_response->action_list_length));
+
+    *action_list_length = (uint16_t)(BYTE_ORDER__ntohl(context_action_list_response->action_list_length));
+    *base_address = BYTE_ORDER__ntohl(context_action_list_response->base_address);
+    *is_action_list_end = context_action_list_response->is_action_list_end;
+    *batch_counter = BYTE_ORDER__ntohl(context_action_list_response->batch_counter);
+
+    status = HAILO_SUCCESS;
+exit:
+    return status;
+}
+
+hailo_status Control::download_context_action_list(Device &device, uint8_t context_index, size_t action_list_max_size,
+        uint32_t *base_address, uint8_t *action_list, uint16_t *action_list_length, uint32_t *batch_counter)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    bool is_action_list_end = false;
+    uint16_t chunk_action_list_length = 0;
+    uint16_t accumulated_action_list_length = 0;
+    uint8_t *action_list_current_offset = 0;
+    size_t remaining_action_list_max_size = 0;
+    uint32_t chunk_base_address = 0;
+    uint32_t batch_counter_local = 0;
+
+    /* Validate arguments */
+    CHECK_ARG_NOT_NULL(base_address);
+    CHECK_ARG_NOT_NULL(action_list);
+    CHECK_ARG_NOT_NULL(action_list_length);
+
+    action_list_current_offset = action_list;
+    remaining_action_list_max_size = action_list_max_size;
+
+    do {
+        status = download_context_action_list_chunk(device, context_index, accumulated_action_list_length,
+                remaining_action_list_max_size, &chunk_base_address, action_list_current_offset,
+                &chunk_action_list_length, &is_action_list_end, &batch_counter_local);
+        CHECK_SUCCESS(status);
+
+        accumulated_action_list_length = (uint16_t)(accumulated_action_list_length + chunk_action_list_length);
+        action_list_current_offset += chunk_action_list_length;
+        remaining_action_list_max_size -= chunk_action_list_length;
+    }
+    while (!is_action_list_end);
+
+    /* Set output variables */
+    *base_address =  chunk_base_address;
+    *action_list_length = accumulated_action_list_length;
+    *batch_counter = batch_counter_local;
+
+    return HAILO_SUCCESS;
+}
+
+hailo_status Control::change_context_switch_status(Device &device,
+        CONTROL_PROTOCOL__CONTEXT_SWITCH_STATUS_t state_machine_status,
+        uint8_t network_group_index)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    HAILO_COMMON_STATUS_t common_status = HAILO_COMMON_STATUS__UNINITIALIZED;
+    CONTROL_PROTOCOL__request_t request = {};
+    size_t request_size = 0;
+    uint8_t response_buffer[RESPONSE_MAX_BUFFER_SIZE] = {};
+    size_t response_size = sizeof(response_buffer);
+    CONTROL_PROTOCOL__response_header_t *header = NULL;
+    CONTROL_PROTOCOL__payload_t *payload = NULL;
+
+    common_status = CONTROL_PROTOCOL__pack_change_context_switch_status_request(&request, &request_size,
+            device.get_control_sequence(), state_machine_status, network_group_index);
+    status = (HAILO_COMMON_STATUS__SUCCESS == common_status) ? HAILO_SUCCESS : HAILO_INTERNAL_FAILURE;
+    if (HAILO_SUCCESS != status) {
+        goto exit;
+    }
+
+    status = device.fw_interact((uint8_t*)(&request), request_size, (uint8_t*)&response_buffer, &response_size);
+    if (HAILO_SUCCESS != status) {
+        goto exit;
+    }
+
+    /* Parse response */
+    status = parse_and_validate_response(response_buffer, (uint32_t)(response_size), &header, &payload,
+            &request);
+    if (HAILO_SUCCESS != status) {
+        goto exit;
+    }
+
+    status = HAILO_SUCCESS;
+exit:
+    return status;
+}
+
+hailo_status Control::enable_network_group(Device &device, uint8_t network_group_index)
+{
+    return Control::change_context_switch_status(device, CONTROL_PROTOCOL__CONTEXT_SWITCH_STATUS_ENABLED, network_group_index);
+}
+
+hailo_status Control::reset_context_switch_state_machine(Device &device)
+{
+    return Control::change_context_switch_status(device, CONTROL_PROTOCOL__CONTEXT_SWITCH_STATUS_RESET, 0);
+}
+
+hailo_status Control::wd_enable(Device &device, uint8_t cpu_id, bool should_enable)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    HAILO_COMMON_STATUS_t common_status = HAILO_COMMON_STATUS__UNINITIALIZED;
+    CONTROL_PROTOCOL__request_t request = {};
+    size_t request_size = 0;
+    uint8_t response_buffer[RESPONSE_MAX_BUFFER_SIZE] = {};
+    size_t response_size = RESPONSE_MAX_BUFFER_SIZE;
+    CONTROL_PROTOCOL__response_header_t *header = NULL;
+    CONTROL_PROTOCOL__payload_t *payload = NULL;
+
+    LOGGER__DEBUG("Sending wd_enable with parameters cpu_id: {}, should_enable: {}", cpu_id, should_enable);
+
+    common_status = CONTROL_PROTOCOL__pack_wd_enable(&request, &request_size, device.get_control_sequence(), cpu_id, should_enable);
+    status = (HAILO_COMMON_STATUS__SUCCESS == common_status) ? HAILO_SUCCESS : HAILO_INTERNAL_FAILURE;
+    if (HAILO_SUCCESS != status) {
+        LOGGER__ERROR("failed CONTROL_PROTOCOL__pack_wd_enable with status {:#X}", common_status);
+        goto exit;
+    }
+
+    status = device.fw_interact((uint8_t*)(&request), request_size, (uint8_t*)&response_buffer, &response_size);
+    if (HAILO_SUCCESS != status) {
+        goto exit;
+    }
+
+    /* Parse response */
+    status = parse_and_validate_response(response_buffer, (uint32_t)(response_size), &header, &payload,
+            &request);
+    if (HAILO_SUCCESS != status) {
+        LOGGER__ERROR("failed wd_enable control with status {}", status);
+        goto exit;
+    }
+
+    status = HAILO_SUCCESS;
+exit:
+    return status;
+}
+hailo_status Control::wd_config(Device &device, uint8_t cpu_id, uint32_t wd_cycles, CONTROL_PROTOCOL__WATCHDOG_MODE_t wd_mode)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    HAILO_COMMON_STATUS_t common_status = HAILO_COMMON_STATUS__UNINITIALIZED;
+    CONTROL_PROTOCOL__request_t request = {};
+    size_t request_size = 0;
+    uint8_t response_buffer[RESPONSE_MAX_BUFFER_SIZE] = {};
+    size_t response_size = RESPONSE_MAX_BUFFER_SIZE;
+    CONTROL_PROTOCOL__response_header_t *header = NULL;
+    CONTROL_PROTOCOL__payload_t *payload = NULL;
+
+    LOGGER__DEBUG("Sending wd_config with parameters cpu_id: {}, wd_cycles: {} wd_mode {}", cpu_id, wd_cycles, wd_mode);
+
+    common_status = CONTROL_PROTOCOL__pack_wd_config(&request, &request_size, device.get_control_sequence(), cpu_id, wd_cycles, wd_mode);
+    status = (HAILO_COMMON_STATUS__SUCCESS == common_status) ? HAILO_SUCCESS : HAILO_INTERNAL_FAILURE;
+    if (HAILO_SUCCESS != status) {
+        LOGGER__ERROR("failed CONTROL_PROTOCOL__pack_wd_config with status {:#X}", common_status);
+        goto exit;
+    }
+
+    status = device.fw_interact((uint8_t*)(&request), request_size, (uint8_t*)&response_buffer, &response_size);
+    if (HAILO_SUCCESS != status) {
+        goto exit;
+    }
+
+    /* Parse response */
+    status = parse_and_validate_response(response_buffer, (uint32_t)(response_size), &header, &payload,
+            &request);
+    if (HAILO_SUCCESS != status) {
+        LOGGER__ERROR("failed wd_config control with status {}", status);
+        goto exit;
+    }
+
+    status = HAILO_SUCCESS;
+exit:
+    return status;
+}
+
+hailo_status Control::previous_system_state(Device &device, uint8_t cpu_id, CONTROL_PROTOCOL__system_state_t *system)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    HAILO_COMMON_STATUS_t common_status = HAILO_COMMON_STATUS__UNINITIALIZED;
+    CONTROL_PROTOCOL__request_t request = {};
+    size_t request_size = 0;
+    uint8_t response_buffer[RESPONSE_MAX_BUFFER_SIZE] = {};
+    size_t response_size = RESPONSE_MAX_BUFFER_SIZE;
+    CONTROL_PROTOCOL__response_header_t *header = NULL;
+    CONTROL_PROTOCOL__payload_t *payload = NULL;
+    CONTROL_PROTOCOL__previous_system_state_response_t *previous_system_state_response = NULL;
+
+    LOGGER__DEBUG("Sending previous system state with parameter cpu_id: {}", cpu_id);
+
+    CHECK_ARG_NOT_NULL(system);
+
+    common_status = CONTROL_PROTOCOL__pack_previous_system_state(&request, &request_size, device.get_control_sequence(), cpu_id);
+    status = (HAILO_COMMON_STATUS__SUCCESS == common_status) ? HAILO_SUCCESS : HAILO_INTERNAL_FAILURE;
+    if (HAILO_SUCCESS != status) {
+        LOGGER__ERROR("failed CONTROL_PROTOCOL__pack_previous_system_state with status {:#X}", common_status);
+        goto exit;
+    }
+
+    status = device.fw_interact((uint8_t*)(&request), request_size, (uint8_t*)&response_buffer, &response_size);
+    if (HAILO_SUCCESS != status) {
+        goto exit;
+    }
+
+    /* Parse response */
+    status = parse_and_validate_response(response_buffer, (uint32_t)(response_size), &header, &payload,
+            &request);
+    if (HAILO_SUCCESS != status) {
+        LOGGER__ERROR("failed previous_system_state control with status {}", status);
+        goto exit;
+    }
+
+    previous_system_state_response = (CONTROL_PROTOCOL__previous_system_state_response_t *)(payload->parameters);
+
+    /*copy the measurement*/
+    *system = BYTE_ORDER__ntohl(previous_system_state_response->system_state);
+
+    status = HAILO_SUCCESS;
+exit:
+    return status;
+}
+
+hailo_status Control::set_dataflow_interrupt(Device &device, uint8_t interrupt_type, uint8_t interrupt_index,
+        uint8_t interrupt_sub_index)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    HAILO_COMMON_STATUS_t common_status = HAILO_COMMON_STATUS__UNINITIALIZED;
+    CONTROL_PROTOCOL__request_t request = {};
+    size_t request_size = 0;
+    uint8_t response_buffer[RESPONSE_MAX_BUFFER_SIZE] = {};
+    size_t response_size = sizeof(response_buffer);
+    CONTROL_PROTOCOL__response_header_t *header = NULL;
+    CONTROL_PROTOCOL__payload_t *payload = NULL;
+
+    common_status = CONTROL_PROTOCOL__pack_set_dataflow_interrupt_request(&request, &request_size, device.get_control_sequence(),
+            interrupt_type, interrupt_index, interrupt_sub_index);
+    status = (HAILO_COMMON_STATUS__SUCCESS == common_status) ? HAILO_SUCCESS : HAILO_INTERNAL_FAILURE;
+    if (HAILO_SUCCESS != status) {
+        goto exit;
+    }
+
+    status = device.fw_interact((uint8_t*)(&request), request_size, (uint8_t*)&response_buffer, &response_size);
+    if (HAILO_SUCCESS != status) {
+        goto exit;
+    }
+
+    /* Parse response */
+    status = parse_and_validate_response(response_buffer, (uint32_t)(response_size), &header, &payload,
+            &request);
+    if (HAILO_SUCCESS != status) {
+        goto exit;
+    }
+
+    status = HAILO_SUCCESS;
+exit:
+    return status;
+}
+
+hailo_status Control::d2h_notification_manager_set_host_info(Device &device, uint16_t host_port, uint32_t host_ip_address)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    HAILO_COMMON_STATUS_t common_status = HAILO_COMMON_STATUS__UNINITIALIZED;
+    CONTROL_PROTOCOL__request_t request = {};
+    size_t request_size = 0;
+    uint8_t response_buffer[RESPONSE_MAX_BUFFER_SIZE] = {};
+    size_t response_size = sizeof(response_buffer);
+    CONTROL_PROTOCOL__response_header_t *header = NULL;
+    CONTROL_PROTOCOL__payload_t *payload = NULL;
+    auto connection_type = (Device::Type::PCIE == device.get_type() ? D2H_EVENT_COMMUNICATION_TYPE_PCIE : D2H_EVENT_COMMUNICATION_TYPE_UDP);
+
+    LOGGER__DEBUG("Set d2h notification manager new host info : connection_type {}, remote_port {}, remote_ip_address {}", connection_type, host_port, host_ip_address);
+
+    common_status = CONTROL_PROTOCOL__pack_d2h_event_manager_set_host_info_request(&request, &request_size, device.get_control_sequence(),
+            static_cast<uint8_t>(connection_type), host_port, host_ip_address);
+    status = (HAILO_COMMON_STATUS__SUCCESS == common_status) ? HAILO_SUCCESS : HAILO_INTERNAL_FAILURE;
+    if (HAILO_SUCCESS != status) {
+        goto exit;
+    }
+
+    status = device.fw_interact((uint8_t*)(&request), request_size, (uint8_t*)&response_buffer, &response_size);
+    if (HAILO_SUCCESS != status) {
+        goto exit;
+    }
+
+    /* Parse response */
+    status = parse_and_validate_response(response_buffer, (uint32_t)(response_size), &header, &payload,
+            &request);
+    if (HAILO_SUCCESS != status) {
+        goto exit;
+    }
+
+    status = HAILO_SUCCESS;
+exit:
+    return status;
+}
+
+hailo_status Control::d2h_notification_manager_send_host_info_notification(Device &device, uint8_t notification_priority)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    HAILO_COMMON_STATUS_t common_status = HAILO_COMMON_STATUS__UNINITIALIZED;
+    CONTROL_PROTOCOL__request_t request = {};
+    size_t request_size = 0;
+    uint8_t response_buffer[RESPONSE_MAX_BUFFER_SIZE] = {};
+    size_t response_size = sizeof(response_buffer);
+    CONTROL_PROTOCOL__response_header_t *header = NULL;
+    CONTROL_PROTOCOL__payload_t *payload = NULL;
+
+    LOGGER__DEBUG("Send d2h host notification with priority {}", notification_priority);
+
+    common_status = CONTROL_PROTOCOL__pack_d2h_event_manager_send_host_info_event_request(&request, &request_size, device.get_control_sequence(), notification_priority);
+    status = (HAILO_COMMON_STATUS__SUCCESS == common_status) ? HAILO_SUCCESS : HAILO_INTERNAL_FAILURE;
+    if (HAILO_SUCCESS != status) {
+        goto exit;
+    }
+
+    status = device.fw_interact((uint8_t*)(&request), request_size, (uint8_t*)&response_buffer, &response_size);
+    if (HAILO_SUCCESS != status) {
+        goto exit;
+    }
+
+    /* Parse response */
+    status = parse_and_validate_response(response_buffer, (uint32_t)(response_size), &header, &payload,
+            &request);
+    if (HAILO_SUCCESS != status) {
+        goto exit;
+    }
+
+    status = HAILO_SUCCESS;
+exit:
+    return status;
+}
+
+hailo_status Control::switch_network_group(Device &device, uint8_t network_group_index)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    HAILO_COMMON_STATUS_t common_status = HAILO_COMMON_STATUS__UNINITIALIZED;
+    CONTROL_PROTOCOL__request_t request = {};
+    size_t request_size = 0;
+    uint8_t response_buffer[RESPONSE_MAX_BUFFER_SIZE] = {};
+    size_t response_size = sizeof(response_buffer);
+    CONTROL_PROTOCOL__response_header_t *header = NULL;
+    CONTROL_PROTOCOL__payload_t *payload = NULL;
+
+
+    LOGGER__DEBUG("Set network_group_index {}", network_group_index);
+
+    common_status = CONTROL_PROTOCOL__pack_switch_application_request(&request, &request_size, device.get_control_sequence(),
+            network_group_index);
+    status = (HAILO_COMMON_STATUS__SUCCESS == common_status) ? HAILO_SUCCESS : HAILO_INTERNAL_FAILURE;
+    if (HAILO_SUCCESS != status) {
+        goto exit;
+    }
+
+    status = device.fw_interact((uint8_t*)(&request), request_size, (uint8_t*)&response_buffer, &response_size);
+    if (HAILO_SUCCESS != status) {
+        goto exit;
+    }
+
+    /* Parse response */
+    status = parse_and_validate_response(response_buffer, (uint32_t)(response_size), &header, &payload,
+            &request);
+    if (HAILO_SUCCESS != status) {
+        goto exit;
+    }
+
+    status = HAILO_SUCCESS;
+exit:
+    return status;
+}
+
+hailo_status Control::get_chip_temperature(Device &device, hailo_chip_temperature_info_t *temp_info)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    HAILO_COMMON_STATUS_t common_status = HAILO_COMMON_STATUS__UNINITIALIZED;
+    CONTROL_PROTOCOL__request_t request = {};
+    size_t request_size = 0;
+    uint8_t response_buffer[RESPONSE_MAX_BUFFER_SIZE] = {};
+    size_t response_size = sizeof(response_buffer);
+    CONTROL_PROTOCOL__response_header_t *header = NULL;
+    CONTROL_PROTOCOL__payload_t *payload = NULL;
+    CONTROL_PROTOCOL__get_chip_temperature_response_t* temps = NULL;
+
+    common_status = CONTROL_PROTOCOL__pack_get_chip_temperature_request(&request, &request_size, device.get_control_sequence());
+    status = (HAILO_COMMON_STATUS__SUCCESS == common_status) ? HAILO_SUCCESS : HAILO_INTERNAL_FAILURE;
+    if (HAILO_SUCCESS != status) {
+        goto exit;
+    }
+
+    status = device.fw_interact((uint8_t*)(&request), request_size, (uint8_t*)&response_buffer, &response_size);
+    if (HAILO_SUCCESS != status) {
+        goto exit;
+    }
+
+    /* Parse response */
+    status = parse_and_validate_response(response_buffer, (uint32_t)(response_size), &header, &payload,
+            &request);
+    if (HAILO_SUCCESS != status) {
+        goto exit;
+    }
+
+    temps = (CONTROL_PROTOCOL__get_chip_temperature_response_t *)(payload->parameters);
+    temp_info->sample_count = BYTE_ORDER__ntohs(temps->info.sample_count);
+    temp_info->ts0_temperature = temps->info.ts0_temperature;
+    temp_info->ts1_temperature = temps->info.ts1_temperature;
+
+    status = HAILO_SUCCESS;
+exit:
+    return status;
+}
+
+hailo_status Control::enable_debugging(Device &device, bool is_rma)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    HAILO_COMMON_STATUS_t common_status = HAILO_COMMON_STATUS__UNINITIALIZED;
+    CONTROL_PROTOCOL__request_t request = {};
+    size_t request_size = 0;
+    uint8_t response_buffer[RESPONSE_MAX_BUFFER_SIZE] = {};
+    size_t response_size = sizeof(response_buffer);
+    CONTROL_PROTOCOL__response_header_t *header = NULL;
+    CONTROL_PROTOCOL__payload_t *payload = NULL;
+
+    common_status = CONTROL_PROTOCOL__pack_enable_debugging_request(&request, &request_size, device.get_control_sequence(), is_rma);
+    status = (HAILO_COMMON_STATUS__SUCCESS == common_status) ? HAILO_SUCCESS : HAILO_INTERNAL_FAILURE;
+    if (HAILO_SUCCESS != status) {
+        goto exit;
+    }
+
+    status = device.fw_interact((uint8_t*)(&request), request_size, (uint8_t*)&response_buffer, &response_size);
+    if (HAILO_SUCCESS != status) {
+        goto exit;
+    }
+
+    /* Parse response */
+    status = parse_and_validate_response(response_buffer, (uint32_t)(response_size), &header, &payload,
+            &request);
+    if (HAILO_SUCCESS != status) {
+        goto exit;
+    }
+
+    status = HAILO_SUCCESS;
+exit:
+    return status;
+}
+
+Expected<hailo_extended_device_information_t> Control::get_extended_device_information(Device &device)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    HAILO_COMMON_STATUS_t common_status = HAILO_COMMON_STATUS__UNINITIALIZED;
+    CONTROL_PROTOCOL__request_t request = {};
+    size_t request_size = 0;
+    uint8_t response_buffer[RESPONSE_MAX_BUFFER_SIZE] = {};
+    size_t response_size = RESPONSE_MAX_BUFFER_SIZE;
+    CONTROL_PROTOCOL__response_header_t *header = NULL;
+    CONTROL_PROTOCOL__payload_t *payload = NULL;
+    CONTROL_PROTOCOL__get_extended_device_information_response_t *get_extended_device_information_response = NULL;
+
+    /* Validate arguments */
+
+    common_status = CONTROL_PROTOCOL__pack_get_extended_device_information_request(&request, &request_size, device.get_control_sequence());
+    status = (HAILO_COMMON_STATUS__SUCCESS == common_status) ? HAILO_SUCCESS : HAILO_INTERNAL_FAILURE;
+    CHECK_SUCCESS_AS_EXPECTED(status);
+
+    status = device.fw_interact((uint8_t*)(&request), request_size, (uint8_t*)&response_buffer, &response_size);
+    CHECK_SUCCESS_AS_EXPECTED(status);
+
+    /* Parse response */
+    status = parse_and_validate_response(response_buffer, (uint32_t)(response_size), &header, &payload, &request);
+    CHECK_SUCCESS_AS_EXPECTED(status);
+
+    get_extended_device_information_response = (CONTROL_PROTOCOL__get_extended_device_information_response_t *)(payload->parameters);
+
+    return control__parse_get_extended_device_information_results(get_extended_device_information_response);
+}
+
+Expected<hailo_health_info_t> Control::get_health_information(Device &device)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    HAILO_COMMON_STATUS_t common_status = HAILO_COMMON_STATUS__UNINITIALIZED;
+    CONTROL_PROTOCOL__request_t request = {};
+    size_t request_size = 0;
+    uint8_t response_buffer[RESPONSE_MAX_BUFFER_SIZE] = {};
+    size_t response_size = RESPONSE_MAX_BUFFER_SIZE;
+    CONTROL_PROTOCOL__response_header_t *header = NULL;
+    CONTROL_PROTOCOL__payload_t *payload = NULL;
+    CONTROL_PROTOCOL__get_health_information_response_t *get_health_information_response = NULL;
+
+    /* Validate arguments */
+
+    common_status = CONTROL_PROTOCOL__pack_get_health_information_request(&request, &request_size, device.get_control_sequence());
+    status = (HAILO_COMMON_STATUS__SUCCESS == common_status) ? HAILO_SUCCESS : HAILO_INTERNAL_FAILURE;
+    CHECK_SUCCESS_AS_EXPECTED(status);
+
+    status = device.fw_interact((uint8_t*)(&request), request_size, (uint8_t*)&response_buffer, &response_size);
+    CHECK_SUCCESS_AS_EXPECTED(status);
+
+    /* Parse response */
+    status = parse_and_validate_response(response_buffer, (uint32_t)(response_size), &header, &payload, &request);
+    CHECK_SUCCESS_AS_EXPECTED(status);
+
+    get_health_information_response = (CONTROL_PROTOCOL__get_health_information_response_t *)(payload->parameters);
+
+    return control__parse_get_health_information_results(get_health_information_response);
+}
+
+hailo_status Control::config_context_switch_breakpoint(Device &device, uint8_t breakpoint_id,
+        CONTROL_PROTOCOL__context_switch_breakpoint_control_t breakpoint_control,
+        CONTROL_PROTOCOL__context_switch_breakpoint_data_t *breakpoint_data)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    HAILO_COMMON_STATUS_t common_status = HAILO_COMMON_STATUS__UNINITIALIZED;
+    CONTROL_PROTOCOL__request_t request = {};
+    size_t request_size = 0;
+    uint8_t response_buffer[RESPONSE_MAX_BUFFER_SIZE] = {};
+    size_t response_size = sizeof(response_buffer);
+    CONTROL_PROTOCOL__response_header_t *header = NULL;
+    CONTROL_PROTOCOL__payload_t *payload = NULL;
+
+    common_status = CONTROL_PROTOCOL__pack_config_context_switch_breakpoint_request(
+            &request, &request_size, device.get_control_sequence(), breakpoint_id, breakpoint_control, breakpoint_data);
+    status = (HAILO_COMMON_STATUS__SUCCESS == common_status) ? HAILO_SUCCESS : HAILO_INTERNAL_FAILURE;
+    CHECK_SUCCESS(status);
+
+    status = device.fw_interact((uint8_t*)(&request), request_size, (uint8_t*)&response_buffer, &response_size);
+    CHECK_SUCCESS(status);
+
+    /* Parse response */
+    status = parse_and_validate_response(response_buffer, (uint32_t)(response_size), &header, &payload,
+            &request);
+    if (HAILO_SUCCESS != status) {
+        goto exit;
+    }
+
+    status = HAILO_SUCCESS;
+exit:
+    return status;
+}
+
+hailo_status Control::get_context_switch_breakpoint_status(Device &device, uint8_t breakpoint_id,
+        CONTROL_PROTOCOL__context_switch_debug_sys_status_t *breakpoint_status)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    HAILO_COMMON_STATUS_t common_status = HAILO_COMMON_STATUS__UNINITIALIZED;
+    CONTROL_PROTOCOL__request_t request = {};
+    size_t request_size = 0;
+    uint8_t response_buffer[RESPONSE_MAX_BUFFER_SIZE] = {};
+    size_t response_size = sizeof(response_buffer);
+    CONTROL_PROTOCOL__response_header_t *header = NULL;
+    CONTROL_PROTOCOL__payload_t *payload = NULL;
+    CONTROL_PROTOCOL__get_context_switch_breakpoint_status_response_t *get_context_switch_breakpoint_status_response = NULL;
+
+    RETURN_IF_ARG_NULL(breakpoint_status);
+
+    common_status = CONTROL_PROTOCOL__pack_get_context_switch_breakpoint_status_request(
+            &request, &request_size, device.get_control_sequence(), breakpoint_id);
+    status = (HAILO_COMMON_STATUS__SUCCESS == common_status) ? HAILO_SUCCESS : HAILO_INTERNAL_FAILURE;
+    CHECK_SUCCESS(status);
+
+    status = device.fw_interact((uint8_t*)(&request), request_size, (uint8_t*)&response_buffer, &response_size);
+    CHECK_SUCCESS(status);
+
+    /* Parse response */
+    status = parse_and_validate_response(response_buffer, (uint32_t)(response_size), &header, &payload,
+            &request);
+    if (HAILO_SUCCESS != status) {
+        goto exit;
+    }
+
+    get_context_switch_breakpoint_status_response =
+        (CONTROL_PROTOCOL__get_context_switch_breakpoint_status_response_t *)(payload->parameters);
+
+    memcpy(breakpoint_status,
+            &(get_context_switch_breakpoint_status_response->breakpoint_status),
+            sizeof((*breakpoint_status)));
+    status = HAILO_SUCCESS;
+exit:
+    return status;
+}
+
+hailo_status Control::get_context_switch_main_header(Device &device, CONTROL_PROTOCOL__context_switch_main_header_t *main_header)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    HAILO_COMMON_STATUS_t common_status = HAILO_COMMON_STATUS__UNINITIALIZED;
+    CONTROL_PROTOCOL__request_t request = {};
+    size_t request_size = 0;
+    uint8_t response_buffer[RESPONSE_MAX_BUFFER_SIZE] = {};
+    size_t response_size = sizeof(response_buffer);
+    CONTROL_PROTOCOL__response_header_t *header = NULL;
+    CONTROL_PROTOCOL__payload_t *payload = NULL;
+    CONTROL_PROTOCOL__get_context_switch_main_header_response_t *get_context_switch_main_header_response = NULL;
+
+    RETURN_IF_ARG_NULL(main_header);
+
+    common_status = CONTROL_PROTOCOL__pack_get_context_switch_main_header_request(
+            &request, &request_size, device.get_control_sequence());
+    status = (HAILO_COMMON_STATUS__SUCCESS == common_status) ? HAILO_SUCCESS : HAILO_INTERNAL_FAILURE;
+    CHECK_SUCCESS(status);
+
+    status = device.fw_interact((uint8_t*)(&request), request_size, (uint8_t*)&response_buffer, &response_size);
+    CHECK_SUCCESS(status);
+
+    /* Parse response */
+    status = parse_and_validate_response(response_buffer, (uint32_t)(response_size), &header, &payload,
+            &request);
+    if (HAILO_SUCCESS != status) {
+        goto exit;
+    }
+
+    get_context_switch_main_header_response =
+        (CONTROL_PROTOCOL__get_context_switch_main_header_response_t *)(payload->parameters);
+
+    memcpy(main_header,
+            &(get_context_switch_main_header_response->main_header),
+            sizeof((*main_header)));
+
+    status = HAILO_SUCCESS;
+exit:
+    return status;
+}
+
+hailo_status Control::config_context_switch_timestamp(Device &device, uint16_t batch_index, bool enable_user_configuration)
+{
+    CONTROL_PROTOCOL__request_t request = {};
+    size_t request_size = 0;
+    uint8_t response_buffer[RESPONSE_MAX_BUFFER_SIZE] = {};
+    size_t response_size = sizeof(response_buffer);
+    CONTROL_PROTOCOL__response_header_t *header = NULL;
+    CONTROL_PROTOCOL__payload_t *payload = NULL;
+
+    auto common_status = CONTROL_PROTOCOL__pack_config_context_switch_timestamp_request(
+        &request, &request_size, device.get_control_sequence(), batch_index, enable_user_configuration);
+    auto status = (HAILO_COMMON_STATUS__SUCCESS == common_status) ? HAILO_SUCCESS : HAILO_INTERNAL_FAILURE;
+    CHECK_SUCCESS(status);
+
+    status = device.fw_interact((uint8_t*)(&request), request_size, (uint8_t*)&response_buffer, &response_size);
+    CHECK_SUCCESS(status);
+
+    /* Parse response */
+    status = parse_and_validate_response(response_buffer, (uint32_t)(response_size), &header, &payload,
+        &request);
+    CHECK_SUCCESS(status);
+
+    return HAILO_SUCCESS;
+}
+
+hailo_status Control::test_chip_memories(Device &device)
+{
+    uint32_t top_bypass_bitmap = 0;
+    hailo_status status = HAILO_UNINITIALIZED;
+
+    /*cluster bypass and index are irrelevant for top*/
+    uint32_t cluster_bypass_bitmap_0 = 0;
+    uint32_t cluster_bypass_bitmap_1 = 0;
+
+    for (size_t mem_block = 0; mem_block <  CONTROL_PROTOCOL__TOP_NUM_MEM_BLOCKS; mem_block++) {
+        /*only run test on allowed blocks */
+        if (0 == (CONTROL_PROTOCOL__BIST_TOP_WHITELIST & (1 << mem_block))) {
+            continue;
+        }
+        top_bypass_bitmap = CONTROL_PROTOCOL__BIST_TOP_BYPASS_ALL_MASK ^ (1 << mem_block);
+        auto block_status = run_bist_test(device, true, top_bypass_bitmap, 0, cluster_bypass_bitmap_0, cluster_bypass_bitmap_1);
+        if (HAILO_SUCCESS != block_status) {
+            LOGGER__ERROR("bist test failed on memory block {}", mem_block);
+            status = block_status;
+        }
+    }
+
+    for (uint8_t cluster_index = 0; cluster_index < CONTROL_PROTOCOL_NUM_BIST_CLUSTER_STEPS; cluster_index++) {
+        /*top bypass irrelevant for clusters*/
+        top_bypass_bitmap = 0;
+        /*run on all memory blocks, bypass = 0*/
+        cluster_bypass_bitmap_0 = 0;
+        cluster_bypass_bitmap_1 = 0;
+        auto cluster_status = run_bist_test(device, false, top_bypass_bitmap, cluster_index, cluster_bypass_bitmap_0, cluster_bypass_bitmap_1);
+        if (HAILO_SUCCESS != cluster_status) {
+            LOGGER__ERROR("bist test failed on cluster block {}", cluster_index);
+            status = cluster_status;
+        }
+    }
+
+    /*No errors encountered*/
+    if (HAILO_UNINITIALIZED == status){
+        status = HAILO_SUCCESS;
+    }
+
+    return status;
+}
+
+hailo_status Control::run_bist_test(Device &device, bool is_top_test, uint32_t top_bypass_bitmap,
+                     uint8_t cluster_index, uint32_t cluster_bypass_bitmap_0, uint32_t cluster_bypass_bitmap_1)
+{
+    CONTROL_PROTOCOL__request_t request = {};
+    size_t request_size = 0;
+    uint8_t response_buffer[RESPONSE_MAX_BUFFER_SIZE] = {};
+    size_t response_size = sizeof(response_buffer);
+    CONTROL_PROTOCOL__response_header_t *header = NULL;
+    CONTROL_PROTOCOL__payload_t *payload = NULL;
+
+    auto common_status = CONTROL_PROTOCOL__pack_run_bist_test_request(
+        &request, &request_size, device.get_control_sequence(),
+        is_top_test, top_bypass_bitmap, cluster_index, cluster_bypass_bitmap_0, cluster_bypass_bitmap_1);
+    auto status = (HAILO_COMMON_STATUS__SUCCESS == common_status) ? HAILO_SUCCESS : HAILO_INTERNAL_FAILURE;
+    CHECK_SUCCESS(status);
+
+    status = device.fw_interact((uint8_t*)(&request), request_size, (uint8_t*)&response_buffer, &response_size);
+    CHECK_SUCCESS(status);
+
+    /* Parse response */
+    status = parse_and_validate_response(response_buffer, (uint32_t)(response_size), &header, &payload,
+        &request);
+    CHECK_SUCCESS(status);
+
+    return HAILO_SUCCESS;
+}
+
+} /* namespace hailort */
diff --git a/hailort/libhailort/src/control.hpp b/hailort/libhailort/src/control.hpp
new file mode 100644 (file)
index 0000000..3702993
--- /dev/null
@@ -0,0 +1,412 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file control.hpp
+ * @brief Contains Defines and declarations related to controlling hailo8
+ **/
+
+#ifndef __CONTROL_H__
+#define __CONTROL_H__
+
+#include <stdbool.h>
+
+#include <hailo/hailort.h>
+#include "hailo/device.hpp"
+#include "control_protocol.h"
+#include "control_protocol.hpp"
+
+namespace hailort
+{
+
+#define CONTROL__MAX_SEQUENCE (0xFFFFFFFF)
+#define CONTROL__MAX_WRITE_MEMORY_CHUNK_SIZE (1024)
+
+#define FW_MAGIC (0x1DD89DE0)
+#define FW_SUPPORTED_HEADER_VERSION (0)
+#define BOARD_CONFIG_SIZE (500)
+
+/* TODO: Is this the correct size? */
+#define RESPONSE_MAX_BUFFER_SIZE (2048)
+#define WRITE_CHUNK_SIZE (1024)
+#define WORD_SIZE (4)
+
+
+class Control final
+{
+public:
+    Control() = delete;
+
+    static hailo_status parse_and_validate_response(uint8_t *message, uint32_t message_size, 
+        CONTROL_PROTOCOL__response_header_t **header, CONTROL_PROTOCOL__payload_t **payload, 
+        CONTROL_PROTOCOL__request_t *request);
+
+    /**
+     * Receive information about the device.
+     * 
+     * @param[in]     device - The Hailo device.
+     * @return The information about the board.
+     */
+    static Expected<hailo_device_identity_t> identify(Device &device);
+
+
+    /**
+     * Receive extended information about the device.
+     * 
+     * @param[in]     device - The Hailo device.
+     * @return The extended information about the board.
+     */
+    static Expected<hailo_extended_device_information_t> get_extended_device_information(Device &device);
+
+    /**
+     * Receive information about the core cpu.
+     * 
+     * @param[in]     device - The Hailo device.
+     * @param[out]    core_info - The information about the core cpu.
+     * @return Upon success, returns @a HAILO_SUCCESS. Otherwise, returns an @a static hailo_status error.
+     */
+    static hailo_status core_identify(Device &device, hailo_core_information_t *core_info);
+
+    /**
+     * Configure a UDP input dataflow stream at a Hailo device.
+     * 
+     * @param[in]     device - The Hailo device.
+     * @param[in]     params - The stream params that would be configured.
+     * @param[out]    dataflow_manager_id - Unique id of the dataflow manager.
+     * @return Upon success, returns @a HAILO_SUCCESS. Otherwise, returns an @a static hailo_status error.
+     */
+    static hailo_status config_stream_udp_input(Device &device, CONTROL_PROTOCOL__config_stream_params_t *params, uint8_t &dataflow_manager_id);
+
+    /**
+     * Configure a UDP output dataflow stream at a Hailo device.
+     * 
+     * @param[in]     device - The Hailo device.
+     * @param[in]     params - The stream params that would be configured.
+     * @param[out]    dataflow_manager_id - Unique id of the dataflow manager.
+     * @return Upon success, returns @a HAILO_SUCCESS. Otherwise, returns an @a static hailo_status error.
+     */
+    static hailo_status config_stream_udp_output(Device &device, CONTROL_PROTOCOL__config_stream_params_t *params, uint8_t &dataflow_manager_id);
+
+    /**
+     * Configure a MIPI input dataflow stream at a Hailo device.
+     * 
+     * @param[in]     device - The Hailo device.
+     * @param[in]     params - The stream params that would be configured.
+     * @param[out]    dataflow_manager_id - Unique id of the dataflow manager.
+     * @return Upon success, returns @a HAILO_SUCCESS. Otherwise, returns an @a static hailo_status error.
+     */
+    static hailo_status config_stream_mipi_input(Device &device, CONTROL_PROTOCOL__config_stream_params_t *params, uint8_t &dataflow_manager_id);
+
+    /**
+     * Configure a MIPI output dataflow stream at a Hailo device.
+     * 
+     * @param[in]     device - The Hailo device.
+     * @param[in]     params - The stream params that would be configured.
+     * @param[out]    dataflow_manager_id - Unique id of the dataflow manager.
+     * @return Upon success, returns @a HAILO_SUCCESS. Otherwise, returns an @a static hailo_status error.
+     */
+    static hailo_status config_stream_mipi_output(Device &device, CONTROL_PROTOCOL__config_stream_params_t *params, uint8_t &dataflow_manager_id);
+
+    /**
+     * Configure a PCIe input dataflow stream at a Hailo device.
+     * 
+     * @param[in]     device - The Hailo device.
+     * @param[in]     params - The stream params that would be configured.
+     * @param[out]    dataflow_manager_id - Unique id of the dataflow manager.
+     * @return Upon success, returns @a HAILO_SUCCESS. Otherwise, returns an @a static hailo_status error.
+     */
+    static hailo_status config_stream_pcie_input(Device &device, CONTROL_PROTOCOL__config_stream_params_t *params, uint8_t &dataflow_manager_id);
+
+    /**
+     * Configure a PCIe output dataflow stream at a Hailo device.
+     * 
+     * @param[in]     device - The Hailo device.
+     * @param[in]     params - The stream params that would be configured.
+     * @param[out]    dataflow_manager_id - Unique id of the dataflow manager.
+     * @return Upon success, returns @a HAILO_SUCCESS. Otherwise, returns an @a static hailo_status error.
+     */
+    static hailo_status config_stream_pcie_output(Device &device, CONTROL_PROTOCOL__config_stream_params_t *params, uint8_t &dataflow_manager_id);
+
+    /**
+     * Open a stream at a Hailo device.
+     * 
+     * @param[in]     device - The Hailo device.
+     * @param[in]     dataflow_manager_id - Unique id of the dataflow manager.
+     * @param[in]     is_input - Indicates whether the stream is an input or an output.
+     * @note The stream must be configured prior its opening;
+     * @return Upon success, returns @a HAILO_SUCCESS. Otherwise, returns an @a static hailo_status error.
+     */
+    static hailo_status open_stream(Device &device, uint8_t dataflow_manager_id, bool is_input);
+
+    /**
+     * Close a stream at a Hailo device.
+     * 
+     * @param[in]     device - The Hailo device.
+     * @param[in]     dataflow_manager_id - Unique id of the dataflow manager.
+     * @param[in]     is_input - Indicates whether the stream is an input or an output.
+     * @note 
+     *      1.  A stream must be opened before closing.
+     *      2.  A stream cannot be closed twice.
+     *      3.  In order to close all the streams, call \ref close_all_streams.
+     * @return Upon success, returns @a HAILO_SUCCESS. Otherwise, returns an @a static hailo_status error.
+     */
+    static hailo_status close_stream(Device &device, uint8_t dataflow_manager_id, bool is_input);
+    static hailo_status close_all_streams(Device &device);
+
+    /**
+     * Get idle time accumulated measurement.
+     * 
+     * @param[in]     device - The Hailo device.
+     * @param[out]    measurement - pointer to store the measurement
+     * @return Upon success, returns @a HAILO_SUCCESS. Otherwise, returns an @a static hailo_status error.
+     */
+    static hailo_status idle_time_get_measurement(Device &device, uint64_t *measurement);
+
+    /**
+     * start/stop idle time measurement
+     * 
+     * @param[in]     device - The Hailo device.
+     * @param[in]     measurement_enable - start/stop the measurement
+     * @return Upon success, returns @a HAILO_SUCCESS. Otherwise, returns an @a static hailo_status error.
+     */
+    static hailo_status idle_time_set_measurement(Device &device, uint8_t measurement_enable);
+
+    /**
+     *  Start firmware update of a Hailo device.
+     * 
+     * @param[in]     device - The Hailo device.
+     * @return Upon success, returns @a HAILO_SUCCESS. Otherwise, returns an @a static hailo_status error.
+     */
+    static hailo_status start_firmware_update(Device &device);
+    static hailo_status write_firmware_update(Device &device, uint32_t offset, const uint8_t *data, uint32_t data_length);
+    static hailo_status validate_firmware_update(Device &device, MD5_SUM_t *expected_md5, uint32_t firmware_size);
+    static hailo_status finish_firmware_update(Device &device);
+    static hailo_status write_second_stage_to_internal_memory(Device &device, uint32_t offset, uint8_t *data, uint32_t data_length);
+    static hailo_status copy_second_stage_to_flash(Device &device, MD5_SUM_t *expected_md5, uint32_t second_stage_size);
+
+    static hailo_status examine_user_config(Device &device, hailo_fw_user_config_information_t *info);
+
+    static hailo_status read_user_config(Device &device, uint8_t *buffer, uint32_t buffer_length);
+
+    static hailo_status write_user_config(Device &device, const uint8_t *data, uint32_t data_length);
+
+    static hailo_status erase_user_config(Device &device);
+
+    static hailo_status read_board_config(Device &device, uint8_t *buffer, uint32_t buffer_length);
+
+    static hailo_status write_board_config(Device &device, const uint8_t *data, uint32_t data_length);
+
+    static hailo_status phy_operation(Device &device, CONTROL_PROTOCOL__phy_operation_t operation_type);
+    
+    static hailo_status config_core_top(Device &device, CONTROL_PROTOCOL__config_core_top_type_t config_type,
+        CONTROL_PROTOCOL__config_core_top_params_t *params);
+
+    /**
+     *  Write data to an I2C slave over a hailo device.
+     * 
+     * @param[in]     device - The Hailo device.
+     * @param[in]     slave_config - The configuration of the slave.
+     * @param[in]     register_address - The address of the register to which the data will be written
+     * @param[in]     data - A pointer to a buffer that contains the data to be written to the slave.
+     * @param[in]     length - The size of @a data in bytes.
+     * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns a ::hailo_status error.
+     */
+    static hailo_status i2c_write(Device &device, const hailo_i2c_slave_config_t *slave_config, uint32_t register_address, 
+        const uint8_t *data, uint32_t length);
+
+    /**
+     *  Read data from an I2C slave over a hailo device.
+     * 
+     * @param[in]     device - The Hailo device.
+     * @param[in]     slave_config - The configuration of the slave.
+     * @param[in]     register_address - The address of the register from which the data will be read.
+     * @param[in]     data - Pointer to a buffer that would store the read data.
+     * @param[in]     length - The number of bytes to read into the buffer pointed by @a data.
+     * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns a ::hailo_status error.
+     */
+    static hailo_status i2c_read(Device &device, const hailo_i2c_slave_config_t *slave_config, uint32_t register_address,
+        uint8_t *data, uint32_t length);
+
+    /**
+     *  Measure the latency of a single image at the nn core of a hailo device.
+     * 
+     * @param[in]     device - The Hailo device.
+     * @param[in]     latency_measurement_en - Boolean if the latency should be enabled or not.
+     * @param[in]     inbound_start_buffer_number - The inbound buffer from which the system start the latency measurement.
+     * @param[in]     outbound_start_buffer_number - The outbound buffer from which the system ends the latency measurement.
+     * @param[in]     inbound_stream_index - Which input stream to measure latency from.
+     * @param[in]     outbound_stream_index - Which output stream to measure latency from.
+     *
+     * @return Upon success, returns @a HAILO_SUCCESS. Otherwise, returns an @a static hailo_status error.
+     */
+    static hailo_status latency_measurement_config(Device &device, uint8_t latency_measurement_en,
+
+            uint32_t inbound_start_buffer_number, uint32_t outbound_stop_buffer_number, uint32_t inbound_stream_index,
+            uint32_t outbound_stream_index);
+    /**
+     *  Read the measurement of the latency of a single image at the nn core of a hailo device.
+     * 
+     * @param[in]     device - The Hailo device.
+     * @param[out]    inbound_to_outbound_latency_nsec - The latency in nanoseconds.
+     *
+     * @return Upon success, returns @a HAILO_SUCCESS. Otherwise, returns an @a static hailo_status error.
+     */
+    static hailo_status latency_measurement_read(Device &device, uint32_t *inbound_to_outbound_latency_nsec);
+    static hailo_status sensor_store_config(Device &device, uint32_t is_first, uint32_t section_index, uint32_t start_offset, uint32_t reset_data_size, uint32_t sensor_type, uint32_t total_data_size,
+                                        uint8_t  *data, uint32_t data_length, uint16_t config_height, uint16_t config_width, uint16_t config_fps, uint32_t config_name_length, uint8_t *config_name);
+    static hailo_status sensor_get_config(Device &device, uint32_t section_index, uint32_t offset, uint32_t data_length, uint8_t *data);
+    static hailo_status sensor_set_i2c_bus_index(Device &device, uint32_t sensor_type, uint32_t bus_index);
+    static hailo_status sensor_load_and_start_config(Device &device, uint32_t section_index);
+    static hailo_status sensor_reset(Device &device, uint32_t section_index);
+    static hailo_status sensor_set_generic_i2c_slave(Device &device, uint16_t slave_address, uint8_t register_address_size, uint8_t bus_index, uint8_t should_hold_bus, uint8_t endianness);
+    static hailo_status sensor_get_sections_info(Device &device, uint8_t *data);
+    /**
+     *  Send to FW the context switch header details
+     * 
+     * @param[in]     device - The Hailo device.
+     * @param[in]     context_switch_info - struct including all the data relevant for the fw
+     *
+     * @return Upon success, returns @a HAILO_SUCCESS. Otherwise, returns an @a static hailo_status error.
+     */
+    static hailo_status write_context_switch_info(Device &device, CONTROL_PROTOCOL__context_switch_info_t *context_switch_info);
+    /**
+     *  Download generated context switch action list per single context
+     * 
+     * @param[in]     device - The Hailo device.
+     * @param[in]     context_index - context index of the context the user wishes to download the action list
+     * @param[out]    base address - base address of the context action list in the FW memory
+     * @param[out]    action list - buffer of the action list
+     * @param[out]    action_list_length - size of the action list buffer
+     *
+     * @return Upon success, returns @a HAILO_SUCCESS. Otherwise, returns an @a static hailo_status error.
+     */
+    // TODO: fix
+    static hailo_status download_context_action_list(Device &device, uint8_t context_index, size_t action_list_max_size, 
+            uint32_t *base_address, uint8_t *action_list, uint16_t *action_list_length, uint32_t *batch_counter);
+            
+    /**
+     *  Enable network group
+     * 
+     * @param[in]     device - The Hailo device.
+     * @param[in]     network_group_index - network_group index 
+     *
+     * @return Upon success, returns @a HAILO_SUCCESS. Otherwise, returns an @a static hailo_status error.
+     */
+    static hailo_status enable_network_group(Device &device, 
+            uint8_t network_group_index);
+    /**
+     *  reset context switch state machine
+     * 
+     * @param[in]     device - The Hailo device.
+     * @param[in]     network_group_index - network_group index 
+     *
+     * @return Upon success, returns @a HAILO_SUCCESS. Otherwise, returns an @a static hailo_status error.
+     */
+    static hailo_status reset_context_switch_state_machine(Device &device);
+    /**
+     *  set dataflow interrupt by control
+     * 
+     * @param[in]     device - The Hailo device.
+     * @param[in]     interrupt_type - casted from enum into unit8_t - type of the interrupt
+     * @param[in]     interrupt_index  - interrupt index (PCIe channel or Cluster index)
+     * @param[in]     interrupt_sub_index  - interrupt index (LCU index in cluster)
+     *
+     * @return Upon success, returns @a HAILO_SUCCESS. Otherwise, returns an @a static hailo_status error.
+     */
+    static hailo_status set_dataflow_interrupt(Device &device, uint8_t interrupt_type, uint8_t interrupt_index,
+            uint8_t interrupt_sub_index);
+
+    /**
+     *  set d2h manager a new host configuration by control
+     * 
+     * @param[in]     device - The Hailo device.
+     * @param[in]     host_port  - host port in case connection_type is Ethernet  , otherwise neglected.
+     * @param[in]     host_ip_address  - host ip in case connection_type is Ethernet , otherwise neglected,
+     *                0 means auto detect IP address from control.
+     *
+     * @return Upon success, returns @a HAILO_SUCCESS. Otherwise, returns an @a static hailo_status error.
+     */
+    static hailo_status d2h_notification_manager_set_host_info(Device &device, uint16_t host_port, uint32_t host_ip_address);
+    static hailo_status d2h_notification_manager_send_host_info_notification(Device &device, uint8_t notification_priority);
+
+    /**
+     *  set the user desired network_group (must be sent after sending the meta data header)
+     * 
+     * @param[in]     device - The Hailo device.
+     * @param[in]     network_group_index - network_group index 
+     *
+     * @return Upon success, returns @a HAILO_SUCCESS. Otherwise, returns an @a static hailo_status error.
+     */
+    static hailo_status switch_network_group(Device &device, uint8_t network_group_index);
+
+    /**
+     *  Enable/disable halt transmition following Rx pause frame
+     * 
+     * @param[in]     device - The Hailo device.
+     * @param[in]     rx_pause_frames_enable - Bool indicating weather to enable or disable rx pause frames
+     * @return Upon success, returns @a HAILO_SUCCESS. Otherwise, returns an @a static hailo_status error.
+     */
+    static hailo_status set_pause_frames(Device &device, uint8_t rx_pause_frames_enable);
+
+    static hailo_status set_fw_logger(Device &device, hailo_fw_logger_level_t level, uint32_t interface_mask);
+    static hailo_status write_memory(Device &device, uint32_t address, const uint8_t *data, uint32_t data_length);
+    static hailo_status read_memory(Device &device, uint32_t address, uint8_t *data, uint32_t data_length);
+    static hailo_status context_switch_set_context_info(Device &device,
+            CONTROL_PROTOCOL__context_switch_context_info_t *context_info);
+    static hailo_status context_switch_set_main_header(Device &device,
+            CONTROL_PROTOCOL__context_switch_main_header_t *context_switch_header);
+    static hailo_status wd_enable(Device &device, uint8_t cpu_id, bool should_enable);
+    static hailo_status wd_config(Device &device, uint8_t cpu_id, uint32_t wd_cycles, CONTROL_PROTOCOL__WATCHDOG_MODE_t wd_mode);
+    static hailo_status previous_system_state(Device &device, uint8_t cpu_id, CONTROL_PROTOCOL__system_state_t *system_state);
+    static hailo_status get_chip_temperature(Device &device, hailo_chip_temperature_info_t *temp_info);
+    static hailo_status enable_debugging(Device &device, bool is_rma);
+    
+    static hailo_status config_context_switch_breakpoint(Device &device, uint8_t breakpoint_id,
+            CONTROL_PROTOCOL__context_switch_breakpoint_control_t breakpoint_control,
+            CONTROL_PROTOCOL__context_switch_breakpoint_data_t *breakpoint_data);
+    static hailo_status get_context_switch_breakpoint_status(Device &device, uint8_t breakpoint_id,
+            CONTROL_PROTOCOL__context_switch_debug_sys_status_t *breakpoint_status);
+    static hailo_status get_context_switch_main_header(Device &device, 
+            CONTROL_PROTOCOL__context_switch_main_header_t *main_header);
+    static hailo_status config_context_switch_timestamp(Device &device, uint16_t batch_index, bool enable_user_configuration);
+    static hailo_status test_chip_memories(Device &device);
+    static hailo_status run_bist_test(Device &device, bool is_top_test, uint32_t top_bypass_bitmap,
+                     uint8_t cluster_index, uint32_t cluster_bypass_bitmap_0, uint32_t cluster_bypass_bitmap_1);
+    static hailo_status set_clock_freq(Device &device, uint32_t clock_freq);
+    static Expected<hailo_health_info_t> get_health_information(Device &device);
+    static hailo_status set_throttling_state(Device &device, bool should_activate);
+    static Expected<bool> get_throttling_state(Device &device);
+    static hailo_status set_overcurrent_state(Device &device, bool should_activate);
+    static Expected<bool> get_overcurrent_state(Device &device);
+
+    // TODO: needed?
+    static hailo_status power_measurement(Device &device, CONTROL_PROTOCOL__dvm_options_t dvm,
+        CONTROL_PROTOCOL__power_measurement_types_t measurement_type, float32_t *measurement);
+    static hailo_status set_power_measurement(Device &device, uint32_t index, CONTROL_PROTOCOL__dvm_options_t dvm,
+        CONTROL_PROTOCOL__power_measurement_types_t measurement_type);
+    static hailo_status get_power_measurement(Device &device, uint32_t index, bool should_clear,
+        hailo_power_measurement_data_t *measurement_data);
+    static hailo_status start_power_measurement(Device &device, uint32_t delay_milliseconds,
+        CONTROL_PROTOCOL__averaging_factor_t averaging_factor, CONTROL_PROTOCOL__sampling_period_t sampling_period);
+    static hailo_status stop_power_measurement(Device &device);
+
+private:
+    static hailo_status write_memory_chunk(Device &device, uint32_t address, const uint8_t *data, uint32_t chunk_size);
+    static hailo_status read_memory_chunk(Device &device, uint32_t address, uint8_t *data, uint32_t chunk_size);
+    static hailo_status read_user_config_chunk(Device &device, uint32_t read_offset, uint32_t read_length,
+        uint8_t *buffer, uint32_t *actual_read_data_length);
+    static hailo_status write_user_config_chunk(Device &device, uint32_t offset, const uint8_t *data, uint32_t chunk_size);
+    static hailo_status download_context_action_list_chunk(Device &device, uint8_t context_index, uint16_t action_list_offset,
+            size_t action_list_max_size, uint32_t *base_address, uint8_t *action_list, uint16_t *action_list_length,
+            bool *is_action_list_end, uint32_t *batch_counter);
+    static hailo_status context_switch_set_context_info_chunk(Device &device,
+            CONTROL_PROTOCOL__context_switch_context_info_single_control_t *context_info);
+    static hailo_status change_context_switch_status(Device &device, 
+            CONTROL_PROTOCOL__CONTEXT_SWITCH_STATUS_t state_machine_status,
+            uint8_t network_group_index);
+};
+
+} /* namespace hailort */
+
+#endif /* __CONTROL_H__ */
diff --git a/hailort/libhailort/src/control_protocol.cpp b/hailort/libhailort/src/control_protocol.cpp
new file mode 100644 (file)
index 0000000..e400560
--- /dev/null
@@ -0,0 +1,2379 @@
+/* 
+ * =============================================================================
+ *
+ *                               HAILO
+ *
+ *  Property of HAILO Tech
+ *  For Unrestricted Internal Use Only
+ *  Unauthorized reproduction and/or distribution is strictly prohibited.
+ *  This product is protected under copyright law and trade secret law
+ *  Created 2018, (C) Copyright 2018 Hailo Tech .  All rights reserved.
+ *  as an unpublished work.
+ */
+/**
+*   Filename:      control_protocol.c
+*
+*   Description:   Implements control protocol packing/unpacking.
+*
+*=============================================================================*/
+
+#include <stdint.h>
+#include <string.h>
+#include "control_protocol.h"
+#include "control_protocol.hpp"
+#include "byte_order.h"
+#include "status.h"
+#include "common/utils.hpp"
+
+using namespace hailort;
+
+#ifndef FIRMWARE_ARCH /*this file should not be compiled for firmware*/
+
+bool g_CONTROL_PROTOCOL__is_critical[HAILO_CONTROL_OPCODE_COUNT] = {
+#define CONTROL_PROTOCOL__OPCODE_X(name, is_critical, cpu_id) is_critical,
+    CONTROL_PROTOCOL__OPCODES_VARIABLES
+#undef CONTROL_PROTOCOL__OPCODE_X
+};
+
+CPU_ID_t g_CONTROL_PROTOCOL__cpu_id[HAILO_CONTROL_OPCODE_COUNT] = {
+#define CONTROL_PROTOCOL__OPCODE_X(name, is_critical, cpu_id) cpu_id,
+    CONTROL_PROTOCOL__OPCODES_VARIABLES
+#undef CONTROL_PROTOCOL__OPCODE_X
+};
+
+const char *CONTROL_PROTOCOL__textual_format[] =
+{
+#define STRINGIFY(name) #name
+#define CONTROL_PROTOCOL__OPCODE_X(name, is_critical, cpu_id) STRINGIFY(name),
+    CONTROL_PROTOCOL__OPCODES_VARIABLES
+#undef CONTROL_PROTOCOL__OPCODE_X
+};
+
+const char *CONTROL_PROTOCOL__get_textual_opcode(CONTROL_PROTOCOL__OPCODE_t opcode)
+{
+    return CONTROL_PROTOCOL__textual_format[opcode];
+}
+
+
+/* Functions declarations */
+HAILO_COMMON_STATUS_t control_protocol__parse_message(uint8_t *message,
+        uint32_t message_size,
+        CONTROL_PROTOCOL__common_header_t **header,
+        uint16_t full_header_size,
+        CONTROL_PROTOCOL__payload_t **payload,
+        uint8_t expected_ack_value);
+
+HAILO_COMMON_STATUS_t CONTROL_PROTOCOL__parse_response(uint8_t *message,
+        uint32_t message_size,
+        CONTROL_PROTOCOL__response_header_t **header,
+        CONTROL_PROTOCOL__payload_t **payload,
+        CONTROL_PROTOCOL__status_t *fw_status)
+{
+    HAILO_COMMON_STATUS_t status = HAILO_COMMON_STATUS__UNINITIALIZED;
+    if ((NULL == message) || (NULL == header) || (NULL == payload) || (NULL == fw_status)) {
+        status = HAILO_STATUS__CONTROL_PROTOCOL__NULL_ARGUMENT_PASSED;
+        goto exit;
+    }
+
+    status = control_protocol__parse_message(message,
+            message_size,
+            (CONTROL_PROTOCOL__common_header_t**)header,
+            sizeof(**header),
+            payload,
+            CONTROL_PROTOCOL__ACK_SET);
+    if (HAILO_COMMON_STATUS__SUCCESS != status) {
+        goto exit;
+    }
+
+    /* Copy firmware status from header */
+    fw_status->major_status = BYTE_ORDER__ntohl((*header)->status.major_status);
+    fw_status->minor_status = BYTE_ORDER__ntohl((*header)->status.minor_status);
+
+    status = HAILO_COMMON_STATUS__SUCCESS;
+
+exit:
+    return status;
+}
+
+HAILO_COMMON_STATUS_t control_protocol__parse_message(uint8_t *message,
+        uint32_t message_size,
+        CONTROL_PROTOCOL__common_header_t **header,
+        uint16_t full_header_size,
+        CONTROL_PROTOCOL__payload_t **payload,
+        uint8_t expected_ack_value)
+{
+    HAILO_COMMON_STATUS_t status = HAILO_COMMON_STATUS__UNINITIALIZED;
+    size_t current_offset = 0;
+    CONTROL_PROTOCOL__parameter_t *current_parameter = NULL;
+    uint32_t parameter_count = 0;
+    CONTROL_PROTOCOL_flags_t control_flags = {};
+    CONTROL_PROTOCOL__common_header_t *local_common_header = NULL;
+    CONTROL_PROTOCOL__payload_t *local_payload = NULL;
+    uint32_t protocol_version = 0;
+
+    local_common_header = (CONTROL_PROTOCOL__common_header_t *)(message);
+    protocol_version = BYTE_ORDER__ntohl(local_common_header->version);
+
+    switch (protocol_version) {
+        case CONTROL_PROTOCOL__PROTOCOL_VERSION_2:
+            break;
+        default:
+            status = HAILO_STATUS__CONTROL_PROTOCOL__INVALID_VERSION;
+            goto exit;
+            break;
+    }
+
+    control_flags.integer = BYTE_ORDER__ntohl(local_common_header->flags.integer);
+    if (expected_ack_value != control_flags.bitstruct.ack) {
+        status = HAILO_STATUS__CONTROL_PROTOCOL__UNEXPECTED_ACK_VALUE;
+        goto exit;
+    }
+
+    current_offset = full_header_size;
+    /* Check if there are any parameters to parse */
+    if (current_offset < message_size) {
+        local_payload = (CONTROL_PROTOCOL__payload_t *)(message + current_offset);
+        current_offset += sizeof(*local_payload);
+
+        /* If the are any parameters, start parsing them */
+        if (0 < BYTE_ORDER__ntohl(local_payload->parameter_count)) {
+            /* Check that the frame doesn't overrun after parameter count */
+            if (current_offset > message_size) {
+                status = HAILO_STATUS__CONTROL_PROTOCOL__OVERRUN_BEFORE_PARAMETER;
+                goto exit;
+            }
+            /* Validate each parameter */
+            for (parameter_count = 0;
+                    parameter_count < BYTE_ORDER__ntohl(local_payload->parameter_count);
+                    ++parameter_count) {
+                current_parameter = (CONTROL_PROTOCOL__parameter_t *)(
+                        (message) + current_offset);
+                /* Check that the parameter donesn't overrun the packet */
+                current_offset += sizeof(*current_parameter) + BYTE_ORDER__ntohl(current_parameter->length);
+                if (current_offset > message_size) {
+                    status = HAILO_STATUS__CONTROL_PROTOCOL__OVERRUN_AT_PARAMETER;
+                    goto exit;
+                }
+            }
+        }
+    }
+
+    /* Validate all of the message was parsed */
+    if (current_offset != message_size) {
+        status = HAILO_STATUS__CONTROL_PROTOCOL__PART_OF_THE_MESSAGE_NOT_PARSED;
+        goto exit;
+    }
+
+    /* Packet is valid, assign out parameters */
+    *header = local_common_header;
+    local_common_header = NULL;
+    *payload = local_payload;
+    local_payload = NULL;
+
+    status = HAILO_COMMON_STATUS__SUCCESS;
+
+exit:
+    return status;
+}
+
+
+HAILO_COMMON_STATUS_t CONTROL_PROTOCOL__get_sequence_from_response_buffer(uint8_t *response_buffer,
+        size_t response_buffer_size, uint32_t *sequence)
+{
+    HAILO_COMMON_STATUS_t status = HAILO_COMMON_STATUS__UNINITIALIZED;
+    uint32_t local_sequence = 0;
+    CONTROL_PROTOCOL__common_header_t *common_header = NULL;
+
+    if ((NULL == response_buffer) || (NULL == sequence)) {
+        status = HAILO_STATUS__CONTROL_PROTOCOL__NULL_ARGUMENT_PASSED;
+        goto exit;
+    }
+
+    if (sizeof(CONTROL_PROTOCOL__common_header_t) > response_buffer_size) {
+        status = HAILO_STATUS__CONTROL_PROTOCOL__INVALID_BUFFER_SIZE;
+        goto exit;
+    }
+
+    /* Get the sequence from the common header */
+    common_header = ((CONTROL_PROTOCOL__common_header_t*)(response_buffer));
+    local_sequence = BYTE_ORDER__ntohl(common_header->sequence);
+
+    *sequence = local_sequence;
+    status = HAILO_COMMON_STATUS__SUCCESS;
+exit:
+    return status;
+}
+
+void control_protocol__pack_request_header(CONTROL_PROTOCOL__request_t *request, uint32_t sequence, CONTROL_PROTOCOL__OPCODE_t opcode, uint32_t parameter_count)
+{
+    request->header.common_header.opcode = BYTE_ORDER__htonl(opcode);
+    request->header.common_header.sequence = BYTE_ORDER__htonl(sequence);
+    request->header.common_header.version = BYTE_ORDER__htonl(CONTROL_PROTOCOL__PROTOCOL_VERSION);
+
+    request->parameter_count = BYTE_ORDER__htonl(parameter_count);
+}
+
+HAILO_COMMON_STATUS_t control_protocol__pack_empty_request(CONTROL_PROTOCOL__request_t *request, size_t *request_size, uint32_t sequence, CONTROL_PROTOCOL__OPCODE_t opcode)
+{
+    HAILO_COMMON_STATUS_t status = HAILO_COMMON_STATUS__UNINITIALIZED;
+    size_t local_request_size = 0;
+
+    if ((NULL == request) || (NULL == request_size)) {
+        status = HAILO_STATUS__CONTROL_PROTOCOL__NULL_ARGUMENT_PASSED;
+        goto exit;
+    }
+
+    /* Header */
+    local_request_size = CONTROL_PROTOCOL__REQUEST_BASE_SIZE;
+    control_protocol__pack_request_header(request, sequence, opcode, 0);
+
+    *request_size = local_request_size;
+    status = HAILO_COMMON_STATUS__SUCCESS;
+exit:
+    return status;
+}
+
+HAILO_COMMON_STATUS_t CONTROL_PROTOCOL__pack_identify_request(CONTROL_PROTOCOL__request_t *request, size_t *request_size, uint32_t sequence)
+{
+    return control_protocol__pack_empty_request(request, request_size, sequence, HAILO_CONTROL_OPCODE_IDENTIFY);
+}
+
+HAILO_COMMON_STATUS_t CONTROL_PROTOCOL__pack_core_identify_request(CONTROL_PROTOCOL__request_t *request, size_t *request_size, uint32_t sequence)
+{
+    return control_protocol__pack_empty_request(request, request_size, sequence, HAILO_CONTROL_OPCODE_CORE_IDENTIFY);
+}
+
+HAILO_COMMON_STATUS_t CONTROL_PROTOCOL__pack_set_fw_logger_request(CONTROL_PROTOCOL__request_t *request, size_t *request_size, uint32_t sequence,
+                                                                   hailo_fw_logger_level_t level, uint8_t interface_mask)
+{
+    size_t local_request_size = 0;
+
+    CHECK(request != nullptr, HAILO_STATUS__CONTROL_PROTOCOL__NULL_ARGUMENT_PASSED);
+    CHECK(request_size != nullptr, HAILO_STATUS__CONTROL_PROTOCOL__NULL_ARGUMENT_PASSED);
+
+    CHECK(level <= (uint8_t) CONTROL_PROTOCOL__FW_MAX_LOGGER_LEVEL, HAILO_STATUS__CONTROL_PROTOCOL__INVALID_ARGUMENT);
+    CHECK(interface_mask <= CONTROL_PROTOCOL__FW_MAX_LOGGER_INTERFACE, HAILO_STATUS__CONTROL_PROTOCOL__INVALID_ARGUMENT);
+
+    static_assert((uint32_t) FW_LOGGER_LEVEL_TRACE == (uint32_t) HAILO_FW_LOGGER_LEVEL_TRACE,
+        "mismatch in FW_LOGGER_LEVEL_TRACE and HAILO_FW_LOGGER_LEVEL_TRACE");
+    static_assert((uint32_t) FW_LOGGER_LEVEL_DEBUG == (uint32_t) HAILO_FW_LOGGER_LEVEL_DEBUG,
+        "mismatch in FW_LOGGER_LEVEL_DEBUG and HAILO_FW_LOGGER_LEVEL_DEBUG");
+    static_assert((uint32_t) FW_LOGGER_LEVEL_INFO == (uint32_t) HAILO_FW_LOGGER_LEVEL_INFO,
+        "mismatch in FW_LOGGER_LEVEL_INFO and HAILO_FW_LOGGER_LEVEL_INFO");
+    static_assert((uint32_t) FW_LOGGER_LEVEL_WARN == (uint32_t) HAILO_FW_LOGGER_LEVEL_WARN,
+        "mismatch in FW_LOGGER_LEVEL_WARN and HAILO_FW_LOGGER_LEVEL_WARN");
+    static_assert((uint32_t) FW_LOGGER_LEVEL_ERROR == (uint32_t) HAILO_FW_LOGGER_LEVEL_ERROR,
+        "mismatch in FW_LOGGER_LEVEL_ERROR and HAILO_FW_LOGGER_LEVEL_ERROR");
+    static_assert((uint32_t) FW_LOGGER_LEVEL_FATAL == (uint32_t) HAILO_FW_LOGGER_LEVEL_FATAL,
+        "mismatch in FW_LOGGER_LEVEL_FATAL and HAILO_FW_LOGGER_LEVEL_FATAL");
+    static_assert((uint32_t)CONTROL_PROTOCOL__INTERFACE_PCIE == (uint32_t)HAILO_FW_LOGGER_INTERFACE_PCIE,
+        "mismatch in CONTROL_PROTOCOL__INTERFACE_PCIE and HAILO_FW_LOGGER_INTERFACE_PCIE");
+    static_assert((uint32_t)CONTROL_PROTOCOL__INTERFACE_UART == (uint32_t)HAILO_FW_LOGGER_INTERFACE_UART,
+        "mismatch in CONTROL_PROTOCOL__INTERFACE_UART and HAILO_FW_LOGGER_INTERFACE_UART");
+
+    /* Header */
+    local_request_size = CONTROL_PROTOCOL__REQUEST_BASE_SIZE + sizeof(CONTROL_PROTOCOL__set_fw_logger_request_t);
+    control_protocol__pack_request_header(request, sequence, HAILO_CONTROL_OPCODE_SET_FW_LOGGER, 2);
+
+    request->parameters.set_fw_logger_request.level_length = BYTE_ORDER__htonl(sizeof(request->parameters.set_fw_logger_request.level));
+    request->parameters.set_fw_logger_request.level = static_cast<uint8_t>(level);
+
+    request->parameters.set_fw_logger_request.logger_interface_bit_mask_length = BYTE_ORDER__htonl(sizeof(request->parameters.set_fw_logger_request.logger_interface_bit_mask));
+    request->parameters.set_fw_logger_request.logger_interface_bit_mask = interface_mask;
+    
+    *request_size = local_request_size;
+    return HAILO_COMMON_STATUS__SUCCESS;
+}
+
+HAILO_COMMON_STATUS_t CONTROL_PROTOCOL__pack_set_throttling_state_request(CONTROL_PROTOCOL__request_t *request, size_t *request_size, uint32_t sequence,
+                                                                          bool should_activate)
+{
+    size_t local_request_size = 0;
+
+    CHECK_NOT_NULL(request, HAILO_STATUS__CONTROL_PROTOCOL__NULL_ARGUMENT_PASSED);
+    CHECK_NOT_NULL(request_size, HAILO_STATUS__CONTROL_PROTOCOL__NULL_ARGUMENT_PASSED);
+
+    /* Header */
+    local_request_size = CONTROL_PROTOCOL__REQUEST_BASE_SIZE + sizeof(CONTROL_PROTOCOL__set_throttling_state_request_t);
+    control_protocol__pack_request_header(request, sequence, HAILO_CONTROL_OPCODE_SET_THROTTLING_STATE, 1);
+
+    request->parameters.set_throttling_state_request.should_activate_length = BYTE_ORDER__htonl(sizeof(request->parameters.set_throttling_state_request.should_activate));
+    request->parameters.set_throttling_state_request.should_activate = should_activate;
+    
+    *request_size = local_request_size;
+    return HAILO_COMMON_STATUS__SUCCESS;
+}
+
+HAILO_COMMON_STATUS_t CONTROL_PROTOCOL__pack_get_throttling_state_request(CONTROL_PROTOCOL__request_t *request, size_t *request_size, uint32_t sequence)
+{
+    return control_protocol__pack_empty_request(request, request_size, sequence, HAILO_CONTROL_OPCODE_GET_THROTTLING_STATE);
+}
+
+HAILO_COMMON_STATUS_t CONTROL_PROTOCOL__pack_set_overcurrent_state_request(CONTROL_PROTOCOL__request_t *request, size_t *request_size, uint32_t sequence,
+    bool should_activate)
+{
+    size_t local_request_size = 0;
+
+    CHECK_NOT_NULL(request, HAILO_STATUS__CONTROL_PROTOCOL__NULL_ARGUMENT_PASSED);
+    CHECK_NOT_NULL(request_size, HAILO_STATUS__CONTROL_PROTOCOL__NULL_ARGUMENT_PASSED);
+
+    /* Header */
+    local_request_size = CONTROL_PROTOCOL__REQUEST_BASE_SIZE + sizeof(CONTROL_PROTOCOL__set_overcurrent_state_request_t);
+    control_protocol__pack_request_header(request, sequence, HAILO_CONTROL_OPCODE_SET_OVERCURRENT_STATE, 1);
+
+    request->parameters.set_overcurrent_state_request.should_activate_length = BYTE_ORDER__htonl(sizeof(request->parameters.set_overcurrent_state_request.should_activate));
+    request->parameters.set_overcurrent_state_request.should_activate = should_activate;
+    
+    *request_size = local_request_size;
+    return HAILO_COMMON_STATUS__SUCCESS;
+}
+
+HAILO_COMMON_STATUS_t CONTROL_PROTOCOL__pack_get_overcurrent_state_request(CONTROL_PROTOCOL__request_t *request, size_t *request_size, uint32_t sequence)
+{
+    return control_protocol__pack_empty_request(request, request_size, sequence, HAILO_CONTROL_OPCODE_GET_OVERCURRENT_STATE);
+}
+
+HAILO_COMMON_STATUS_t CONTROL_PROTOCOL__pack_set_clock_freq_request(CONTROL_PROTOCOL__request_t *request, size_t *request_size, uint32_t sequence,
+                                                                     uint32_t clock_freq)
+{
+    size_t local_request_size = 0;
+
+    CHECK(request != nullptr, HAILO_STATUS__CONTROL_PROTOCOL__NULL_ARGUMENT_PASSED);
+    CHECK(request_size != nullptr, HAILO_STATUS__CONTROL_PROTOCOL__NULL_ARGUMENT_PASSED);
+
+    /* Header */
+    local_request_size = CONTROL_PROTOCOL__REQUEST_BASE_SIZE + sizeof(CONTROL_PROTOCOL__set_clock_freq_request_t);
+    control_protocol__pack_request_header(request, sequence, HAILO_CONTROL_OPCODE_SET_CLOCK_FREQ, 1);
+
+    request->parameters.set_clock_freq_request.clock_freq_length = BYTE_ORDER__htonl(sizeof(request->parameters.set_clock_freq_request.clock_freq));
+    request->parameters.set_clock_freq_request.clock_freq = BYTE_ORDER__htonl(clock_freq);
+    
+    *request_size = local_request_size;
+    return HAILO_COMMON_STATUS__SUCCESS;
+}
+
+HAILO_COMMON_STATUS_t CONTROL_PROTOCOL__pack_write_memory_request(CONTROL_PROTOCOL__request_t *request, size_t *request_size, uint32_t sequence, uint32_t address, const uint8_t *data, uint32_t data_length)
+{
+    HAILO_COMMON_STATUS_t status = HAILO_COMMON_STATUS__UNINITIALIZED;
+    size_t local_request_size = 0;
+
+    if ((NULL == request) || (NULL == request_size) || (NULL == data)) {
+        status = HAILO_STATUS__CONTROL_PROTOCOL__NULL_ARGUMENT_PASSED;
+        goto exit;
+    }
+
+    /* Header */
+    local_request_size = CONTROL_PROTOCOL__REQUEST_BASE_SIZE + sizeof(CONTROL_PROTOCOL__write_memory_request_t) + data_length;
+    control_protocol__pack_request_header(request, sequence, HAILO_CONTROL_OPCODE_WRITE_MEMORY, 2);
+
+    /* Address */
+    request->parameters.write_memory_request.address_length = BYTE_ORDER__htonl(sizeof(request->parameters.write_memory_request.address));
+    request->parameters.write_memory_request.address = BYTE_ORDER__htonl(address);
+
+    /* Data */
+    request->parameters.write_memory_request.data_length = BYTE_ORDER__htonl(data_length);
+    memcpy(&(request->parameters.write_memory_request.data), data, data_length);
+
+
+    *request_size = local_request_size;
+    status = HAILO_COMMON_STATUS__SUCCESS;
+exit:
+    return status;
+}
+
+HAILO_COMMON_STATUS_t CONTROL_PROTOCOL__pack_read_memory_request(CONTROL_PROTOCOL__request_t *request, size_t *request_size, uint32_t sequence, uint32_t address, uint32_t data_length)
+{
+    HAILO_COMMON_STATUS_t status = HAILO_COMMON_STATUS__UNINITIALIZED;
+    size_t local_request_size = 0;
+
+    if ((NULL == request) || (NULL == request_size)) {
+        status = HAILO_STATUS__CONTROL_PROTOCOL__NULL_ARGUMENT_PASSED;
+        goto exit;
+    }
+
+    /* Header */
+    local_request_size = CONTROL_PROTOCOL__REQUEST_BASE_SIZE + sizeof(CONTROL_PROTOCOL__read_memory_request_t);
+    control_protocol__pack_request_header(request, sequence, HAILO_CONTROL_OPCODE_READ_MEMORY, 2);
+
+    /* Address */
+    request->parameters.read_memory_request.address_length = BYTE_ORDER__htonl(sizeof(request->parameters.read_memory_request.address));
+    request->parameters.read_memory_request.address = BYTE_ORDER__htonl(address);
+
+    /* Data count */
+    request->parameters.read_memory_request.data_count_length = BYTE_ORDER__htonl(sizeof(request->parameters.read_memory_request.data_count));
+    request->parameters.read_memory_request.data_count = BYTE_ORDER__htonl(data_length);
+
+
+    *request_size = local_request_size;
+    status = HAILO_COMMON_STATUS__SUCCESS;
+exit:
+    return status;
+}
+
+HAILO_COMMON_STATUS_t CONTROL_PROTOCOL__pack_open_stream_request(CONTROL_PROTOCOL__request_t *request, size_t *request_size, uint32_t sequence, uint8_t dataflow_manager_id, uint8_t is_input)
+{
+    HAILO_COMMON_STATUS_t status = HAILO_COMMON_STATUS__UNINITIALIZED;
+    size_t local_request_size = 0;
+
+    if ((NULL == request) || (NULL == request_size)) {
+        status = HAILO_STATUS__CONTROL_PROTOCOL__NULL_ARGUMENT_PASSED;
+        goto exit;
+    }
+
+    /* Header */
+    local_request_size = CONTROL_PROTOCOL__REQUEST_BASE_SIZE + sizeof(CONTROL_PROTOCOL__open_stream_request_t);
+    control_protocol__pack_request_header(request, sequence, HAILO_CONTROL_OPCODE_OPEN_STREAM, 2);
+
+    /* dataflow_manager_id */
+    request->parameters.open_stream_request.dataflow_manager_id_length = BYTE_ORDER__htonl(sizeof(request->parameters.open_stream_request.dataflow_manager_id));
+    request->parameters.open_stream_request.dataflow_manager_id = dataflow_manager_id;
+
+    /* is_input */
+    request->parameters.open_stream_request.is_input_length = BYTE_ORDER__htonl(sizeof(request->parameters.open_stream_request.is_input));
+    request->parameters.open_stream_request.is_input = is_input;
+
+
+    *request_size = local_request_size;
+    status = HAILO_COMMON_STATUS__SUCCESS;
+exit:
+    return status;
+}
+
+HAILO_COMMON_STATUS_t CONTROL_PROTOCOL__pack_close_stream_request(CONTROL_PROTOCOL__request_t *request, size_t *request_size, uint32_t sequence, uint8_t dataflow_manager_id, uint8_t is_input)
+{
+    HAILO_COMMON_STATUS_t status = HAILO_COMMON_STATUS__UNINITIALIZED;
+    size_t local_request_size = 0;
+
+    if ((NULL == request) || (NULL == request_size)) {
+        status = HAILO_STATUS__CONTROL_PROTOCOL__NULL_ARGUMENT_PASSED;
+        goto exit;
+    }
+
+    /* Header */
+    local_request_size = CONTROL_PROTOCOL__REQUEST_BASE_SIZE + sizeof(CONTROL_PROTOCOL__close_stream_request_t);
+    control_protocol__pack_request_header(request, sequence, HAILO_CONTROL_OPCODE_CLOSE_STREAM, 2);
+
+    /* dataflow_manager_id */
+    request->parameters.close_stream_request.dataflow_manager_id_length = BYTE_ORDER__htonl(sizeof(request->parameters.close_stream_request.dataflow_manager_id));
+    request->parameters.close_stream_request.dataflow_manager_id = dataflow_manager_id;
+
+    /* is_input */
+    request->parameters.close_stream_request.is_input_length = BYTE_ORDER__htonl(sizeof(request->parameters.close_stream_request.is_input));
+    request->parameters.close_stream_request.is_input = is_input;
+
+
+    *request_size = local_request_size;
+    status = HAILO_COMMON_STATUS__SUCCESS;
+exit:
+    return status;
+}
+
+HAILO_COMMON_STATUS_t control_protocol__pack_config_stream_base_request(CONTROL_PROTOCOL__request_t *request, CONTROL_PROTOCOL__config_stream_params_t *params)
+{
+    HAILO_COMMON_STATUS_t status = HAILO_COMMON_STATUS__UNINITIALIZED;
+
+    /* stream index */
+    request->parameters.config_stream_request.stream_index_length = BYTE_ORDER__htonl(sizeof(request->parameters.config_stream_request.stream_index));
+    request->parameters.config_stream_request.stream_index = params->stream_index;
+
+    /* is_input */
+    request->parameters.config_stream_request.is_input_length = BYTE_ORDER__htonl(sizeof(request->parameters.config_stream_request.is_input));
+    request->parameters.config_stream_request.is_input = params->is_input;
+
+    /* communication_type */
+    request->parameters.config_stream_request.communication_type_length = BYTE_ORDER__htonl(sizeof(request->parameters.config_stream_request.communication_type));
+    request->parameters.config_stream_request.communication_type = BYTE_ORDER__htonl(params->communication_type);
+
+    /* skip_nn_stream_config */
+    request->parameters.config_stream_request.skip_nn_stream_config_length = BYTE_ORDER__htonl(sizeof(request->parameters.config_stream_request.skip_nn_stream_config));
+    request->parameters.config_stream_request.skip_nn_stream_config = params->skip_nn_stream_config;
+
+    /* power_mode */
+    request->parameters.config_stream_request.power_mode_length = BYTE_ORDER__htonl(sizeof(request->parameters.config_stream_request.power_mode));
+    request->parameters.config_stream_request.power_mode = params->power_mode;
+
+    /* nn_stream_config */
+    request->parameters.config_stream_request.nn_stream_config_length = BYTE_ORDER__htonl(sizeof(request->parameters.config_stream_request.nn_stream_config));
+    request->parameters.config_stream_request.nn_stream_config.core_bytes_per_buffer = BYTE_ORDER__htons(params->nn_stream_config.core_bytes_per_buffer);
+    request->parameters.config_stream_request.nn_stream_config.core_buffers_per_frame = BYTE_ORDER__htons(params->nn_stream_config.core_buffers_per_frame);
+    request->parameters.config_stream_request.nn_stream_config.periph_bytes_per_buffer = BYTE_ORDER__htons(params->nn_stream_config.periph_bytes_per_buffer);
+    request->parameters.config_stream_request.nn_stream_config.feature_padding_payload = BYTE_ORDER__htons(params->nn_stream_config.feature_padding_payload);
+    request->parameters.config_stream_request.nn_stream_config.buffer_padding_payload = BYTE_ORDER__htons(params->nn_stream_config.buffer_padding_payload);
+    request->parameters.config_stream_request.nn_stream_config.buffer_padding = BYTE_ORDER__htons(params->nn_stream_config.buffer_padding);
+
+    status = HAILO_COMMON_STATUS__SUCCESS;
+    goto exit;
+exit:
+    return status;
+}
+
+HAILO_COMMON_STATUS_t CONTROL_PROTOCOL__pack_config_stream_udp_input_request(CONTROL_PROTOCOL__request_t *request, size_t *request_size, uint32_t sequence, CONTROL_PROTOCOL__config_stream_params_t *params)
+{
+    HAILO_COMMON_STATUS_t status = HAILO_COMMON_STATUS__UNINITIALIZED;
+    size_t local_request_size = 0;
+
+    if ((NULL == request) || (NULL == request_size) || (NULL == params)) {
+        status = HAILO_STATUS__CONTROL_PROTOCOL__NULL_ARGUMENT_PASSED;
+        goto exit;
+    }
+
+    /* Header */
+    local_request_size = CONTROL_PROTOCOL__REQUEST_BASE_SIZE + sizeof(CONTROL_PROTOCOL__config_stream_request_t) - sizeof(CONTROL_PROTOCOL__communication_config_prams_t) + sizeof(CONTROL_PROTOCOL__udp_input_config_params_t);
+    control_protocol__pack_request_header(request, sequence, HAILO_CONTROL_OPCODE_CONFIG_STREAM, 7);
+
+    status = control_protocol__pack_config_stream_base_request(request, params);
+    if (HAILO_COMMON_STATUS__SUCCESS != status) {
+        goto exit;
+    }
+
+    request->parameters.config_stream_request.communication_params_length = BYTE_ORDER__htonl(sizeof(params->communication_params.udp_input));
+    request->parameters.config_stream_request.communication_params.udp_input.listening_port = BYTE_ORDER__htons(params->communication_params.udp_input.listening_port);
+
+    request->parameters.config_stream_request.communication_params.udp_input.sync.should_sync = params->communication_params.udp_input.sync.should_sync;
+    request->parameters.config_stream_request.communication_params.udp_input.sync.frames_per_sync = BYTE_ORDER__htonl(params->communication_params.udp_input.sync.frames_per_sync);
+    request->parameters.config_stream_request.communication_params.udp_input.sync.packets_per_frame = BYTE_ORDER__htonl(params->communication_params.udp_input.sync.packets_per_frame);
+    request->parameters.config_stream_request.communication_params.udp_input.sync.sync_size = BYTE_ORDER__htons(params->communication_params.udp_input.sync.sync_size);
+
+    request->parameters.config_stream_request.communication_params.udp_input.buffers_threshold = BYTE_ORDER__htonl(params->communication_params.udp_input.buffers_threshold);
+    request->parameters.config_stream_request.communication_params.udp_input.use_rtp = params->communication_params.udp_input.use_rtp;
+
+    *request_size = local_request_size;
+    status = HAILO_COMMON_STATUS__SUCCESS;
+exit:
+    return status;
+}
+
+HAILO_COMMON_STATUS_t CONTROL_PROTOCOL__pack_config_stream_udp_output_request(CONTROL_PROTOCOL__request_t *request, size_t *request_size, uint32_t sequence, CONTROL_PROTOCOL__config_stream_params_t *params)
+{
+    HAILO_COMMON_STATUS_t status = HAILO_COMMON_STATUS__UNINITIALIZED;
+    size_t local_request_size = 0;
+
+    if ((NULL == request) || (NULL == request_size) || (NULL == params)) {
+        status = HAILO_STATUS__CONTROL_PROTOCOL__NULL_ARGUMENT_PASSED;
+        goto exit;
+    }
+
+    /* Header */
+    local_request_size = CONTROL_PROTOCOL__REQUEST_BASE_SIZE + sizeof(CONTROL_PROTOCOL__config_stream_request_t) - sizeof(CONTROL_PROTOCOL__communication_config_prams_t) + sizeof(CONTROL_PROTOCOL__udp_output_config_params_t);
+
+    control_protocol__pack_request_header(request, sequence, HAILO_CONTROL_OPCODE_CONFIG_STREAM, 7);
+
+    status = control_protocol__pack_config_stream_base_request(request, params);
+    if (HAILO_COMMON_STATUS__SUCCESS != status) {
+        goto exit;
+    }
+
+    request->parameters.config_stream_request.communication_params_length = BYTE_ORDER__htonl(sizeof(params->communication_params.udp_output));
+    request->parameters.config_stream_request.communication_params.udp_output.host_udp_port = BYTE_ORDER__htons(params->communication_params.udp_output.host_udp_port);
+    request->parameters.config_stream_request.communication_params.udp_output.chip_udp_port = BYTE_ORDER__htons(params->communication_params.udp_output.chip_udp_port);
+    request->parameters.config_stream_request.communication_params.udp_output.max_udp_payload_size = BYTE_ORDER__htons(params->communication_params.udp_output.max_udp_payload_size);
+    request->parameters.config_stream_request.communication_params.udp_output.should_send_sync_packets = params->communication_params.udp_output.should_send_sync_packets;
+    request->parameters.config_stream_request.communication_params.udp_output.buffers_threshold = BYTE_ORDER__htonl(params->communication_params.udp_output.buffers_threshold);
+    request->parameters.config_stream_request.communication_params.udp_output.use_rtp = params->communication_params.udp_output.use_rtp;
+
+    *request_size = local_request_size;
+    status = HAILO_COMMON_STATUS__SUCCESS;
+exit:
+    return status;
+}
+
+HAILO_COMMON_STATUS_t CONTROL_PROTOCOL__pack_config_stream_mipi_input_request(CONTROL_PROTOCOL__request_t *request, size_t *request_size, uint32_t sequence, CONTROL_PROTOCOL__config_stream_params_t *params)
+{
+    HAILO_COMMON_STATUS_t status = HAILO_COMMON_STATUS__UNINITIALIZED;
+    size_t local_request_size = 0;
+
+    if ((NULL == request) || (NULL == request_size) || (NULL == params)) {
+        status = HAILO_STATUS__CONTROL_PROTOCOL__NULL_ARGUMENT_PASSED;
+        goto exit;
+    }
+
+    /* Header */
+    /* Calculate the size of the exact mipi_input configuration struct instead of the entire union */
+    local_request_size = CONTROL_PROTOCOL__REQUEST_BASE_SIZE + sizeof(CONTROL_PROTOCOL__config_stream_request_t) - sizeof(CONTROL_PROTOCOL__communication_config_prams_t) + sizeof(CONTROL_PROTOCOL__mipi_input_config_params_t);
+    control_protocol__pack_request_header(request, sequence, HAILO_CONTROL_OPCODE_CONFIG_STREAM, 7);
+
+    status = control_protocol__pack_config_stream_base_request(request, params);
+    if (HAILO_COMMON_STATUS__SUCCESS != status) {
+        goto exit;
+    }
+
+    request->parameters.config_stream_request.communication_params_length = BYTE_ORDER__htonl(sizeof(params->communication_params.mipi_input));
+    request->parameters.config_stream_request.communication_params.mipi_input.common_params.data_type = params->communication_params.mipi_input.common_params.data_type;
+    request->parameters.config_stream_request.communication_params.mipi_input.common_params.pixels_per_clock = params->communication_params.mipi_input.common_params.pixels_per_clock;
+    request->parameters.config_stream_request.communication_params.mipi_input.mipi_rx_id = params->communication_params.mipi_input.mipi_rx_id;
+    request->parameters.config_stream_request.communication_params.mipi_input.common_params.number_of_lanes = params->communication_params.mipi_input.common_params.number_of_lanes;
+    request->parameters.config_stream_request.communication_params.mipi_input.common_params.clock_selection = params->communication_params.mipi_input.common_params.clock_selection;
+    request->parameters.config_stream_request.communication_params.mipi_input.common_params.data_rate = BYTE_ORDER__htonl(params->communication_params.mipi_input.common_params.data_rate);
+    request->parameters.config_stream_request.communication_params.mipi_input.common_params.virtual_channel_index = params->communication_params.mipi_input.common_params.virtual_channel_index;
+    request->parameters.config_stream_request.communication_params.mipi_input.common_params.img_width_pixels = params->communication_params.mipi_input.common_params.img_width_pixels;
+    request->parameters.config_stream_request.communication_params.mipi_input.common_params.img_height_pixels = params->communication_params.mipi_input.common_params.img_height_pixels;
+    request->parameters.config_stream_request.communication_params.mipi_input.isp_params.isp_enable = params->communication_params.mipi_input.isp_params.isp_enable;
+    request->parameters.config_stream_request.communication_params.mipi_input.isp_params.isp_img_in_order = params->communication_params.mipi_input.isp_params.isp_img_in_order;
+    request->parameters.config_stream_request.communication_params.mipi_input.isp_params.isp_img_out_data_type = params->communication_params.mipi_input.isp_params.isp_img_out_data_type;
+    request->parameters.config_stream_request.communication_params.mipi_input.isp_params.isp_crop_enable = params->communication_params.mipi_input.isp_params.isp_crop_enable;
+    request->parameters.config_stream_request.communication_params.mipi_input.isp_params.isp_crop_output_width_pixels = params->communication_params.mipi_input.isp_params.isp_crop_output_width_pixels;
+    request->parameters.config_stream_request.communication_params.mipi_input.isp_params.isp_crop_output_height_pixels = params->communication_params.mipi_input.isp_params.isp_crop_output_height_pixels;
+    request->parameters.config_stream_request.communication_params.mipi_input.isp_params.isp_crop_output_width_start_offset_pixels = params->communication_params.mipi_input.isp_params.isp_crop_output_width_start_offset_pixels;
+    request->parameters.config_stream_request.communication_params.mipi_input.isp_params.isp_crop_output_height_start_offset_pixels = params->communication_params.mipi_input.isp_params.isp_crop_output_height_start_offset_pixels;
+    request->parameters.config_stream_request.communication_params.mipi_input.isp_params.isp_test_pattern_enable = params->communication_params.mipi_input.isp_params.isp_test_pattern_enable;
+    request->parameters.config_stream_request.communication_params.mipi_input.isp_params.isp_configuration_bypass = params->communication_params.mipi_input.isp_params.isp_configuration_bypass;
+    request->parameters.config_stream_request.communication_params.mipi_input.isp_params.isp_run_time_ae_enable = params->communication_params.mipi_input.isp_params.isp_run_time_ae_enable;
+    request->parameters.config_stream_request.communication_params.mipi_input.isp_params.isp_run_time_awb_enable = params->communication_params.mipi_input.isp_params.isp_run_time_awb_enable;
+    request->parameters.config_stream_request.communication_params.mipi_input.isp_params.isp_run_time_adt_enable = params->communication_params.mipi_input.isp_params.isp_run_time_adt_enable;
+    request->parameters.config_stream_request.communication_params.mipi_input.isp_params.isp_run_time_af_enable = params->communication_params.mipi_input.isp_params.isp_run_time_af_enable;
+    request->parameters.config_stream_request.communication_params.mipi_input.isp_params.isp_run_time_calculations_interval_ms = params->communication_params.mipi_input.isp_params.isp_run_time_calculations_interval_ms;
+    request->parameters.config_stream_request.communication_params.mipi_input.isp_params.isp_light_frequency = params->communication_params.mipi_input.isp_params.isp_light_frequency;
+
+    *request_size = local_request_size;
+    status = HAILO_COMMON_STATUS__SUCCESS;
+exit:
+    return status;
+}
+
+HAILO_COMMON_STATUS_t CONTROL_PROTOCOL__pack_config_stream_mipi_output_request(CONTROL_PROTOCOL__request_t *request, size_t *request_size, uint32_t sequence, CONTROL_PROTOCOL__config_stream_params_t *params)
+{
+    HAILO_COMMON_STATUS_t status = HAILO_COMMON_STATUS__UNINITIALIZED;
+    size_t local_request_size = 0;
+
+    if ((NULL == request) || (NULL == request_size) || (NULL == params)) {
+        status = HAILO_STATUS__CONTROL_PROTOCOL__NULL_ARGUMENT_PASSED;
+        goto exit;
+    }
+
+    /* Header */
+    /* Calculate the size of the exact mipi_output configuration struct instead of the entire union */
+    local_request_size = CONTROL_PROTOCOL__REQUEST_BASE_SIZE + sizeof(CONTROL_PROTOCOL__config_stream_request_t) - sizeof(CONTROL_PROTOCOL__communication_config_prams_t) + sizeof(CONTROL_PROTOCOL__mipi_output_config_params_t);
+    control_protocol__pack_request_header(request, sequence, HAILO_CONTROL_OPCODE_CONFIG_STREAM, 7);
+
+    status = control_protocol__pack_config_stream_base_request(request, params);
+    if (HAILO_COMMON_STATUS__SUCCESS != status) {
+        goto exit;
+    }
+
+    request->parameters.config_stream_request.communication_params_length = BYTE_ORDER__htonl(sizeof(params->communication_params.mipi_output));
+    request->parameters.config_stream_request.communication_params.mipi_output.fifo_threshold_percent = params->communication_params.mipi_output.fifo_threshold_percent;
+    request->parameters.config_stream_request.communication_params.mipi_output.mipi_tx_id = params->communication_params.mipi_output.mipi_tx_id;
+    request->parameters.config_stream_request.communication_params.mipi_output.deskew_enable = params->communication_params.mipi_output.deskew_enable;
+    request->parameters.config_stream_request.communication_params.mipi_output.common_params.data_rate = BYTE_ORDER__htonl(params->communication_params.mipi_output.common_params.data_rate);
+    request->parameters.config_stream_request.communication_params.mipi_output.common_params.clock_selection = params->communication_params.mipi_output.common_params.clock_selection;
+    request->parameters.config_stream_request.communication_params.mipi_output.common_params.data_type = params->communication_params.mipi_output.common_params.data_type;
+    request->parameters.config_stream_request.communication_params.mipi_output.common_params.number_of_lanes = params->communication_params.mipi_output.common_params.number_of_lanes;
+    request->parameters.config_stream_request.communication_params.mipi_output.common_params.pixels_per_clock = params->communication_params.mipi_output.common_params.pixels_per_clock;
+    request->parameters.config_stream_request.communication_params.mipi_output.common_params.virtual_channel_index = params->communication_params.mipi_output.common_params.virtual_channel_index;
+
+    *request_size = local_request_size;
+    status = HAILO_COMMON_STATUS__SUCCESS;
+exit:
+    return status;
+}
+
+HAILO_COMMON_STATUS_t CONTROL_PROTOCOL__pack_config_stream_pcie_input_request(CONTROL_PROTOCOL__request_t *request, size_t *request_size, uint32_t sequence, CONTROL_PROTOCOL__config_stream_params_t *params)
+{
+    HAILO_COMMON_STATUS_t status = HAILO_COMMON_STATUS__UNINITIALIZED;
+    size_t local_request_size = 0;
+
+    if ((NULL == request) || (NULL == request_size) || (NULL == params)) {
+        status = HAILO_STATUS__CONTROL_PROTOCOL__NULL_ARGUMENT_PASSED;
+        goto exit;
+    }
+
+    /* Header */
+    local_request_size = CONTROL_PROTOCOL__REQUEST_BASE_SIZE + sizeof(CONTROL_PROTOCOL__config_stream_request_t) 
+        - sizeof(CONTROL_PROTOCOL__communication_config_prams_t) + sizeof(CONTROL_PROTOCOL__pcie_input_config_params_t);
+
+    control_protocol__pack_request_header(request, sequence, HAILO_CONTROL_OPCODE_CONFIG_STREAM, 7);
+
+    status = control_protocol__pack_config_stream_base_request(request, params);
+    if (HAILO_COMMON_STATUS__SUCCESS != status) {
+        goto exit;
+    }
+
+    request->parameters.config_stream_request.communication_params_length = 
+        BYTE_ORDER__htonl(sizeof(params->communication_params.pcie_input));
+    request->parameters.config_stream_request.communication_params.pcie_input.pcie_channel_index = 
+        params->communication_params.pcie_input.pcie_channel_index;
+    request->parameters.config_stream_request.communication_params.pcie_input.pcie_dataflow_type = 
+        params->communication_params.pcie_input.pcie_dataflow_type;
+
+    *request_size = local_request_size;
+    status = HAILO_COMMON_STATUS__SUCCESS;
+exit:
+    return status;
+}
+
+HAILO_COMMON_STATUS_t CONTROL_PROTOCOL__pack_config_stream_pcie_output_request(CONTROL_PROTOCOL__request_t *request, size_t *request_size, uint32_t sequence, CONTROL_PROTOCOL__config_stream_params_t *params)
+{
+    HAILO_COMMON_STATUS_t status = HAILO_COMMON_STATUS__UNINITIALIZED;
+    size_t local_request_size = 0;
+
+    if ((NULL == request) || (NULL == request_size) || (NULL == params)) {
+        status = HAILO_STATUS__CONTROL_PROTOCOL__NULL_ARGUMENT_PASSED;
+        goto exit;
+    }
+
+    /* Header */
+    local_request_size = CONTROL_PROTOCOL__REQUEST_BASE_SIZE + sizeof(CONTROL_PROTOCOL__config_stream_request_t) 
+        - sizeof(CONTROL_PROTOCOL__communication_config_prams_t) + sizeof(CONTROL_PROTOCOL__pcie_output_config_params_t);
+
+    control_protocol__pack_request_header(request, sequence, HAILO_CONTROL_OPCODE_CONFIG_STREAM, 7);
+
+    status = control_protocol__pack_config_stream_base_request(request, params);
+    if (HAILO_COMMON_STATUS__SUCCESS != status) {
+        goto exit;
+    }
+
+    request->parameters.config_stream_request.communication_params_length = 
+        BYTE_ORDER__htonl(sizeof(params->communication_params.pcie_output));
+    request->parameters.config_stream_request.communication_params.pcie_output.pcie_channel_index = 
+        params->communication_params.pcie_output.pcie_channel_index;
+    request->parameters.config_stream_request.communication_params.pcie_output.desc_page_size = 
+        params->communication_params.pcie_output.desc_page_size;
+
+    *request_size = local_request_size;
+    status = HAILO_COMMON_STATUS__SUCCESS;
+exit:
+    return status;
+}
+
+HAILO_COMMON_STATUS_t CONTROL_PROTOCOL__pack_reset_request(CONTROL_PROTOCOL__request_t *request, size_t *request_size, uint32_t sequence, CONTROL_PROTOCOL__reset_type_t reset_type)
+{
+    HAILO_COMMON_STATUS_t status = HAILO_COMMON_STATUS__UNINITIALIZED;
+    size_t local_request_size = 0;
+
+    if ((NULL == request) || (NULL == request_size)) {
+        status = HAILO_STATUS__CONTROL_PROTOCOL__NULL_ARGUMENT_PASSED;
+        goto exit;
+    }
+
+    /* Header */
+    local_request_size = CONTROL_PROTOCOL__REQUEST_BASE_SIZE + sizeof(CONTROL_PROTOCOL__reset_request_t);
+    control_protocol__pack_request_header(request, sequence, HAILO_CONTROL_OPCODE_RESET, 1);
+
+    /* reset_type */
+    request->parameters.reset_resquest.reset_type_length = BYTE_ORDER__htonl(sizeof(request->parameters.reset_resquest.reset_type));
+    request->parameters.reset_resquest.reset_type = BYTE_ORDER__htonl((uint32_t)reset_type);
+
+    *request_size = local_request_size;
+    status = HAILO_COMMON_STATUS__SUCCESS;
+exit:
+    return status;
+}
+
+HAILO_COMMON_STATUS_t CONTROL_PROTOCOL__pack_power_measurement_request(CONTROL_PROTOCOL__request_t *request, size_t *request_size, uint32_t sequence, CONTROL_PROTOCOL__dvm_options_t dvm, CONTROL_PROTOCOL__power_measurement_types_t measurement_type)
+{
+    HAILO_COMMON_STATUS_t status = HAILO_COMMON_STATUS__UNINITIALIZED;
+    size_t local_request_size = 0;
+
+    if ((NULL == request) || (NULL == request_size)) {
+        status = HAILO_STATUS__CONTROL_PROTOCOL__NULL_ARGUMENT_PASSED;
+        goto exit;
+    }
+
+    /* Header */
+    local_request_size = CONTROL_PROTOCOL__REQUEST_BASE_SIZE + sizeof(CONTROL_PROTOCOL__power_measurement_request_t);
+    control_protocol__pack_request_header(request, sequence, HAILO_CONTROL_OPCODE_POWER_MEASUEMENT, 2);
+
+    /* dvm */
+    request->parameters.measure_power_request.dvm_length = BYTE_ORDER__htonl(sizeof(request->parameters.measure_power_request.dvm_length));
+    request->parameters.measure_power_request.dvm = BYTE_ORDER__htonl((uint32_t)dvm);
+
+
+    /* measurement_type */
+    request->parameters.measure_power_request.measurement_type_length = BYTE_ORDER__htonl(sizeof(request->parameters.measure_power_request.measurement_type));
+    request->parameters.measure_power_request.measurement_type = BYTE_ORDER__htonl((uint32_t)measurement_type);
+
+    *request_size = local_request_size;
+    status = HAILO_COMMON_STATUS__SUCCESS;
+exit:
+    return status;
+}
+
+HAILO_COMMON_STATUS_t CONTROL_PROTOCOL__pack_set_power_measurement_request(CONTROL_PROTOCOL__request_t *request, size_t *request_size, uint32_t sequence, uint32_t index, CONTROL_PROTOCOL__dvm_options_t dvm, CONTROL_PROTOCOL__power_measurement_types_t measurement_type)
+{
+    HAILO_COMMON_STATUS_t status = HAILO_COMMON_STATUS__UNINITIALIZED;
+    size_t local_request_size = 0;
+
+    if ((NULL == request) || (NULL == request_size)) {
+        status = HAILO_STATUS__CONTROL_PROTOCOL__NULL_ARGUMENT_PASSED;
+        goto exit;
+    }
+
+    /* Header */
+    local_request_size = CONTROL_PROTOCOL__REQUEST_BASE_SIZE + sizeof(CONTROL_PROTOCOL__set_power_measurement_request_t);
+    control_protocol__pack_request_header(request, sequence, HAILO_CONTROL_OPCODE_SET_POWER_MEASUEMENT, 3);
+
+    /* index */
+    request->parameters.set_measure_power_request.index_length = BYTE_ORDER__htonl(
+            sizeof(request->parameters.set_measure_power_request.index));
+    request->parameters.set_measure_power_request.index = BYTE_ORDER__htonl(index);
+
+    /* dvm */
+    request->parameters.set_measure_power_request.dvm_length = BYTE_ORDER__htonl(
+            sizeof(request->parameters.set_measure_power_request.dvm));
+    request->parameters.set_measure_power_request.dvm = BYTE_ORDER__htonl((uint32_t)dvm);
+
+
+    /* measurement_type */
+    request->parameters.set_measure_power_request.measurement_type_length = BYTE_ORDER__htonl(
+            sizeof(request->parameters.set_measure_power_request.measurement_type));
+    request->parameters.set_measure_power_request.measurement_type = BYTE_ORDER__htonl((uint32_t)measurement_type);
+
+    *request_size = local_request_size;
+    status = HAILO_COMMON_STATUS__SUCCESS;
+exit:
+    return status;
+}
+
+HAILO_COMMON_STATUS_t CONTROL_PROTOCOL__pack_get_power_measurement_request(CONTROL_PROTOCOL__request_t *request, size_t *request_size, uint32_t sequence, uint32_t index, bool should_clear)
+{
+    HAILO_COMMON_STATUS_t status = HAILO_COMMON_STATUS__UNINITIALIZED;
+    size_t local_request_size = 0;
+
+    if ((NULL == request) || (NULL == request_size)) {
+        status = HAILO_STATUS__CONTROL_PROTOCOL__NULL_ARGUMENT_PASSED;
+        goto exit;
+    }
+
+    /* Header */
+    local_request_size = CONTROL_PROTOCOL__REQUEST_BASE_SIZE + sizeof(CONTROL_PROTOCOL__get_power_measurement_request_t);
+    control_protocol__pack_request_header(request, sequence, HAILO_CONTROL_OPCODE_GET_POWER_MEASUEMENT, 2);
+
+    /* index */
+    request->parameters.get_measure_power_request.index_length = BYTE_ORDER__htonl(
+            sizeof(request->parameters.get_measure_power_request.index));
+    request->parameters.get_measure_power_request.index = BYTE_ORDER__htonl(index);
+
+    /* should_clear */
+    request->parameters.get_measure_power_request.should_clear_length = BYTE_ORDER__htonl(
+            sizeof(request->parameters.get_measure_power_request.should_clear));
+    request->parameters.get_measure_power_request.should_clear = (uint8_t)should_clear;
+
+    *request_size = local_request_size;
+    status = HAILO_COMMON_STATUS__SUCCESS;
+exit:
+    return status;
+}
+
+HAILO_COMMON_STATUS_t CONTROL_PROTOCOL__pack_start_power_measurement_request(CONTROL_PROTOCOL__request_t *request, size_t *request_size, uint32_t sequence, uint32_t delay_milliseconds, CONTROL_PROTOCOL__averaging_factor_t averaging_factor , CONTROL_PROTOCOL__sampling_period_t sampling_period)
+{
+    HAILO_COMMON_STATUS_t status = HAILO_COMMON_STATUS__UNINITIALIZED;
+    size_t local_request_size = 0;
+    uint16_t local_averaging_factor = 0;
+    uint16_t local_sampling_period = 0;
+
+    if ((NULL == request) || (NULL == request_size)) {
+        status = HAILO_STATUS__CONTROL_PROTOCOL__NULL_ARGUMENT_PASSED;
+        goto exit;
+    }
+
+    local_averaging_factor = ((uint16_t)(averaging_factor));
+    local_sampling_period = ((uint16_t)(sampling_period));
+
+    /* Header */
+    local_request_size = CONTROL_PROTOCOL__REQUEST_BASE_SIZE + sizeof(CONTROL_PROTOCOL__start_power_measurement_request_t);
+    control_protocol__pack_request_header(request, sequence, HAILO_CONTROL_OPCODE_START_POWER_MEASUEMENT, 3);
+
+    /* delay_milliseconds */
+    request->parameters.start_measure_power_request.delay_milliseconds_length = BYTE_ORDER__htonl(
+            sizeof(request->parameters.start_measure_power_request.delay_milliseconds));
+    request->parameters.start_measure_power_request.delay_milliseconds = BYTE_ORDER__htonl(delay_milliseconds);
+
+    /* averaging_factor */
+    request->parameters.start_measure_power_request.averaging_factor_length = BYTE_ORDER__htonl(
+            sizeof(request->parameters.start_measure_power_request.averaging_factor));
+    request->parameters.start_measure_power_request.averaging_factor = BYTE_ORDER__htons(local_averaging_factor);
+
+    /* sampling_period */
+    request->parameters.start_measure_power_request.sampling_period_length = BYTE_ORDER__htonl(
+            sizeof(request->parameters.start_measure_power_request.sampling_period));
+    request->parameters.start_measure_power_request.sampling_period = BYTE_ORDER__htons(local_sampling_period);
+
+    *request_size = local_request_size;
+    status = HAILO_COMMON_STATUS__SUCCESS;
+exit:
+    return status;
+}
+
+HAILO_COMMON_STATUS_t CONTROL_PROTOCOL__pack_i2c_write_request(CONTROL_PROTOCOL__request_t *request, size_t *request_size,
+        uint32_t sequence, uint32_t register_address, uint8_t endianness, uint16_t slave_address,
+        uint8_t register_address_size, uint8_t bus_index, const uint8_t *data, uint32_t length)
+{
+    HAILO_COMMON_STATUS_t status = HAILO_COMMON_STATUS__UNINITIALIZED;
+    size_t local_request_size = 0;
+
+    if ((NULL == request) || (NULL == request_size) || (NULL == data)) {
+        status = HAILO_STATUS__CONTROL_PROTOCOL__NULL_ARGUMENT_PASSED;
+        goto exit;
+    }
+
+    /* Header */
+    local_request_size = CONTROL_PROTOCOL__REQUEST_BASE_SIZE + sizeof(CONTROL_PROTOCOL__i2c_write_request_t) + length;
+    control_protocol__pack_request_header(request, sequence, HAILO_CONTROL_OPCODE_I2C_WRITE, 7);
+
+    /* register_address */
+    request->parameters.i2c_write_request.register_address_size = BYTE_ORDER__htonl(
+            sizeof(request->parameters.i2c_write_request.register_address));
+    request->parameters.i2c_write_request.register_address = BYTE_ORDER__htonl(register_address);
+
+    /* endianness */
+    request->parameters.i2c_write_request.slave_config.endianness_length = BYTE_ORDER__htonl(
+            sizeof(request->parameters.i2c_write_request.slave_config.endianness));
+    request->parameters.i2c_write_request.slave_config.endianness = endianness;
+
+    /* slave_address */
+    request->parameters.i2c_write_request.slave_config.slave_address_length = BYTE_ORDER__htonl(
+            sizeof(request->parameters.i2c_write_request.slave_config.slave_address));
+    request->parameters.i2c_write_request.slave_config.slave_address = BYTE_ORDER__htons(slave_address);
+
+    /* register_address_size */
+    request->parameters.i2c_write_request.slave_config.register_address_size_length = BYTE_ORDER__htonl(
+            sizeof(request->parameters.i2c_write_request.slave_config.register_address_size));
+    request->parameters.i2c_write_request.slave_config.register_address_size = register_address_size;
+
+    /* bus_index */
+    request->parameters.i2c_write_request.slave_config.bus_index_length = BYTE_ORDER__htonl(
+            sizeof(request->parameters.i2c_write_request.slave_config.bus_index));
+    request->parameters.i2c_write_request.slave_config.bus_index = bus_index;
+
+    /* Data */
+    request->parameters.i2c_write_request.data_length = BYTE_ORDER__htonl(length);
+    memcpy(&(request->parameters.i2c_write_request.data), data, length);
+
+    /* should_hold_bus */
+    request->parameters.i2c_write_request.slave_config.should_hold_bus_length = BYTE_ORDER__htonl(
+            sizeof(request->parameters.i2c_write_request.slave_config.should_hold_bus));
+    request->parameters.i2c_write_request.slave_config.should_hold_bus = false;
+
+    *request_size = local_request_size;
+    status = HAILO_COMMON_STATUS__SUCCESS;
+exit:
+    return status;
+}
+
+
+HAILO_COMMON_STATUS_t CONTROL_PROTOCOL__pack_i2c_read_request(CONTROL_PROTOCOL__request_t *request, size_t *request_size,
+        uint32_t sequence, uint32_t register_address, uint8_t endianness,
+        uint16_t slave_address, uint8_t register_address_size, uint8_t bus_index, uint32_t length, bool should_hold_bus)
+{
+    HAILO_COMMON_STATUS_t status = HAILO_COMMON_STATUS__UNINITIALIZED;
+    size_t local_request_size = 0;
+
+    if ((NULL == request) || (NULL == request_size)) {
+        status = HAILO_STATUS__CONTROL_PROTOCOL__NULL_ARGUMENT_PASSED;
+        goto exit;
+    }
+
+    /* Header */
+    local_request_size = CONTROL_PROTOCOL__REQUEST_BASE_SIZE + sizeof(CONTROL_PROTOCOL__i2c_read_request_t);
+    control_protocol__pack_request_header(request, sequence, HAILO_CONTROL_OPCODE_I2C_READ, 7);
+
+    /* data_length */
+    request->parameters.i2c_read_request.data_length_length = BYTE_ORDER__htonl(
+            sizeof(request->parameters.i2c_read_request.data_length));
+    request->parameters.i2c_read_request.data_length = BYTE_ORDER__htonl(length);
+    
+    /* register_address */
+    request->parameters.i2c_read_request.register_address_size = BYTE_ORDER__htonl(
+            sizeof(request->parameters.i2c_read_request.register_address));
+    request->parameters.i2c_read_request.register_address = BYTE_ORDER__htonl(register_address);
+
+    /* endianness */
+    request->parameters.i2c_read_request.slave_config.endianness_length = BYTE_ORDER__htonl(
+            sizeof(request->parameters.i2c_read_request.slave_config.endianness));
+    request->parameters.i2c_read_request.slave_config.endianness = endianness;
+
+    /* slave_address */
+    request->parameters.i2c_read_request.slave_config.slave_address_length = BYTE_ORDER__htonl(
+            sizeof(request->parameters.i2c_read_request.slave_config.slave_address));
+    request->parameters.i2c_read_request.slave_config.slave_address = BYTE_ORDER__htons(slave_address);
+
+    /* register_address_size */
+    request->parameters.i2c_read_request.slave_config.register_address_size_length = BYTE_ORDER__htonl(
+            sizeof(request->parameters.i2c_read_request.slave_config.register_address_size));
+    request->parameters.i2c_read_request.slave_config.register_address_size = register_address_size;
+
+    /* bus_index */
+    request->parameters.i2c_read_request.slave_config.bus_index_length = BYTE_ORDER__htonl(
+            sizeof(request->parameters.i2c_read_request.slave_config.bus_index));
+    request->parameters.i2c_read_request.slave_config.bus_index = bus_index;
+
+    /* should_hold_bus */
+    request->parameters.i2c_read_request.slave_config.should_hold_bus_length = BYTE_ORDER__htonl(
+            sizeof(request->parameters.i2c_read_request.slave_config.should_hold_bus));
+    request->parameters.i2c_read_request.slave_config.should_hold_bus = should_hold_bus;
+
+    *request_size = local_request_size;
+    status = HAILO_COMMON_STATUS__SUCCESS;
+exit:
+    return status;
+}
+
+HAILO_COMMON_STATUS_t CONTROL_PROTOCOL__pack_stop_power_measurement_request(CONTROL_PROTOCOL__request_t *request, size_t *request_size, uint32_t sequence)
+{
+    return control_protocol__pack_empty_request(request, request_size, sequence, HAILO_CONTROL_OPCODE_STOP_POWER_MEASUEMENT);
+}
+
+HAILO_COMMON_STATUS_t CONTROL_PROTOCOL__pack_config_core_top_request(CONTROL_PROTOCOL__request_t *request, size_t *request_size, uint32_t sequence, CONTROL_PROTOCOL__config_core_top_type_t config_type, CONTROL_PROTOCOL__config_core_top_params_t *params)
+{
+    HAILO_COMMON_STATUS_t status = HAILO_COMMON_STATUS__UNINITIALIZED;
+    size_t local_request_size = 0;
+
+    if ((NULL == request) || (NULL == request_size) || (NULL == params)) {
+        status = HAILO_STATUS__CONTROL_PROTOCOL__NULL_ARGUMENT_PASSED;
+        goto exit;
+    }
+
+    /* Header */
+    local_request_size = CONTROL_PROTOCOL__REQUEST_BASE_SIZE + sizeof(CONTROL_PROTOCOL__config_core_top_request_t);
+    control_protocol__pack_request_header(request, sequence, HAILO_CONTROL_OPCODE_CONFIG_CORE_TOP, 2);
+
+    /* config_type */
+    request->parameters.config_core_top_request.config_type_length = BYTE_ORDER__htonl(
+            sizeof(request->parameters.config_core_top_request.config_type));
+    request->parameters.config_core_top_request.config_type = BYTE_ORDER__htonl(config_type);
+
+    /* params */
+    request->parameters.config_core_top_request.config_params_length = BYTE_ORDER__htonl(
+            sizeof(request->parameters.config_core_top_request.config_params));
+    (void)memcpy(&request->parameters.config_core_top_request.config_params,
+            params,
+            sizeof(request->parameters.config_core_top_request.config_params));
+
+    *request_size = local_request_size;
+    status = HAILO_COMMON_STATUS__SUCCESS;
+exit:
+    return status;
+}
+
+HAILO_COMMON_STATUS_t CONTROL_PROTOCOL__pack_phy_operation_request(CONTROL_PROTOCOL__request_t *request, size_t *request_size, uint32_t sequence, CONTROL_PROTOCOL__phy_operation_t operation_type)
+{
+    HAILO_COMMON_STATUS_t status = HAILO_COMMON_STATUS__UNINITIALIZED;
+    size_t local_request_size = 0;
+
+    if ((NULL == request) || (NULL == request_size)) {
+        status = HAILO_STATUS__CONTROL_PROTOCOL__NULL_ARGUMENT_PASSED;
+        goto exit;
+    }
+
+    /* Header */
+    local_request_size = CONTROL_PROTOCOL__REQUEST_BASE_SIZE + sizeof(CONTROL_PROTOCOL__phy_operation_request_t);
+    control_protocol__pack_request_header(request, sequence, HAILO_CONTROL_OPCODE_PHY_OPERATION, 1);
+
+    /* operation_type */
+    request->parameters.phy_operation_request.operation_type_length = BYTE_ORDER__htonl(
+            sizeof(request->parameters.phy_operation_request.operation_type));
+    request->parameters.phy_operation_request.operation_type = BYTE_ORDER__htonl((uint32_t)operation_type);
+
+    *request_size = local_request_size;
+    status = HAILO_COMMON_STATUS__SUCCESS;
+exit:
+    return status;
+}
+
+HAILO_COMMON_STATUS_t CONTROL_PROTOCOL__pack_read_user_config(CONTROL_PROTOCOL__request_t *request, size_t *request_size, uint32_t sequence, uint32_t address, uint32_t data_length)
+{
+    HAILO_COMMON_STATUS_t status = HAILO_COMMON_STATUS__UNINITIALIZED;
+    size_t local_request_size = 0;
+
+    if ((NULL == request) || (NULL == request_size)) {
+        status = HAILO_STATUS__CONTROL_PROTOCOL__NULL_ARGUMENT_PASSED;
+        goto exit;
+    }
+
+    /* Header */
+    local_request_size = CONTROL_PROTOCOL__REQUEST_BASE_SIZE + sizeof(CONTROL_PROTOCOL__read_user_config_request_t);
+    control_protocol__pack_request_header(request, sequence, HAILO_CONTROL_OPCODE_READ_USER_CONFIG, 2);
+
+    /* Address */
+    request->parameters.read_user_config_request.address_length = BYTE_ORDER__htonl(sizeof(request->parameters.read_user_config_request.address));
+    request->parameters.read_user_config_request.address = BYTE_ORDER__htonl(address);
+
+    /* Data count */
+    request->parameters.read_user_config_request.data_count_length = BYTE_ORDER__htonl(sizeof(request->parameters.read_user_config_request.data_count));
+    request->parameters.read_user_config_request.data_count = BYTE_ORDER__htonl(data_length);
+
+
+    *request_size = local_request_size;
+    status = HAILO_COMMON_STATUS__SUCCESS;
+exit:
+    return status;
+}
+
+HAILO_COMMON_STATUS_t CONTROL_PROTOCOL__pack_examine_user_config(CONTROL_PROTOCOL__request_t *request, size_t *request_size, uint32_t sequence)
+{
+    HAILO_COMMON_STATUS_t status = HAILO_COMMON_STATUS__UNINITIALIZED;
+    size_t local_request_size = 0;
+
+    if ((NULL == request) || (NULL == request_size)) {
+        status = HAILO_STATUS__CONTROL_PROTOCOL__NULL_ARGUMENT_PASSED;
+        goto exit;
+    }
+
+    /* Header */
+    local_request_size = CONTROL_PROTOCOL__REQUEST_BASE_SIZE;
+    control_protocol__pack_request_header(request, sequence, HAILO_CONTROL_OPCODE_EXAMINE_USER_CONFIG, 0);
+
+    *request_size = local_request_size;
+    status = HAILO_COMMON_STATUS__SUCCESS;
+exit:
+    return status;
+}
+
+HAILO_COMMON_STATUS_t CONTROL_PROTOCOL__pack_write_user_config_request(CONTROL_PROTOCOL__request_t *request, size_t *request_size, uint32_t sequence, uint32_t address, const uint8_t *data, uint32_t data_length)
+{
+    HAILO_COMMON_STATUS_t status = HAILO_COMMON_STATUS__UNINITIALIZED;
+    size_t local_request_size = 0;
+
+    if ((NULL == request) || (NULL == request_size) || (NULL == data)) {
+        status = HAILO_STATUS__CONTROL_PROTOCOL__NULL_ARGUMENT_PASSED;
+        goto exit;
+    }
+
+    /* Header */
+    local_request_size = CONTROL_PROTOCOL__REQUEST_BASE_SIZE + sizeof(CONTROL_PROTOCOL__write_user_config_request_t) + data_length;
+    control_protocol__pack_request_header(request, sequence, HAILO_CONTROL_OPCODE_WRITE_USER_CONFIG, 2);
+
+    /* Address */
+    request->parameters.write_user_config_request.address_length = BYTE_ORDER__htonl(sizeof(request->parameters.write_user_config_request.address));
+    request->parameters.write_user_config_request.address = BYTE_ORDER__htonl(address);
+
+    /* Data */
+    request->parameters.write_user_config_request.data_length = BYTE_ORDER__htonl(data_length);
+    memcpy(&(request->parameters.write_user_config_request.data), data, data_length);
+
+
+    *request_size = local_request_size;
+    status = HAILO_COMMON_STATUS__SUCCESS;
+exit:
+    return status;
+}
+
+HAILO_COMMON_STATUS_t CONTROL_PROTOCOL__pack_erase_user_config_request(CONTROL_PROTOCOL__request_t *request, size_t *request_size, uint32_t sequence)
+{
+    HAILO_COMMON_STATUS_t status = HAILO_COMMON_STATUS__UNINITIALIZED;
+    size_t local_request_size = 0;
+
+    if ((NULL == request) || (NULL == request_size)) {
+        status = HAILO_STATUS__CONTROL_PROTOCOL__NULL_ARGUMENT_PASSED;
+        goto exit;
+    }
+
+    /* Header */
+    local_request_size = CONTROL_PROTOCOL__REQUEST_BASE_SIZE;
+    control_protocol__pack_request_header(request, sequence, HAILO_CONTROL_OPCODE_ERASE_USER_CONFIG, 0);
+
+    *request_size = local_request_size;
+    status = HAILO_COMMON_STATUS__SUCCESS;
+exit:
+    return status;
+}
+
+
+HAILO_COMMON_STATUS_t CONTROL_PROTOCOL__pack_start_firmware_update_request(CONTROL_PROTOCOL__request_t *request, size_t *request_size, uint32_t sequence)
+{
+    HAILO_COMMON_STATUS_t status = HAILO_COMMON_STATUS__UNINITIALIZED;
+    size_t local_request_size = 0;
+
+    if ((NULL == request) || (NULL == request_size)) {
+        status = HAILO_STATUS__CONTROL_PROTOCOL__NULL_ARGUMENT_PASSED;
+        goto exit;
+    }
+
+    /* Header */
+    local_request_size = CONTROL_PROTOCOL__REQUEST_BASE_SIZE ;
+    control_protocol__pack_request_header(request, sequence, HAILO_CONTROL_OPCODE_START_FIRMWARE_UPDATE, 0);
+
+
+    *request_size = local_request_size;
+    status = HAILO_COMMON_STATUS__SUCCESS;
+exit:
+    return status;
+}
+
+HAILO_COMMON_STATUS_t CONTROL_PROTOCOL__pack_finish_firmware_update_request(CONTROL_PROTOCOL__request_t *request, size_t *request_size, uint32_t sequence)
+{
+    HAILO_COMMON_STATUS_t status = HAILO_COMMON_STATUS__UNINITIALIZED;
+    size_t local_request_size = 0;
+
+    if ((NULL == request) || (NULL == request_size)) {
+        status = HAILO_STATUS__CONTROL_PROTOCOL__NULL_ARGUMENT_PASSED;
+        goto exit;
+    }
+
+    /* Header */
+    local_request_size = CONTROL_PROTOCOL__REQUEST_BASE_SIZE ;
+    control_protocol__pack_request_header(request, sequence, HAILO_CONTROL_OPCODE_FINISH_FIRMWARE_UPDATE, 0);
+
+    *request_size = local_request_size;
+    status = HAILO_COMMON_STATUS__SUCCESS;
+exit:
+    return status;
+}
+
+HAILO_COMMON_STATUS_t CONTROL_PROTOCOL__write_firmware_update_request(CONTROL_PROTOCOL__request_t *request, size_t *request_size, uint32_t sequence, uint32_t offset, const uint8_t *data, uint32_t data_length)
+{
+    HAILO_COMMON_STATUS_t status = HAILO_COMMON_STATUS__UNINITIALIZED;
+    size_t local_request_size = 0;
+
+    if ((NULL == request) || (NULL == request_size) || (NULL == data)) {
+        status = HAILO_STATUS__CONTROL_PROTOCOL__NULL_ARGUMENT_PASSED;
+        goto exit;
+    }
+
+    /* Header */
+    local_request_size = CONTROL_PROTOCOL__REQUEST_BASE_SIZE + sizeof(CONTROL_PROTOCOL__write_firmware_update_request_t) + data_length;
+    control_protocol__pack_request_header(request, sequence, HAILO_CONTROL_OPCODE_WRITE_FIRMWARE_UPDATE, 2);
+
+    /* offset */
+    request->parameters.write_firmware_update_request.offset_length = BYTE_ORDER__htonl(sizeof(request->parameters.write_firmware_update_request.offset));
+    request->parameters.write_firmware_update_request.offset = BYTE_ORDER__htonl(offset);
+
+    /* data */
+    request->parameters.write_firmware_update_request.data_length = BYTE_ORDER__htonl(data_length);
+    memcpy(&(request->parameters.write_firmware_update_request.data), data, data_length);
+
+
+    *request_size = local_request_size;
+    status = HAILO_COMMON_STATUS__SUCCESS;
+exit:
+    return status;
+}
+
+HAILO_COMMON_STATUS_t CONTROL_PROTOCOL__write_second_stage_to_internal_memory_request(CONTROL_PROTOCOL__request_t *request, size_t *request_size, uint32_t sequence, uint32_t offset, uint8_t *data, uint32_t data_length)
+{
+    HAILO_COMMON_STATUS_t status = HAILO_COMMON_STATUS__UNINITIALIZED;
+    size_t local_request_size = 0;
+
+    if ((NULL == request) || (NULL == request_size) || (NULL == data)) {
+        status = HAILO_STATUS__CONTROL_PROTOCOL__NULL_ARGUMENT_PASSED;
+        goto exit;
+    }
+
+    /* Header */
+    local_request_size = CONTROL_PROTOCOL__REQUEST_BASE_SIZE + sizeof(CONTROL_PROTOCOL__write_second_stage_to_internal_memory_request_t) + data_length;
+    control_protocol__pack_request_header(request, sequence, HAILO_CONTROL_OPCODE_WRITE_SECOND_STAGE_TO_INTERNAL_MEMORY, 2);
+
+    /* offset */
+    request->parameters.write_second_stage_to_internal_memory_request.offset_length = BYTE_ORDER__htonl(sizeof(request->parameters.write_second_stage_to_internal_memory_request.offset));
+    request->parameters.write_second_stage_to_internal_memory_request.offset = BYTE_ORDER__htonl(offset);
+
+    /* data */
+    request->parameters.write_second_stage_to_internal_memory_request.data_length = BYTE_ORDER__htonl(data_length);
+    memcpy(&(request->parameters.write_second_stage_to_internal_memory_request.data), data, data_length);
+
+
+    *request_size = local_request_size;
+    status = HAILO_COMMON_STATUS__SUCCESS;
+exit:
+    return status;
+}
+
+HAILO_COMMON_STATUS_t CONTROL_PROTOCOL__copy_second_stage_to_flash_request(CONTROL_PROTOCOL__request_t *request, size_t *request_size, uint32_t sequence, MD5_SUM_t *expected_md5, uint32_t second_stage_size)
+{
+    HAILO_COMMON_STATUS_t status = HAILO_COMMON_STATUS__UNINITIALIZED;
+    size_t local_request_size = 0;
+
+    if ((NULL == request) || (NULL == request_size)) {
+        status = HAILO_STATUS__CONTROL_PROTOCOL__NULL_ARGUMENT_PASSED;
+        goto exit;
+    }
+
+    /* Header */
+    local_request_size = CONTROL_PROTOCOL__REQUEST_BASE_SIZE + sizeof(CONTROL_PROTOCOL__copy_second_stage_to_flash_request_t);
+    control_protocol__pack_request_header(request, sequence, HAILO_CONTROL_OPCODE_COPY_SECOND_STAGE_TO_FLASH, 2);
+
+    /* expected md5 */
+    request->parameters.copy_second_stage_to_flash_request.expected_md5_length = BYTE_ORDER__htonl(sizeof(request->parameters.copy_second_stage_to_flash_request.expected_md5));
+    memcpy(&(request->parameters.copy_second_stage_to_flash_request.expected_md5),
+            *expected_md5,
+            sizeof(request->parameters.copy_second_stage_to_flash_request.expected_md5));
+
+    /* second_stage_size */
+    request->parameters.copy_second_stage_to_flash_request.second_stage_size_length = BYTE_ORDER__htonl(sizeof(request->parameters.copy_second_stage_to_flash_request.second_stage_size));
+    request->parameters.copy_second_stage_to_flash_request.second_stage_size = BYTE_ORDER__htonl(second_stage_size);
+
+    *request_size = local_request_size;
+    status = HAILO_COMMON_STATUS__SUCCESS;
+exit:
+    return status;
+}
+
+HAILO_COMMON_STATUS_t CONTROL_PROTOCOL__pack_validate_firmware_update_request(CONTROL_PROTOCOL__request_t *request, size_t *request_size, uint32_t sequence, MD5_SUM_t *expected_md5, uint32_t firmware_size)
+{
+    HAILO_COMMON_STATUS_t status = HAILO_COMMON_STATUS__UNINITIALIZED;
+    size_t local_request_size = 0;
+
+    if ((NULL == request) || (NULL == request_size)) {
+        status = HAILO_STATUS__CONTROL_PROTOCOL__NULL_ARGUMENT_PASSED;
+        goto exit;
+    }
+
+    /* Header */
+    local_request_size = CONTROL_PROTOCOL__REQUEST_BASE_SIZE + sizeof(CONTROL_PROTOCOL__validate_firmware_update_request_t);
+    control_protocol__pack_request_header(request, sequence, HAILO_CONTROL_OPCODE_VALIDATE_FIRMWARE_UPDATE, 2);
+
+    /* expected md5 */
+    request->parameters.validate_firmware_update_request.expected_md5_length = BYTE_ORDER__htonl(sizeof(request->parameters.validate_firmware_update_request.expected_md5));
+    memcpy(&(request->parameters.validate_firmware_update_request.expected_md5),
+            *expected_md5,
+            sizeof(request->parameters.validate_firmware_update_request.expected_md5));
+
+    /* firmware_size */
+    request->parameters.validate_firmware_update_request.firmware_size_length = BYTE_ORDER__htonl(sizeof(request->parameters.validate_firmware_update_request.firmware_size));
+    request->parameters.validate_firmware_update_request.firmware_size = BYTE_ORDER__htonl(firmware_size);
+
+    *request_size = local_request_size;
+    status = HAILO_COMMON_STATUS__SUCCESS;
+exit:
+    return status;
+}
+
+HAILO_COMMON_STATUS_t CONTROL_PROTOCOL__pack_latency_measurement_config_request(CONTROL_PROTOCOL__request_t *request, size_t *request_size, uint32_t sequence, uint8_t latency_measurement_en, uint32_t inbound_start_buffer_number, uint32_t outbound_stop_buffer_number, uint32_t inbound_stream_index, uint32_t outbound_stream_index)
+{
+    HAILO_COMMON_STATUS_t status = HAILO_COMMON_STATUS__UNINITIALIZED;
+    size_t local_request_size = 0;
+
+    if ((NULL == request) || (NULL == request_size)) {
+        status = HAILO_STATUS__CONTROL_PROTOCOL__NULL_ARGUMENT_PASSED;
+        goto exit;
+    }
+
+    /* Header */
+    local_request_size = CONTROL_PROTOCOL__REQUEST_BASE_SIZE + sizeof(CONTROL_PROTOCOL__latency_config_request_t);
+    control_protocol__pack_request_header(request, sequence, HAILO_CONTROL_OPCODE_NN_CORE_LATENCY_MEASUREMENT_CONFIG, 5);
+
+    /* latency_measurement_en */
+    request->parameters.latency_config_request.latency_measurement_en_length = BYTE_ORDER__htonl(
+            sizeof(request->parameters.latency_config_request.latency_measurement_en));
+    request->parameters.latency_config_request.latency_measurement_en = latency_measurement_en;
+
+    /* inbound_start_buffer_number */
+    request->parameters.latency_config_request.inbound_start_buffer_number_length = BYTE_ORDER__htonl(
+            sizeof(request->parameters.latency_config_request.inbound_start_buffer_number));
+    request->parameters.latency_config_request.inbound_start_buffer_number = BYTE_ORDER__htonl(inbound_start_buffer_number);
+
+    /* outbound_stop_buffer_number */
+    request->parameters.latency_config_request.outbound_stop_buffer_number_length = BYTE_ORDER__htonl(
+            sizeof(request->parameters.latency_config_request.outbound_stop_buffer_number));
+    request->parameters.latency_config_request.outbound_stop_buffer_number = BYTE_ORDER__htonl(outbound_stop_buffer_number);
+
+    /* inbound_stream_index */
+    request->parameters.latency_config_request.inbound_stream_index_length = BYTE_ORDER__htonl(
+            sizeof(request->parameters.latency_config_request.inbound_stream_index));
+    request->parameters.latency_config_request.inbound_stream_index = BYTE_ORDER__htonl(inbound_stream_index);
+
+    /* outbound_stream_index */
+    request->parameters.latency_config_request.outbound_stream_index_length = BYTE_ORDER__htonl(
+            sizeof(request->parameters.latency_config_request.outbound_stream_index));
+    request->parameters.latency_config_request.outbound_stream_index = BYTE_ORDER__htonl(outbound_stream_index);
+
+
+    *request_size = local_request_size;
+    status = HAILO_COMMON_STATUS__SUCCESS;
+exit:
+    return status;
+}
+
+HAILO_COMMON_STATUS_t CONTROL_PROTOCOL__pack_latency_measurement_read_request(CONTROL_PROTOCOL__request_t *request, size_t *request_size, uint32_t sequence)
+{
+    HAILO_COMMON_STATUS_t status = HAILO_COMMON_STATUS__UNINITIALIZED;
+    size_t local_request_size = 0;
+
+    if ((NULL == request) || (NULL == request_size)) {
+        status = HAILO_STATUS__CONTROL_PROTOCOL__NULL_ARGUMENT_PASSED;
+        goto exit;
+    }
+
+    /* Header */
+    local_request_size = CONTROL_PROTOCOL__REQUEST_BASE_SIZE;
+    control_protocol__pack_request_header(request, sequence, HAILO_CONTROL_OPCODE_NN_CORE_LATENCY_MEASUREMENT_READ, 0);
+
+    *request_size = local_request_size;
+    status = HAILO_COMMON_STATUS__SUCCESS;
+exit:
+    return status;
+}
+
+
+HAILO_COMMON_STATUS_t CONTROL_PROTOCOL__pack_sensor_store_config_request(CONTROL_PROTOCOL__request_t *request, size_t *request_size, uint32_t sequence, uint32_t is_first, uint32_t section_index,
+                                                                  uint32_t start_offset, uint32_t reset_data_size, uint32_t sensor_type, uint32_t total_data_size,
+                                                                  uint8_t  *data, uint32_t data_length, uint16_t config_height, uint16_t config_width, uint16_t config_fps,
+                                                                  uint32_t config_name_length, uint8_t *config_name)
+
+{
+    HAILO_COMMON_STATUS_t status = HAILO_COMMON_STATUS__UNINITIALIZED;
+    size_t local_request_size = 0;
+
+    if ((NULL == request) || (NULL == request_size) || (NULL == data)) {
+        status = HAILO_STATUS__CONTROL_PROTOCOL__NULL_ARGUMENT_PASSED;
+        goto exit;
+    }
+
+    /* Header */
+    local_request_size = CONTROL_PROTOCOL__REQUEST_BASE_SIZE + sizeof(CONTROL_PROTOCOL__sensor_store_config_request_t) + data_length;
+    
+    control_protocol__pack_request_header(request, sequence, HAILO_CONTROL_OPCODE_SENSOR_STORE_CONFIG, 11);
+
+    /* section index */
+    request->parameters.sensor_store_config_request.section_index_length = BYTE_ORDER__htonl(sizeof(request->parameters.sensor_store_config_request.section_index));
+    request->parameters.sensor_store_config_request.section_index = BYTE_ORDER__htonl(section_index);
+
+    /* is_first */
+    request->parameters.sensor_store_config_request.is_first_length = BYTE_ORDER__htonl(sizeof(request->parameters.sensor_store_config_request.is_first));
+    request->parameters.sensor_store_config_request.is_first = BYTE_ORDER__htonl(is_first);
+
+    /* start_offset */
+    request->parameters.sensor_store_config_request.start_offset_length = BYTE_ORDER__htonl(sizeof(request->parameters.sensor_store_config_request.start_offset));
+    request->parameters.sensor_store_config_request.start_offset = BYTE_ORDER__htonl(start_offset);
+
+    /* reset_data_size */
+    request->parameters.sensor_store_config_request.reset_data_size_length = BYTE_ORDER__htonl(sizeof(request->parameters.sensor_store_config_request.reset_data_size));
+    request->parameters.sensor_store_config_request.reset_data_size = BYTE_ORDER__htonl(reset_data_size);
+    /* sensor_type */
+    request->parameters.sensor_store_config_request.sensor_type_length = BYTE_ORDER__htonl(sizeof(request->parameters.sensor_store_config_request.sensor_type));
+    request->parameters.sensor_store_config_request.sensor_type = BYTE_ORDER__htonl(sensor_type);
+
+    /* total_data_size */
+    request->parameters.sensor_store_config_request.total_data_size_length = BYTE_ORDER__htonl(sizeof(request->parameters.sensor_store_config_request.total_data_size));
+    request->parameters.sensor_store_config_request.total_data_size = BYTE_ORDER__htonl(total_data_size);
+
+    /* config_width */
+    request->parameters.sensor_store_config_request.config_width_length = BYTE_ORDER__htonl(sizeof(request->parameters.sensor_store_config_request.config_width));
+    request->parameters.sensor_store_config_request.config_width = BYTE_ORDER__htons(config_width);
+    
+    /* config_height */
+    request->parameters.sensor_store_config_request.config_height_length = BYTE_ORDER__htonl(sizeof(request->parameters.sensor_store_config_request.config_height));
+    request->parameters.sensor_store_config_request.config_height = BYTE_ORDER__htons(config_height);
+    
+    /* config_fps */
+    request->parameters.sensor_store_config_request.config_fps_length = BYTE_ORDER__htonl(sizeof(request->parameters.sensor_store_config_request.config_fps));
+    request->parameters.sensor_store_config_request.config_fps = BYTE_ORDER__htons(config_fps);
+
+    /* Config_name */
+    if(config_name_length <= MAX_CONFIG_NAME_LEN){
+        request->parameters.sensor_store_config_request.config_name_length = BYTE_ORDER__htonl(MAX_CONFIG_NAME_LEN);
+        memcpy(&(request->parameters.sensor_store_config_request.config_name), config_name, config_name_length);
+    }
+    else{
+        status = HAILO_STATUS__CONTROL_PROTOCOL__INVALID_ARGUMENT;
+        goto exit;
+    }
+    
+    /* Data */
+    request->parameters.sensor_store_config_request.data_length = BYTE_ORDER__htonl(data_length);
+    memcpy(&(request->parameters.sensor_store_config_request.data), data, data_length);
+
+    *request_size = local_request_size;
+    status = HAILO_COMMON_STATUS__SUCCESS;
+exit:
+    return status;
+}
+
+HAILO_COMMON_STATUS_t CONTROL_PROTOCOL__pack_sensor_get_config_request(CONTROL_PROTOCOL__request_t *request, size_t *request_size, uint32_t sequence, 
+                                                                       uint32_t section_index, uint32_t offset, uint32_t data_length)
+{
+    HAILO_COMMON_STATUS_t status = HAILO_COMMON_STATUS__UNINITIALIZED;
+    size_t local_request_size = 0;
+
+    if ((NULL == request) || (NULL == request_size)) {
+        status = HAILO_STATUS__CONTROL_PROTOCOL__NULL_ARGUMENT_PASSED;
+        goto exit;
+    }
+
+    /* Header */
+    local_request_size = CONTROL_PROTOCOL__REQUEST_BASE_SIZE + sizeof(CONTROL_PROTOCOL__sensor_get_config_request_t);
+    control_protocol__pack_request_header(request, sequence, HAILO_CONTROL_OPCODE_SENSOR_GET_CONFIG, 3);
+
+    /* section_index */
+    request->parameters.sensor_get_config_request.section_index_length = BYTE_ORDER__htonl(sizeof(request->parameters.sensor_get_config_request.section_index));
+    request->parameters.sensor_get_config_request.section_index = BYTE_ORDER__htonl(section_index);
+
+     /* offset */
+    request->parameters.sensor_get_config_request.offset_length = BYTE_ORDER__htonl(sizeof(request->parameters.sensor_get_config_request.offset));
+    request->parameters.sensor_get_config_request.offset = BYTE_ORDER__htonl(offset);
+
+    /* Data count */
+    request->parameters.sensor_get_config_request.data_size_length = BYTE_ORDER__htonl(sizeof(request->parameters.sensor_get_config_request.data_size));
+    request->parameters.sensor_get_config_request.data_size = BYTE_ORDER__htonl(data_length);
+
+    *request_size = local_request_size;
+    status = HAILO_COMMON_STATUS__SUCCESS;
+exit:
+    return status;
+}
+
+hailo_status CONTROL_PROTOCOL__pack_sensor_set_i2c_bus_index_request(CONTROL_PROTOCOL__request_t *request, size_t *request_size, uint32_t sequence, uint32_t sensor_type, uint32_t bus_index)
+{
+    size_t local_request_size = 0;
+
+    CHECK_ARG_NOT_NULL(request);
+    CHECK_ARG_NOT_NULL(request_size);
+
+    /* Header */
+    local_request_size = CONTROL_PROTOCOL__REQUEST_BASE_SIZE + sizeof(CONTROL_PROTOCOL__sensor_set_i2c_bus_index_t);
+    control_protocol__pack_request_header(request, sequence, HAILO_CONTROL_OPCODE_SENSOR_SET_I2C_BUS_INDEX, 2);
+
+    /* section index */
+    request->parameters.sensor_set_i2c_bus_index.sensor_type_length = BYTE_ORDER__htonl(sizeof(request->parameters.sensor_set_i2c_bus_index.sensor_type));
+    request->parameters.sensor_set_i2c_bus_index.sensor_type = BYTE_ORDER__htonl(sensor_type);
+
+    /* bus_index */
+    request->parameters.sensor_set_i2c_bus_index.i2c_bus_index_length = BYTE_ORDER__htonl(sizeof(request->parameters.sensor_set_i2c_bus_index.i2c_bus_index));
+    request->parameters.sensor_set_i2c_bus_index.i2c_bus_index = BYTE_ORDER__htonl(bus_index);
+
+    *request_size = local_request_size;
+
+    return HAILO_SUCCESS;
+}
+
+HAILO_COMMON_STATUS_t CONTROL_PROTOCOL__pack_sensor_load_and_start_config_request(CONTROL_PROTOCOL__request_t *request, size_t *request_size, uint32_t sequence, uint32_t section_index)
+{
+    HAILO_COMMON_STATUS_t status = HAILO_COMMON_STATUS__UNINITIALIZED;
+    size_t local_request_size = 0;
+
+    if ((NULL == request) || (NULL == request_size) ) {
+        status = HAILO_STATUS__CONTROL_PROTOCOL__NULL_ARGUMENT_PASSED;
+        goto exit;
+    }
+
+    /* Header */
+    local_request_size = CONTROL_PROTOCOL__REQUEST_BASE_SIZE + sizeof(CONTROL_PROTOCOL__sensor_load_config_request_t);
+    control_protocol__pack_request_header(request, sequence, HAILO_CONTROL_OPCODE_SENSOR_LOAD_AND_START, 1);
+
+    /* section index */
+    request->parameters.sensor_load_config_request.section_index_length = BYTE_ORDER__htonl(sizeof(request->parameters.sensor_load_config_request.section_index));
+    request->parameters.sensor_load_config_request.section_index = BYTE_ORDER__htonl(section_index);
+
+    *request_size = local_request_size;
+    status = HAILO_COMMON_STATUS__SUCCESS;
+exit:
+    return status;
+}
+
+HAILO_COMMON_STATUS_t CONTROL_PROTOCOL__pack_sensor_reset_request(CONTROL_PROTOCOL__request_t *request, size_t *request_size, uint32_t sequence, uint32_t section_index)
+{
+    HAILO_COMMON_STATUS_t status = HAILO_COMMON_STATUS__UNINITIALIZED;
+    size_t local_request_size = 0;
+
+    if ((NULL == request) || (NULL == request_size) ) {
+        status = HAILO_STATUS__CONTROL_PROTOCOL__NULL_ARGUMENT_PASSED;
+        goto exit;
+    }
+
+    /* Header */
+    local_request_size = CONTROL_PROTOCOL__REQUEST_BASE_SIZE + sizeof(CONTROL_PROTOCOL__sensor_reset_request_t);
+    control_protocol__pack_request_header(request, sequence, HAILO_CONTROL_OPCODE_SENSOR_RESET, 1);
+
+    /* section index */
+    request->parameters.sensor_reset_request.section_index_length = BYTE_ORDER__htonl(sizeof(request->parameters.sensor_reset_request.section_index));
+    request->parameters.sensor_reset_request.section_index = BYTE_ORDER__htonl(section_index);
+
+    *request_size = local_request_size;
+    status = HAILO_COMMON_STATUS__SUCCESS;
+exit:
+    return status;
+}
+
+HAILO_COMMON_STATUS_t CONTROL_PROTOCOL__pack_sensor_set_generic_i2c_slave_request(CONTROL_PROTOCOL__request_t *request, size_t *request_size, uint32_t sequence, uint16_t slave_address,
+                                                                                  uint8_t register_address_size, uint8_t bus_index, uint8_t should_hold_bus, uint8_t endianness)
+{
+    HAILO_COMMON_STATUS_t status = HAILO_COMMON_STATUS__UNINITIALIZED;
+    size_t local_request_size = 0;
+
+    if ((NULL == request) || (NULL == request_size) ) {
+        status = HAILO_STATUS__CONTROL_PROTOCOL__NULL_ARGUMENT_PASSED;
+        goto exit;
+    }
+
+    /* Header */
+    local_request_size = CONTROL_PROTOCOL__REQUEST_BASE_SIZE + sizeof(CONTROL_PROTOCOL__sensor_set_generic_i2c_slave_request_t);
+    control_protocol__pack_request_header(request, sequence, HAILO_CONTROL_OPCODE_SENSOR_SET_GENERIC_I2C_SLAVE, 5);
+
+    /* slave_address */
+    request->parameters.sensor_set_generic_i2c_slave_request.slave_address_length = BYTE_ORDER__htonl(sizeof(request->parameters.sensor_set_generic_i2c_slave_request.slave_address));
+    request->parameters.sensor_set_generic_i2c_slave_request.slave_address = BYTE_ORDER__htons(slave_address);
+    
+    /* register_address_size */
+    request->parameters.sensor_set_generic_i2c_slave_request.register_address_size_length = BYTE_ORDER__htonl(sizeof(request->parameters.sensor_set_generic_i2c_slave_request.register_address_size));
+    request->parameters.sensor_set_generic_i2c_slave_request.register_address_size = register_address_size;
+
+    /* bus index */
+    request->parameters.sensor_set_generic_i2c_slave_request.bus_index_length = BYTE_ORDER__htonl(sizeof(request->parameters.sensor_set_generic_i2c_slave_request.bus_index));
+    request->parameters.sensor_set_generic_i2c_slave_request.bus_index = bus_index;
+
+    /* should_hold_bus */
+    request->parameters.sensor_set_generic_i2c_slave_request.should_hold_bus_length = BYTE_ORDER__htonl(sizeof(request->parameters.sensor_set_generic_i2c_slave_request.should_hold_bus));
+    request->parameters.sensor_set_generic_i2c_slave_request.should_hold_bus = should_hold_bus;
+
+    /* endianness */
+    request->parameters.sensor_set_generic_i2c_slave_request.endianness_length = BYTE_ORDER__htonl(sizeof(request->parameters.sensor_set_generic_i2c_slave_request.endianness));
+    request->parameters.sensor_set_generic_i2c_slave_request.endianness = endianness;
+
+    *request_size = local_request_size;
+    status = HAILO_COMMON_STATUS__SUCCESS;
+exit:
+    return status;
+}
+
+HAILO_COMMON_STATUS_t CONTROL_PROTOCOL__pack_sensor_get_sections_info_request(CONTROL_PROTOCOL__request_t *request, size_t *request_size, uint32_t sequence)
+{
+    return control_protocol__pack_empty_request(request, request_size, sequence, HAILO_CONTROL_OPCODE_SENSOR_GET_SECTIONS_INFO);
+}
+
+HAILO_COMMON_STATUS_t CONTROL_PROTOCOL__pack_context_switch_set_main_header_request(
+        CONTROL_PROTOCOL__request_t *request, size_t *request_size, uint32_t sequence, 
+        CONTROL_PROTOCOL__context_switch_main_header_t *context_switch_header)
+{
+    HAILO_COMMON_STATUS_t status = HAILO_COMMON_STATUS__UNINITIALIZED;
+    size_t local_request_size = 0;
+
+    if ((NULL == request) || (NULL == request_size) || (NULL == context_switch_header)) {
+        status = HAILO_STATUS__CONTROL_PROTOCOL__NULL_ARGUMENT_PASSED;
+        goto exit;
+    }
+
+    /* Header */
+    local_request_size = CONTROL_PROTOCOL__REQUEST_BASE_SIZE + sizeof(CONTROL_PROTOCOL__context_switch_set_main_header_request_t);
+    control_protocol__pack_request_header(request, sequence, HAILO_CONTROL_OPCODE_CONTEXT_SWITCH_SET_MAIN_HEADER, 4);
+
+    /* context_switch_version */
+    request->parameters.context_switch_set_main_header_request.context_switch_version_length = 
+        BYTE_ORDER__htonl(sizeof(request->parameters.context_switch_set_main_header_request.context_switch_version));
+    request->parameters.context_switch_set_main_header_request.context_switch_version = 
+        context_switch_header->context_switch_version;
+
+    /* validation_features */
+    request->parameters.context_switch_set_main_header_request.validation_features_length = 
+        BYTE_ORDER__htonl(sizeof(request->parameters.context_switch_set_main_header_request.validation_features));
+    request->parameters.context_switch_set_main_header_request.validation_features = context_switch_header->validation_features;
+
+    /* application_count */
+    request->parameters.context_switch_set_main_header_request.application_count_length = 
+        BYTE_ORDER__htonl(sizeof(request->parameters.context_switch_set_main_header_request.application_count));
+    request->parameters.context_switch_set_main_header_request.application_count = 
+        context_switch_header->application_count;
+
+    /* application_header */
+    request->parameters.context_switch_set_main_header_request.application_header_length = 
+        BYTE_ORDER__htonl(sizeof(request->parameters.context_switch_set_main_header_request.application_header));
+    memcpy(&(request->parameters.context_switch_set_main_header_request.application_header), 
+            &(context_switch_header->application_header), 
+            sizeof(request->parameters.context_switch_set_main_header_request.application_header));
+
+    *request_size = local_request_size;
+    status = HAILO_COMMON_STATUS__SUCCESS;
+exit:
+    return status;
+}
+
+HAILO_COMMON_STATUS_t CONTROL_PROTOCOL__pack_context_switch_set_context_info_request(
+        CONTROL_PROTOCOL__request_t *request, size_t *request_size, uint32_t sequence, 
+        CONTROL_PROTOCOL__context_switch_context_info_single_control_t *context_info)
+{
+    HAILO_COMMON_STATUS_t status = HAILO_COMMON_STATUS__UNINITIALIZED;
+    size_t local_request_size = 0;
+
+    if ((NULL == request) || (NULL == request_size) || (NULL == context_info)) {
+        status = HAILO_STATUS__CONTROL_PROTOCOL__NULL_ARGUMENT_PASSED;
+        goto exit;
+    }
+
+    /* Header */
+    local_request_size = CONTROL_PROTOCOL__REQUEST_BASE_SIZE + 
+        sizeof(CONTROL_PROTOCOL__context_switch_set_context_info_request_t) + context_info->context_network_data_length;
+    control_protocol__pack_request_header(request, sequence, HAILO_CONTROL_OPCODE_CONTEXT_SWITCH_SET_CONTEXT_INFO, 8);
+
+    /* is_first_control_per_context */
+    request->parameters.context_switch_set_context_info_request.is_first_control_per_context_length = 
+        BYTE_ORDER__htonl(sizeof(request->parameters.context_switch_set_context_info_request.is_first_control_per_context));
+    request->parameters.context_switch_set_context_info_request.is_first_control_per_context = 
+        context_info->is_first_control_per_context;
+
+    /* is_last_control_per_context */
+    request->parameters.context_switch_set_context_info_request.is_last_control_per_context_length = 
+        BYTE_ORDER__htonl(sizeof(request->parameters.context_switch_set_context_info_request.is_last_control_per_context));
+    request->parameters.context_switch_set_context_info_request.is_last_control_per_context = 
+        context_info->is_last_control_per_context;
+
+    /* context cfg base address */
+    request->parameters.context_switch_set_context_info_request.context_cfg_base_address_length = 
+        BYTE_ORDER__htonl(sizeof(request->parameters.context_switch_set_context_info_request.context_cfg_base_address));
+    memcpy(&(request->parameters.context_switch_set_context_info_request.context_cfg_base_address), 
+            &(context_info->context_cfg_base_address), 
+            sizeof(request->parameters.context_switch_set_context_info_request.context_cfg_base_address));
+
+    /* context total descriptors */
+    request->parameters.context_switch_set_context_info_request.context_cfg_total_descriptors_length = 
+        BYTE_ORDER__htonl(sizeof(request->parameters.context_switch_set_context_info_request.context_cfg_total_descriptors));
+    memcpy(&(request->parameters.context_switch_set_context_info_request.context_cfg_total_descriptors), 
+            &(context_info->context_cfg_total_descriptors), 
+            sizeof(request->parameters.context_switch_set_context_info_request.context_cfg_total_descriptors));
+
+    /* context unused stream index */
+    request->parameters.context_switch_set_context_info_request.context_stream_remap_data_length = 
+        BYTE_ORDER__htonl(sizeof(request->parameters.context_switch_set_context_info_request.context_stream_remap_data));
+    request->parameters.context_switch_set_context_info_request.context_stream_remap_data = 
+        context_info->context_stream_remap_data;
+
+    /* number of edge layer */
+    request->parameters.context_switch_set_context_info_request.number_of_edge_layers_length = 
+        BYTE_ORDER__htonl(sizeof(request->parameters.context_switch_set_context_info_request.number_of_edge_layers));
+    request->parameters.context_switch_set_context_info_request.number_of_edge_layers = context_info->number_of_edge_layers;
+
+    /* number of trigger groups */
+    request->parameters.context_switch_set_context_info_request.number_of_trigger_groups_length = 
+        BYTE_ORDER__htonl(sizeof(request->parameters.context_switch_set_context_info_request.number_of_trigger_groups));
+    request->parameters.context_switch_set_context_info_request.number_of_trigger_groups = 
+        context_info->number_of_trigger_groups;
+
+    /* Network data (edge layers + Trigger groups) */
+    if (CONTROL_PROTOCOL__CONTEXT_NETWORK_DATA_MAX_SIZE < context_info->context_network_data_length) {
+        status = HAILO_STATUS__CONTROL_PROTOCOL__INVALID_BUFFER_SIZE;
+        goto exit;
+    }
+    request->parameters.context_switch_set_context_info_request.context_network_data_length = 
+        BYTE_ORDER__htonl(context_info->context_network_data_length);
+    memcpy(&(request->parameters.context_switch_set_context_info_request.context_network_data), 
+            &(context_info->context_network_data), context_info->context_network_data_length);
+
+    *request_size = local_request_size;
+    status = HAILO_COMMON_STATUS__SUCCESS;
+exit:
+    return status;
+}
+
+HAILO_COMMON_STATUS_t CONTROL_PROTOCOL__pack_idle_time_get_measuremment_request(CONTROL_PROTOCOL__request_t *request, 
+            size_t *request_size, 
+            uint32_t sequence)
+{    
+    return control_protocol__pack_empty_request(request, request_size, sequence, HAILO_CONTROL_OPCODE_IDLE_TIME_GET_MEASUREMENT);
+}
+
+HAILO_COMMON_STATUS_t CONTROL_PROTOCOL__pack_idle_time_set_measuremment_request(CONTROL_PROTOCOL__request_t *request, 
+            size_t *request_size, 
+            uint32_t sequence, 
+            uint8_t measurement_enable)
+{
+    HAILO_COMMON_STATUS_t status = HAILO_COMMON_STATUS__UNINITIALIZED;
+    size_t local_request_size = 0;
+
+    if ((NULL == request) || (NULL == request_size)) {
+        status = HAILO_STATUS__CONTROL_PROTOCOL__NULL_ARGUMENT_PASSED;
+        goto exit;
+    }
+
+    /* Header */
+    local_request_size = CONTROL_PROTOCOL__REQUEST_BASE_SIZE + sizeof(CONTROL_PROTOCOL__idle_time_set_measurement_request_t);
+    control_protocol__pack_request_header(request, sequence, HAILO_CONTROL_OPCODE_IDLE_TIME_SET_MEASUREMENT, 1);
+
+    /*measurement duration*/
+    request->parameters.idle_time_set_measurement_request.measurement_enable_length = 
+        BYTE_ORDER__htonl(sizeof(request->parameters.idle_time_set_measurement_request.measurement_enable));
+    request->parameters.idle_time_set_measurement_request.measurement_enable = measurement_enable;
+    
+    *request_size = local_request_size;
+    status = HAILO_COMMON_STATUS__SUCCESS;
+exit:
+    return status;
+}
+
+HAILO_COMMON_STATUS_t CONTROL_PROTOCOL__pack_set_pause_frames_request(CONTROL_PROTOCOL__request_t *request, 
+            size_t *request_size, uint32_t sequence, uint8_t rx_pause_frames_enable)
+{
+
+    CHECK_NOT_NULL(request, HAILO_STATUS__CONTROL_PROTOCOL__NULL_ARGUMENT_PASSED);
+    CHECK_NOT_NULL(request_size, HAILO_STATUS__CONTROL_PROTOCOL__NULL_ARGUMENT_PASSED);
+
+    /* Header */
+    size_t local_request_size = CONTROL_PROTOCOL__REQUEST_BASE_SIZE + sizeof(CONTROL_PROTOCOL__set_pause_frames_t);
+    control_protocol__pack_request_header(request, sequence, HAILO_CONTROL_OPCODE_SET_PAUSE_FRAMES, 1);
+
+    /*measurement duration*/
+    request->parameters.set_pause_frames_request.rx_pause_frames_enable_length = 
+        BYTE_ORDER__htonl(sizeof(request->parameters.set_pause_frames_request.rx_pause_frames_enable));
+    request->parameters.set_pause_frames_request.rx_pause_frames_enable = rx_pause_frames_enable;
+    
+    *request_size = local_request_size;
+
+    return HAILO_COMMON_STATUS__SUCCESS;
+}
+
+HAILO_COMMON_STATUS_t CONTROL_PROTOCOL__pack_download_context_action_list_request(CONTROL_PROTOCOL__request_t *request, 
+        size_t *request_size, uint32_t sequence, uint8_t context_index, uint16_t action_list_offset)
+{
+    HAILO_COMMON_STATUS_t status = HAILO_COMMON_STATUS__UNINITIALIZED;
+    size_t local_request_size = 0;
+
+    if ((NULL == request) || (NULL == request_size)) {
+        status = HAILO_STATUS__CONTROL_PROTOCOL__NULL_ARGUMENT_PASSED;
+        goto exit;
+    }
+
+    /* Header */
+    local_request_size = CONTROL_PROTOCOL__REQUEST_BASE_SIZE + sizeof(CONTROL_PROTOCOL__download_context_action_list_request_t);
+    control_protocol__pack_request_header(request, sequence, HAILO_CONTROL_OPCODE_DOWNLOAD_CONTEXT_ACTION_LIST, 2);
+
+    /* context_index */
+    request->parameters.download_context_action_list_request.context_index_length = 
+        BYTE_ORDER__htonl(sizeof(request->parameters.download_context_action_list_request.context_index));
+    memcpy(&(request->parameters.download_context_action_list_request.context_index), 
+            &(context_index), sizeof(request->parameters.download_context_action_list_request.context_index));
+
+    /* action_list_offset */
+    request->parameters.download_context_action_list_request.action_list_offset_length = 
+        BYTE_ORDER__htonl(sizeof(request->parameters.download_context_action_list_request.action_list_offset));
+    memcpy(&(request->parameters.download_context_action_list_request.action_list_offset), 
+            &(action_list_offset), sizeof(request->parameters.download_context_action_list_request.action_list_offset));
+
+    *request_size = local_request_size;
+    status = HAILO_COMMON_STATUS__SUCCESS;
+exit:
+    return status;
+}
+
+HAILO_COMMON_STATUS_t CONTROL_PROTOCOL__pack_change_context_switch_status_request(
+        CONTROL_PROTOCOL__request_t *request, size_t *request_size, uint32_t sequence, 
+        CONTROL_PROTOCOL__CONTEXT_SWITCH_STATUS_t state_machine_status, uint8_t application_index)
+{
+    HAILO_COMMON_STATUS_t status = HAILO_COMMON_STATUS__UNINITIALIZED;
+    size_t local_request_size = 0;
+
+    if ((NULL == request) || (NULL == request_size)) {
+        status = HAILO_STATUS__CONTROL_PROTOCOL__NULL_ARGUMENT_PASSED;
+        goto exit;
+    }
+
+    /* Header */
+    local_request_size = CONTROL_PROTOCOL__REQUEST_BASE_SIZE + 
+        sizeof(CONTROL_PROTOCOL__change_context_switch_status_request_t);
+    control_protocol__pack_request_header(request, sequence, HAILO_CONTROL_OPCODE_CHANGE_CONTEXT_SWITCH_STATUS, 2);
+
+    /* state_machine_status */
+    request->parameters.change_context_switch_status_request.state_machine_status_length = 
+        BYTE_ORDER__htonl(sizeof(request->parameters.change_context_switch_status_request.state_machine_status));
+    memcpy(&(request->parameters.change_context_switch_status_request.state_machine_status), 
+            &(state_machine_status), 
+            sizeof(request->parameters.change_context_switch_status_request.state_machine_status));
+
+    /* application_index */
+    request->parameters.change_context_switch_status_request.application_index_length = 
+        BYTE_ORDER__htonl(sizeof(request->parameters.change_context_switch_status_request.application_index));
+    request->parameters.change_context_switch_status_request.application_index = application_index;
+    
+    *request_size = local_request_size;
+    status = HAILO_COMMON_STATUS__SUCCESS;
+exit:
+    return status;
+}
+
+HAILO_COMMON_STATUS_t CONTROL_PROTOCOL__pack_wd_enable(
+    CONTROL_PROTOCOL__request_t *request,
+    size_t *request_size,
+    uint32_t sequence,
+    uint8_t cpu_id,
+    bool should_enable)
+{
+    HAILO_COMMON_STATUS_t status = HAILO_COMMON_STATUS__UNINITIALIZED;
+    size_t local_request_size = 0;
+    CONTROL_PROTOCOL__OPCODE_t opcode = HAILO_CONTROL_OPCODE_COUNT;
+
+    if ((NULL == request) || (NULL == request_size)) {
+        status = HAILO_STATUS__CONTROL_PROTOCOL__NULL_ARGUMENT_PASSED;
+        goto exit;
+    }
+
+    if (CPU_ID_CORE_CPU < cpu_id){
+        status = HAILO_STATUS__CONTROL_PROTOCOL__INVALID_ARGUMENT;
+        goto exit;
+    }
+    
+    opcode = (CPU_ID_CORE_CPU == cpu_id) ? HAILO_CONTROL_OPCODE_CORE_WD_ENABLE : HAILO_CONTROL_OPCODE_APP_WD_ENABLE;
+
+    /* Header */
+    local_request_size = CONTROL_PROTOCOL__REQUEST_BASE_SIZE + sizeof(CONTROL_PROTOCOL__wd_enable_request_t);
+    control_protocol__pack_request_header(request, sequence, opcode, 1);
+
+    request->parameters.wd_enable_request.should_enable_length = 
+        BYTE_ORDER__htonl(sizeof(request->parameters.wd_enable_request.should_enable));
+    request->parameters.wd_enable_request.should_enable = should_enable;
+    
+    *request_size = local_request_size;
+    status = HAILO_COMMON_STATUS__SUCCESS;
+
+exit:
+    return status;
+}
+
+HAILO_COMMON_STATUS_t CONTROL_PROTOCOL__pack_wd_config(
+    CONTROL_PROTOCOL__request_t *request,
+    size_t *request_size,
+    uint32_t sequence,
+    uint8_t cpu_id,
+    uint32_t wd_cycles,
+    CONTROL_PROTOCOL__WATCHDOG_MODE_t wd_mode)
+{
+    HAILO_COMMON_STATUS_t status = HAILO_COMMON_STATUS__UNINITIALIZED;
+    size_t local_request_size = 0;
+    CONTROL_PROTOCOL__OPCODE_t opcode = HAILO_CONTROL_OPCODE_COUNT; 
+
+    if ((NULL == request) || (NULL == request_size)) {
+        status = HAILO_STATUS__CONTROL_PROTOCOL__NULL_ARGUMENT_PASSED;
+        goto exit;
+    }
+    if (CPU_ID_CORE_CPU < cpu_id){
+        status = HAILO_STATUS__CONTROL_PROTOCOL__INVALID_ARGUMENT;
+        goto exit;
+    }
+   
+    opcode = (CPU_ID_CORE_CPU == cpu_id) ? HAILO_CONTROL_OPCODE_CORE_WD_CONFIG : HAILO_CONTROL_OPCODE_APP_WD_CONFIG;
+    
+    /* Header */
+    local_request_size = CONTROL_PROTOCOL__REQUEST_BASE_SIZE + sizeof(CONTROL_PROTOCOL__wd_config_request_t);
+    control_protocol__pack_request_header(request, sequence, opcode, 2);
+
+    request->parameters.wd_config_request.wd_cycles_length = 
+        BYTE_ORDER__htonl(sizeof(request->parameters.wd_config_request.wd_cycles));
+    request->parameters.wd_config_request.wd_cycles = BYTE_ORDER__htonl(wd_cycles);
+    request->parameters.wd_config_request.wd_mode_length = 
+        BYTE_ORDER__htonl(sizeof(request->parameters.wd_config_request.wd_mode));
+    request->parameters.wd_config_request.wd_mode = static_cast<uint8_t>(wd_mode);
+    
+    *request_size = local_request_size;
+    status = HAILO_COMMON_STATUS__SUCCESS;
+
+exit:
+    return status;
+}
+
+HAILO_COMMON_STATUS_t CONTROL_PROTOCOL__pack_previous_system_state(
+    CONTROL_PROTOCOL__request_t *request,
+    size_t *request_size,
+    uint32_t sequence,
+    uint8_t cpu_id)
+{
+    HAILO_COMMON_STATUS_t status = HAILO_COMMON_STATUS__UNINITIALIZED;
+    size_t local_request_size = 0;
+    CONTROL_PROTOCOL__OPCODE_t opcode = HAILO_CONTROL_OPCODE_COUNT; 
+
+    if ((NULL == request) || (NULL == request_size)) {
+        status = HAILO_STATUS__CONTROL_PROTOCOL__NULL_ARGUMENT_PASSED;
+        goto exit;
+    }
+    if (CPU_ID_CORE_CPU < cpu_id){
+        status = HAILO_STATUS__CONTROL_PROTOCOL__INVALID_ARGUMENT;
+        goto exit;
+    }
+  
+    opcode = (CPU_ID_CORE_CPU == cpu_id) ? HAILO_CONTROL_OPCODE_CORE_PREVIOUS_SYSTEM_STATE : HAILO_CONTROL_OPCODE_APP_PREVIOUS_SYSTEM_STATE;
+    /* Header */
+    local_request_size = CONTROL_PROTOCOL__REQUEST_BASE_SIZE;
+    *request_size = local_request_size;
+    control_protocol__pack_empty_request(request, request_size, sequence, opcode);
+    
+    status = HAILO_COMMON_STATUS__SUCCESS;
+
+exit:
+    return status;
+}
+
+HAILO_COMMON_STATUS_t CONTROL_PROTOCOL__pack_set_dataflow_interrupt_request(
+        CONTROL_PROTOCOL__request_t *request, size_t *request_size, uint32_t sequence, 
+        uint8_t interrupt_type, uint8_t interrupt_index, uint8_t interrupt_sub_index)
+{
+    HAILO_COMMON_STATUS_t status = HAILO_COMMON_STATUS__UNINITIALIZED;
+    size_t local_request_size = 0;
+
+    if ((NULL == request) || (NULL == request_size)) {
+        status = HAILO_STATUS__CONTROL_PROTOCOL__NULL_ARGUMENT_PASSED;
+        goto exit;
+    }
+
+    /* Header */
+    local_request_size = CONTROL_PROTOCOL__REQUEST_BASE_SIZE + 
+        sizeof(CONTROL_PROTOCOL__set_dataflow_interrupt_request_t);
+    control_protocol__pack_request_header(request, sequence, HAILO_CONTROL_OPCODE_SET_DATAFLOW_INTERRUPT, 3);
+
+    /* Interrupt_type */
+    request->parameters.set_dataflow_interrupt_request.interrupt_type_length = 
+        BYTE_ORDER__htonl(sizeof(request->parameters.set_dataflow_interrupt_request.interrupt_type));
+    memcpy(&(request->parameters.set_dataflow_interrupt_request.interrupt_type), 
+            &(interrupt_type), 
+            sizeof(request->parameters.set_dataflow_interrupt_request.interrupt_type));
+
+    /* Interrupt_index */
+    request->parameters.set_dataflow_interrupt_request.interrupt_index_length =
+        BYTE_ORDER__htonl(sizeof(request->parameters.set_dataflow_interrupt_request.interrupt_index));
+    memcpy(&(request->parameters.set_dataflow_interrupt_request.interrupt_index), 
+            &(interrupt_index), 
+            sizeof(request->parameters.set_dataflow_interrupt_request.interrupt_index));
+
+    /* Interrupt_sub_index */
+    request->parameters.set_dataflow_interrupt_request.interrupt_sub_index_length =
+        BYTE_ORDER__htonl(sizeof(request->parameters.set_dataflow_interrupt_request.interrupt_sub_index));
+    memcpy(&(request->parameters.set_dataflow_interrupt_request.interrupt_sub_index), 
+            &(interrupt_sub_index), 
+            sizeof(request->parameters.set_dataflow_interrupt_request.interrupt_sub_index));
+
+    *request_size = local_request_size;
+    status = HAILO_COMMON_STATUS__SUCCESS;
+exit:
+    return status;
+}
+
+HAILO_COMMON_STATUS_t CONTROL_PROTOCOL__pack_d2h_event_manager_set_host_info_request(
+        CONTROL_PROTOCOL__request_t *request, size_t *request_size, uint32_t sequence, 
+        uint8_t connection_type, uint16_t host_port, uint32_t host_ip_address)
+{
+    HAILO_COMMON_STATUS_t status = HAILO_COMMON_STATUS__UNINITIALIZED;
+    size_t local_request_size = 0;
+
+    if ((NULL == request) || (NULL == request_size)) {
+        status = HAILO_STATUS__CONTROL_PROTOCOL__NULL_ARGUMENT_PASSED;
+        goto exit;
+    }
+
+    /* Header */
+    local_request_size = CONTROL_PROTOCOL__REQUEST_BASE_SIZE + 
+        sizeof(CONTROL_PROTOCOL__d2h_event_manager_set_new_host_info_request_t);
+    control_protocol__pack_request_header(request, sequence, HAILO_CONTROL_OPCODE_D2H_EVENT_MANAGER_SET_HOST_INFO, 3);
+
+    /* connection_type */
+    request->parameters.d2h_event_manager_set_new_host_info_request.connection_type_length = 
+        BYTE_ORDER__htonl(sizeof(request->parameters.d2h_event_manager_set_new_host_info_request.connection_type));
+    request->parameters.d2h_event_manager_set_new_host_info_request.connection_type = connection_type;
+    
+
+    /* remote_port */
+    request->parameters.d2h_event_manager_set_new_host_info_request.host_port_length = 
+        BYTE_ORDER__htonl(sizeof(request->parameters.d2h_event_manager_set_new_host_info_request.host_port));
+    request->parameters.d2h_event_manager_set_new_host_info_request.host_port = BYTE_ORDER__htons(host_port);
+    
+
+    /* remote_ip_address */
+    request->parameters.d2h_event_manager_set_new_host_info_request.host_ip_address_length = 
+        BYTE_ORDER__htonl(sizeof(request->parameters.d2h_event_manager_set_new_host_info_request.host_ip_address));
+    request->parameters.d2h_event_manager_set_new_host_info_request.host_ip_address = BYTE_ORDER__htonl(host_ip_address);
+    
+
+    *request_size = local_request_size;
+    status = HAILO_COMMON_STATUS__SUCCESS;
+exit:
+    return status;
+}
+
+HAILO_COMMON_STATUS_t CONTROL_PROTOCOL__pack_d2h_event_manager_send_host_info_event_request(
+        CONTROL_PROTOCOL__request_t *request, size_t *request_size, uint32_t sequence, 
+        uint8_t event_priority)
+{
+    HAILO_COMMON_STATUS_t status = HAILO_COMMON_STATUS__UNINITIALIZED;
+    size_t local_request_size = 0;
+
+    if ((NULL == request) || (NULL == request_size)) {
+        status = HAILO_STATUS__CONTROL_PROTOCOL__NULL_ARGUMENT_PASSED;
+        goto exit;
+    }
+
+    /* Header */
+    local_request_size = CONTROL_PROTOCOL__REQUEST_BASE_SIZE + 
+        sizeof(CONTROL_PROTOCOL__d2h_event_manager_send_host_info_event_request_t);
+    control_protocol__pack_request_header(request, sequence, HAILO_CONTROL_OPCODE_D2H_EVENT_MANAGER_SEND_EVENT_HOST_INFO, 1);
+
+    /* event_priority */
+    request->parameters.d2h_event_manager_send_host_info_event_request.priority_length =
+        BYTE_ORDER__htonl(sizeof(request->parameters.d2h_event_manager_send_host_info_event_request.priority));
+    request->parameters.d2h_event_manager_send_host_info_event_request.priority = event_priority;
+    
+
+    *request_size = local_request_size;
+    status = HAILO_COMMON_STATUS__SUCCESS;
+exit:
+    return status;
+}
+HAILO_COMMON_STATUS_t CONTROL_PROTOCOL__pack_switch_application_request(
+        CONTROL_PROTOCOL__request_t *request, size_t *request_size, uint32_t sequence, 
+        uint8_t application_index)
+{
+    HAILO_COMMON_STATUS_t status = HAILO_COMMON_STATUS__UNINITIALIZED;
+    size_t local_request_size = 0;
+
+    if ((NULL == request) || (NULL == request_size)) {
+        status = HAILO_STATUS__CONTROL_PROTOCOL__NULL_ARGUMENT_PASSED;
+        goto exit;
+    }
+
+    /* Header */
+    local_request_size = CONTROL_PROTOCOL__REQUEST_BASE_SIZE + sizeof(CONTROL_PROTOCOL__switch_application_request_t);
+    control_protocol__pack_request_header(request, sequence, HAILO_CONTROL_OPCODE_SWITCH_APPLICATION, 1);
+
+    /* application_index */
+    request->parameters.switch_application_request.application_index_length =
+        BYTE_ORDER__htonl(sizeof(request->parameters.switch_application_request.application_index));
+    request->parameters.switch_application_request.application_index = application_index;
+        
+    *request_size = local_request_size;
+    status = HAILO_COMMON_STATUS__SUCCESS;
+exit:
+    return status;
+}
+
+HAILO_COMMON_STATUS_t CONTROL_PROTOCOL__pack_get_chip_temperature_request(CONTROL_PROTOCOL__request_t *request, size_t *request_size, uint32_t sequence)
+{
+    return control_protocol__pack_empty_request(request, request_size, sequence, HAILO_CONTROL_OPCODE_GET_CHIP_TEMPERATURE);
+}
+
+HAILO_COMMON_STATUS_t CONTROL_PROTOCOL__pack_read_board_config(CONTROL_PROTOCOL__request_t *request, size_t *request_size, uint32_t sequence, uint32_t address, uint32_t data_length)
+{
+    HAILO_COMMON_STATUS_t status = HAILO_COMMON_STATUS__UNINITIALIZED;
+    size_t local_request_size = 0;
+
+    if ((NULL == request) || (NULL == request_size)) {
+        status = HAILO_STATUS__CONTROL_PROTOCOL__NULL_ARGUMENT_PASSED;
+        goto exit;
+    }
+
+    /* Header */
+    local_request_size = CONTROL_PROTOCOL__REQUEST_BASE_SIZE + sizeof(CONTROL_PROTOCOL__read_board_config_request_t);
+    control_protocol__pack_request_header(request, sequence, HAILO_CONTROL_OPCODE_READ_BOARD_CONFIG, 2);
+
+    /* Address */
+    request->parameters.read_board_config_request.address_length = BYTE_ORDER__htonl(sizeof(request->parameters.read_board_config_request.address));
+    request->parameters.read_board_config_request.address = BYTE_ORDER__htonl(address);
+
+    /* Data count */
+    request->parameters.read_board_config_request.data_count_length = BYTE_ORDER__htonl(sizeof(request->parameters.read_board_config_request.data_count));
+    request->parameters.read_board_config_request.data_count = BYTE_ORDER__htonl(data_length);
+
+
+    *request_size = local_request_size;
+    status = HAILO_COMMON_STATUS__SUCCESS;
+exit:
+    return status;
+}
+
+HAILO_COMMON_STATUS_t CONTROL_PROTOCOL__pack_write_board_config_request(CONTROL_PROTOCOL__request_t *request, size_t *request_size,
+                                                                         uint32_t sequence, uint32_t address, const uint8_t *data, uint32_t data_length)
+{
+    HAILO_COMMON_STATUS_t status = HAILO_COMMON_STATUS__UNINITIALIZED;
+    size_t local_request_size = 0;
+
+    if ((NULL == request) || (NULL == request_size) || (NULL == data)) {
+        status = HAILO_STATUS__CONTROL_PROTOCOL__NULL_ARGUMENT_PASSED;
+        goto exit;
+    }
+
+    /* Header */
+    local_request_size = CONTROL_PROTOCOL__REQUEST_BASE_SIZE + sizeof(CONTROL_PROTOCOL__write_board_config_request_t) + data_length;
+    control_protocol__pack_request_header(request, sequence, HAILO_CONTROL_OPCODE_WRITE_BOARD_CONFIG, 2);
+
+    /* Address */
+    request->parameters.write_board_config_request.address_length = BYTE_ORDER__htonl(sizeof(request->parameters.write_board_config_request.address));
+    request->parameters.write_board_config_request.address = BYTE_ORDER__htonl(address);
+
+    /* Data */
+    request->parameters.write_board_config_request.data_length = BYTE_ORDER__htonl(data_length);
+
+    memcpy(&(request->parameters.write_board_config_request.data), data, data_length);
+
+
+    *request_size = local_request_size;
+    status = HAILO_COMMON_STATUS__SUCCESS;
+exit:
+    return status;
+}
+
+HAILO_COMMON_STATUS_t CONTROL_PROTOCOL__pack_enable_debugging_request(CONTROL_PROTOCOL__request_t *request, size_t *request_size, uint32_t sequence, bool is_rma)
+{
+    /* Header */
+    control_protocol__pack_request_header(request, sequence, HAILO_CONTROL_OPCODE_ENABLE_DEBUGGING, 1);
+
+    /* is_rma */
+    request->parameters.enable_debugging_request.is_rma_length =
+        BYTE_ORDER__htonl(sizeof(request->parameters.enable_debugging_request.is_rma));
+    request->parameters.enable_debugging_request.is_rma = is_rma;
+
+    *request_size = CONTROL_PROTOCOL__REQUEST_BASE_SIZE + sizeof(CONTROL_PROTOCOL__enable_debugging_request_t);
+
+    return HAILO_COMMON_STATUS__SUCCESS;
+}
+
+HAILO_COMMON_STATUS_t CONTROL_PROTOCOL__pack_get_extended_device_information_request(CONTROL_PROTOCOL__request_t *request, size_t *request_size, uint32_t sequence)
+{
+    return control_protocol__pack_empty_request(request, request_size, sequence, HAILO_CONTROL_OPCODE_GET_DEVICE_INFORMATION);
+}
+
+HAILO_COMMON_STATUS_t CONTROL_PROTOCOL__pack_get_health_information_request(CONTROL_PROTOCOL__request_t *request, size_t *request_size, uint32_t sequence)
+{
+    return control_protocol__pack_empty_request(request, request_size, sequence, HAILO_CONTROL_OPCODE_GET_HEALTH_INFORMATION);
+}
+
+
+HAILO_COMMON_STATUS_t CONTROL_PROTOCOL__pack_config_context_switch_breakpoint_request(
+        CONTROL_PROTOCOL__request_t *request, size_t *request_size, uint32_t sequence,
+        uint8_t breakpoint_id,
+        CONTROL_PROTOCOL__context_switch_breakpoint_control_t breakpoint_control, 
+        CONTROL_PROTOCOL__context_switch_breakpoint_data_t *breakpoint_data)
+{
+    HAILO_COMMON_STATUS_t status = HAILO_COMMON_STATUS__UNINITIALIZED;
+    size_t local_request_size = 0;
+
+    if ((NULL == request) || (NULL == request_size) || (NULL == breakpoint_data)) {
+        status = HAILO_STATUS__CONTROL_PROTOCOL__NULL_ARGUMENT_PASSED;
+        goto exit;
+    }
+
+    /* Header */
+    local_request_size = CONTROL_PROTOCOL__REQUEST_BASE_SIZE + 
+        sizeof(CONTROL_PROTOCOL__config_context_switch_breakpoint_request_t);
+    control_protocol__pack_request_header(request, sequence, HAILO_CONTROL_OPCODE_CONFIG_CONTEXT_SWITCH_BREAKPOINT, 3);
+
+    /* breakpoint id */
+    request->parameters.config_context_switch_breakpoint_request.breakpoint_id_length = 
+        BYTE_ORDER__htonl(sizeof(request->parameters.config_context_switch_breakpoint_request.breakpoint_id));
+    request->parameters.config_context_switch_breakpoint_request.breakpoint_id = breakpoint_id;
+
+    /* breakpoint status */
+    request->parameters.config_context_switch_breakpoint_request.breakpoint_control_length = 
+        BYTE_ORDER__htonl(sizeof(request->parameters.config_context_switch_breakpoint_request.breakpoint_control));
+    request->parameters.config_context_switch_breakpoint_request.breakpoint_control = (uint8_t)breakpoint_control;
+
+    /* breakpoint data */
+    request->parameters.config_context_switch_breakpoint_request.breakpoint_data_length = 
+        BYTE_ORDER__htonl(sizeof(request->parameters.config_context_switch_breakpoint_request.breakpoint_data));
+    memcpy(&(request->parameters.config_context_switch_breakpoint_request.breakpoint_data), 
+            breakpoint_data, 
+            sizeof(request->parameters.config_context_switch_breakpoint_request.breakpoint_data));
+
+    *request_size = local_request_size;
+    status = HAILO_COMMON_STATUS__SUCCESS;
+exit:
+    return status;
+}
+
+HAILO_COMMON_STATUS_t CONTROL_PROTOCOL__pack_get_context_switch_breakpoint_status_request(
+        CONTROL_PROTOCOL__request_t *request, size_t *request_size, uint32_t sequence,
+        uint8_t breakpoint_id) 
+{
+    HAILO_COMMON_STATUS_t status = HAILO_COMMON_STATUS__UNINITIALIZED;
+    size_t local_request_size = 0;
+
+    if ((NULL == request) || (NULL == request_size)) {
+        status = HAILO_STATUS__CONTROL_PROTOCOL__NULL_ARGUMENT_PASSED;
+        goto exit;
+    }
+
+    /* Header */
+    local_request_size = CONTROL_PROTOCOL__REQUEST_BASE_SIZE + 
+        sizeof(CONTROL_PROTOCOL__get_context_switch_breakpoint_status_request_t);
+    control_protocol__pack_request_header(request, sequence, HAILO_CONTROL_OPCODE_GET_CONTEXT_SWITCH_BREAKPOINT_STATUS, 1);
+
+    /* breakpoint id */
+    request->parameters.config_context_switch_breakpoint_request.breakpoint_id_length = 
+        BYTE_ORDER__htonl(sizeof(request->parameters.config_context_switch_breakpoint_request.breakpoint_id));
+    request->parameters.config_context_switch_breakpoint_request.breakpoint_id = breakpoint_id;
+
+    *request_size = local_request_size;
+    status = HAILO_COMMON_STATUS__SUCCESS;
+exit:
+    return status;
+}
+
+HAILO_COMMON_STATUS_t CONTROL_PROTOCOL__pack_get_context_switch_main_header_request(
+        CONTROL_PROTOCOL__request_t *request, size_t *request_size, uint32_t sequence) 
+{
+    HAILO_COMMON_STATUS_t status = HAILO_COMMON_STATUS__UNINITIALIZED;
+    size_t local_request_size = 0;
+
+    if ((NULL == request) || (NULL == request_size)) {
+        status = HAILO_STATUS__CONTROL_PROTOCOL__NULL_ARGUMENT_PASSED;
+        goto exit;
+    }
+
+    /* Header */
+    local_request_size = CONTROL_PROTOCOL__REQUEST_BASE_SIZE;
+    control_protocol__pack_request_header(request, sequence, HAILO_CONTROL_OPCODE_GET_CONTEXT_SWITCH_MAIN_HEADER, 0);
+
+    *request_size = local_request_size;
+    status = HAILO_COMMON_STATUS__SUCCESS;
+exit:
+    return status;
+}
+
+HAILO_COMMON_STATUS_t CONTROL_PROTOCOL__pack_config_context_switch_timestamp_request(
+        CONTROL_PROTOCOL__request_t *request, size_t *request_size, uint32_t sequence,
+        uint16_t batch_index, bool enable_user_configuration)
+{
+    HAILO_COMMON_STATUS_t status = HAILO_COMMON_STATUS__UNINITIALIZED;
+    size_t local_request_size = 0;
+
+    if ((NULL == request) || (NULL == request_size)) {
+        status = HAILO_STATUS__CONTROL_PROTOCOL__NULL_ARGUMENT_PASSED;
+        goto exit;
+    }
+
+    /* Header */
+    local_request_size = CONTROL_PROTOCOL__REQUEST_BASE_SIZE + 
+        sizeof(CONTROL_PROTOCOL__config_context_switch_timestamp_request_t);
+    control_protocol__pack_request_header(request, sequence, HAILO_CONTROL_OPCODE_CONFIG_CONTEXT_SWITCH_TIMESTAMP, 2);
+
+    /* batch index */
+    request->parameters.config_context_switch_timestamp_request.batch_index_length = 
+        BYTE_ORDER__htonl(sizeof(request->parameters.config_context_switch_timestamp_request.batch_index));
+    request->parameters.config_context_switch_timestamp_request.batch_index = BYTE_ORDER__htons(batch_index);
+
+    /* enable_user_configuration */
+    request->parameters.config_context_switch_timestamp_request.enable_user_configuration_length = 
+       BYTE_ORDER__htonl(sizeof(request->parameters.config_context_switch_timestamp_request.enable_user_configuration));
+    request->parameters.config_context_switch_timestamp_request.enable_user_configuration = enable_user_configuration;
+
+    *request_size = local_request_size;
+    status = HAILO_COMMON_STATUS__SUCCESS;
+exit:
+    return status;
+}
+
+HAILO_COMMON_STATUS_t CONTROL_PROTOCOL__pack_run_bist_test_request(
+        CONTROL_PROTOCOL__request_t *request, size_t *request_size, uint32_t sequence, bool is_top_test, 
+        uint32_t top_bypass_bitmap, uint8_t cluster_index, uint32_t cluster_bypass_bitmap_0, uint32_t cluster_bypass_bitmap_1)
+{
+    size_t local_request_size = 0;
+
+    CHECK_NOT_NULL(request, HAILO_STATUS__CONTROL_PROTOCOL__NULL_ARGUMENT_PASSED);
+    CHECK_NOT_NULL(request_size, HAILO_STATUS__CONTROL_PROTOCOL__NULL_ARGUMENT_PASSED);
+
+    /* Header */
+    local_request_size = CONTROL_PROTOCOL__REQUEST_BASE_SIZE + 
+        sizeof(CONTROL_PROTOCOL__run_bist_test_request_t);
+    control_protocol__pack_request_header(request, sequence, HAILO_CONTROL_OPCODE_RUN_BIST_TEST, 5);
+
+    /* running on top */
+    request->parameters.run_bist_test_request.is_top_test_length = 
+        BYTE_ORDER__htonl(sizeof(request->parameters.run_bist_test_request.is_top_test));
+    request->parameters.run_bist_test_request.is_top_test = is_top_test;
+
+    /* top bypass */
+    request->parameters.run_bist_test_request.top_bypass_bitmap_length = 
+        BYTE_ORDER__htonl(sizeof(request->parameters.run_bist_test_request.top_bypass_bitmap));
+    request->parameters.run_bist_test_request.top_bypass_bitmap = BYTE_ORDER__htonl(top_bypass_bitmap);
+
+    /* cluster index */
+    request->parameters.run_bist_test_request.cluster_index_length = 
+        BYTE_ORDER__htonl(sizeof(request->parameters.run_bist_test_request.cluster_index));
+    request->parameters.run_bist_test_request.cluster_index = cluster_index;
+
+    /* cluster bypass 0 */
+    request->parameters.run_bist_test_request.cluster_bypass_bitmap_0_length = 
+        BYTE_ORDER__htonl(sizeof(request->parameters.run_bist_test_request.cluster_bypass_bitmap_0));
+    request->parameters.run_bist_test_request.cluster_bypass_bitmap_0 = BYTE_ORDER__htonl(cluster_bypass_bitmap_0);
+
+    /* cluster bypass 1 */
+    request->parameters.run_bist_test_request.cluster_bypass_bitmap_1_length = 
+        BYTE_ORDER__htonl(sizeof(request->parameters.run_bist_test_request.cluster_bypass_bitmap_1));
+    request->parameters.run_bist_test_request.cluster_bypass_bitmap_1 = BYTE_ORDER__htonl(cluster_bypass_bitmap_1);
+
+    *request_size = local_request_size;
+
+    return HAILO_COMMON_STATUS__SUCCESS;
+}
+
+#endif /* FIRMWARE_ARCH */
diff --git a/hailort/libhailort/src/control_protocol.hpp b/hailort/libhailort/src/control_protocol.hpp
new file mode 100644 (file)
index 0000000..c59129a
--- /dev/null
@@ -0,0 +1,196 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file control_protocol.hpp
+ * @brief Contains Defines and declarations related to control protocl
+ **/
+
+#ifndef _CONTROL_PROTOCOL_HPP_
+#define _CONTROL_PROTOCOL_HPP_
+
+#include "control_protocol.h"
+#include "firmware_status.h"
+#include "hailo/hailort.h"
+#include <stdint.h>
+
+typedef enum {
+    HAILO8_CLOCK_RATE = 400 * 1000 * 1000,
+    HAILO8R_CLOCK_RATE = 200 * 1000 * 1000
+} CONTROL_PROTOCOL__HAILO8_CLOCK_RATE_t;
+
+typedef struct {
+    uint8_t stream_index;
+    uint8_t is_input;
+    uint32_t communication_type;
+    uint8_t skip_nn_stream_config;
+    uint8_t power_mode; // CONTROL_PROTOCOL__power_mode_t
+    CONTROL_PROTOCOL__nn_stream_config_t nn_stream_config;
+    CONTROL_PROTOCOL__communication_config_prams_t communication_params;
+} CONTROL_PROTOCOL__config_stream_params_t;
+
+/* Context switch user structs */
+#define CONTROL_PROTCOL__CONTEXT_SLICING_OFFSETS \
+    ((CONTROL_PROTOCOL__CONTEXT_NETWORK_DATA_MAX_SIZE / CONTROL_PROTOCOL__CONTEXT_NETWORK_DATA_SINGLE_CONTROL_MAX_SIZE) + 1)
+
+typedef struct {
+    /* Tempurary building info, used when creating meta data*/
+    CONTROL_PROTOCOL__trigger_group_t *current_building_trigger;
+    uint8_t current_building_slice_index;
+    uint16_t current_buillding_offset_inside_slice;
+    /* Control slicing information */
+    uint16_t control_slicing_offsets[CONTROL_PROTCOL__CONTEXT_SLICING_OFFSETS];
+    uint8_t slice_edge_layers[CONTROL_PROTCOL__CONTEXT_SLICING_OFFSETS];
+    uint8_t slice_triggers[CONTROL_PROTCOL__CONTEXT_SLICING_OFFSETS];
+} CONTEXT_SWITCH__context_control_slicing_data_t;
+
+typedef struct {
+    uint64_t context_cfg_base_address[CONTROL_PROTOCOL__MAX_CFG_CHANNELS];
+    uint16_t context_total_descriptors[CONTROL_PROTOCOL__MAX_CFG_CHANNELS];
+    uint32_t context_network_data_length;
+    CONTROL_PROTOCOL__stream_remap_data_t context_stream_remap_data;
+    uint8_t context_network_data[CONTROL_PROTOCOL__CONTEXT_NETWORK_DATA_MAX_SIZE];
+    /* control_slicing_data is used in internal hef_metadata.cpp functions only */
+    CONTEXT_SWITCH__context_control_slicing_data_t control_slicing_data;
+} CONTROL_PROTOCOL__context_switch_context_info_t;
+
+typedef struct {
+    CONTROL_PROTOCOL__context_switch_main_header_t context_switch_main_header;
+    CONTROL_PROTOCOL__context_switch_context_info_t context[CONTROL_PROTOCOL__MAX_TOTAL_CONTEXTS];
+} CONTROL_PROTOCOL__context_switch_info_t;
+/* End of context switch structs */
+
+const char *CONTROL_PROTOCOL__get_textual_opcode(CONTROL_PROTOCOL__OPCODE_t opcode);
+
+HAILO_COMMON_STATUS_t CONTROL_PROTOCOL__parse_response(uint8_t *message,
+        uint32_t message_size,
+        CONTROL_PROTOCOL__response_header_t **header,
+        CONTROL_PROTOCOL__payload_t **payload,
+        CONTROL_PROTOCOL__status_t *fw_status);
+HAILO_COMMON_STATUS_t CONTROL_PROTOCOL__get_sequence_from_response_buffer(uint8_t *response_buffer,
+        size_t response_buffer_size, uint32_t *sequence);
+HAILO_COMMON_STATUS_t CONTROL_PROTOCOL__pack_identify_request(CONTROL_PROTOCOL__request_t *request, size_t *request_size, uint32_t sequence);
+HAILO_COMMON_STATUS_t CONTROL_PROTOCOL__pack_core_identify_request(CONTROL_PROTOCOL__request_t *request, size_t *request_size, uint32_t sequence);
+HAILO_COMMON_STATUS_t CONTROL_PROTOCOL__pack_read_memory_request(CONTROL_PROTOCOL__request_t *request, size_t *request_size, uint32_t sequence, uint32_t address, uint32_t data_length);
+HAILO_COMMON_STATUS_t CONTROL_PROTOCOL__pack_write_memory_request(CONTROL_PROTOCOL__request_t *request, size_t *request_size, uint32_t sequence, uint32_t address, const uint8_t *data, uint32_t data_length);
+HAILO_COMMON_STATUS_t CONTROL_PROTOCOL__pack_set_fw_logger_request(CONTROL_PROTOCOL__request_t *request, size_t *request_size, uint32_t sequence, hailo_fw_logger_level_t level, uint8_t interface_mask);
+HAILO_COMMON_STATUS_t CONTROL_PROTOCOL__pack_open_stream_request(CONTROL_PROTOCOL__request_t *request, size_t *request_size, uint32_t sequence, uint8_t dataflow_manager_id, uint8_t is_input);
+HAILO_COMMON_STATUS_t CONTROL_PROTOCOL__pack_config_stream_udp_input_request(CONTROL_PROTOCOL__request_t *request, size_t *request_size, uint32_t sequence, CONTROL_PROTOCOL__config_stream_params_t *params);
+HAILO_COMMON_STATUS_t CONTROL_PROTOCOL__pack_config_stream_udp_output_request(CONTROL_PROTOCOL__request_t *request, size_t *request_size, uint32_t sequence, CONTROL_PROTOCOL__config_stream_params_t *params);
+HAILO_COMMON_STATUS_t CONTROL_PROTOCOL__pack_config_stream_mipi_input_request(CONTROL_PROTOCOL__request_t *request, size_t *request_size, uint32_t sequence, CONTROL_PROTOCOL__config_stream_params_t *params);
+HAILO_COMMON_STATUS_t CONTROL_PROTOCOL__pack_config_stream_mipi_output_request(CONTROL_PROTOCOL__request_t *request, size_t *request_size, uint32_t sequence, CONTROL_PROTOCOL__config_stream_params_t *params);
+HAILO_COMMON_STATUS_t CONTROL_PROTOCOL__pack_config_stream_pcie_input_request(CONTROL_PROTOCOL__request_t *request, size_t *request_size, uint32_t sequence, CONTROL_PROTOCOL__config_stream_params_t *params);
+HAILO_COMMON_STATUS_t CONTROL_PROTOCOL__pack_config_stream_pcie_output_request(CONTROL_PROTOCOL__request_t *request, size_t *request_size, uint32_t sequence, CONTROL_PROTOCOL__config_stream_params_t *params);
+HAILO_COMMON_STATUS_t CONTROL_PROTOCOL__pack_close_stream_request(CONTROL_PROTOCOL__request_t *request, size_t *request_size, uint32_t sequence, uint8_t dataflow_manager_id, uint8_t is_input);
+HAILO_COMMON_STATUS_t CONTROL_PROTOCOL__pack_reset_request(CONTROL_PROTOCOL__request_t *request, size_t *request_size, uint32_t sequence, CONTROL_PROTOCOL__reset_type_t reset_type);
+HAILO_COMMON_STATUS_t CONTROL_PROTOCOL__pack_power_measurement_request(CONTROL_PROTOCOL__request_t *request, size_t *request_size, uint32_t sequence, CONTROL_PROTOCOL__dvm_options_t dvm, CONTROL_PROTOCOL__power_measurement_types_t measurement_type);
+HAILO_COMMON_STATUS_t CONTROL_PROTOCOL__pack_set_power_measurement_request(CONTROL_PROTOCOL__request_t *request, size_t *request_size, uint32_t sequence, uint32_t index, CONTROL_PROTOCOL__dvm_options_t dvm, CONTROL_PROTOCOL__power_measurement_types_t measurement_type);
+HAILO_COMMON_STATUS_t CONTROL_PROTOCOL__pack_get_power_measurement_request(CONTROL_PROTOCOL__request_t *request, size_t *request_size, uint32_t sequence, uint32_t index, bool should_clear);
+HAILO_COMMON_STATUS_t CONTROL_PROTOCOL__pack_start_power_measurement_request(CONTROL_PROTOCOL__request_t *request, size_t *request_size, uint32_t sequence, uint32_t delay_milliseconds, CONTROL_PROTOCOL__averaging_factor_t averaging_factor , CONTROL_PROTOCOL__sampling_period_t sampling_period);
+HAILO_COMMON_STATUS_t CONTROL_PROTOCOL__pack_stop_power_measurement_request(CONTROL_PROTOCOL__request_t *request, size_t *request_size, uint32_t sequence);
+HAILO_COMMON_STATUS_t CONTROL_PROTOCOL__pack_start_firmware_update_request(CONTROL_PROTOCOL__request_t *request, size_t *request_size, uint32_t sequence);
+HAILO_COMMON_STATUS_t CONTROL_PROTOCOL__pack_finish_firmware_update_request(CONTROL_PROTOCOL__request_t *request, size_t *request_size, uint32_t sequence);
+HAILO_COMMON_STATUS_t CONTROL_PROTOCOL__write_firmware_update_request(CONTROL_PROTOCOL__request_t *request, size_t *request_size, uint32_t sequence, uint32_t offset, const uint8_t *data, uint32_t data_length);
+HAILO_COMMON_STATUS_t CONTROL_PROTOCOL__pack_validate_firmware_update_request(CONTROL_PROTOCOL__request_t *request, size_t *request_size, uint32_t sequence, MD5_SUM_t *expected_md5, uint32_t firmware_size);
+HAILO_COMMON_STATUS_t CONTROL_PROTOCOL__pack_examine_user_config(CONTROL_PROTOCOL__request_t *request, size_t *request_size, uint32_t sequence);
+HAILO_COMMON_STATUS_t CONTROL_PROTOCOL__pack_read_user_config(CONTROL_PROTOCOL__request_t *request, size_t *request_size, uint32_t sequence, uint32_t address, uint32_t data_length);
+HAILO_COMMON_STATUS_t CONTROL_PROTOCOL__pack_write_user_config_request(CONTROL_PROTOCOL__request_t *request, size_t *request_size, uint32_t sequence, uint32_t address, const uint8_t *data, uint32_t data_length);
+HAILO_COMMON_STATUS_t CONTROL_PROTOCOL__pack_erase_user_config_request(CONTROL_PROTOCOL__request_t *request, size_t *request_size, uint32_t sequence);
+HAILO_COMMON_STATUS_t CONTROL_PROTOCOL__pack_phy_operation_request(CONTROL_PROTOCOL__request_t *request, size_t *request_size, uint32_t sequence, CONTROL_PROTOCOL__phy_operation_t operation_type);
+HAILO_COMMON_STATUS_t CONTROL_PROTOCOL__pack_config_core_top_request(CONTROL_PROTOCOL__request_t *request, size_t *request_size, uint32_t sequence, CONTROL_PROTOCOL__config_core_top_type_t config_type, CONTROL_PROTOCOL__config_core_top_params_t *params);
+HAILO_COMMON_STATUS_t CONTROL_PROTOCOL__pack_i2c_write_request(CONTROL_PROTOCOL__request_t *request, size_t *request_size,
+        uint32_t sequence, uint32_t offset, uint8_t endianness,
+        uint16_t slave_address, uint8_t register_address_size, uint8_t bus_index, const uint8_t *data, uint32_t data_length);
+HAILO_COMMON_STATUS_t CONTROL_PROTOCOL__pack_i2c_read_request(CONTROL_PROTOCOL__request_t *request, size_t *request_size,
+        uint32_t sequence, uint32_t offset, uint8_t endianness,
+        uint16_t slave_address, uint8_t register_address_size, uint8_t bus_index, uint32_t data_length, bool should_hold_bus);
+HAILO_COMMON_STATUS_t CONTROL_PROTOCOL__pack_latency_measurement_read_request(CONTROL_PROTOCOL__request_t *request, size_t *request_size, uint32_t sequence);
+HAILO_COMMON_STATUS_t CONTROL_PROTOCOL__pack_latency_measurement_config_request(CONTROL_PROTOCOL__request_t *request, size_t *request_size, uint32_t sequence, uint8_t latency_measurement_en, uint32_t inbound_start_buffer_number, uint32_t outbound_stop_buffer_number, uint32_t inbound_stream_index, uint32_t outbound_stream_index);
+HAILO_COMMON_STATUS_t CONTROL_PROTOCOL__pack_sensor_store_config_request(CONTROL_PROTOCOL__request_t *request, size_t *request_size, uint32_t sequence, uint32_t is_first, uint32_t section_index,
+                                                                   uint32_t start_offset, uint32_t reset_data_size, uint32_t sensor_type, uint32_t total_data_size, uint8_t *data, uint32_t data_length,
+                                                                   uint16_t config_height, uint16_t config_width, uint16_t config_fps, uint32_t config_name_length, uint8_t *config_name);
+HAILO_COMMON_STATUS_t CONTROL_PROTOCOL__pack_sensor_get_config_request(CONTROL_PROTOCOL__request_t *request, size_t *request_size, uint32_t sequence, 
+                                                                       uint32_t section_index, uint32_t offset, uint32_t data_length);
+hailo_status CONTROL_PROTOCOL__pack_sensor_set_i2c_bus_index_request(CONTROL_PROTOCOL__request_t *request, size_t *request_size, uint32_t sequence, uint32_t sensor_type, uint32_t bus_index);
+HAILO_COMMON_STATUS_t CONTROL_PROTOCOL__pack_sensor_load_and_start_config_request(CONTROL_PROTOCOL__request_t *request, size_t *request_size, uint32_t sequence, uint32_t section_index);
+HAILO_COMMON_STATUS_t CONTROL_PROTOCOL__pack_sensor_reset_request(CONTROL_PROTOCOL__request_t *request, size_t *request_size, uint32_t sequence, uint32_t section_index);
+HAILO_COMMON_STATUS_t CONTROL_PROTOCOL__pack_sensor_set_generic_i2c_slave_request(CONTROL_PROTOCOL__request_t *request, size_t *request_size, uint32_t sequence, uint16_t slave_address,
+                                                                                  uint8_t register_address_size, uint8_t bus_index, uint8_t should_hold_bus, uint8_t endianness);
+HAILO_COMMON_STATUS_t CONTROL_PROTOCOL__pack_sensor_get_sections_info_request(CONTROL_PROTOCOL__request_t *request, size_t *request_size, uint32_t sequence);
+HAILO_COMMON_STATUS_t CONTROL_PROTOCOL__pack_context_switch_set_main_header_request(
+        CONTROL_PROTOCOL__request_t *request, size_t *request_size, uint32_t sequence, 
+        CONTROL_PROTOCOL__context_switch_main_header_t *context_switch_header);
+HAILO_COMMON_STATUS_t CONTROL_PROTOCOL__pack_context_switch_set_context_info_request(
+        CONTROL_PROTOCOL__request_t *request, size_t *request_size, uint32_t sequence, 
+        CONTROL_PROTOCOL__context_switch_context_info_single_control_t *context_info);
+HAILO_COMMON_STATUS_t CONTROL_PROTOCOL__pack_idle_time_set_measuremment_request(CONTROL_PROTOCOL__request_t *request, size_t *request_size, uint32_t sequence, uint8_t measurement_enable);
+HAILO_COMMON_STATUS_t CONTROL_PROTOCOL__pack_idle_time_get_measuremment_request(CONTROL_PROTOCOL__request_t *request, size_t *request_size, uint32_t sequence);
+HAILO_COMMON_STATUS_t CONTROL_PROTOCOL__pack_download_context_action_list_request(CONTROL_PROTOCOL__request_t *request, 
+        size_t *request_size, uint32_t sequence, uint8_t context_index, uint16_t action_list_offset);
+HAILO_COMMON_STATUS_t CONTROL_PROTOCOL__pack_change_context_switch_status_request(
+        CONTROL_PROTOCOL__request_t *request, size_t *request_size, uint32_t sequence,
+        CONTROL_PROTOCOL__CONTEXT_SWITCH_STATUS_t state_machine_status, uint8_t application_index);
+HAILO_COMMON_STATUS_t CONTROL_PROTOCOL__pack_wd_enable(
+    CONTROL_PROTOCOL__request_t *request,
+    size_t *request_size,
+    uint32_t sequence,
+    uint8_t cpu_id,
+    bool should_enable);
+HAILO_COMMON_STATUS_t CONTROL_PROTOCOL__pack_wd_config(
+    CONTROL_PROTOCOL__request_t *request,
+    size_t *request_size,
+    uint32_t sequence,
+    uint8_t cpu_id,
+    uint32_t wd_cycles,
+    CONTROL_PROTOCOL__WATCHDOG_MODE_t wd_mode);
+HAILO_COMMON_STATUS_t CONTROL_PROTOCOL__pack_previous_system_state(CONTROL_PROTOCOL__request_t *request, size_t *request_size, uint32_t sequence, uint8_t cpu_id);
+HAILO_COMMON_STATUS_t CONTROL_PROTOCOL__pack_set_dataflow_interrupt_request(
+        CONTROL_PROTOCOL__request_t *request, size_t *request_size, uint32_t sequence, 
+        uint8_t interrupt_type, uint8_t interrupt_index, uint8_t interrupt_sub_index);
+HAILO_COMMON_STATUS_t CONTROL_PROTOCOL__pack_d2h_event_manager_set_host_info_request( CONTROL_PROTOCOL__request_t *request, size_t *request_size, uint32_t sequence, 
+        uint8_t connection_type, uint16_t host_port, uint32_t host_ip_address);
+HAILO_COMMON_STATUS_t CONTROL_PROTOCOL__pack_d2h_event_manager_send_host_info_event_request( CONTROL_PROTOCOL__request_t *request, size_t *request_size, uint32_t sequence, 
+        uint8_t event_priority);
+HAILO_COMMON_STATUS_t CONTROL_PROTOCOL__pack_switch_application_request(CONTROL_PROTOCOL__request_t *request, size_t *request_size, uint32_t sequence, 
+        uint8_t application_index);
+HAILO_COMMON_STATUS_t CONTROL_PROTOCOL__pack_get_chip_temperature_request(CONTROL_PROTOCOL__request_t *request, size_t *request_size, uint32_t sequence);
+HAILO_COMMON_STATUS_t CONTROL_PROTOCOL__pack_read_board_config(CONTROL_PROTOCOL__request_t *request, size_t *request_size, uint32_t sequence, uint32_t address, uint32_t data_length);
+HAILO_COMMON_STATUS_t CONTROL_PROTOCOL__pack_write_board_config_request(CONTROL_PROTOCOL__request_t *request, size_t *request_size, uint32_t sequence, uint32_t address, const uint8_t *data, uint32_t data_length);
+HAILO_COMMON_STATUS_t CONTROL_PROTOCOL__pack_enable_debugging_request(CONTROL_PROTOCOL__request_t *request, size_t *request_size, uint32_t sequence, bool is_rma);
+HAILO_COMMON_STATUS_t CONTROL_PROTOCOL__pack_get_extended_device_information_request(CONTROL_PROTOCOL__request_t *request, size_t *request_size, uint32_t sequence);
+HAILO_COMMON_STATUS_t CONTROL_PROTOCOL__pack_config_context_switch_breakpoint_request(
+        CONTROL_PROTOCOL__request_t *request, size_t *request_size, uint32_t sequence,
+        uint8_t breakpoint_id, 
+        CONTROL_PROTOCOL__context_switch_breakpoint_control_t breakpoint_control, 
+        CONTROL_PROTOCOL__context_switch_breakpoint_data_t *breakpoint_data);
+HAILO_COMMON_STATUS_t CONTROL_PROTOCOL__pack_get_context_switch_breakpoint_status_request(
+        CONTROL_PROTOCOL__request_t *request, size_t *request_size, uint32_t sequence,
+        uint8_t breakpoint_id); 
+HAILO_COMMON_STATUS_t CONTROL_PROTOCOL__pack_get_context_switch_main_header_request(
+        CONTROL_PROTOCOL__request_t *request, size_t *request_size, uint32_t sequence); 
+HAILO_COMMON_STATUS_t CONTROL_PROTOCOL__write_second_stage_to_internal_memory_request(
+    CONTROL_PROTOCOL__request_t *request, size_t *request_size, uint32_t sequence,
+     uint32_t offset, uint8_t *data, uint32_t data_length);
+HAILO_COMMON_STATUS_t CONTROL_PROTOCOL__copy_second_stage_to_flash_request(
+    CONTROL_PROTOCOL__request_t *request, size_t *request_size, uint32_t sequence,
+     MD5_SUM_t *expected_md5, uint32_t second_stage_size);
+HAILO_COMMON_STATUS_t CONTROL_PROTOCOL__pack_set_pause_frames_request(CONTROL_PROTOCOL__request_t *request, 
+            size_t *request_size, 
+            uint32_t sequence, 
+            uint8_t rx_pause_frames_enable);
+HAILO_COMMON_STATUS_t CONTROL_PROTOCOL__pack_config_context_switch_timestamp_request(
+        CONTROL_PROTOCOL__request_t *request, size_t *request_size, uint32_t sequence,
+        uint16_t batch_index, bool enable_user_configuration);
+HAILO_COMMON_STATUS_t CONTROL_PROTOCOL__pack_run_bist_test_request(
+        CONTROL_PROTOCOL__request_t *request, size_t *request_size, uint32_t sequence, bool is_top_test,
+        uint32_t top_bypass_bitmap, uint8_t cluster_index, uint32_t cluster_bypass_bitmap_0, uint32_t cluster_bypass_bitmap_1);
+HAILO_COMMON_STATUS_t CONTROL_PROTOCOL__pack_set_clock_freq_request(CONTROL_PROTOCOL__request_t *request, size_t *request_size, uint32_t sequence,
+        uint32_t clock_freq);
+HAILO_COMMON_STATUS_t CONTROL_PROTOCOL__pack_get_health_information_request(CONTROL_PROTOCOL__request_t *request, size_t *request_size, uint32_t sequence);
+HAILO_COMMON_STATUS_t CONTROL_PROTOCOL__pack_set_throttling_state_request(CONTROL_PROTOCOL__request_t *request, size_t *request_size, uint32_t sequence, bool should_activate);
+HAILO_COMMON_STATUS_t CONTROL_PROTOCOL__pack_get_throttling_state_request(CONTROL_PROTOCOL__request_t *request, size_t *request_size, uint32_t sequence);
+HAILO_COMMON_STATUS_t CONTROL_PROTOCOL__pack_set_overcurrent_state_request(CONTROL_PROTOCOL__request_t *request, size_t *request_size, uint32_t sequence, bool should_activate);
+HAILO_COMMON_STATUS_t CONTROL_PROTOCOL__pack_get_overcurrent_state_request(CONTROL_PROTOCOL__request_t *request, size_t *request_size, uint32_t sequence);
+
+#endif /* _CONTROL_PROTOCOL_HPP_ */
\ No newline at end of file
diff --git a/hailort/libhailort/src/core_device.cpp b/hailort/libhailort/src/core_device.cpp
new file mode 100644 (file)
index 0000000..867bb30
--- /dev/null
@@ -0,0 +1,111 @@
+#include "core_device.hpp"
+#include "hailo/hailort.h"
+#include "hailo/expected.hpp"
+#include "common/logger_macros.hpp"
+#include "context_switch/multi_context/vdma_config_manager.hpp"
+#include "md5.h"
+
+#include <memory>
+
+static const std::chrono::milliseconds CORE_DEFAULT_TIMEOUT = std::chrono::milliseconds(1000);
+static const std::string CORE_DRIVER_PATH = "/dev/hailo_core";
+
+namespace hailort
+{
+
+bool CoreDevice::is_loaded()
+{
+#if defined(_MSC_VER)
+    // windows is not supported for core driver
+    return false;
+#else
+    return (access(CORE_DRIVER_PATH.c_str(), F_OK) == 0);
+#endif // defined(_MSC_VER)
+}
+
+Expected<std::unique_ptr<CoreDevice>> CoreDevice::create()
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+
+    auto driver = HailoRTDriver::create(CORE_DRIVER_PATH);
+    CHECK_EXPECTED(driver, "Failed to initialize HailoRTDriver");
+
+    auto device = std::unique_ptr<CoreDevice>(new (std::nothrow) CoreDevice(driver.release(), status));
+    CHECK_AS_EXPECTED((nullptr != device), HAILO_OUT_OF_HOST_MEMORY);
+    CHECK_SUCCESS_AS_EXPECTED(status, "Failed creating CoreDevice");
+
+    return device;
+}
+
+
+CoreDevice::CoreDevice(HailoRTDriver &&driver, hailo_status &status) : 
+    VdmaDevice::VdmaDevice(std::move(driver), Device::Type::CORE)
+{
+    status = update_fw_state();
+    if (HAILO_SUCCESS != status) {
+        LOGGER__ERROR("update_fw_state() failed with status {}", status);
+        return;
+    }
+
+    status = HAILO_SUCCESS;
+}
+
+CoreDevice::~CoreDevice() {}
+
+Expected<hailo_device_architecture_t> CoreDevice::get_architecture() const {
+    return Expected<hailo_device_architecture_t>(m_device_architecture);
+}
+
+hailo_status CoreDevice::fw_interact_impl(uint8_t *request_buffer, size_t request_size,
+        uint8_t *response_buffer, size_t *response_size, hailo_cpu_id_t cpu_id)
+{
+    uint8_t request_md5[PCIE_EXPECTED_MD5_LENGTH];
+    MD5_CTX ctx;
+
+    // TODO HRT-5358 - Unify MD5 functions. Use by pcie and core driver (and FW)
+    MD5_Init(&ctx);
+    MD5_Update(&ctx, request_buffer, request_size);
+    MD5_Final(request_md5, &ctx);
+
+    uint8_t response_md5[PCIE_EXPECTED_MD5_LENGTH];
+    uint8_t expected_response_md5[PCIE_EXPECTED_MD5_LENGTH];
+
+    auto status = m_driver.fw_control(request_buffer, request_size, request_md5,
+        response_buffer, response_size, response_md5,
+        CORE_DEFAULT_TIMEOUT, cpu_id);
+    CHECK_SUCCESS(status, "Failed to send fw control");
+
+    MD5_Init(&ctx);
+    MD5_Update(&ctx, response_buffer, (*response_size));
+    MD5_Final(expected_response_md5, &ctx);
+
+    auto memcmp_result = memcmp(expected_response_md5, response_md5, sizeof(response_md5));
+    if (0 != memcmp_result) {
+        LOGGER__ERROR("MD5 validation of control reesponse failed.");
+        return HAILO_INTERNAL_FAILURE;
+    }
+
+    return HAILO_SUCCESS;
+}
+
+hailo_status CoreDevice::reset_impl(CONTROL_PROTOCOL__reset_type_t reset_type)
+{
+    if (CONTROL_PROTOCOL__RESET_TYPE__NN_CORE == reset_type) {
+        return m_driver.reset_nn_core();
+    }
+
+    LOGGER__ERROR("Can't reset CoreDevice, please use linux reboot");
+    return HAILO_NOT_IMPLEMENTED;
+}
+
+Expected<size_t> CoreDevice::read_log(MemoryView &buffer, hailo_cpu_id_t cpu_id)
+{
+    if (hailo_cpu_id_t::HAILO_CPU_ID_0 == cpu_id) {
+        LOGGER__ERROR("Read FW log is supported only on core CPU");
+        return make_unexpected(HAILO_INVALID_ARGUMENT);
+    }
+
+    return VdmaDevice::read_log(buffer, cpu_id);
+}
+
+} /* namespace hailort */
\ No newline at end of file
diff --git a/hailort/libhailort/src/core_device.hpp b/hailort/libhailort/src/core_device.hpp
new file mode 100644 (file)
index 0000000..1c19e8f
--- /dev/null
@@ -0,0 +1,60 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file core_device
+ * @brief Device used by Hailo-15
+ *
+ **/
+
+#ifndef _HAILO_CORE_DEVICE_HPP_
+#define _HAILO_CORE_DEVICE_HPP_
+
+#include "hailo/expected.hpp"
+#include "hailo/hailort.h"
+#include "vdma_device.hpp"
+
+#include <memory>
+
+namespace hailort
+{
+
+class CoreDevice : public VdmaDevice {
+public:
+    virtual ~CoreDevice();
+    static bool is_loaded();
+    static Expected<std::unique_ptr<CoreDevice>> create();
+
+    virtual Expected<hailo_device_architecture_t> get_architecture() const override;
+    virtual const char* get_dev_id() const override {return "Core";}
+    Expected<size_t> read_log(MemoryView &buffer, hailo_cpu_id_t cpu_id);
+
+    virtual bool is_stream_interface_supported(const hailo_stream_interface_t &stream_interface) const override
+    {
+        switch (stream_interface) {
+        case HAILO_STREAM_INTERFACE_CORE:
+            return true;
+        case HAILO_STREAM_INTERFACE_PCIE:
+        case HAILO_STREAM_INTERFACE_ETH:
+        case HAILO_STREAM_INTERFACE_MIPI:
+            return false;
+        default:
+            LOGGER__ERROR("Invalid stream interface");
+            return false;
+        }
+    }
+
+protected:
+    virtual hailo_status fw_interact_impl(uint8_t *request_buffer, size_t request_size,
+        uint8_t *response_buffer, size_t *response_size, hailo_cpu_id_t cpu_id) override;
+    virtual hailo_status reset_impl(CONTROL_PROTOCOL__reset_type_t reset_type) override;
+
+private:
+    CoreDevice(HailoRTDriver &&driver, hailo_status &status);
+};
+
+
+} /* namespace hailort */
+
+#endif /* _HAILO_CORE_DEVICE_HPP_ */
\ No newline at end of file
diff --git a/hailort/libhailort/src/core_stream.cpp b/hailort/libhailort/src/core_stream.cpp
new file mode 100644 (file)
index 0000000..27d9897
--- /dev/null
@@ -0,0 +1,78 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file core_stream.cpp
+ **/
+
+#include "core_stream.hpp"
+#include "control.hpp"
+
+namespace hailort
+{
+
+Expected<std::unique_ptr<CoreInputStream>> CoreInputStream::create(Device &device,
+    uint8_t channel_index, const LayerInfo &edge_layer,
+    uint16_t batch_size, EventPtr network_group_activated_event, LatencyMeterPtr latency_meter)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+
+    CHECK_AS_EXPECTED(device.get_type() == Device::Type::CORE, HAILO_INTERNAL_FAILURE,
+        "Invalid device type");
+
+    CoreDevice *core_device = reinterpret_cast<CoreDevice*>(&device);
+    std::unique_ptr<CoreInputStream> local_stream(new (std::nothrow) CoreInputStream(*core_device,
+        channel_index, edge_layer, std::move(network_group_activated_event), batch_size,
+        latency_meter, DEFAULT_TRANSFER_TIMEOUT, status));
+    CHECK((nullptr != local_stream), make_unexpected(HAILO_OUT_OF_HOST_MEMORY));
+    CHECK_SUCCESS_AS_EXPECTED(status);
+
+    return local_stream;
+}
+
+CoreInputStream::CoreInputStream(
+    CoreDevice &device,
+    uint8_t channel_index,
+    const LayerInfo &edge_layer,
+    EventPtr network_group_activated_event,
+    uint16_t batch_size,
+    LatencyMeterPtr latency_meter,
+    const std::chrono::milliseconds &transfer_timeout,
+    hailo_status &status) :
+        VdmaInputStream(device, channel_index, edge_layer, network_group_activated_event,
+            batch_size, latency_meter, transfer_timeout, HAILO_STREAM_INTERFACE_CORE, status)
+{}
+
+Expected<std::unique_ptr<CoreOutputStream>> CoreOutputStream::create(Device &device,
+    uint8_t channel_index, const LayerInfo &edge_layer, uint16_t batch_size,
+    EventPtr network_group_activated_event, LatencyMeterPtr latency_meter)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    CHECK_AS_EXPECTED(device.get_type() == Device::Type::CORE, HAILO_INTERNAL_FAILURE,
+        "Invalid device type");
+
+    CoreDevice *core_device = reinterpret_cast<CoreDevice*>(&device);
+    std::unique_ptr<CoreOutputStream> local_stream(new (std::nothrow) CoreOutputStream(*core_device,
+        channel_index, edge_layer, std::move(network_group_activated_event),
+        batch_size, latency_meter, DEFAULT_TRANSFER_TIMEOUT, status));
+    CHECK((nullptr != local_stream), make_unexpected(HAILO_OUT_OF_HOST_MEMORY));
+    CHECK_SUCCESS_AS_EXPECTED(status);
+
+    return local_stream;
+}
+
+CoreOutputStream::CoreOutputStream(
+    CoreDevice &device,
+    uint8_t channel_index,
+    const LayerInfo &edge_layer,
+    EventPtr network_group_activated_event,
+    uint16_t batch_size,
+    LatencyMeterPtr latency_meter,
+    const std::chrono::milliseconds &transfer_timeout,
+    hailo_status &status) :
+        VdmaOutputStream(device, channel_index, edge_layer,
+            network_group_activated_event, batch_size, latency_meter, transfer_timeout, status)
+{}
+
+} /* namespace hailort */
diff --git a/hailort/libhailort/src/core_stream.hpp b/hailort/libhailort/src/core_stream.hpp
new file mode 100644 (file)
index 0000000..c0b95a8
--- /dev/null
@@ -0,0 +1,68 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file core_stream.hpp
+ * @brief Stream object for Core device
+ **/
+
+#ifndef _HAILO_CORE_STREAM_HPP_
+#define _HAILO_CORE_STREAM_HPP_
+
+#include "vdma_stream.hpp"
+#include "core_device.hpp"
+
+
+namespace hailort
+{
+
+class CoreInputStream : public VdmaInputStream {
+public:
+    CoreInputStream(CoreInputStream &&other) = default;
+    virtual ~CoreInputStream() = default;
+
+    static Expected<std::unique_ptr<CoreInputStream>> create(Device &device, uint8_t channel_index,
+        const LayerInfo &edge_layer, uint16_t batch_size, EventPtr network_group_activated_event,
+        LatencyMeterPtr latency_meter = nullptr);
+
+    virtual hailo_stream_interface_t get_interface() const override { return HAILO_STREAM_INTERFACE_CORE; }
+
+private:
+    CoreInputStream(
+        CoreDevice &device,
+        uint8_t channel_index,
+        const LayerInfo &edge_layer,
+        EventPtr network_group_activated_event,
+        uint16_t batch_size,
+        LatencyMeterPtr latency_meter,
+        const std::chrono::milliseconds &transfer_timeout,
+        hailo_status &status);
+};
+
+class CoreOutputStream : public VdmaOutputStream {
+public:
+    CoreOutputStream(CoreOutputStream &&other) = default;
+    virtual ~CoreOutputStream() = default;
+
+    static Expected<std::unique_ptr<CoreOutputStream>> create(Device &device, uint8_t channel_index,
+        const LayerInfo &edge_layer, uint16_t batch_size, EventPtr network_group_activated_event,
+        LatencyMeterPtr latency_meter);
+
+    virtual hailo_stream_interface_t get_interface() const override { return HAILO_STREAM_INTERFACE_CORE; }
+
+private:
+    explicit CoreOutputStream(
+        CoreDevice &device,
+        uint8_t channel_index,
+        const LayerInfo &edge_layer,
+        EventPtr network_group_activated_event,
+        uint16_t batch_size,
+        LatencyMeterPtr latency_meter,
+        const std::chrono::milliseconds &transfer_timeout,
+        hailo_status &status);
+};
+
+} /* namespace hailort */
+
+#endif /* _HAILO_CORE_STREAM_HPP_ */
diff --git a/hailort/libhailort/src/d2h_event_queue.hpp b/hailort/libhailort/src/d2h_event_queue.hpp
new file mode 100644 (file)
index 0000000..5e8d13e
--- /dev/null
@@ -0,0 +1,31 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file d2h_event_queue.hpp
+ * @brief TODO: brief
+ *
+ * TODO: doc
+ **/
+
+#ifndef HAILO_D2H_EVENT_QUEUE_HPP_
+#define HAILO_D2H_EVENT_QUEUE_HPP_
+
+#include "d2h_events.h"
+#include "thread_safe_queue.hpp"
+
+namespace hailort
+{
+
+class D2hEventQueue : public SafeQueue<D2H_EVENT_MESSAGE_t> {
+public:
+    void clear() {
+        std::unique_lock<std::mutex> lock(m_mutex);
+        m_queue = std::queue<D2H_EVENT_MESSAGE_t>();
+    }
+};
+
+} /* namespace hailort */
+
+#endif // HAILO_D2H_EVENT_QUEUE_HPP_
diff --git a/hailort/libhailort/src/d2h_events_parser.cpp b/hailort/libhailort/src/d2h_events_parser.cpp
new file mode 100644 (file)
index 0000000..ab60f20
--- /dev/null
@@ -0,0 +1,392 @@
+/* 
+ * =============================================================================
+ *
+ *                               HAILO
+ *
+ *  Property of HAILO Tech
+ *  For Unrestricted Internal Use Only
+ *  Unauthorized reproduction and/or distribution is strictly prohibited.
+ *  This product is protected under copyright law and trade secret law
+ *  Created 2018, (C) Copyright 2018 Hailo Tech .  All rights reserved.
+ *  as an unpublished work.
+ */
+/**
+*   Filename:      d2h_events_parser.c
+*
+*   Description:   Implements parsing device to host notifications.
+*
+*=============================================================================*/
+
+#include <stdint.h>
+#include <string.h>
+#include "common/utils.hpp"
+#include "d2h_events.h"
+#include "byte_order.h"
+#include "common/logger_macros.hpp"
+
+using namespace hailort;
+
+/* Function prototype for control operations */
+typedef HAILO_COMMON_STATUS_t (*firmware_notifications_parser_t) (D2H_EVENT_MESSAGE_t *d2h_notification_message);
+
+/**********************************************************************
+ * Private Declarations
+ **********************************************************************/
+static HAILO_COMMON_STATUS_t D2H_EVENTS__parse_rx_error(D2H_EVENT_MESSAGE_t *d2h_notification_message) ;
+static HAILO_COMMON_STATUS_t D2H_EVENTS__parse_host_info_notification(D2H_EVENT_MESSAGE_t *d2h_notification_message);
+static HAILO_COMMON_STATUS_t D2H_EVENTS__parse_health_monitor_temperature_alarm_notification(D2H_EVENT_MESSAGE_t *d2h_notification_message);
+static HAILO_COMMON_STATUS_t D2H_EVENTS__parse_health_monitor_closed_streams_notification(D2H_EVENT_MESSAGE_t *d2h_notification_message);
+static HAILO_COMMON_STATUS_t D2H_EVENTS__parse_health_monitor_overcurrent_alert_notification(D2H_EVENT_MESSAGE_t *d2h_notification_message);
+static HAILO_COMMON_STATUS_t D2H_EVENTS__parse_health_monitor_lcu_ecc_nonfatal_notification(D2H_EVENT_MESSAGE_t *d2h_notification_message);
+static HAILO_COMMON_STATUS_t D2H_EVENTS__parse_health_monitor_lcu_ecc_fatal_notification(D2H_EVENT_MESSAGE_t *d2h_notification_message);
+static HAILO_COMMON_STATUS_t D2H_EVENTS__parse_health_monitor_cpu_ecc_error_notification(D2H_EVENT_MESSAGE_t *d2h_notification_message);
+static HAILO_COMMON_STATUS_t D2H_EVENTS__parse_health_monitor_cpu_ecc_fatal_notification(D2H_EVENT_MESSAGE_t *d2h_notification_message);
+static HAILO_COMMON_STATUS_t D2H_EVENTS__parse_context_switch_breakpoint_reached(D2H_EVENT_MESSAGE_t *d2h_notification_message);
+static HAILO_COMMON_STATUS_t D2H_EVENTS__parse_health_monitor_clock_changed_event_notification(D2H_EVENT_MESSAGE_t *d2h_notification_message);
+
+/**********************************************************************
+ * Globals
+ **********************************************************************/
+firmware_notifications_parser_t g_firmware_notifications_parser[D2H_EVENT_ID_COUNT] = {
+    D2H_EVENTS__parse_rx_error,
+    D2H_EVENTS__parse_host_info_notification,
+    D2H_EVENTS__parse_health_monitor_temperature_alarm_notification,
+    D2H_EVENTS__parse_health_monitor_closed_streams_notification,
+    D2H_EVENTS__parse_health_monitor_overcurrent_alert_notification,
+    D2H_EVENTS__parse_health_monitor_lcu_ecc_nonfatal_notification,
+    D2H_EVENTS__parse_health_monitor_lcu_ecc_fatal_notification,
+    D2H_EVENTS__parse_health_monitor_cpu_ecc_error_notification,
+    D2H_EVENTS__parse_health_monitor_cpu_ecc_fatal_notification,
+    D2H_EVENTS__parse_context_switch_breakpoint_reached,
+    D2H_EVENTS__parse_health_monitor_clock_changed_event_notification
+};
+/**********************************************************************
+ * Internal Functions
+ **********************************************************************/
+static HAILO_COMMON_STATUS_t D2H_EVENTS__parse_rx_error(D2H_EVENT_MESSAGE_t *d2h_notification_message) 
+{
+    HAILO_COMMON_STATUS_t status = HAILO_COMMON_STATUS__UNINITIALIZED;
+
+    if (D2H_EVENT_RX_ERROR_EVENT_PARAMETER_COUNT != d2h_notification_message->header.parameter_count) {
+        LOGGER__ERROR("d2h notification invalid parameter count: {}", d2h_notification_message->header.parameter_count);
+        status = HAILO_STATUS__D2H_EVENTS__INCORRECT_PARAMETER_COUNT;
+        goto l_exit;
+    }
+
+    if(d2h_notification_message->header.payload_length != sizeof(d2h_notification_message->message_parameters.rx_error_event)) {
+        LOGGER__ERROR("d2h notification invalid payload_length: {}", d2h_notification_message->header.payload_length);
+        status = HAILO_STATUS__D2H_EVENTS__INCORRECT_PARAMETER_LENGTH;
+        goto l_exit;
+    }
+   
+    LOGGER__INFO("Got Rx Error {} Event From module_id {} with error {}, queue {}",((D2H_EVENT_PRIORITY_CRITICAL == d2h_notification_message->header.priority) ?"Critical":"Info"),
+    d2h_notification_message->header.module_id, d2h_notification_message->message_parameters.rx_error_event.error, d2h_notification_message->message_parameters.rx_error_event.queue_number);
+    
+    status = HAILO_COMMON_STATUS__SUCCESS;
+
+l_exit:
+    return status;
+}
+
+static HAILO_COMMON_STATUS_t D2H_EVENTS__parse_host_info_notification(D2H_EVENT_MESSAGE_t *d2h_notification_message) 
+{
+    HAILO_COMMON_STATUS_t status = HAILO_COMMON_STATUS__UNINITIALIZED;
+
+    if (D2H_EVENT_HOST_INFO_EVENT_PARAMETER_COUNT != d2h_notification_message->header.parameter_count) {
+        LOGGER__ERROR("d2h notification invalid parameter count: {}", d2h_notification_message->header.parameter_count);
+        status = HAILO_STATUS__D2H_EVENTS__INCORRECT_PARAMETER_COUNT;
+        goto l_exit;
+    }
+
+    if(d2h_notification_message->header.payload_length != sizeof(d2h_notification_message->message_parameters.host_info_event)) {
+        LOGGER__ERROR("d2h notification invalid payload_length: {}", d2h_notification_message->header.payload_length);
+        status = HAILO_STATUS__D2H_EVENTS__INCORRECT_PARAMETER_LENGTH;
+        goto l_exit;
+    }
+   
+    LOGGER__INFO("Got host config {} Event From module_id {} with connection type {}",((D2H_EVENT_PRIORITY_CRITICAL == d2h_notification_message->header.priority) ?"Critical":"Info"),
+    d2h_notification_message->header.module_id, ((D2H_EVENT_COMMUNICATION_TYPE_UDP == d2h_notification_message->message_parameters.host_info_event.connection_type) ?"UDP":"PCIe"));
+    
+    status = HAILO_COMMON_STATUS__SUCCESS;
+
+l_exit:
+    return status;
+}
+
+static HAILO_COMMON_STATUS_t D2H_EVENTS__parse_health_monitor_temperature_alarm_notification(D2H_EVENT_MESSAGE_t *d2h_notification_message) 
+{
+    HAILO_COMMON_STATUS_t status = HAILO_COMMON_STATUS__UNINITIALIZED;
+
+    if (D2H_EVENT_HEALTH_MONITOR_TEMPERATURE_ALARM_EVENT_PARAMETER_COUNT != d2h_notification_message->header.parameter_count) {
+        LOGGER__ERROR("d2h notification invalid parameter count: {}", d2h_notification_message->header.parameter_count);
+        status = HAILO_STATUS__D2H_EVENTS__INCORRECT_PARAMETER_COUNT;
+        goto l_exit;
+    }
+
+    switch (d2h_notification_message->message_parameters.health_monitor_temperature_alarm_event.temperature_zone) {
+        case HAILO_TEMPERATURE_PROTECTION_TEMPERATURE_ZONE__GREEN:
+            LOGGER__INFO("Got health monitor notification - temperature reached green zone. sensor id={}, TS00={}c, TS01={}c", 
+                            d2h_notification_message->message_parameters.health_monitor_temperature_alarm_event.alarm_ts_id,
+                            d2h_notification_message->message_parameters.health_monitor_temperature_alarm_event.ts0_temperature,
+                            d2h_notification_message->message_parameters.health_monitor_temperature_alarm_event.ts1_temperature);
+            break;
+
+        case HAILO_TEMPERATURE_PROTECTION_TEMPERATURE_ZONE__ORANGE:
+            LOGGER__WARNING("Got health monitor notification - temperature reached orange zone. sensor id={}, TS00={}c, TS01={}c", 
+                                d2h_notification_message->message_parameters.health_monitor_temperature_alarm_event.alarm_ts_id,
+                                d2h_notification_message->message_parameters.health_monitor_temperature_alarm_event.ts0_temperature,
+                                d2h_notification_message->message_parameters.health_monitor_temperature_alarm_event.ts1_temperature);
+            break;
+
+        case HAILO_TEMPERATURE_PROTECTION_TEMPERATURE_ZONE__RED:
+            LOGGER__CRITICAL("Got health monitor notification - temperature reached red zone. sensor id={}, TS00={}c, TS01={}c", 
+                                d2h_notification_message->message_parameters.health_monitor_temperature_alarm_event.alarm_ts_id,
+                                d2h_notification_message->message_parameters.health_monitor_temperature_alarm_event.ts0_temperature,
+                                d2h_notification_message->message_parameters.health_monitor_temperature_alarm_event.ts1_temperature);
+            break;
+
+        default:
+            LOGGER__ERROR("Got invalid health monitor notification - temperature zone could not be parsed.");
+            status = HAILO_STATUS__D2H_EVENTS__INVALID_ARGUMENT;
+            goto l_exit;
+    }
+
+    status = HAILO_COMMON_STATUS__SUCCESS;
+
+l_exit:
+    return status;
+}
+
+static HAILO_COMMON_STATUS_t D2H_EVENTS__parse_health_monitor_clock_changed_event_notification(D2H_EVENT_MESSAGE_t *d2h_notification_message)
+{
+    HAILO_COMMON_STATUS_t status = HAILO_COMMON_STATUS__UNINITIALIZED;
+
+    if (D2H_EVENT_HEALTH_MONITOR_CLOCK_CHANGED_EVENT_PARAMETER_COUNT != d2h_notification_message->header.parameter_count) {
+        LOGGER__ERROR("d2h notification invalid parameter count: {}", d2h_notification_message->header.parameter_count);
+        status = HAILO_STATUS__D2H_EVENTS__INCORRECT_PARAMETER_COUNT;
+        goto l_exit;
+    }
+    LOGGER__WARNING("Got health monitor notification - System's clock has been changed from {} to {}",
+                        d2h_notification_message->message_parameters.health_monitor_clock_changed_event.previous_clock,
+                        d2h_notification_message->message_parameters.health_monitor_clock_changed_event.current_clock);
+
+    status = HAILO_COMMON_STATUS__SUCCESS;
+
+l_exit:
+    return status;
+}
+
+static HAILO_COMMON_STATUS_t D2H_EVENTS__parse_health_monitor_closed_streams_notification(D2H_EVENT_MESSAGE_t *d2h_notification_message) 
+{
+    HAILO_COMMON_STATUS_t status = HAILO_COMMON_STATUS__UNINITIALIZED;
+
+    if (D2H_EVENT_HEALTH_MONITOR_CLOSED_STREAMS_EVENT_PARAMETER_COUNT != d2h_notification_message->header.parameter_count) {
+        LOGGER__ERROR("d2h notification invalid parameter count: {}", d2h_notification_message->header.parameter_count);
+        status = HAILO_STATUS__D2H_EVENTS__INCORRECT_PARAMETER_COUNT;
+        goto l_exit;
+    }
+
+    if(d2h_notification_message->header.payload_length != sizeof(d2h_notification_message->message_parameters.health_monitor_closed_streams_event)) {
+        LOGGER__ERROR("d2h notification invalid payload_length: {}", d2h_notification_message->header.payload_length);
+        status = HAILO_STATUS__D2H_EVENTS__INCORRECT_PARAMETER_LENGTH;
+        goto l_exit;
+    }
+
+    LOGGER__CRITICAL("Got health monitor closed streams notification. temperature: TS00={} c, TS01={} c, inputs bitfield:{:x}, outputs bitfield:{:x}", 
+        d2h_notification_message->message_parameters.health_monitor_closed_streams_event.ts0_temperature,
+        d2h_notification_message->message_parameters.health_monitor_closed_streams_event.ts1_temperature,
+        d2h_notification_message->message_parameters.health_monitor_closed_streams_event.closed_input_streams,
+        d2h_notification_message->message_parameters.health_monitor_closed_streams_event.closed_output_streams);
+
+    status = HAILO_COMMON_STATUS__SUCCESS;
+
+l_exit:
+    return status;
+}
+
+static HAILO_COMMON_STATUS_t D2H_EVENTS__parse_health_monitor_overcurrent_alert_notification(D2H_EVENT_MESSAGE_t *d2h_notification_message)
+{
+    HAILO_COMMON_STATUS_t status = HAILO_COMMON_STATUS__UNINITIALIZED;
+
+    if (D2H_EVENT_HEALTH_MONITOR_OVERCURRENT_ALERT_EVENT_PARAMETER_COUNT != d2h_notification_message->header.parameter_count) {
+        LOGGER__ERROR("d2h event invalid parameter count: {}", d2h_notification_message->header.parameter_count);
+        status = HAILO_STATUS__D2H_EVENTS__INCORRECT_PARAMETER_COUNT;
+        goto l_exit;
+    }
+
+    if(d2h_notification_message->header.payload_length != sizeof(d2h_notification_message->message_parameters.health_monitor_overcurrent_alert_event)) {
+        LOGGER__ERROR("d2h event invalid payload_length: {}", d2h_notification_message->header.payload_length);
+        status = HAILO_STATUS__D2H_EVENTS__INCORRECT_PARAMETER_LENGTH;
+        goto l_exit;
+    }
+
+    switch (d2h_notification_message->message_parameters.health_monitor_overcurrent_alert_event.overcurrent_zone) {
+        case HAILO_OVERCURRENT_PROTECTION_OVERCURRENT_ZONE__NONE:
+            LOGGER__INFO("Got health monitor notification - overcurrent alert state cleared.");
+            break;
+
+        case HAILO_OVERCURRENT_PROTECTION_OVERCURRENT_ZONE__ORANGE:
+            LOGGER__WARNING("Got health monitor notification - overcurrent alert state exceeded orange threshold ({} mA), and sampled current during alert ({} mA)", 
+                d2h_notification_message->message_parameters.health_monitor_overcurrent_alert_event.exceeded_alert_threshold,
+                d2h_notification_message->message_parameters.health_monitor_overcurrent_alert_event.sampled_current_during_alert);
+            break;
+
+        case HAILO_OVERCURRENT_PROTECTION_OVERCURRENT_ZONE__RED:
+            LOGGER__CRITICAL("Got health monitor notification - overcurrent alert state exceeded red threshold ({} mA), and sampled current during alert ({} mA)", 
+                d2h_notification_message->message_parameters.health_monitor_overcurrent_alert_event.exceeded_alert_threshold,
+                d2h_notification_message->message_parameters.health_monitor_overcurrent_alert_event.sampled_current_during_alert);
+            break;
+
+        default:
+            LOGGER__ERROR("Got invalid health monitor notification - overcurrent alert state could not be parsed.");
+            status = HAILO_STATUS__D2H_EVENTS__INVALID_ARGUMENT;
+            goto l_exit;
+    }
+
+    status = HAILO_COMMON_STATUS__SUCCESS;
+
+l_exit:
+    return status;
+
+}
+
+static HAILO_COMMON_STATUS_t D2H_EVENTS__parse_health_monitor_lcu_ecc_nonfatal_notification(
+    D2H_EVENT_MESSAGE_t *d2h_notification_message)
+{
+    HAILO_COMMON_STATUS_t status = HAILO_COMMON_STATUS__UNINITIALIZED;
+
+    if (D2H_EVENT_HEALTH_MONITOR_LCU_ECC_ERROR_EVENT_PARAMETER_COUNT != d2h_notification_message->header.parameter_count) {
+        LOGGER__ERROR("d2h event lcu ecc uncorrectable error invalid parameter count: {}", d2h_notification_message->header.parameter_count);
+        status = HAILO_STATUS__D2H_EVENTS__INCORRECT_PARAMETER_COUNT;
+        goto l_exit;
+    }
+
+    if(sizeof(d2h_notification_message->message_parameters.health_monitor_lcu_ecc_error_event) != d2h_notification_message->header.payload_length) {
+        LOGGER__ERROR("d2h event invalid payload_length: {}", d2h_notification_message->header.payload_length);
+        status = HAILO_STATUS__D2H_EVENTS__INCORRECT_PARAMETER_LENGTH;
+        goto l_exit;
+    }
+
+    LOGGER__WARNING("Got health monitor LCU ECC correctable error event. cluster_bitmap={}",
+        d2h_notification_message->message_parameters.health_monitor_lcu_ecc_error_event.cluster_bitmap);
+
+    status = HAILO_COMMON_STATUS__SUCCESS;
+
+l_exit:
+    return status;
+}
+
+static HAILO_COMMON_STATUS_t D2H_EVENTS__parse_health_monitor_lcu_ecc_fatal_notification(
+    D2H_EVENT_MESSAGE_t *d2h_notification_message)
+{
+    HAILO_COMMON_STATUS_t status = HAILO_COMMON_STATUS__UNINITIALIZED;
+
+    if (D2H_EVENT_HEALTH_MONITOR_LCU_ECC_ERROR_EVENT_PARAMETER_COUNT != d2h_notification_message->header.parameter_count) {
+        LOGGER__ERROR("d2h event invalid lcu ecc uncorrectable error parameter count: {}", d2h_notification_message->header.parameter_count);
+        status = HAILO_STATUS__D2H_EVENTS__INCORRECT_PARAMETER_COUNT;
+        goto l_exit;
+    }
+
+    if(sizeof(d2h_notification_message->message_parameters.health_monitor_lcu_ecc_error_event) != d2h_notification_message->header.payload_length) {
+        LOGGER__ERROR("d2h event invalid payload_length: {}", d2h_notification_message->header.payload_length);
+        status = HAILO_STATUS__D2H_EVENTS__INCORRECT_PARAMETER_LENGTH;
+        goto l_exit;
+    }
+
+    LOGGER__CRITICAL("Got health monitor LCU ECC uncorrectable error event. cluster_bitmap={}",
+        d2h_notification_message->message_parameters.health_monitor_lcu_ecc_error_event.cluster_bitmap);
+
+    status = HAILO_COMMON_STATUS__SUCCESS;
+
+l_exit:
+    return status;
+}
+
+static HAILO_COMMON_STATUS_t D2H_EVENTS__parse_health_monitor_cpu_ecc_error_notification(
+    D2H_EVENT_MESSAGE_t *d2h_notification_message)
+{
+    HAILO_COMMON_STATUS_t status = HAILO_COMMON_STATUS__UNINITIALIZED;
+
+    CHECK(D2H_EVENT_HEALTH_MONITOR_CPU_ECC_EVENT_PARAMETER_COUNT == d2h_notification_message->header.parameter_count,
+            HAILO_STATUS__D2H_EVENTS__INCORRECT_PARAMETER_COUNT,
+            "d2h event invalid parameter count: {}", d2h_notification_message->header.parameter_count);
+
+    CHECK(sizeof(d2h_notification_message->message_parameters.health_monitor_cpu_ecc_event) == d2h_notification_message->header.payload_length,
+            HAILO_STATUS__D2H_EVENTS__INCORRECT_PARAMETER_LENGTH,
+            "d2h event invalid payload_length: {}", d2h_notification_message->header.payload_length);
+
+    LOGGER__ERROR("Got health monitor CPU ECC error event. memory_bitmap={}",
+        d2h_notification_message->message_parameters.health_monitor_cpu_ecc_event.memory_bitmap);
+
+    status = HAILO_COMMON_STATUS__SUCCESS;
+
+    return status;
+}
+
+static HAILO_COMMON_STATUS_t D2H_EVENTS__parse_health_monitor_cpu_ecc_fatal_notification(
+    D2H_EVENT_MESSAGE_t *d2h_notification_message)
+{
+    HAILO_COMMON_STATUS_t status = HAILO_COMMON_STATUS__UNINITIALIZED;
+
+    if (D2H_EVENT_HEALTH_MONITOR_CPU_ECC_EVENT_PARAMETER_COUNT != d2h_notification_message->header.parameter_count) {
+        LOGGER__ERROR("d2h event invalid cpu ecc uncorrectable error parameter count: {}", d2h_notification_message->header.parameter_count);
+        status = HAILO_STATUS__D2H_EVENTS__INCORRECT_PARAMETER_COUNT;
+        goto l_exit;
+    }
+
+    if(sizeof(d2h_notification_message->message_parameters.health_monitor_cpu_ecc_event) != d2h_notification_message->header.payload_length) {
+        LOGGER__ERROR("d2h event invalid payload_length: {}", d2h_notification_message->header.payload_length);
+        status = HAILO_STATUS__D2H_EVENTS__INCORRECT_PARAMETER_LENGTH;
+        goto l_exit;
+    }
+
+    LOGGER__CRITICAL("Got health monitor CPU ECC fatal event. memory_bitmap={}",
+        d2h_notification_message->message_parameters.health_monitor_cpu_ecc_event.memory_bitmap);
+
+    status = HAILO_COMMON_STATUS__SUCCESS;
+
+l_exit:
+    return status;
+}
+
+static HAILO_COMMON_STATUS_t D2H_EVENTS__parse_context_switch_breakpoint_reached(D2H_EVENT_MESSAGE_t *d2h_notification_message)
+{
+    HAILO_COMMON_STATUS_t status = HAILO_COMMON_STATUS__UNINITIALIZED;
+
+    CHECK(D2H_EVENT_CONTEXT_SWITCH_BREAKPOINT_REACHED_EVENT_PARAMETER_COUNT == d2h_notification_message->header.parameter_count,
+            HAILO_STATUS__D2H_EVENTS__INCORRECT_PARAMETER_COUNT,
+            "d2h event invalid parameter count: {}", d2h_notification_message->header.parameter_count);
+
+    CHECK(d2h_notification_message->header.payload_length == 
+            sizeof(d2h_notification_message->message_parameters.context_switch_breakpoint_reached_event),
+            HAILO_STATUS__D2H_EVENTS__INCORRECT_PARAMETER_LENGTH,
+            "d2h event invalid payload_length: {}", d2h_notification_message->header.payload_length);
+    
+    LOGGER__INFO("Got Context switch breakpoint with net_group index {}, batch index {}, context index {}, action index {}",
+            d2h_notification_message->message_parameters.context_switch_breakpoint_reached_event.application_index,
+            d2h_notification_message->message_parameters.context_switch_breakpoint_reached_event.batch_index,
+            d2h_notification_message->message_parameters.context_switch_breakpoint_reached_event.context_index,
+            d2h_notification_message->message_parameters.context_switch_breakpoint_reached_event.action_index);
+
+    status = HAILO_COMMON_STATUS__SUCCESS;
+
+    return status;
+}
+
+/**********************************************************************
+ * Public Functions
+ **********************************************************************/
+HAILO_COMMON_STATUS_t D2H_EVENTS__parse_event(D2H_EVENT_MESSAGE_t *d2h_notification_message){
+
+    HAILO_COMMON_STATUS_t status = HAILO_COMMON_STATUS__UNINITIALIZED;
+
+    if (D2H_EVENT_ID_COUNT < d2h_notification_message->header.event_id){
+        LOGGER__ERROR("d2h notification invalid notification_id: {}", d2h_notification_message->header.event_id);
+        status = HAILO_STATUS__D2H_EVENTS__INVALID_ARGUMENT;
+        goto l_exit;
+    }
+    status = g_firmware_notifications_parser[d2h_notification_message->header.event_id](d2h_notification_message); 
+
+l_exit:
+    return status;
+}
diff --git a/hailort/libhailort/src/device.cpp b/hailort/libhailort/src/device.cpp
new file mode 100644 (file)
index 0000000..aef126b
--- /dev/null
@@ -0,0 +1,442 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file device.cpp
+ * @brief TODO: brief
+ *
+ * TODO: doc
+ **/
+
+#include <hailo/hailort.h>
+#include "hailo/device.hpp"
+#include "common/utils.hpp"
+#include "control.hpp"
+#include <memory>
+#include "byte_order.h"
+#include "firmware_header_utils.h"
+#include "control_protocol.h"
+#include "pcie_device.hpp"
+#include "eth_device.hpp"
+#include "core_device.hpp"
+
+#ifndef _MSC_VER
+#include <sys/utsname.h>
+#endif
+
+namespace hailort
+{
+
+#define WRITE_CHUNK_SIZE (1024)
+#define DEVICE_WORD_SIZE (4)
+
+Device::Device(Type type) : 
+    m_type(type),
+    m_control_sequence(0),
+    m_is_control_version_supported(false),
+    m_device_architecture(HAILO_ARCH_MAX_ENUM)
+{
+#ifndef _MSC_VER
+    struct utsname uname_data;
+    if (-1 != uname(&uname_data)) {
+        LOGGER__INFO("OS Version: {} {} {} {}", uname_data.sysname, uname_data.release,
+            uname_data.version,uname_data.machine);
+    } else {
+        LOGGER__ERROR("uname failed (errno = {})", errno);
+    }
+#endif
+}
+
+Expected<std::vector<hailo_pcie_device_info_t>> Device::scan_pcie()
+{
+    return PcieDevice::scan();
+}
+
+Expected<std::vector<hailo_eth_device_info_t>> Device::scan_eth(const std::string &interface_name,
+    std::chrono::milliseconds timeout)
+{
+    return EthernetDevice::scan(interface_name, timeout);
+}
+
+Expected<std::vector<hailo_eth_device_info_t>> Device::scan_eth_by_host_address(const std::string &host_address,
+    std::chrono::milliseconds timeout)
+{
+    return EthernetDevice::scan_by_host_address(host_address, timeout);
+}
+
+Expected<std::unique_ptr<Device>> Device::create_pcie()
+{
+    auto pcie_device = PcieDevice::create();
+    CHECK_EXPECTED(pcie_device);
+    // Upcasting to Device unique_ptr (from PcieDevice unique_ptr)
+    auto device = std::unique_ptr<Device>(pcie_device.release());
+    return device;
+}
+
+Expected<std::unique_ptr<Device>> Device::create_pcie(const hailo_pcie_device_info_t &device_info)
+{
+    auto pcie_device = PcieDevice::create(device_info);
+    CHECK_EXPECTED(pcie_device);
+    // Upcasting to Device unique_ptr (from PcieDevice unique_ptr)
+    auto device = std::unique_ptr<Device>(pcie_device.release());
+    return device;
+}
+
+Expected<std::unique_ptr<Device>> Device::create_eth(const hailo_eth_device_info_t &device_info)
+{
+    auto eth_device = EthernetDevice::create(device_info);
+    CHECK_EXPECTED(eth_device);
+    // Upcasting to Device unique_ptr (from EthernetDevice unique_ptr)
+    auto device = std::unique_ptr<Device>(eth_device.release());
+    return device;
+}
+
+Expected<std::unique_ptr<Device>> Device::create_eth(const std::string &ip_addr)
+{
+    auto eth_device = EthernetDevice::create(ip_addr);
+    CHECK_EXPECTED(eth_device);
+    // Upcasting to Device unique_ptr (from EthernetDevice unique_ptr)
+    auto device = std::unique_ptr<Device>(eth_device.release());
+    return device;
+}
+
+Expected<hailo_pcie_device_info_t> Device::parse_pcie_device_info(const std::string &device_info_str)
+{
+    return PcieDevice::parse_pcie_device_info(device_info_str);
+}
+
+Expected<std::string> Device::pcie_device_info_to_string(const hailo_pcie_device_info_t &device_info)
+{
+    return PcieDevice::pcie_device_info_to_string(device_info);
+}
+
+bool Device::is_core_driver_loaded()
+{
+    return CoreDevice::is_loaded();
+}
+
+Expected<std::unique_ptr<Device>> Device::create_core_device()
+{
+    auto core_device = CoreDevice::create();
+    CHECK_EXPECTED(core_device);
+    // Upcasting to Device unique_ptr (from CoreDevice unique_ptr)
+    auto device = std::unique_ptr<Device>(core_device.release());
+    return device;
+}
+
+uint32_t Device::get_control_sequence()
+{
+    return m_control_sequence;
+}
+
+bool Device::is_control_version_supported()
+{
+    return m_is_control_version_supported;
+}
+
+Device::Type Device::get_type() const
+{
+    return m_type;
+}
+
+Expected<hailo_stream_interface_t> Device::get_default_streams_interface() const
+{
+    switch(m_type) {
+    case Type::PCIE:
+        return HAILO_STREAM_INTERFACE_PCIE;
+    case Type::CORE:
+        return HAILO_STREAM_INTERFACE_CORE;
+    case Type::ETH:
+        return HAILO_STREAM_INTERFACE_ETH;
+    default:
+        LOGGER__ERROR("Failed to get default streams interface.");
+        return make_unexpected(HAILO_INTERNAL_FAILURE);
+    }
+}
+
+hailo_status Device::set_fw_logger(hailo_fw_logger_level_t level, uint32_t interface_mask)
+{
+    return Control::set_fw_logger(*this, level, interface_mask);
+}
+
+hailo_status Device::set_throttling_state(bool should_activate)
+{
+    return Control::set_throttling_state(*this, should_activate);
+}
+
+Expected<bool> Device::get_throttling_state()
+{
+    return Control::get_throttling_state(*this);
+}
+
+hailo_status Device::write_memory(uint32_t address, const MemoryView &data)
+{
+    return Control::write_memory(*this, address, data.data(), static_cast<uint32_t>(data.size()));
+}
+
+hailo_status Device::read_memory(uint32_t address, MemoryView &data)
+{
+    return Control::read_memory(*this, address, data.data(), static_cast<uint32_t>(data.size()));
+}
+
+hailo_status Device::wd_enable(hailo_cpu_id_t cpu_id)
+{
+    return static_cast<hailo_status>(Control::wd_enable(*this, static_cast<uint8_t>(cpu_id), true));
+}
+
+hailo_status Device::wd_disable(hailo_cpu_id_t cpu_id)
+{
+    return Control::wd_enable(*this, static_cast<uint8_t>(cpu_id), false);
+}
+
+hailo_status Device::wd_config(hailo_cpu_id_t cpu_id, uint32_t wd_cycles, hailo_watchdog_mode_t wd_mode)
+{
+    CONTROL_PROTOCOL__WATCHDOG_MODE_t wd_type = CONTROL_PROTOCOL__WATCHDOG_NUM_MODES; // set invalid value
+    switch(wd_mode) {
+    case HAILO_WATCHDOG_MODE_HW_SW:
+        wd_type = CONTROL_PROTOCOL__WATCHDOG_MODE_HW_SW;
+        break;
+    case HAILO_WATCHDOG_MODE_HW_ONLY:
+        wd_type = CONTROL_PROTOCOL__WATCHDOG_MODE_HW_ONLY;
+        break;
+    default:
+        LOGGER__ERROR("Invalid wd_mode");
+        return HAILO_INVALID_ARGUMENT;
+    }
+    return Control::wd_config(*this, static_cast<uint8_t>(cpu_id), wd_cycles, wd_type);
+}
+
+Expected<uint32_t> Device::previous_system_state(hailo_cpu_id_t cpu_id)
+{
+    CONTROL_PROTOCOL__system_state_t res = {};
+    auto status = Control::previous_system_state(*this, static_cast<uint8_t>(cpu_id), &res);
+    CHECK_SUCCESS_AS_EXPECTED(status);
+    return res;
+}
+
+hailo_status Device::set_pause_frames(bool rx_pause_frames_enable)
+{
+    return Control::set_pause_frames(*this, rx_pause_frames_enable);
+}
+
+hailo_status Device::i2c_read(const hailo_i2c_slave_config_t &slave_config, uint32_t register_address, MemoryView &data)
+{
+    return Control::i2c_read(*this, &slave_config, register_address, data.data(), static_cast<uint32_t>(data.size()));
+}
+
+hailo_status Device::i2c_write(const hailo_i2c_slave_config_t &slave_config, uint32_t register_address, const MemoryView &data)
+{
+    return Control::i2c_write(*this, &slave_config, register_address, data.data(), static_cast<uint32_t>(data.size()));
+}
+
+Expected<float32_t> Device::power_measurement(hailo_dvm_options_t dvm, hailo_power_measurement_types_t measurement_type)
+{
+    float32_t res = 0;
+    auto status = Control::power_measurement(*this, static_cast<CONTROL_PROTOCOL__dvm_options_t>(dvm),
+        static_cast<CONTROL_PROTOCOL__power_measurement_types_t>(measurement_type), &res);
+    CHECK_SUCCESS_AS_EXPECTED(status);
+    return res;
+}
+
+hailo_status Device::start_power_measurement(uint32_t delay_milliseconds, hailo_averaging_factor_t averaging_factor, hailo_sampling_period_t sampling_period)
+{
+    return Control::start_power_measurement(*this, delay_milliseconds, static_cast<CONTROL_PROTOCOL__averaging_factor_t>(averaging_factor),
+        static_cast<CONTROL_PROTOCOL__sampling_period_t>(sampling_period));
+}
+
+hailo_status Device::set_power_measurement(uint32_t index, hailo_dvm_options_t dvm, hailo_power_measurement_types_t measurement_type)
+{
+    return Control::set_power_measurement(*this, index, static_cast<CONTROL_PROTOCOL__dvm_options_t>(dvm), static_cast<CONTROL_PROTOCOL__power_measurement_types_t>(measurement_type));
+}
+
+Expected<hailo_power_measurement_data_t> Device::get_power_measurement(uint32_t index, bool should_clear)
+{
+    hailo_power_measurement_data_t measurement_data = {};
+    auto status = Control::get_power_measurement(*this, index, should_clear, &measurement_data);
+    CHECK_SUCCESS_AS_EXPECTED(status);
+    return measurement_data;
+}
+
+hailo_status Device::stop_power_measurement()
+{
+    return Control::stop_power_measurement(*this);
+}
+
+Expected<hailo_chip_temperature_info_t> Device::get_chip_temperature()
+{
+    hailo_chip_temperature_info_t res = {};
+    auto status = Control::get_chip_temperature(*this, &res);
+    CHECK_SUCCESS_AS_EXPECTED(status);
+    return res;
+}
+
+hailo_status Device::test_chip_memories()
+{
+    return Control::test_chip_memories(*this);
+}
+
+hailo_status Device::direct_write_memory(uint32_t address, const void *buffer, uint32_t size)
+{
+    (void) address;
+    (void) buffer;
+    (void) size;
+    return HAILO_NOT_IMPLEMENTED;
+}
+
+hailo_status Device::direct_read_memory(uint32_t address, void *buffer, uint32_t size)
+{
+    (void) address;
+    (void) buffer;
+    (void) size;
+    return HAILO_NOT_IMPLEMENTED;
+}
+
+Expected<hailo_device_identity_t> Device::identify()
+{
+    return Control::identify(*this);
+}
+
+Expected<hailo_core_information_t> Device::core_identify()
+{
+    hailo_core_information_t res = {};
+    auto status = Control::core_identify(*this, &res);
+    CHECK_SUCCESS_AS_EXPECTED(status);
+    return res;
+}
+
+Expected<hailo_extended_device_information_t> Device::get_extended_device_information()
+{
+    return Control::get_extended_device_information(*this);
+}
+
+// Note: This function needs to be called after each reset/fw_update if we want the device's
+//       state to remain valid after these ops (see HRT-3116)
+hailo_status Device::update_fw_state()
+{
+    // Assuming FW is loaded, send identify
+    auto board_info_expected = Control::identify(*this);
+    CHECK_EXPECTED_AS_STATUS(board_info_expected);
+    hailo_device_identity_t board_info = board_info_expected.release();
+
+    if ((FIRMWARE_VERSION_MAJOR == board_info.fw_version.major) &&
+         (FIRMWARE_VERSION_MINOR == board_info.fw_version.minor)) {
+        m_is_control_version_supported = true;
+    } else {
+        LOGGER__WARNING("Unsupported firmware operation. Host: {}.{}.{}, Device: {}.{}.{}{}",
+                FIRMWARE_VERSION_MAJOR,
+                FIRMWARE_VERSION_MINOR,
+                FIRMWARE_VERSION_REVISION,
+                board_info.fw_version.major,
+                board_info.fw_version.minor,
+                board_info.fw_version.revision, 
+                DEV_STRING_NOTE(board_info.is_release));
+        m_is_control_version_supported = false;
+    }
+    m_device_architecture = board_info.device_architecture;
+
+    return HAILO_SUCCESS;
+}
+
+hailo_status Device::fw_interact(uint8_t *request_buffer, size_t request_size,
+    uint8_t *response_buffer, size_t *response_size)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    CONTROL_PROTOCOL__request_t *request = (CONTROL_PROTOCOL__request_t *)(request_buffer);
+    uint32_t opcode = HAILO_CONTROL_OPCODE_COUNT;
+    ASSERT(NULL != request_buffer);
+    ASSERT(NULL != response_buffer);
+    hailo_cpu_id_t cpu_id;
+
+    opcode = BYTE_ORDER__ntohl(request->header.common_header.opcode);
+    /* Make sure that the version is supported or opcode is critical */
+    if (!m_is_control_version_supported && 
+            !g_CONTROL_PROTOCOL__is_critical[opcode]){
+        LOGGER__ERROR(
+                "Operation {} is not allowed when FW version in not supported. Host supported FW version is {}.{}.{}",
+                BYTE_ORDER__ntohl(request->header.common_header.opcode),
+                FIRMWARE_VERSION_MAJOR, FIRMWARE_VERSION_MINOR, FIRMWARE_VERSION_REVISION
+                );     
+        return HAILO_UNSUPPORTED_FW_VERSION;
+    }
+    /* Get the CPU ID */
+    cpu_id = (hailo_cpu_id_t)g_CONTROL_PROTOCOL__cpu_id[opcode];
+    
+    status = this->fw_interact_impl(request_buffer, request_size, response_buffer, response_size, cpu_id);
+
+    // Always increment sequence
+    this->increment_control_sequence();
+    // Check this->fw_interact_impl
+    CHECK_SUCCESS(status);
+
+    return HAILO_SUCCESS;
+}
+
+hailo_status Device::set_overcurrent_state(bool should_activate)
+{
+    return Control::set_overcurrent_state(*this, should_activate);
+}
+
+Expected<bool> Device::get_overcurrent_state()
+{
+    return Control::get_overcurrent_state(*this);
+}
+
+Expected<hailo_health_info_t> Device::get_health_information()
+{
+    return Control::get_health_information(*this);
+}
+
+Expected<std::vector<uint8_t>> Device::get_number_of_contexts_per_network_group()
+{
+    CONTROL_PROTOCOL__context_switch_main_header_t context_switch_main_header{};
+    const auto status = Control::get_context_switch_main_header(*this, &context_switch_main_header);
+    CHECK_SUCCESS_AS_EXPECTED(status);
+
+    uint32_t total_number_of_contexts = 0;
+    std::vector<uint8_t> number_of_contexts_per_network_group;
+    for (auto network_group_index = 0; network_group_index < context_switch_main_header.application_count; network_group_index++) {
+        // # of contexts in a network group = # of non preliminary contexts + 1 for the preliminary context
+        const uint32_t num_contexts = context_switch_main_header.application_header[network_group_index].dynamic_contexts_count + 1;
+        CHECK_AS_EXPECTED(IS_FIT_IN_UINT8(num_contexts), HAILO_INTERNAL_FAILURE, "num_contexts must fit in one byte");
+        number_of_contexts_per_network_group.emplace_back(static_cast<uint8_t>(num_contexts));
+        total_number_of_contexts += number_of_contexts_per_network_group.back();
+    }
+
+    // Total number of contexts need to fit in 1B - checking for overflow
+    CHECK_AS_EXPECTED(IS_FIT_IN_UINT8(total_number_of_contexts), HAILO_INTERNAL_FAILURE,
+        "Context indexes are expected to fit in 1B. actual size is {}", total_number_of_contexts);
+
+    return number_of_contexts_per_network_group;
+}
+
+Expected<Buffer> Device::download_context_action_list(uint8_t context_index, uint32_t *base_address,
+    uint32_t *batch_counter, uint16_t max_size)
+{
+    CHECK_ARG_NOT_NULL_AS_EXPECTED(base_address);
+    CHECK_ARG_NOT_NULL_AS_EXPECTED(batch_counter);
+
+    // Allocate room for an action list of at most max_size bytes
+    auto action_list = Buffer::create(max_size);
+    CHECK_EXPECTED(action_list);
+
+    uint32_t base_address_local = 0;
+    uint32_t batch_counter_local = 0;
+    uint16_t actual_size = 0;
+    const auto status = Control::download_context_action_list(*this, context_index, action_list->size(), 
+        &base_address_local, action_list->data(), &actual_size, &batch_counter_local);
+    CHECK_SUCCESS_AS_EXPECTED(status);
+    CHECK_AS_EXPECTED(actual_size <= max_size, HAILO_INTERNAL_FAILURE);
+
+    // Create a copy of the list, truncating to the needed size
+    auto final_action_list = Buffer::create(action_list->data(), actual_size);
+    CHECK_EXPECTED(action_list);
+
+    // Transfer ownership of out params
+    *base_address = base_address_local;
+    *batch_counter = batch_counter_local;
+
+    return final_action_list.release();
+}
+
+} /* namespace hailort */
diff --git a/hailort/libhailort/src/device_internal.cpp b/hailort/libhailort/src/device_internal.cpp
new file mode 100644 (file)
index 0000000..c1b1e37
--- /dev/null
@@ -0,0 +1,742 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file device_internal.cpp
+ * @brief Implementation of DeviceBase class
+ **/
+
+#include "device_internal.hpp"
+#include "hailo/hailort.h"
+#include "control.hpp"
+#include "sensor_config_utils.hpp"
+
+namespace hailort
+{
+
+DeviceBase::DeviceBase(Type type) :
+    Device::Device(type),
+    m_d2h_notification_queue(),
+    m_d2h_notification_thread(),
+    m_notif_fetch_thread_params(make_shared_nothrow<NotificationThreadSharedParams>()),
+    m_d2h_callbacks{{0,0}},
+    m_callbacks_lock()
+    // TODO: Handle m_notif_fetch_thread_params null pointer
+{
+#ifndef NDEBUG
+    LOGGER__WARNING("libhailort is running in \"debug\" mode. Overall performance might be affected!");
+#endif
+#ifdef HAILO_EMULATOR
+    LOGGER__WARNING("libhailort is running in \"Emulator\" mode.");
+#endif
+}
+
+DeviceBase::~DeviceBase()
+{
+    stop_d2h_notification_thread();
+}
+
+Expected<ConfiguredNetworkGroupVector> DeviceBase::configure(Hef &hef,
+    const NetworkGroupsParamsMap &configure_params)
+{
+    auto start_time = std::chrono::steady_clock::now();
+
+    auto status = check_hef_is_compatible(hef);
+    CHECK_SUCCESS_AS_EXPECTED(status);
+
+    auto context_switch_manager = get_config_manager();
+    CHECK_EXPECTED(context_switch_manager);
+
+    auto network_groups = context_switch_manager->get().add_hef(hef, configure_params);
+    CHECK_EXPECTED(network_groups);
+
+    auto elapsed_time_ms = std::chrono::duration<double, std::milli>(std::chrono::steady_clock::now() - start_time).count();
+    LOGGER__INFO("Configuring HEF took {} milliseconds", elapsed_time_ms);
+
+    return network_groups;
+}
+
+hailo_status DeviceBase::reset(hailo_reset_device_mode_t mode)
+{
+    CONTROL_PROTOCOL__reset_type_t reset_type = CONTROL_PROTOCOL__RESET_TYPE__COUNT; // set invalid value
+    switch(mode) {
+    case HAILO_RESET_DEVICE_MODE_CHIP:
+        reset_type = CONTROL_PROTOCOL__RESET_TYPE__CHIP;
+        break;
+    case HAILO_RESET_DEVICE_MODE_NN_CORE:
+        reset_type = CONTROL_PROTOCOL__RESET_TYPE__NN_CORE;
+        break;
+    case HAILO_RESET_DEVICE_MODE_SOFT:
+        reset_type = CONTROL_PROTOCOL__RESET_TYPE__SOFT;
+        break;
+    case HAILO_RESET_DEVICE_MODE_FORCED_SOFT:
+        reset_type = CONTROL_PROTOCOL__RESET_TYPE__FORCED_SOFT;
+        break; 
+    default:
+        return HAILO_INVALID_ARGUMENT;
+    }
+    return reset_impl(reset_type);
+}
+
+hailo_status DeviceBase::set_notification_callback(NotificationCallback func, hailo_notification_id_t notification_id, void *opaque)
+{
+    CHECK((0 <= notification_id) && (HAILO_NOTIFICATION_ID_COUNT > notification_id), HAILO_INVALID_ARGUMENT,
+        "Notification id value is invalid");
+    CHECK_ARG_NOT_NULL(func);
+
+    const std::lock_guard<std::mutex> lock(m_callbacks_lock);
+    m_d2h_callbacks[notification_id].func = func;
+    m_d2h_callbacks[notification_id].opaque = opaque;
+    return HAILO_SUCCESS;
+}
+
+hailo_status DeviceBase::remove_notification_callback(hailo_notification_id_t notification_id)
+{
+    CHECK((0 <= notification_id) && (HAILO_NOTIFICATION_ID_COUNT > notification_id), HAILO_INVALID_ARGUMENT,
+        "Notification id value is invalid");
+
+    const std::lock_guard<std::mutex> lock(m_callbacks_lock);
+    m_d2h_callbacks[notification_id].func = nullptr;
+    m_d2h_callbacks[notification_id].opaque = nullptr;
+
+    return HAILO_SUCCESS;
+}
+
+void DeviceBase::activate_notifications(const std::string &device_id)
+{
+    this->start_d2h_notification_thread(device_id);
+    this->start_notification_fetch_thread(&m_d2h_notification_queue);
+}
+
+hailo_status DeviceBase::stop_notification_fetch_thread()
+{
+    hailo_status status = HAILO_SUCCESS; // best effort
+    
+    if (m_notif_fetch_thread_params->is_running) {
+        m_notif_fetch_thread_params->is_running = false;
+        auto disable_status = this->disable_notifications();
+        if (HAILO_SUCCESS != disable_status) {
+            status = disable_status;
+            LOGGER__WARNING("Failed disabling notifications using ioctl command");
+        }
+    }
+
+    // join thread even if disable_notifications failed - so we don't have non-joined thread
+    if (m_notification_fetch_thread.joinable()) {
+        m_notification_fetch_thread.join();
+    }
+
+    return status;
+}
+
+void DeviceBase::start_notification_fetch_thread(D2hEventQueue *write_queue)
+{
+    m_notif_fetch_thread_params->write_queue = write_queue;
+    m_notif_fetch_thread_params->is_running = true;
+    m_notification_fetch_thread = std::thread(&DeviceBase::notification_fetch_thread, this, m_notif_fetch_thread_params);
+}
+
+void DeviceBase::notification_fetch_thread(std::shared_ptr<NotificationThreadSharedParams> params)
+{
+    while (params->is_running) {
+        auto expected_notification = this->read_notification();
+        if (HAILO_SUCCESS != expected_notification.status()) {
+            if (params->is_running) {
+                LOGGER__ERROR("Read notification failed with status={}", expected_notification.status());
+            }
+            break;
+        }
+        params->write_queue->push(expected_notification.release());
+    }
+}
+
+Expected<firmware_type_t> DeviceBase::get_fw_type()
+{
+    firmware_type_t firmware_type;
+    const auto architecture = get_architecture();
+    CHECK_EXPECTED(architecture);
+
+    if (architecture.value() == HAILO_ARCH_HAILO8_B0) {
+        firmware_type = FIRMWARE_TYPE_HAILO8;
+    }
+    else if (architecture.value() == HAILO_ARCH_MERCURY_CA || architecture.value() == HAILO_ARCH_MERCURY_VPU) {
+        firmware_type = FIRMWARE_TYPE_MERCURY;
+    }
+    else {
+        LOGGER__ERROR("Invalid device arcitecture. {}", architecture.value());
+        return make_unexpected(HAILO_INVALID_DEVICE_ARCHITECTURE);
+    }
+
+    return Expected<firmware_type_t>(firmware_type);
+}
+
+hailo_status DeviceBase::firmware_update(const MemoryView &firmware_binary, bool should_reset)
+{
+    HAILO_COMMON_STATUS_t fw_header_status = HAILO_COMMON_STATUS__UNINITIALIZED;
+    hailo_status status = HAILO_UNINITIALIZED;
+    firmware_version_t *current_fw_version = NULL;
+    firmware_version_t new_app_fw_version = {};
+    firmware_version_t new_core_fw_version = {};
+    uint32_t offset = 0;
+    uint32_t chunk_size = 0;
+    MD5_CTX md5_ctx = {};
+    MD5_SUM_t md5_sum = {};
+    firmware_header_t *new_app_firmware_header = NULL;
+    firmware_header_t *new_core_firmware_header = NULL;
+
+    MD5_Init(&md5_ctx);
+    MD5_Update(&md5_ctx, firmware_binary.data(), firmware_binary.size());
+    MD5_Final(md5_sum, &md5_ctx);
+
+    const auto firmware_type = get_fw_type();
+    CHECK_EXPECTED_AS_STATUS(firmware_type);
+
+    fw_header_status = FIRMWARE_HEADER_UTILS__validate_fw_headers((uintptr_t) firmware_binary.data(), static_cast<uint32_t>(firmware_binary.size()), false,
+        &new_app_firmware_header, &new_core_firmware_header, NULL, firmware_type.value());
+    CHECK(HAILO_COMMON_STATUS__SUCCESS == fw_header_status, HAILO_INVALID_FIRMWARE,
+        "FW update validation failed with status {}", fw_header_status);
+
+    // TODO: Are we ok with doing another identify here?
+    auto board_info_before_update_expected = Control::identify(*this);
+    CHECK_EXPECTED_AS_STATUS(board_info_before_update_expected);
+    hailo_device_identity_t board_info_before_update = board_info_before_update_expected.release();
+
+    if (board_info_before_update.device_architecture != HAILO_ARCH_HAILO8_A0) {
+        if ((new_app_firmware_header->firmware_major != new_core_firmware_header->firmware_major) ||
+            (new_app_firmware_header->firmware_minor != new_core_firmware_header->firmware_minor) ||
+            (GET_REVISION_NUMBER_VALUE(new_app_firmware_header->firmware_revision) != GET_REVISION_NUMBER_VALUE(new_core_firmware_header->firmware_revision))) {
+            LOGGER__ERROR("FW versions mismatch between APP and CORE firmwares.");
+            return HAILO_INVALID_FIRMWARE;
+        }
+    }
+
+    new_app_fw_version.firmware_major = new_app_firmware_header->firmware_major;
+    new_app_fw_version.firmware_minor = new_app_firmware_header->firmware_minor;
+    new_app_fw_version.firmware_revision = new_app_firmware_header->firmware_revision;
+
+    new_core_fw_version.firmware_major = new_core_firmware_header->firmware_major;
+    new_core_fw_version.firmware_minor = new_core_firmware_header->firmware_minor;
+    new_core_fw_version.firmware_revision = new_core_firmware_header->firmware_revision;
+
+    status = validate_fw_version_for_platform(board_info_before_update, new_app_fw_version, FW_BINARY_TYPE_APP_FIRMWARE);
+    CHECK_SUCCESS(status, "Invalid APP firmware binary was supplied");
+    status = validate_fw_version_for_platform(board_info_before_update, new_core_fw_version, FW_BINARY_TYPE_CORE_FIRMWARE);
+    CHECK_SUCCESS(status, "Invalid CORE firmware binary was supplied");
+    
+    // TODO: Fix cast, we are assuming they are the same (HRT-3177)
+    current_fw_version = reinterpret_cast<firmware_version_t*>(&(board_info_before_update.fw_version));
+
+    LOGGER__INFO("Current Version: {}.{}.{}{}. Updating to version: {}.{}.{}{}", current_fw_version->firmware_major,
+      current_fw_version->firmware_minor, current_fw_version->firmware_revision, 
+      DEV_STRING_NOTE(board_info_before_update.is_release),
+      new_app_fw_version.firmware_major, new_app_fw_version.firmware_minor,
+      GET_REVISION_NUMBER_VALUE(new_app_fw_version.firmware_revision), 
+      DEV_STRING_NOTE((!IS_REVISION_DEV(new_app_fw_version.firmware_revision))));
+
+
+    if (IS_REVISION_DEV(new_app_fw_version.firmware_revision)) {
+        LOGGER__INFO("New firmware version is a develop version, and may be unstable!");
+    }
+
+    if (FIRMWARE_HEADER_UTILS__is_binary_being_downgraded(current_fw_version, &new_app_fw_version)) {
+        LOGGER__INFO("Firmware is being downgraded.");
+    }
+
+    status = Control::start_firmware_update(*this);
+    CHECK_SUCCESS(status);
+    LOGGER__INFO("Update started.");
+
+    while (offset < firmware_binary.size()) {
+        chunk_size = MIN(WRITE_CHUNK_SIZE, (static_cast<uint32_t>(firmware_binary.size()) - offset));
+        LOGGER__DEBUG("Writing {} of data to offset {} / {}", chunk_size, offset, firmware_binary.size());
+        status = Control::write_firmware_update(*this, offset, firmware_binary.data() + offset, chunk_size);
+        CHECK_SUCCESS(status);
+        offset += chunk_size;
+    }
+    LOGGER__INFO("Finished writing.");
+
+    status = Control::validate_firmware_update(*this, &md5_sum, static_cast<uint32_t>(firmware_binary.size()));
+    CHECK_SUCCESS(status);
+
+    LOGGER__INFO("Firmware validation done.");
+
+    status = Control::finish_firmware_update(*this);
+    CHECK_SUCCESS(status);
+    LOGGER__INFO("Firmware update finished.");
+
+    if (should_reset) {
+        LOGGER__INFO("Resetting...");
+        status = reset(get_default_reset_mode());
+        CHECK(HAILO_COMMON_STATUS__SUCCESS == fw_header_status, HAILO_INVALID_FIRMWARE,
+            "FW update validation failed with status {}", fw_header_status);
+        CHECK((status == HAILO_SUCCESS) || (status == HAILO_UNSUPPORTED_CONTROL_PROTOCOL_VERSION), status);
+
+        auto board_info_after_install_expected = Control::identify(*this);
+        if (board_info_after_install_expected.status() == HAILO_UNSUPPORTED_CONTROL_PROTOCOL_VERSION) {
+            LOGGER__INFO("Successfully updated firmware. Protocol version has changed so firmware cannot be specified");
+            return HAILO_SUCCESS;
+        }
+
+        CHECK_EXPECTED_AS_STATUS(board_info_after_install_expected);
+        hailo_device_identity_t board_info_after_install = board_info_after_install_expected.release();
+    
+        LOGGER__INFO("New App FW version: {}.{}.{}{}", board_info_after_install.fw_version.major, board_info_after_install.fw_version.minor, 
+            board_info_after_install.fw_version.revision, DEV_STRING_NOTE(board_info_after_install.is_release));
+
+        // Validating that the new fw version is as expected
+        if ((board_info_after_install.fw_version.major != new_app_fw_version.firmware_major) ||
+            (board_info_after_install.fw_version.minor != new_app_fw_version.firmware_minor) ||
+            (GET_REVISION_NUMBER_VALUE(board_info_after_install.fw_version.revision) != GET_REVISION_NUMBER_VALUE(new_app_fw_version.firmware_revision))) {
+            LOGGER__WARNING("New App FW version is different than expected!");
+        }
+        
+        if (board_info_after_install.device_architecture != HAILO_ARCH_HAILO8_A0) {
+            hailo_core_information_t core_info_after_install{};
+            status = Control::core_identify(*this, &core_info_after_install);
+            CHECK_SUCCESS(status);
+            LOGGER__INFO("New Core FW version: {}.{}.{}{}", core_info_after_install.fw_version.major, core_info_after_install.fw_version.minor, 
+                core_info_after_install.fw_version.revision, DEV_STRING_NOTE(core_info_after_install.is_release));
+            if ((core_info_after_install.fw_version.major != new_app_fw_version.firmware_major) ||
+                (core_info_after_install.fw_version.minor != new_app_fw_version.firmware_minor) ||
+                (GET_REVISION_NUMBER_VALUE(core_info_after_install.fw_version.revision) != GET_REVISION_NUMBER_VALUE(new_app_fw_version.firmware_revision))) {
+                LOGGER__WARNING("New Core FW version is different than expected!");
+            }
+        }
+    }
+
+    return HAILO_SUCCESS;
+}
+
+hailo_status DeviceBase::second_stage_update(uint8_t* second_stage_binary, uint32_t second_stage_binary_length)
+{
+    HAILO_COMMON_STATUS_t second_stage_header_status = HAILO_COMMON_STATUS__UNINITIALIZED;
+    hailo_status status = HAILO_UNINITIALIZED;
+    firmware_version_t new_second_stage_version = {};
+    firmware_version_t minimum_second_stage_version = {1, 1, 0};
+    uint32_t offset = 0;
+    uint32_t chunk_size = 0;
+    MD5_CTX md5_ctx = {};
+    MD5_SUM_t md5_sum = {};
+    firmware_header_t *new_second_stage_header = NULL;
+
+    /* Validate arguments */
+    CHECK_ARG_NOT_NULL(second_stage_binary);
+
+    MD5_Init(&md5_ctx);
+    MD5_Update(&md5_ctx, second_stage_binary, second_stage_binary_length);
+    MD5_Final(md5_sum, &md5_ctx);
+
+    const auto firmware_type = get_fw_type();
+    CHECK_EXPECTED_AS_STATUS(firmware_type);
+
+    second_stage_header_status = FIRMWARE_HEADER_UTILS__validate_second_stage_headers((uintptr_t) second_stage_binary,
+        second_stage_binary_length, &new_second_stage_header, firmware_type.value());
+    CHECK(HAILO_COMMON_STATUS__SUCCESS == second_stage_header_status, HAILO_INVALID_SECOND_STAGE,
+            "Second stage update validation failed with status {}", second_stage_header_status);
+
+    new_second_stage_version.firmware_major = new_second_stage_header->firmware_major;
+    new_second_stage_version.firmware_minor = new_second_stage_header->firmware_minor;
+    new_second_stage_version.firmware_revision = new_second_stage_header->firmware_revision;
+
+    status = validate_binary_version_for_platform(&new_second_stage_version,
+                                                           &minimum_second_stage_version,
+                                                           FW_BINARY_TYPE_SECOND_STAGE_BOOT);
+    CHECK_SUCCESS(status);
+
+    LOGGER__INFO("Updating to version: {}.{}.{}",
+      new_second_stage_version.firmware_major, new_second_stage_version.firmware_minor,
+      GET_REVISION_NUMBER_VALUE(new_second_stage_version.firmware_revision));
+
+    LOGGER__INFO("Writing second stage to internal memory");
+    while (offset < second_stage_binary_length) {
+        chunk_size = MIN(WRITE_CHUNK_SIZE, (second_stage_binary_length - offset));
+        LOGGER__INFO("Writing {} of data to offset {} / {}", chunk_size, offset, second_stage_binary_length);
+        status = Control::write_second_stage_to_internal_memory(*this, offset, second_stage_binary + offset, chunk_size);
+        CHECK_SUCCESS(status);
+        offset += chunk_size;
+    }
+    status = Control::copy_second_stage_to_flash(*this, &md5_sum, second_stage_binary_length);
+    if (HAILO_SUCCESS != status) {
+        LOGGER__CRITICAL("Second stage failed in a critical stage, Please contact Hailo support and DO NOT power off the device");
+    }
+    CHECK_SUCCESS(status);
+
+    LOGGER__INFO("Finished copying second stage to flash.");
+
+    return HAILO_SUCCESS;
+}
+
+hailo_status DeviceBase::store_sensor_config(uint32_t section_index, hailo_sensor_types_t sensor_type,
+    uint32_t reset_config_size, uint16_t config_height, uint16_t config_width, uint16_t config_fps,
+    const std::string &config_file_path, const std::string &config_name)
+{    
+    CHECK((section_index <= MAX_NON_ISP_SECTIONS), HAILO_INVALID_ARGUMENT, 
+        "Cannot store sensor config in invalid section {}. Please choose section index (0-{}).", section_index, MAX_NON_ISP_SECTIONS);
+    CHECK(sensor_type != HAILO_SENSOR_TYPES_HAILO8_ISP, HAILO_INVALID_ARGUMENT,
+        "store_sensor_config intended only for sensor config, for ISP config use store_isp");
+
+    auto control_buffers = SensorConfigUtils::read_config_file(config_file_path);
+    CHECK_EXPECTED_AS_STATUS(control_buffers, "Failed reading config file");
+
+    return store_sensor_control_buffers(control_buffers.value(), section_index, sensor_type,
+        reset_config_size, config_height, config_width, config_fps, config_name);
+}
+
+hailo_status DeviceBase::store_isp_config(uint32_t reset_config_size, uint16_t config_height, uint16_t config_width, uint16_t config_fps, 
+    const std::string &isp_static_config_file_path, const std::string &isp_runtime_config_file_path, const std::string &config_name)
+{    
+    auto control_buffers = SensorConfigUtils::read_isp_config_file(isp_static_config_file_path, isp_runtime_config_file_path);
+    CHECK_EXPECTED_AS_STATUS(control_buffers, "Failed reading ISP config file");
+
+    return store_sensor_control_buffers(control_buffers.value(), SENSOR_CONFIG__ISP_SECTION_INDEX, HAILO_SENSOR_TYPES_HAILO8_ISP,
+        reset_config_size, config_height, config_width, config_fps, config_name);
+
+}
+
+Expected<Buffer> DeviceBase::sensor_get_sections_info()
+{
+    auto buffer = Buffer::create(SENSOR_SECTIONS_INFO_SIZE);
+    CHECK_EXPECTED(buffer);
+
+    hailo_status status = Control::sensor_get_sections_info(*this, buffer->data());
+    CHECK_SUCCESS_AS_EXPECTED(status);
+
+    return buffer;
+}
+
+hailo_status DeviceBase::sensor_dump_config(uint32_t section_index, const std::string &config_file_path)
+{
+    CHECK(SENSOR_CONFIG__TOTAL_SECTIONS_BLOCK_COUNT > section_index, HAILO_INVALID_ARGUMENT, "Section {} is invalid. Section index must be in the range [0 - {}]", section_index, (SENSOR_CONFIG__TOTAL_SECTIONS_BLOCK_COUNT - 1));
+    auto sections_info_buffer = sensor_get_sections_info();
+    CHECK_EXPECTED_AS_STATUS(sections_info_buffer);
+
+    SENSOR_CONFIG__section_info_t *section_info_ptr = &((SENSOR_CONFIG__section_info_t *)sections_info_buffer->data())[section_index];
+    CHECK(section_info_ptr->is_free == 0, HAILO_NOT_FOUND, "Section {} is not active", section_index);
+    CHECK(0 == (section_info_ptr->config_size % sizeof(SENSOR_CONFIG__operation_cfg_t)), HAILO_INVALID_OPERATION, "Section config size is invalid.");
+
+    /* Read config data from device */
+    auto operation_cfg = Buffer::create(section_info_ptr->config_size);
+    CHECK_EXPECTED_AS_STATUS(operation_cfg);
+
+    size_t read_full_buffer_count = (section_info_ptr->config_size / MAX_CONFIG_ENTRIES_DATA_SIZE);
+    uint32_t residue_to_read = static_cast<uint32_t>(section_info_ptr->config_size - (read_full_buffer_count * MAX_CONFIG_ENTRIES_DATA_SIZE)); 
+    uint32_t entries_count = (section_info_ptr->config_size / static_cast<uint32_t>(sizeof(SENSOR_CONFIG__operation_cfg_t)));
+    uint32_t offset = 0;
+
+    hailo_status status = HAILO_UNINITIALIZED;
+    for (uint32_t i = 0; i < read_full_buffer_count; i++) {
+        status = Control::sensor_get_config(*this, section_index, offset, (uint32_t)MAX_CONFIG_ENTRIES_DATA_SIZE, (operation_cfg->data() + offset));
+        CHECK_SUCCESS(status);
+        offset += static_cast<uint32_t>(MAX_CONFIG_ENTRIES_DATA_SIZE);
+    }
+    if (0 < residue_to_read) {
+        status = Control::sensor_get_config(*this, section_index, offset, residue_to_read, (operation_cfg->data() + offset));
+        CHECK_SUCCESS(status);
+    }
+
+    status = SensorConfigUtils::dump_config_to_csv((SENSOR_CONFIG__operation_cfg_t*)operation_cfg->data(), config_file_path, entries_count);
+    CHECK_SUCCESS(status);
+
+    return HAILO_SUCCESS; 
+}
+
+hailo_status DeviceBase::sensor_set_i2c_bus_index(hailo_sensor_types_t sensor_type, uint32_t bus_index)
+{
+    return Control::sensor_set_i2c_bus_index(*this, sensor_type, bus_index);
+}
+
+hailo_status DeviceBase::sensor_load_and_start_config(uint32_t section_index)
+{
+    CHECK((section_index <= MAX_NON_ISP_SECTIONS), HAILO_INVALID_ARGUMENT, 
+        "Cannot load config from invalid section index {}. Please choose section index (0-{}).",
+        section_index, MAX_NON_ISP_SECTIONS);
+    return Control::sensor_load_and_start_config(*this, section_index);
+}
+
+hailo_status DeviceBase::sensor_reset(uint32_t section_index)
+{
+    CHECK((section_index <= MAX_NON_ISP_SECTIONS), HAILO_INVALID_ARGUMENT, 
+        "Cannot reset sensor in invalid section index {}. Please choose section index (0-{}).",
+            section_index, MAX_NON_ISP_SECTIONS);
+    return Control::sensor_reset(*this, section_index);
+}
+
+hailo_status DeviceBase::sensor_set_generic_i2c_slave(uint16_t slave_address, uint8_t offset_size, uint8_t bus_index,
+    uint8_t should_hold_bus, uint8_t slave_endianness)
+{
+    return Control::sensor_set_generic_i2c_slave(*this, slave_address, offset_size, bus_index, should_hold_bus, slave_endianness);
+}
+
+Expected<Buffer> DeviceBase::read_board_config()
+{
+    auto result = Buffer::create(BOARD_CONFIG_SIZE, 0);
+    CHECK_EXPECTED(result);
+
+    auto status = Control::read_board_config(*this, result->data(), static_cast<uint32_t>(result->size()));
+    CHECK_SUCCESS_AS_EXPECTED(status);
+
+    return result.release();
+}
+
+hailo_status DeviceBase::write_board_config(const MemoryView &buffer)
+{
+    return Control::write_board_config(*this, buffer.data(), static_cast<uint32_t>(buffer.size()));
+}
+
+Expected<hailo_fw_user_config_information_t> DeviceBase::examine_user_config()
+{
+    hailo_fw_user_config_information_t result{};
+    auto status = Control::examine_user_config(*this, &result);
+    CHECK_SUCCESS_AS_EXPECTED(status);
+
+    return result;
+}
+
+Expected<Buffer> DeviceBase::read_user_config()
+{
+    auto user_config_info = examine_user_config();
+    CHECK_EXPECTED(user_config_info, "Failed to examine user config");
+
+    auto result = Buffer::create(user_config_info->total_size, 0);
+    CHECK_EXPECTED(result);
+
+    auto status = Control::read_user_config(*this, result->data(), static_cast<uint32_t>(result->size()));
+    CHECK_SUCCESS_AS_EXPECTED(status);
+
+    return result.release();
+}
+
+hailo_status DeviceBase::write_user_config(const MemoryView &buffer)
+{
+    return Control::write_user_config(*this, buffer.data(), static_cast<uint32_t>(buffer.size()));
+}
+
+hailo_status DeviceBase::erase_user_config()
+{
+    return Control::erase_user_config(*this);
+}
+
+void DeviceBase::start_d2h_notification_thread(const std::string &device_id)
+{
+    m_d2h_notification_thread = std::thread([this, device_id]() { this->d2h_notification_thread_main(device_id); });
+}
+
+void DeviceBase::stop_d2h_notification_thread()
+{
+    static const D2H_EVENT_MESSAGE_t TERMINATE {{0, 0, 0, 0, TERMINATE_EVENT_ID, 0, 0}, {}};
+    m_d2h_notification_queue.clear();
+    if (m_d2h_notification_thread.joinable()) {
+        m_d2h_notification_queue.push(TERMINATE);
+        m_d2h_notification_thread.join();
+    }
+}
+
+void DeviceBase::d2h_notification_thread_main(const std::string &device_id)
+{
+    while (true) {
+        auto notification = m_d2h_notification_queue.pop();
+        if (notification.header.event_id == TERMINATE_EVENT_ID) {
+            LOGGER__DEBUG("[{}] D2H notification thread got terminate signal, returning..", device_id);
+            return;
+        }
+        /* Parse and print the Event info */
+        auto d2h_status = D2H_EVENTS__parse_event(&notification);
+        if (HAILO_COMMON_STATUS__SUCCESS != d2h_status) {
+            LOGGER__ERROR("[{}] Fail to Parse firmware notification {} status is {}", device_id, notification.header.event_id, d2h_status);
+            continue;
+        }
+
+        hailo_notification_t callback_notification;
+        uint32_t notification_fw_id = notification.header.event_id;
+        hailo_notification_id_t hailo_notification_id;
+        hailo_status status = fw_notification_id_to_hailo((D2H_EVENT_ID_t)notification_fw_id, &hailo_notification_id);
+        if (HAILO_SUCCESS != status) {
+            LOGGER__ERROR("[{}] Got invalid notification id from fw: {}", device_id, notification_fw_id);
+            continue;
+        }
+
+        LOGGER__INFO("[{}] Got notification from fw with id: {}", device_id, hailo_notification_id);
+
+        NotificationCallback callback_func = nullptr;
+        void *callback_opaque = nullptr;
+        {
+            const std::lock_guard<std::mutex> lock(m_callbacks_lock);
+            callback_func = m_d2h_callbacks[hailo_notification_id].func;
+            callback_opaque = m_d2h_callbacks[hailo_notification_id].opaque;
+            // m_callbacks_lock is freed here because user can call to a function in the callback that will
+            // try to acquire it as well - resulting in a dead lock. I did not used recursive_mutex
+            // because of the overhead
+        }
+
+        if (nullptr != callback_func) {
+            callback_notification.id = hailo_notification_id;
+            callback_notification.sequence = notification.header.sequence;
+            static_assert(sizeof(callback_notification.body) == sizeof(notification.message_parameters), "D2H notification size mismatch");
+            memcpy(&callback_notification.body, &notification.message_parameters, sizeof(notification.message_parameters));
+            callback_func(*this, callback_notification, callback_opaque);
+        }
+    }
+}
+
+hailo_status DeviceBase::check_hef_is_compatible(Hef &hef)
+{    
+    const auto device_arch = get_architecture();
+    CHECK_EXPECTED_AS_STATUS(device_arch, "Can't get device architecture (is the FW loaded?)");
+
+    if (!is_hef_compatible(device_arch.value(), hef.pimpl->get_device_arch())) {
+        // TODO: print here the actual device_arch as a string
+        LOGGER__ERROR("HEF format is not compatible with device. Device arch: {}, HEF arch: {}",
+            device_arch.value(), hef.pimpl->get_device_arch());
+        return HAILO_INVALID_HEF;
+    }
+
+    // TODO: MSW-227 check clock rate for mercury as well.
+    if (HAILO_ARCH_HAILO8_B0 == device_arch.value()) {
+        auto extended_device_info_expected = Control::get_extended_device_information(*this);
+        CHECK_EXPECTED_AS_STATUS(extended_device_info_expected,  "Can't get device extended info");
+        hailo_extended_device_information_t extended_device_information = extended_device_info_expected.release();
+        check_clock_rate_for_hailo8(extended_device_information.neural_network_core_clock_rate,
+            hef.pimpl->get_device_arch());
+    }
+
+    return HAILO_SUCCESS;
+}
+
+hailo_status DeviceBase::fw_notification_id_to_hailo(D2H_EVENT_ID_t fw_notification_id,
+    hailo_notification_id_t* hailo_notification_id)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+
+    switch (fw_notification_id) {
+        case ETHERNET_SERVICE_RX_ERROR_EVENT_ID:
+            *hailo_notification_id = HAILO_NOTIFICATION_ID_ETHERNET_RX_ERROR;
+            break;
+        case D2H_HOST_INFO_EVENT_ID:
+            *hailo_notification_id = HAILO_NOTIFICATION_ID_DEBUG;
+            break;
+        case HEALTH_MONITOR_TEMPERATURE_ALARM_D2H_EVENT_ID:
+            *hailo_notification_id = HAILO_NOTIFICATION_ID_HEALTH_MONITOR_TEMPERATURE_ALARM;
+            break;
+        case HEALTH_MONITOR_CLOSED_STREAMS_D2H_EVENT_ID:
+            *hailo_notification_id = HAILO_NOTIFICATION_ID_HEALTH_MONITOR_DATAFLOW_SHUTDOWN;
+            break;
+        case HEALTH_MONITOR_OVERCURRENT_PROTECTION_ALERT_EVENT_ID:
+            *hailo_notification_id = HAILO_NOTIFICATION_ID_HEALTH_MONITOR_OVERCURRENT_ALARM;
+            break;
+        case HEALTH_MONITOR_LCU_ECC_CORRECTABLE_EVENT_ID:
+            *hailo_notification_id = HAILO_NOTIFICATION_ID_LCU_ECC_CORRECTABLE_ERROR;
+            break;
+        case HEALTH_MONITOR_LCU_ECC_UNCORRECTABLE_EVENT_ID:
+            *hailo_notification_id = HAILO_NOTIFICATION_ID_LCU_ECC_UNCORRECTABLE_ERROR;
+            break;
+        case HEALTH_MONITOR_CPU_ECC_ERROR_EVENT_ID:
+            *hailo_notification_id = HAILO_NOTIFICATION_ID_CPU_ECC_ERROR;
+            break;
+        case HEALTH_MONITOR_CPU_ECC_FATAL_EVENT_ID:
+            *hailo_notification_id = HAILO_NOTIFICATION_ID_CPU_ECC_FATAL;
+            break;
+        case CONTEXT_SWITCH_BREAKPOINT_REACHED:
+            *hailo_notification_id = HAILO_NOTIFICATION_ID_CONTEXT_SWITCH_BREAKPOINT_REACHED;
+            break;
+        case HEALTH_MONITOR_CLOCK_CHANGED_EVENT_ID:
+            *hailo_notification_id = HAILO_NOTIFICATION_ID_HEALTH_MONITOR_CLOCK_CHANGED_EVENT;
+            break;
+        default:
+            status = HAILO_INVALID_ARGUMENT;
+            goto l_exit;
+    }
+
+    status = HAILO_SUCCESS;
+l_exit:
+    return status;
+}
+
+hailo_status DeviceBase::validate_binary_version_for_platform(firmware_version_t *new_binary_version, 
+    firmware_version_t *min_supported_binary_version, FW_BINARY_TYPE_t fw_binary_type)
+{
+    HAILO_COMMON_STATUS_t binary_status = FIRMWARE_HEADER_UTILS__validate_binary_version(new_binary_version, min_supported_binary_version,
+                                                                                         fw_binary_type);
+    CHECK(HAILO_COMMON_STATUS__SUCCESS == binary_status, HAILO_INVALID_FIRMWARE,
+                    "FW binary version validation failed with status {}", binary_status);
+    return HAILO_SUCCESS;
+}
+
+hailo_status DeviceBase::validate_fw_version_for_platform(const hailo_device_identity_t &board_info, firmware_version_t fw_version, FW_BINARY_TYPE_t fw_binary_type)
+{
+    firmware_version_t min_supported_fw_version = {0, 0, 0};
+    const firmware_version_t evb_mdot2_min_version = {2, 1, 0}; 
+    const firmware_version_t mpcie_min_version = {2, 2, 0};
+    
+    if (0 == strncmp(EVB_PART_NUMBER_PREFIX, board_info.part_number, PART_NUMBER_PREFIX_LENGTH) || 
+        0 == strncmp(MDOT2_PART_NUMBER_PREFIX, board_info.part_number, PART_NUMBER_PREFIX_LENGTH)) {
+        min_supported_fw_version = evb_mdot2_min_version;
+    }
+
+    else if (0 == strncmp(MPCIE_PART_NUMBER_PREFIX, board_info.part_number, PART_NUMBER_PREFIX_LENGTH)) {
+        min_supported_fw_version = mpcie_min_version;
+    }
+    else {
+        min_supported_fw_version = evb_mdot2_min_version;
+    }
+
+    return validate_binary_version_for_platform(&fw_version, &min_supported_fw_version, fw_binary_type);
+}
+
+bool DeviceBase::is_hef_compatible(hailo_device_architecture_t device_arch, ProtoHEFHwArch hef_arch)
+{
+    switch (device_arch) {
+    case HAILO_ARCH_HAILO8_B0:
+        return (hef_arch == PROTO__HW_ARCH__HAILO8P) || (hef_arch == PROTO__HW_ARCH__HAILO8R);
+    case HAILO_ARCH_MERCURY_CA:
+    case HAILO_ARCH_MERCURY_VPU:
+        return (hef_arch == PROTO__HW_ARCH__MERCURY) || (hef_arch == PROTO__HW_ARCH__GINGER) ||
+               (hef_arch == PROTO__HW_ARCH__LAVENDER);
+    default:
+        return false;
+    }
+}
+
+void DeviceBase::check_clock_rate_for_hailo8(uint32_t clock_rate, ProtoHEFHwArch hef_hw_arch)
+{
+    uint32_t expected_clock_rate = (hef_hw_arch == ProtoHEFHwArch::PROTO__HW_ARCH__HAILO8P) ? HAILO8_CLOCK_RATE : HAILO8R_CLOCK_RATE;
+    if (expected_clock_rate != clock_rate) {
+        LOGGER__WARNING(
+            "HEF was compiled assuming clock rate of {} MHz, while the device clock rate is {} MHz. " \
+            "FPS calculations might not be accurate.",
+            (expected_clock_rate / CLOCKS_IN_MHZ),
+            (clock_rate / CLOCKS_IN_MHZ));
+    }
+}
+
+hailo_status DeviceBase::store_sensor_control_buffers(const std::vector<SENSOR_CONFIG__operation_cfg_t> &control_buffers, uint32_t section_index, hailo_sensor_types_t sensor_type,
+    uint32_t reset_config_size, uint16_t config_height, uint16_t config_width, uint16_t config_fps, const std::string &config_name)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+
+    uint32_t total_data_size = static_cast<uint32_t>(control_buffers.size() * sizeof(control_buffers[0]));
+    size_t config_info_full_buffer = control_buffers.size() / MAX_CONFIG_INFO_ENTRIES;
+    uint32_t is_first = 1;
+    uint32_t offset = 0;
+
+    for(uint32_t i = 0; i < config_info_full_buffer; i++) {
+        status = Control::sensor_store_config(*this, is_first, section_index, offset, reset_config_size, sensor_type, total_data_size,
+            (uint8_t*)control_buffers.data() + offset, (uint32_t)MAX_CONFIG_ENTRIES_DATA_SIZE, 
+            config_height, config_width, config_fps, static_cast<uint32_t>(config_name.size()), (uint8_t*)config_name.c_str());
+        CHECK_SUCCESS(status, "Failed to store sensor config");
+        
+        offset += (uint32_t)MAX_CONFIG_ENTRIES_DATA_SIZE;
+        is_first = 0;
+    }
+
+    if (offset < total_data_size) {
+        status = Control::sensor_store_config(*this, is_first, section_index, offset, reset_config_size, sensor_type, total_data_size,
+            (uint8_t*)control_buffers.data() + offset, total_data_size - offset, 
+            config_height, config_width, config_fps, static_cast<uint32_t>(config_name.size()), (uint8_t*)config_name.c_str());
+        CHECK_SUCCESS(status,"Failed to store sensor config");
+    }
+
+    return HAILO_SUCCESS;
+}
+
+} /* namespace hailort */
diff --git a/hailort/libhailort/src/device_internal.hpp b/hailort/libhailort/src/device_internal.hpp
new file mode 100644 (file)
index 0000000..cc0a7d5
--- /dev/null
@@ -0,0 +1,135 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file device_internal.hpp
+ * @brief Class declaration for DeviceBase that implements the basic Device "interface" (not technically
+ *        an interface, but good enough). All internal devices should inherit from the DeviceBase class.
+ *        Hence, the hierarchy is as follows:
+ *
+ * Device                       (External "interface")
+ * └── BaseDevice               (Base classes)
+ *     ├── VdmaDevice
+ *     │   ├── PcieDevice
+ *     │   └── CoreDevice
+ *     └── EthernetDevice
+ **/
+
+#ifndef _HAILO_DEVICE_INTERNAL_HPP_
+#define _HAILO_DEVICE_INTERNAL_HPP_
+
+#include "hailo/device.hpp"
+#include "hailo/hailort.h"
+#include "d2h_event_queue.hpp"
+#include "hef_internal.hpp"
+#include "firmware_header.h"
+#include "firmware_header_utils.h"
+#include "control_protocol.h"
+#include "context_switch/config_manager.hpp"
+
+#include <thread>
+
+namespace hailort
+{
+
+#define EVB_PART_NUMBER_PREFIX ("HEV18B1C4GA")
+#define MDOT2_PART_NUMBER_PREFIX ("HM218B1C2FA")
+#define MPCIE_PART_NUMBER_PREFIX ("HMP1RB1C2GA")
+
+// Will be used to perfrom generic validation for all variations of a specific module
+#define PART_NUMBER_PREFIX_LENGTH (11)
+
+#define CLOCKS_IN_MHZ (1000 * 1000)
+
+class DeviceBase : public Device
+{
+public:
+    DeviceBase(Type type);
+    DeviceBase(DeviceBase &&) = delete;
+    DeviceBase(const DeviceBase &) = delete;
+    DeviceBase &operator=(DeviceBase &&) = delete;
+    DeviceBase &operator=(const DeviceBase &) = delete;
+    virtual ~DeviceBase();
+
+    virtual Expected<ConfiguredNetworkGroupVector> configure(Hef &hef,
+        const NetworkGroupsParamsMap &configure_params={}) override;
+    virtual hailo_status reset(hailo_reset_device_mode_t mode) override;
+    virtual hailo_status set_notification_callback(NotificationCallback func, hailo_notification_id_t notification_id, void *opaque) override;
+    virtual hailo_status remove_notification_callback(hailo_notification_id_t notification_id) override;
+    virtual void activate_notifications(const std::string &device_id);
+    virtual void start_notification_fetch_thread(D2hEventQueue *write_queue);
+    virtual hailo_status stop_notification_fetch_thread();
+    virtual hailo_status firmware_update(const MemoryView &firmware_binary, bool should_reset) override;
+    virtual hailo_status second_stage_update(uint8_t *second_stage_binary, uint32_t second_stage_binary_length) override;
+    virtual hailo_status store_sensor_config(uint32_t section_index, hailo_sensor_types_t sensor_type,
+        uint32_t reset_config_size, uint16_t config_height, uint16_t config_width, uint16_t config_fps,
+        const std::string &config_file_path, const std::string &config_name) override;
+    virtual hailo_status store_isp_config(uint32_t reset_config_size, uint16_t config_height, uint16_t config_width, uint16_t config_fps,
+        const std::string &isp_static_config_file_path, const std::string &isp_runtime_config_file_path, const std::string &config_name) override;
+    virtual Expected<Buffer> sensor_get_sections_info() override;
+    virtual hailo_status sensor_dump_config(uint32_t section_index, const std::string &config_file_path) override;
+    virtual hailo_status sensor_set_i2c_bus_index(hailo_sensor_types_t sensor_type, uint32_t bus_index) override;
+    virtual hailo_status sensor_load_and_start_config(uint32_t section_index) override;
+    virtual hailo_status sensor_reset(uint32_t section_index) override;
+    virtual hailo_status sensor_set_generic_i2c_slave(uint16_t slave_address, uint8_t offset_size, uint8_t bus_index,
+        uint8_t should_hold_bus, uint8_t slave_endianness) override;
+    virtual Expected<Buffer> read_board_config() override;
+    virtual hailo_status write_board_config(const MemoryView &buffer) override;
+    virtual Expected<hailo_fw_user_config_information_t> examine_user_config() override;
+    virtual Expected<Buffer> read_user_config() override;
+    virtual hailo_status write_user_config(const MemoryView &buffer) override;
+    virtual hailo_status erase_user_config() override;
+
+protected:
+    struct NotificationThreadSharedParams {
+        NotificationThreadSharedParams() : is_running(false) {}
+        D2hEventQueue *write_queue;
+        bool is_running;
+    };
+
+    // Special value to signal the d2h notification thread to terminate
+    static const uint32_t TERMINATE_EVENT_ID = std::numeric_limits<uint32_t>::max();
+    
+    virtual hailo_reset_device_mode_t get_default_reset_mode() = 0;
+    virtual hailo_status reset_impl(CONTROL_PROTOCOL__reset_type_t reset_type) = 0;
+    virtual Expected<D2H_EVENT_MESSAGE_t> read_notification() = 0;
+    virtual hailo_status disable_notifications() = 0;
+    void start_d2h_notification_thread(const std::string &device_id);
+    void stop_d2h_notification_thread();
+    void d2h_notification_thread_main(const std::string &device_id);
+    hailo_status check_hef_is_compatible(Hef &hef);
+
+    virtual ExpectedRef<ConfigManager> get_config_manager() = 0;
+    
+    D2hEventQueue m_d2h_notification_queue;
+    std::thread m_d2h_notification_thread;
+    std::thread m_notification_fetch_thread;
+    std::shared_ptr<NotificationThreadSharedParams> m_notif_fetch_thread_params;
+
+private:
+    static hailo_status fw_notification_id_to_hailo(D2H_EVENT_ID_t fw_notification_id,
+        hailo_notification_id_t* hailo_notification_id);
+    static hailo_status validate_binary_version_for_platform(firmware_version_t *new_binary_version, 
+        firmware_version_t *min_supported_binary_version, FW_BINARY_TYPE_t fw_binary_type);
+    static hailo_status validate_fw_version_for_platform(const hailo_device_identity_t &board_info,
+        firmware_version_t fw_version, FW_BINARY_TYPE_t fw_binary_type);
+    static bool is_hef_compatible(hailo_device_architecture_t device_arch, ProtoHEFHwArch hw_arch);
+    static void check_clock_rate_for_hailo8(uint32_t clock_rate, ProtoHEFHwArch hef_hw_arch);
+    hailo_status store_sensor_control_buffers(const std::vector<SENSOR_CONFIG__operation_cfg_t> &control_buffers, uint32_t section_index, hailo_sensor_types_t sensor_type,
+        uint32_t reset_config_size, uint16_t config_height, uint16_t config_width, uint16_t config_fps, const std::string &config_name);
+    virtual void notification_fetch_thread(std::shared_ptr<NotificationThreadSharedParams> params);
+    Expected<firmware_type_t> get_fw_type();
+
+    typedef struct {
+        NotificationCallback func;
+        void *opaque;
+    } d2h_notification_callback_t;
+
+    d2h_notification_callback_t m_d2h_callbacks[HAILO_NOTIFICATION_ID_COUNT];
+    std::mutex m_callbacks_lock;
+};
+
+} /* namespace hailort */
+
+#endif /* _HAILO_DEVICE_INTERNAL_HPP_ */
diff --git a/hailort/libhailort/src/eth_device.cpp b/hailort/libhailort/src/eth_device.cpp
new file mode 100644 (file)
index 0000000..441d066
--- /dev/null
@@ -0,0 +1,386 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file eth_device.cpp
+ * @brief TODO: brief
+ *
+ * TODO: doc
+ **/
+
+#include "eth_device.hpp"
+#include "hailo/hailort.h"
+#include "common/utils.hpp"
+#include "hailo/device.hpp"
+#include "control.hpp"
+#include "udp.hpp"
+#include "common/ethernet_utils.hpp"
+#include "hailo/hef.hpp"
+
+#include <stdlib.h>
+#include <errno.h>
+#include <new>
+#include <array>
+
+namespace hailort
+{
+
+#define SCAN_SEQUENCE (0)
+#define WAIT_FOR_DEVICE_WAKEUP_MAX_ATTEMPTS (10)
+#define WAIT_FOR_DEVICE_WAKEUP_TIMEOUT (1000)
+#define ETH_BROADCAST_IP ("255.255.255.255")
+
+
+hailo_status EthernetDevice::fw_interact_impl(uint8_t *request_buffer, size_t request_size,
+    uint8_t *response_buffer, size_t *response_size, hailo_cpu_id_t cpu_id)
+{   
+    /* CPU id is used only in PCIe, for Eth all control goes to APP CPU.*/
+    (void)cpu_id;
+    return m_control_udp.fw_interact(request_buffer, request_size, response_buffer, response_size, m_control_sequence);
+}
+
+hailo_status EthernetDevice::wait_for_wakeup()
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    HAILO_COMMON_STATUS_t common_status = HAILO_COMMON_STATUS__UNINITIALIZED;
+    CONTROL_PROTOCOL__request_t request = {};
+    size_t request_size = 0;
+    uint8_t response_buffer[RESPONSE_MAX_BUFFER_SIZE] = {};
+    size_t response_size = RESPONSE_MAX_BUFFER_SIZE;
+    CONTROL_PROTOCOL__response_header_t *header = NULL;
+    CONTROL_PROTOCOL__payload_t *payload = NULL;
+    
+    /* Create udp socket */
+    auto udp = Udp::create(m_device_info.device_address.sin_addr, m_device_info.device_address.sin_port,
+                       m_device_info.host_address.sin_addr, m_device_info.host_address.sin_port);
+    CHECK_EXPECTED_AS_STATUS(udp);
+
+    status = udp->set_timeout(std::chrono::milliseconds(WAIT_FOR_DEVICE_WAKEUP_TIMEOUT));
+    CHECK_SUCCESS(status);
+
+    status = udp->set_max_number_of_attempts(WAIT_FOR_DEVICE_WAKEUP_MAX_ATTEMPTS);
+    CHECK_SUCCESS(status);
+
+    /* Create and send identify-control until it runs successfully */
+    common_status = CONTROL_PROTOCOL__pack_identify_request(&request, &request_size, m_control_sequence);
+    status = (HAILO_COMMON_STATUS__SUCCESS == common_status) ? HAILO_SUCCESS : HAILO_INTERNAL_FAILURE;
+    CHECK_SUCCESS(status);
+    
+    status = udp->fw_interact((uint8_t*)(&request), request_size, (uint8_t*)&response_buffer, &response_size,
+        m_control_sequence);
+
+    // Always increment sequence
+    m_control_sequence = (m_control_sequence + 1) % CONTROL__MAX_SEQUENCE;
+    CHECK_SUCCESS(status);
+
+    /* Parse and validate the response */
+    return Control::parse_and_validate_response(response_buffer, (uint32_t)(response_size), &header, &payload, &request);
+}
+
+Expected<std::unique_ptr<EthernetDevice>> EthernetDevice::create(const hailo_eth_device_info_t &device_info)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+
+    // Creates control socket
+    auto udp = Udp::create(device_info.device_address.sin_addr, device_info.device_address.sin_port,
+                       device_info.host_address.sin_addr, device_info.host_address.sin_port);
+    CHECK_EXPECTED(udp, "Failed to init control socket.");
+
+    auto device = std::unique_ptr<EthernetDevice>(new (std::nothrow) EthernetDevice(device_info, udp.release(), status));
+    CHECK_AS_EXPECTED((nullptr != device), HAILO_OUT_OF_HOST_MEMORY);
+
+    if (HAILO_SUCCESS != status) {
+        LOGGER__ERROR("Failed creating EthernetDevice");
+        return make_unexpected(status);
+    }
+
+    return device;
+}
+
+Expected<std::unique_ptr<EthernetDevice>> EthernetDevice::create(const std::string &ip_addr)
+{
+    auto status = HAILO_UNINITIALIZED;
+    hailo_eth_device_info_t device_info{};
+
+    device_info.host_address.sin_family = AF_INET;
+    device_info.host_address.sin_port = HAILO_ETH_PORT_ANY;
+
+    status = Socket::pton(AF_INET, HAILO_ETH_ADDRESS_ANY, &(device_info.host_address.sin_addr));
+    CHECK_SUCCESS_AS_EXPECTED(status);
+
+    device_info.device_address.sin_family = AF_INET;
+    device_info.device_address.sin_port = HAILO_DEFAULT_ETH_CONTROL_PORT;
+    status = Socket::pton(AF_INET, ip_addr.c_str(), &(device_info.device_address.sin_addr));
+    CHECK_SUCCESS_AS_EXPECTED(status);
+
+    device_info.timeout_millis = HAILO_DEFAULT_ETH_SCAN_TIMEOUT_MS;
+    device_info.max_number_of_attempts = HAILO_DEFAULT_ETH_MAX_NUMBER_OF_RETRIES;
+    device_info.max_payload_size = HAILO_DEFAULT_ETH_MAX_PAYLOAD_SIZE;
+
+    return create(device_info);
+}
+
+EthernetDevice::EthernetDevice(const hailo_eth_device_info_t &device_info, Udp &&control_udp, hailo_status &status) :
+    DeviceBase::DeviceBase(Device::Type::ETH),
+    m_device_info(device_info),
+    m_control_udp(std::move(control_udp)),
+    m_context_switch_manager()
+{
+    char ip_buffer[INET_ADDRSTRLEN];
+    status = Socket::ntop(AF_INET, &(device_info.device_address.sin_addr), ip_buffer, INET_ADDRSTRLEN);
+    if (HAILO_SUCCESS != status) {
+        LOGGER__ERROR("Socket::ntop() failed with status {}", status);
+        return;
+    }
+    m_device_id = std::string(ip_buffer);
+
+    status = m_control_udp.set_timeout(std::chrono::milliseconds(m_device_info.timeout_millis));
+    if (HAILO_SUCCESS != status) {
+        LOGGER__ERROR("Failed to init set timeout for control socket.");
+        return;
+    }
+
+    status = m_control_udp.set_max_number_of_attempts(m_device_info.max_number_of_attempts);
+    if (HAILO_SUCCESS != status) {
+        LOGGER__ERROR("Failed to init set max_number_of_attempts for control socket.");
+        return;
+    }
+
+    status = update_fw_state();
+    if (HAILO_SUCCESS != status) {
+        LOGGER__ERROR("update_fw_state() failed with status {}", status);
+        return;
+    }
+
+    status = HAILO_SUCCESS;
+}
+
+Expected<size_t> EthernetDevice::read_log(MemoryView &buffer, hailo_cpu_id_t cpu_id)
+{
+    (void) buffer;
+    (void) cpu_id;
+    return make_unexpected(HAILO_NOT_IMPLEMENTED);
+}
+
+static void eth_device__fill_eth_device_info(Udp &udp, hailo_eth_device_info_t *eth_device_info)
+{
+    eth_device_info->device_address.sin_family = AF_INET;
+    eth_device_info->device_address.sin_addr = udp.m_device_address.sin_addr;
+    eth_device_info->device_address.sin_port = HAILO_DEFAULT_ETH_CONTROL_PORT;
+
+    eth_device_info->host_address.sin_family = AF_INET;
+    eth_device_info->host_address.sin_addr.s_addr = INADDR_ANY;
+    eth_device_info->host_address.sin_port = HAILO_ETH_PORT_ANY;
+
+    eth_device_info->max_number_of_attempts = HAILO_DEFAULT_ETH_MAX_NUMBER_OF_RETRIES;
+    eth_device_info->max_payload_size = HAILO_DEFAULT_ETH_MAX_PAYLOAD_SIZE;
+    eth_device_info->timeout_millis = HAILO_DEFAULT_ETH_SCAN_TIMEOUT_MS;
+
+    char textual_ip_address[INET_ADDRSTRLEN];
+    auto inet = inet_ntop(AF_INET, &(udp.m_device_address.sin_addr), textual_ip_address, INET_ADDRSTRLEN);
+    if (NULL != inet) {
+        LOGGER__DEBUG("Found Hailo device: {}", textual_ip_address);
+    }
+}
+
+static Expected<hailo_eth_device_info_t> eth_device__handle_available_data(Udp &udp)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+
+    /* Try to receive data from the udp socket and log timeouts in debug level */
+    status = udp.has_data(true);
+    if (HAILO_TIMEOUT == status) {
+        LOGGER__DEBUG("Scan timeout");
+        return make_unexpected(status);
+    }
+    CHECK_SUCCESS_AS_EXPECTED(status);
+
+    hailo_eth_device_info_t device_info{};
+    eth_device__fill_eth_device_info(udp, &device_info);
+    
+    return device_info;
+}
+
+static Expected<std::vector<hailo_eth_device_info_t>> eth_device__receive_responses(Udp &udp)
+{
+    std::vector<hailo_eth_device_info_t> results;
+    while (true) {
+        auto next_device_info = eth_device__handle_available_data(udp);
+        if (next_device_info.has_value()) {
+            results.emplace_back(next_device_info.release());
+        } else if (HAILO_TIMEOUT == next_device_info.status()) {
+            // We excpect to stop receiving data due to timeout
+            break;
+        } else {
+            // Any other reason indicates a problem
+            return make_unexpected(next_device_info.status());
+        }
+    }
+
+    return results;
+}
+
+Expected<std::vector<hailo_eth_device_info_t>> EthernetDevice::scan(const std::string &interface_name,
+    std::chrono::milliseconds timeout)
+{
+    // Convert interface name to IP address
+    std::array<char, IPV4_STRING_MAX_LENGTH> interface_ip_address{};
+    auto status = EthernetUtils::get_ip_from_interface(interface_name.c_str(), interface_ip_address.data(), interface_ip_address.size());
+    CHECK_SUCCESS_AS_EXPECTED(status);
+
+    return scan_by_host_address(interface_ip_address.data(), timeout);
+}
+
+hailo_status get_udp_broadcast_params(const char *host_address, struct in_addr &interface_ip_address,
+    struct in_addr &broadcast_ip_address)
+{
+    assert(nullptr != host_address);
+
+    auto status = Socket::pton(AF_INET, host_address, &interface_ip_address);
+    CHECK_SUCCESS(status);
+    status = Socket::pton(AF_INET, ETH_BROADCAST_IP, &broadcast_ip_address);
+    CHECK_SUCCESS(status);
+
+    return HAILO_SUCCESS;
+}
+
+Expected<std::vector<hailo_eth_device_info_t>> EthernetDevice::scan_by_host_address(const std::string &host_address,
+    std::chrono::milliseconds timeout)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    HAILO_COMMON_STATUS_t common_status = HAILO_COMMON_STATUS__UNINITIALIZED;
+    CONTROL_PROTOCOL__request_t request{};
+    size_t request_size = 0;
+    uint32_t sequence = SCAN_SEQUENCE;
+    struct in_addr broadcast_ip_address{};
+    struct in_addr interface_ip_address{};
+
+    status = get_udp_broadcast_params(host_address.c_str(), interface_ip_address, broadcast_ip_address);
+    CHECK_SUCCESS_AS_EXPECTED(status);
+
+    /* Create broadcast udp object */
+    auto udp_broadcast = Udp::create(broadcast_ip_address, HAILO_DEFAULT_ETH_CONTROL_PORT, interface_ip_address, 0);
+    CHECK_EXPECTED(udp_broadcast);
+    status = udp_broadcast->set_timeout(timeout);
+    CHECK_SUCCESS_AS_EXPECTED(status);
+
+    /* Build identify request */
+    common_status = CONTROL_PROTOCOL__pack_identify_request(&request, &request_size, sequence);
+    status = (HAILO_COMMON_STATUS__SUCCESS == common_status) ? HAILO_SUCCESS : HAILO_INTERNAL_FAILURE;
+    CHECK_SUCCESS_AS_EXPECTED(status);
+
+    /* Send broadcast identify request */
+    status = udp_broadcast->send((uint8_t *)&request, &request_size, false, MAX_UDP_PAYLOAD_SIZE);
+    CHECK_SUCCESS_AS_EXPECTED(status);
+
+    /* Receive all responses */
+    return eth_device__receive_responses(*udp_broadcast);
+}
+
+void EthernetDevice::increment_control_sequence()
+{
+    m_control_sequence = (m_control_sequence + 1) % CONTROL__MAX_SEQUENCE;
+}
+
+hailo_reset_device_mode_t EthernetDevice::get_default_reset_mode()
+{
+    return HAILO_RESET_DEVICE_MODE_CHIP;
+}
+
+hailo_status EthernetDevice::reset_impl(CONTROL_PROTOCOL__reset_type_t reset_type)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    HAILO_COMMON_STATUS_t common_status = HAILO_COMMON_STATUS__UNINITIALIZED;
+    CONTROL_PROTOCOL__request_t request = {};
+    size_t request_size = 0;
+    uint8_t response_buffer[RESPONSE_MAX_BUFFER_SIZE] = {};
+    size_t response_size = RESPONSE_MAX_BUFFER_SIZE;
+    CONTROL_PROTOCOL__response_header_t *header = NULL;
+    CONTROL_PROTOCOL__payload_t *payload = NULL;
+    bool is_expecting_response = true;
+
+    switch (reset_type) {
+        case CONTROL_PROTOCOL__RESET_TYPE__CHIP:
+            is_expecting_response = false;
+            break;
+        case CONTROL_PROTOCOL__RESET_TYPE__SOFT:
+            /* Fallthrough */
+        case CONTROL_PROTOCOL__RESET_TYPE__FORCED_SOFT:
+            is_expecting_response = false; // TODO: Check boot source, set is_expecting_response = (boot_source != pcie)
+            break;
+        default:
+            is_expecting_response = true;
+            break;
+    }
+
+    common_status = CONTROL_PROTOCOL__pack_reset_request(&request, &request_size, m_control_sequence, reset_type);
+    status = (HAILO_COMMON_STATUS__SUCCESS == common_status) ? HAILO_SUCCESS : HAILO_INTERNAL_FAILURE;
+    CHECK_SUCCESS(status);
+
+    /* On non-reponse controls we set the response_size to 0 */
+    if (!is_expecting_response) {
+        response_size = 0;
+    }
+
+    LOGGER__DEBUG("Sending reset request");
+    status = this->fw_interact((uint8_t*)(&request), request_size, (uint8_t*)&response_buffer, &response_size);
+    // fw_interact should return success even if response is not expected
+    CHECK_SUCCESS(status);
+
+    /* Parse response if expected */
+    // TODO: fix logic with respect to is_expecting_response
+    if (0 != response_size) {
+        status = Control::parse_and_validate_response(response_buffer, (uint32_t)(response_size), &header,
+            &payload, &request);
+        CHECK_SUCCESS(status);
+        CHECK(is_expecting_response, HAILO_INTERNAL_FAILURE,
+            "Recived valid response from FW for control who is not expecting one.");
+    } else {
+        status = this->wait_for_wakeup();
+        CHECK_SUCCESS(status);
+    }
+
+    LOGGER__DEBUG("Board has been reset successfully");
+    return HAILO_SUCCESS;
+}
+
+Expected<hailo_device_architecture_t> EthernetDevice::get_architecture() const
+{
+    // FW is always up if we got here (EthernetDevice's ctor would fail otherwise)
+    // Hence, just return it
+    return Expected<hailo_device_architecture_t>(m_device_architecture);
+}
+
+hailo_eth_device_info_t EthernetDevice::get_device_info() const
+{
+    return m_device_info;
+}
+
+const char *EthernetDevice::get_dev_id() const
+{
+    return m_device_id.c_str();
+}
+
+Expected<D2H_EVENT_MESSAGE_t> EthernetDevice::read_notification()
+{
+    return make_unexpected(HAILO_NOT_IMPLEMENTED);
+}
+
+hailo_status EthernetDevice::disable_notifications()
+{
+    return HAILO_NOT_IMPLEMENTED;
+}
+
+ExpectedRef<ConfigManager> EthernetDevice::get_config_manager()
+{
+    if (!m_context_switch_manager) {
+        m_context_switch_manager = make_unique_nothrow<HcpConfigManager>(*this);
+        CHECK_AS_EXPECTED(nullptr != m_context_switch_manager, HAILO_OUT_OF_HOST_MEMORY);
+    }
+
+    return std::ref(*m_context_switch_manager);
+}
+
+
+} /* namespace hailort */
diff --git a/hailort/libhailort/src/eth_device.hpp b/hailort/libhailort/src/eth_device.hpp
new file mode 100644 (file)
index 0000000..2801e70
--- /dev/null
@@ -0,0 +1,76 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file eth_device.hpp
+ * @brief TODO: brief
+ *
+ * TODO: doc
+ **/
+
+#ifndef HAILO_ETH_DEVICE_H_
+#define HAILO_ETH_DEVICE_H_
+
+#include "hailo/expected.hpp"
+#include "hailo/hailort.h"
+#include "device_internal.hpp"
+#include "udp.hpp"
+#include "context_switch/single_context/hcp_config_manager.hpp"
+
+namespace hailort
+{
+
+class EthernetDevice : public DeviceBase {
+public:
+    virtual hailo_status fw_interact_impl(uint8_t *request_buffer, size_t request_size,
+        uint8_t *response_buffer, size_t *response_size, hailo_cpu_id_t cpu_id) override;
+    virtual Expected<size_t> read_log(MemoryView &buffer, hailo_cpu_id_t cpu_id) override;
+    virtual hailo_status wait_for_wakeup() override;
+    virtual void increment_control_sequence() override;
+    virtual hailo_reset_device_mode_t get_default_reset_mode() override;
+    virtual hailo_status reset_impl(CONTROL_PROTOCOL__reset_type_t reset_type) override;
+
+    virtual bool is_stream_interface_supported(const hailo_stream_interface_t &stream_interface) const override
+    {
+        switch (stream_interface) {
+        case HAILO_STREAM_INTERFACE_PCIE:
+        case HAILO_STREAM_INTERFACE_CORE:
+            return false;
+        case HAILO_STREAM_INTERFACE_ETH:
+        case HAILO_STREAM_INTERFACE_MIPI:
+            return true;
+        default:
+            LOGGER__ERROR("Invalid stream interface");
+            return false;
+        }
+    }
+
+    static Expected<std::vector<hailo_eth_device_info_t>> scan(const std::string &interface_name,
+        std::chrono::milliseconds timeout);
+    static Expected<std::vector<hailo_eth_device_info_t>> scan_by_host_address(const std::string &host_address,
+        std::chrono::milliseconds timeout);
+
+    static Expected<std::unique_ptr<EthernetDevice>> create(const hailo_eth_device_info_t &device_info);
+    static Expected<std::unique_ptr<EthernetDevice>> create(const std::string &ip_addr);
+    virtual Expected<hailo_device_architecture_t> get_architecture() const override;
+    hailo_eth_device_info_t get_device_info() const;
+    virtual const char* get_dev_id() const override;
+
+protected:
+    virtual Expected<D2H_EVENT_MESSAGE_t> read_notification() override;
+    virtual hailo_status disable_notifications() override;
+    virtual ExpectedRef<ConfigManager> get_config_manager() override;
+
+private:
+    EthernetDevice(const hailo_eth_device_info_t &device_info, Udp &&control_udp, hailo_status &status);
+
+    const hailo_eth_device_info_t m_device_info;
+    std::string m_device_id;
+    Udp m_control_udp;
+    std::unique_ptr<ConfigManager> m_context_switch_manager;
+};
+
+} /* namespace hailort */
+
+#endif /* HAILO_ETH_DEVICE_H_ */
\ No newline at end of file
diff --git a/hailort/libhailort/src/eth_stream.cpp b/hailort/libhailort/src/eth_stream.cpp
new file mode 100644 (file)
index 0000000..e82c48d
--- /dev/null
@@ -0,0 +1,735 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file eth_stream.cpp
+ * @brief TODO: brief
+ *
+ * TODO: doc
+ **/
+
+#include <new>
+#include <stdlib.h>
+#include <math.h>
+#include <byte_order.h>
+
+#include <hailo/hailort.h>
+#include "common/utils.hpp"
+#include "hailo/stream.hpp"
+#include "hailo/hef.hpp"
+#include "hailo/hailort_common.hpp"
+#include "eth_stream.hpp"
+#include "eth_device.hpp"
+#include "control.hpp"
+#include "token_bucket.hpp"
+#include "common/ethernet_utils.hpp"
+
+namespace hailort
+{
+
+#define SYNC_PACKET_BARKER (0xa143341a)
+
+
+typedef struct hailo_output_sync_packet_t {
+    uint32_t barker;
+    uint32_t sequence_index;
+} hailo_output_sync_packet_t;
+
+EthernetInputStream::~EthernetInputStream()
+{
+    if (m_is_stream_activated) {
+        auto status = this->deactivate_stream();
+        if (HAILO_SUCCESS != status) {
+            LOGGER__ERROR("Close stream failed! (status {} stream index {})", status, m_stream_info.index);
+        }
+    }
+}
+
+
+Expected<Udp> eth_stream__create_udp(EthernetDevice *eth_device, struct sockaddr_in host_address, uint8_t stream_index,
+    port_t device_port, bool is_input)
+{
+    if (HAILO_DEFAULT_ETH_DEVICE_PORT == device_port) {
+        if (is_input) {
+            device_port = (uint16_t)(stream_index + HailoRTCommon::ETH_INPUT_BASE_PORT);
+        } else {
+            device_port = (uint16_t)(stream_index + HailoRTCommon::ETH_OUTPUT_BASE_PORT);
+        }
+    }
+
+    return Udp::create(eth_device->get_device_info().device_address.sin_addr, device_port, host_address.sin_addr,
+        host_address.sin_port);
+}
+
+/** Input stream **/
+hailo_status EthernetInputStream::deactivate_stream()
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+
+    ASSERT(m_is_stream_activated);
+
+    // TODO: Hold a ref not a pointer
+    status = Control::close_stream(m_device, m_dataflow_manager_id, true);
+    CHECK_SUCCESS(status);
+
+    m_is_stream_activated = false;
+
+    return HAILO_SUCCESS;
+}
+
+hailo_status EthernetInputStream::activate_stream()
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    CONTROL_PROTOCOL__config_stream_params_t params = {};
+    
+    params.nn_stream_config = m_nn_stream_config;
+    params.communication_type = CONTROL_PROTOCOL__COMMUNICATION_TYPE_UDP;
+    params.is_input = true;
+    params.stream_index = m_stream_info.index;
+    params.communication_params.udp_input.listening_port = (uint16_t)(BYTE_ORDER__htons(m_udp.m_device_address.sin_port));
+    params.skip_nn_stream_config = false;
+    // Currently hardcoded assign as there are no power mode optimizations over eth
+    params.power_mode = static_cast<uint8_t>(CONTROL_PROTOCOL__MODE_ULTRA_PERFORMANCE);
+
+    if (this->configuration.is_sync_enabled) {
+        params.communication_params.udp_input.sync.should_sync = true;
+        params.communication_params.udp_input.sync.frames_per_sync = this->configuration.frames_per_sync;
+        params.communication_params.udp_input.sync.packets_per_frame = this->configuration.packets_per_frame;
+        params.communication_params.udp_input.sync.sync_size = this->configuration.sync_size;
+    }
+
+    params.communication_params.udp_input.buffers_threshold = this->configuration.buffers_threshold;
+    params.communication_params.udp_input.use_rtp = false;
+
+    status = Control::config_stream_udp_input(m_device, &params, m_dataflow_manager_id);
+    CHECK_SUCCESS(status);
+
+    status = Control::open_stream(m_device, m_dataflow_manager_id, true);
+    CHECK_SUCCESS(status);
+
+    m_is_stream_activated = true;
+
+    return HAILO_SUCCESS;
+}
+
+Expected<size_t> EthernetInputStream::sync_write_raw_buffer(const MemoryView &buffer)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+
+    status = get_network_group_activated_event()->wait(std::chrono::milliseconds(0));
+    CHECK_AS_EXPECTED(HAILO_TIMEOUT != status, HAILO_NETWORK_GROUP_NOT_ACTIVATED, "Trying to write on stream before its network_group is activated");
+    CHECK_SUCCESS_AS_EXPECTED(status);
+
+    size_t size = buffer.size();
+    status = m_udp.send((uint8_t*)buffer.data(), &size, this->configuration.use_dataflow_padding, this->configuration.max_payload_size);
+    if (HAILO_STREAM_INTERNAL_ABORT == status) {
+        LOGGER__INFO("Udp send was aborted!");
+        return make_unexpected(status);
+    }
+    CHECK_SUCCESS_AS_EXPECTED(status);
+
+    return size;
+}
+
+hailo_status EthernetInputStream::sync_write_all_raw_buffer_no_transform_impl(void *buffer, size_t offset, size_t size)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+
+    ASSERT(NULL != buffer);
+
+    CHECK(size >= MIN_UDP_PAYLOAD_SIZE, HAILO_INVALID_ARGUMENT, "Input must be larger than {}", MIN_UDP_PAYLOAD_SIZE);
+    CHECK(((size % HailoRTCommon::HW_DATA_ALIGNMENT) == 0), HAILO_INVALID_ARGUMENT,
+        "Input must be aligned to {} (got {})", HailoRTCommon::HW_DATA_ALIGNMENT, size);
+
+    if (this->configuration.is_sync_enabled) {
+        status = eth_stream__write_all_with_sync(buffer, offset, size);
+    } else {
+        status = eth_stream__write_all_no_sync(buffer, offset, size);
+    }
+    if (HAILO_STREAM_INTERNAL_ABORT == status) {
+        LOGGER__INFO("eth_stream__write_all was aborted!");
+        return status;
+    }
+    
+    CHECK_SUCCESS(status);
+
+    return HAILO_SUCCESS;
+}
+
+hailo_status EthernetInputStream::eth_stream__write_all_no_sync(void *buffer, size_t offset, size_t size) {
+    size_t remainder_size = 0;
+    size_t packet_size = this->configuration.max_payload_size;
+
+    //if we have padding, consider it when calculating the packet sizes
+    if (this->configuration.use_dataflow_padding) {
+        packet_size -= PADDING_BYTES_SIZE + PADDING_ALIGN_BYTES;
+    }
+
+    remainder_size = size % packet_size;
+
+    if ((0 < remainder_size) && (remainder_size < MIN_UDP_PAYLOAD_SIZE)) {
+        remainder_size = MIN_UDP_PAYLOAD_SIZE;
+    }
+    return eth_stream__write_with_remainder(buffer, offset, size, remainder_size);
+}
+
+hailo_status EthernetInputStream::eth_stream__write_with_remainder(void *buffer, size_t offset, size_t size, size_t remainder_size) {
+    size_t transfer_size = 0;
+    size_t offset_end_without_remainder = offset + size - remainder_size;
+
+    while (offset < offset_end_without_remainder) {
+        transfer_size = offset_end_without_remainder - offset;
+        auto expected_bytes_written = sync_write_raw_buffer(MemoryView(static_cast<uint8_t*>(buffer) + offset, transfer_size));
+        if (HAILO_STREAM_INTERNAL_ABORT == expected_bytes_written.status()) {
+            LOGGER__INFO("sync_write_raw_buffer was aborted!");
+            return expected_bytes_written.status();
+        }
+        CHECK_EXPECTED_AS_STATUS(expected_bytes_written);
+        offset += expected_bytes_written.release();
+    }
+    if (0 < remainder_size) {
+        auto expected_bytes_written = sync_write_raw_buffer(MemoryView(static_cast<uint8_t*>(buffer) + offset, remainder_size));
+        if (HAILO_STREAM_INTERNAL_ABORT == expected_bytes_written.status()) {
+            LOGGER__INFO("sync_write_raw_buffer was aborted!");
+            return expected_bytes_written.status();
+        }
+        CHECK_EXPECTED_AS_STATUS(expected_bytes_written);
+        assert(expected_bytes_written.value() == remainder_size);
+    }
+
+    return HAILO_SUCCESS;
+}
+
+EthernetInputStreamRateLimited::EthernetInputStreamRateLimited(Device &device, Udp &&udp,
+    EventPtr &&network_group_activated_event, uint32_t rate_bytes_per_sec, const LayerInfo &layer_info, hailo_status &status) :
+    EthernetInputStream::EthernetInputStream(device, std::move(udp), std::move(network_group_activated_event), layer_info, status),
+    rate_bytes_per_sec(rate_bytes_per_sec)
+{}
+
+EthernetInputStreamRateLimited::EthernetInputStreamRateLimited(EthernetInputStreamRateLimited &&other) :
+    EthernetInputStream(std::move(other)),
+    rate_bytes_per_sec(other.rate_bytes_per_sec)
+{}
+
+TokenBucketEthernetInputStream::TokenBucketEthernetInputStream(Device &device, Udp &&udp,
+    EventPtr &&network_group_activated_event, uint32_t rate_bytes_per_sec, const LayerInfo &layer_info, hailo_status &status) :
+    EthernetInputStreamRateLimited::EthernetInputStreamRateLimited(device, std::move(udp),
+        std::move(network_group_activated_event), rate_bytes_per_sec, layer_info, status),
+    token_bucket()
+{}
+
+TokenBucketEthernetInputStream::TokenBucketEthernetInputStream(TokenBucketEthernetInputStream &&other) :
+    EthernetInputStreamRateLimited(std::move(other)),
+    token_bucket(std::move(other.token_bucket))
+{}
+
+hailo_status TokenBucketEthernetInputStream::eth_stream__write_with_remainder(void *buffer, size_t offset, size_t size, size_t remainder_size) {
+    size_t transfer_size = 0;
+    size_t offset_end_without_remainder = offset + size - remainder_size;
+
+    assert(remainder_size <= MAX_CONSUME_SIZE);
+    static_assert(MAX_CONSUME_SIZE <= BURST_SIZE, "We are asking to consume more bytes than the size of the token bucket, this will fail");
+
+    while (offset < offset_end_without_remainder) {
+        (void)token_bucket.consumeWithBorrowAndWait(MAX_CONSUME_SIZE, rate_bytes_per_sec, BURST_SIZE);
+    
+        transfer_size = offset_end_without_remainder - offset;
+        auto expected_bytes_written = sync_write_raw_buffer(MemoryView(static_cast<uint8_t*>(buffer) + offset, transfer_size));
+        if (HAILO_STREAM_INTERNAL_ABORT == expected_bytes_written.status()) {
+            LOGGER__INFO("sync_write_raw_buffer was aborted!");
+            return expected_bytes_written.status();
+        }
+        CHECK_EXPECTED_AS_STATUS(expected_bytes_written);
+        offset += expected_bytes_written.release();
+    }
+    if (0 < remainder_size) {
+        // We don't static_assert that "remainder_size <= BURST_SIZE", so the call could fail in theory.
+        // However, since remainder_size is modulo MAX_UDP_PAYLOAD_SIZE and BURST_SIZE == MAX_UDP_PAYLOAD_SIZE, it should be smaller.
+        (void)token_bucket.consumeWithBorrowAndWait(static_cast<double>(remainder_size), rate_bytes_per_sec, BURST_SIZE);
+        
+        auto expected_bytes_written = sync_write_raw_buffer(MemoryView(static_cast<uint8_t*>(buffer) + offset, remainder_size));
+        if (HAILO_STREAM_INTERNAL_ABORT == expected_bytes_written.status()) {
+            LOGGER__INFO("sync_write_raw_buffer was aborted!");
+            return expected_bytes_written.status();
+        }
+        CHECK_EXPECTED_AS_STATUS(expected_bytes_written);
+        assert(expected_bytes_written.value() == remainder_size);
+    }
+
+    return HAILO_SUCCESS;
+}
+
+#if defined(__GNUC__)
+Expected<std::unique_ptr<TrafficControlEthernetInputStream>> TrafficControlEthernetInputStream::create(
+    Device &device, Udp &&udp, EventPtr &&network_group_activated_event, uint32_t rate_bytes_per_sec, const LayerInfo &layer_info)
+{
+    auto board_ip = get_interface_address(&udp.m_device_address.sin_addr);
+    CHECK_EXPECTED(board_ip, "get_interface_address failed with status {}", board_ip.status());
+
+    const auto board_port = BYTE_ORDER__ntohs(udp.m_device_address.sin_port);
+
+    auto tc = TrafficControl::create(board_ip.value(), board_port, rate_bytes_per_sec);
+    CHECK_EXPECTED(tc, "Creating traffic control at rate {} failed with error {}", rate_bytes_per_sec, tc.status());
+
+    auto status = HAILO_UNINITIALIZED;
+    // Note: we don't use make_unique because TrafficControlEthernetInputStream's ctor is private
+    auto tc_ptr = std::unique_ptr<TrafficControlEthernetInputStream>(new (std::nothrow)
+        TrafficControlEthernetInputStream(device, std::move(udp), std::move(network_group_activated_event), rate_bytes_per_sec,
+        tc.release(), layer_info, status));
+    CHECK_AS_EXPECTED(nullptr != tc_ptr, HAILO_OUT_OF_HOST_MEMORY);
+    CHECK_SUCCESS_AS_EXPECTED(status);
+    return tc_ptr;
+}
+
+Expected<std::string> TrafficControlEthernetInputStream::get_interface_address(const struct in_addr *addr)
+{
+    auto ip = Buffer::create(IPV4_STRING_MAX_LENGTH, 0);
+    CHECK_EXPECTED(ip);
+
+    const auto result = Socket::ntop(AF_INET, addr, ip->as_pointer<char>(), EthernetUtils::MAX_INTERFACE_SIZE);
+    CHECK_SUCCESS_AS_EXPECTED(result, "Failed parsing IP to string with status {}", result);
+    
+    return ip->to_string();
+}
+
+TrafficControlEthernetInputStream::TrafficControlEthernetInputStream(Device &device, Udp &&udp,
+    EventPtr &&network_group_activated_event, uint32_t rate_bytes_per_sec, TrafficControl &&tc, const LayerInfo &layer_info, hailo_status &status) :
+    EthernetInputStreamRateLimited(device, std::move(udp), std::move(network_group_activated_event), rate_bytes_per_sec, layer_info, status),
+    m_tc(std::move(tc))
+{}
+#endif
+
+hailo_status EthernetInputStream::eth_stream__write_all_with_sync(void *buffer, size_t offset, size_t size) {
+    hailo_status status = HAILO_UNINITIALIZED;
+    size_t number_of_frames = 0;
+    size_t frame_size = m_stream_info.hw_frame_size;
+
+    if (0 != (size % frame_size)) {
+        LOGGER__ERROR("Read size is not a multiple of frame size."
+                      "This operation is not possible with the sync packet mode."
+                      "Tried to read {} bytes and frame size is {}", size, m_stream_info.hw_frame_size);
+        return HAILO_INVALID_ARGUMENT;
+    }
+
+    number_of_frames = size / frame_size;
+    for (size_t i = 0; i < number_of_frames; i++) {
+        // Write frame by frame, whereas the remainder packet is the sync packet
+        status = eth_stream__write_with_remainder(buffer, offset, frame_size, this->configuration.sync_size);
+        if (HAILO_STREAM_INTERNAL_ABORT == status) {
+            LOGGER__INFO("eth_stream__write_with_remainder was aborted!");
+            return status;
+        }
+        CHECK_SUCCESS(status);
+        offset += frame_size;
+    }
+
+    return HAILO_SUCCESS;
+}
+
+hailo_status EthernetInputStream::eth_stream__config_input_sync_params(uint32_t frames_per_sync)
+{
+    size_t packet_size = MAX_UDP_PAYLOAD_SIZE;
+
+    if (MAX_UDP_PAYLOAD_SIZE >= m_stream_info.hw_frame_size) {
+        LOGGER__WARNING("Input size that isn't larger than {} doesn't benefit from sync, disabling..", MAX_UDP_PAYLOAD_SIZE);
+        this->configuration.is_sync_enabled = false;
+        return HAILO_SUCCESS;
+    }
+    this->configuration.is_sync_enabled = true;
+    CHECK(1 == frames_per_sync, HAILO_NOT_IMPLEMENTED,
+        "Currently not supported frames_per_sync != 1");
+    this->configuration.frames_per_sync = frames_per_sync;
+    //if we have padding, consider it when determining the number of packets
+    if (this->configuration.use_dataflow_padding) {
+        packet_size = MAX_UDP_PADDED_PAYLOAD_SIZE;
+    }
+    // Data packets per frame are all of the packets except the sync
+    this->configuration.packets_per_frame = (uint32_t) ceil((double) m_stream_info.hw_frame_size / (double) packet_size) - 1;
+    if (0 == (m_stream_info.hw_frame_size % packet_size)) {
+        // If there is no remainder to make the sync packet, we will "cut" it from the last data packet, thus increasing the number of packets.
+        this->configuration.packets_per_frame++;
+    }
+    // Make the remainder packet the sync packet
+    this->configuration.sync_size = (uint16_t)(m_stream_info.hw_frame_size % packet_size);
+
+    if (MIN_UDP_PAYLOAD_SIZE > this->configuration.sync_size) {
+        // If the remainder isn't big enough, we'll "cut" from the last data packet enough to fill the minimum size.
+        this->configuration.sync_size = MIN_UDP_PAYLOAD_SIZE;
+    }
+    LOGGER__DEBUG("Configured sync size {}, packets per frame {}", this->configuration.sync_size, this->configuration.packets_per_frame);
+    return HAILO_SUCCESS;
+}
+
+Expected<std::unique_ptr<EthernetInputStream>> EthernetInputStream::create(Device &device,
+    const LayerInfo &edge_layer, const hailo_eth_input_stream_params_t &params, EventPtr network_group_activated_event)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    // TODO: try to avoid cast
+    auto eth_device = reinterpret_cast<EthernetDevice*>(&device);
+    std::unique_ptr<EthernetInputStream> local_stream;
+
+    auto stream_index = edge_layer.index;
+    auto udp = eth_stream__create_udp(eth_device, params.host_address, stream_index, params.device_port, true);
+    CHECK_EXPECTED(udp);
+
+    if (params.rate_limit_bytes_per_sec == 0) {
+        local_stream = std::unique_ptr<EthernetInputStream>(
+            new (std::nothrow) EthernetInputStream(device, udp.release(), std::move(network_group_activated_event), edge_layer, status));
+        CHECK_SUCCESS_AS_EXPECTED(status);
+    } else {
+#ifdef _MSC_VER
+        // TODO: Add factory class
+        local_stream = std::unique_ptr<EthernetInputStream>(
+            new (std::nothrow) TokenBucketEthernetInputStream(device, udp.release(),
+            std::move(network_group_activated_event), params.rate_limit_bytes_per_sec, edge_layer, status));
+        CHECK_SUCCESS_AS_EXPECTED(status);
+#else
+        auto stream_expected = TrafficControlEthernetInputStream::create(device, udp.release(),
+            std::move(network_group_activated_event), params.rate_limit_bytes_per_sec, edge_layer);
+        CHECK_EXPECTED(stream_expected);
+        local_stream = stream_expected.release();
+#endif
+    }
+
+    CHECK_AS_EXPECTED((nullptr != local_stream), HAILO_OUT_OF_HOST_MEMORY);
+    local_stream->m_is_stream_activated = false;
+
+    auto device_architecture = eth_device->get_architecture();
+    CHECK_EXPECTED(device_architecture);
+    local_stream->configuration.use_dataflow_padding = (HAILO_ARCH_HAILO8_B0 == device_architecture.value());
+
+    local_stream->set_max_payload_size(params.max_payload_size);
+
+    local_stream->configuration.is_sync_enabled = params.is_sync_enabled;
+    if (local_stream->configuration.is_sync_enabled) {
+        status = local_stream->eth_stream__config_input_sync_params(params.frames_per_sync);
+        CHECK_SUCCESS_AS_EXPECTED(status);
+    }
+
+    local_stream->configuration.buffers_threshold = params.buffers_threshold;
+
+    return local_stream;
+}
+
+void EthernetInputStream::set_max_payload_size(uint16_t size)
+{
+    if (size > MAX_UDP_PAYLOAD_SIZE) {
+        size = MAX_UDP_PAYLOAD_SIZE;
+    }
+    this->configuration.max_payload_size = size;
+}
+
+hailo_status EthernetInputStream::set_timeout(std::chrono::milliseconds timeout)
+{
+    return m_udp.set_timeout(timeout);
+}
+
+std::chrono::milliseconds EthernetInputStream::get_timeout() const
+{
+    return std::chrono::milliseconds((MILLISECONDS_IN_SECOND * m_udp.m_timeout.tv_sec) + (m_udp.m_timeout.tv_usec / MICROSECONDS_IN_MILLISECOND));
+}
+
+uint16_t EthernetInputStream::get_remote_port()
+{
+    return ntohs(m_udp.m_device_address.sin_port);
+}
+
+/** Output stream **/
+EthernetOutputStream::~EthernetOutputStream()
+{
+    if (m_is_stream_activated) {
+        auto status = this->deactivate_stream();
+        if (HAILO_SUCCESS != status) {
+            LOGGER__ERROR("Close stream failed! (status {} stream index {})", status, m_stream_info.index);
+        }
+    }
+}
+
+hailo_status EthernetOutputStream::deactivate_stream()
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+
+    ASSERT(m_is_stream_activated);
+
+    status = Control::close_stream(m_device, m_dataflow_manager_id, false);
+    CHECK_SUCCESS(status);
+
+    m_is_stream_activated = false;
+
+    return HAILO_SUCCESS;
+}
+
+hailo_status EthernetOutputStream::activate_stream()
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    CONTROL_PROTOCOL__config_stream_params_t params = {};
+
+    params.nn_stream_config = m_nn_stream_config;
+    params.communication_type = CONTROL_PROTOCOL__COMMUNICATION_TYPE_UDP;
+    params.is_input = false;
+    params.stream_index = m_stream_info.index;
+    params.skip_nn_stream_config = false;
+    // Currently hardcoded assign as there are no power mode optimizations over eth
+    params.power_mode = static_cast<uint8_t>(CONTROL_PROTOCOL__MODE_ULTRA_PERFORMANCE);
+
+    params.communication_params.udp_output.chip_udp_port = (uint16_t)(BYTE_ORDER__htons(m_udp.m_device_address.sin_port));
+    params.communication_params.udp_output.host_udp_port = (uint16_t)(BYTE_ORDER__htons(m_udp.m_host_address.sin_port));
+    params.communication_params.udp_output.max_udp_payload_size = this->configuration.max_payload_size;
+    params.communication_params.udp_output.buffers_threshold = this->configuration.buffers_threshold;
+    params.communication_params.udp_output.use_rtp = false;
+
+    if (this->configuration.is_sync_enabled) {
+        params.communication_params.udp_output.should_send_sync_packets = true;
+    }
+
+    status = Control::config_stream_udp_output(m_device, &params, m_dataflow_manager_id);
+    CHECK_SUCCESS(status);
+
+    status = Control::open_stream(m_device, m_dataflow_manager_id, false);
+    CHECK_SUCCESS(status);
+
+    m_is_stream_activated = true;
+
+    return HAILO_SUCCESS;
+}
+
+hailo_status EthernetOutputStream::read_all_no_sync(void *buffer, size_t offset, size_t size) {
+    size_t offset_end = 0;
+    size_t transfer_size = 0;
+
+    offset_end = offset + size;
+    while (offset < offset_end) {
+        transfer_size = offset_end - offset;
+        MemoryView buffer_view(static_cast<uint8_t*>(buffer) + offset, transfer_size);
+        auto expected_bytes_read = this->sync_read_raw_buffer(buffer_view);
+        if (HAILO_STREAM_INTERNAL_ABORT == expected_bytes_read.status()) {
+            LOGGER__INFO("sync_read_raw_buffer was aborted!");
+            return expected_bytes_read.status();
+        }
+        CHECK_EXPECTED_AS_STATUS(expected_bytes_read);
+        offset += expected_bytes_read.release();
+    }
+
+    return HAILO_SUCCESS;
+}
+
+hailo_status EthernetOutputStream::read_all_with_sync(void *buffer, size_t offset, size_t size) {
+    hailo_status status = HAILO_UNINITIALIZED;
+    size_t initial_offset = offset;
+    size_t offset_end = offset + size;
+    bool got_last_sync_early = false;
+    const size_t frame_size = m_stream_info.hw_frame_size;
+    bool is_batch_invalid = false;
+
+    if ((size % frame_size) != 0) {
+        LOGGER__ERROR("Read size is not a multiple of frame size."
+                      "This operation is not possible with the sync packet mode."
+                      "Tried to read {} bytes and frame size is {}", size, frame_size);
+        return HAILO_INVALID_ARGUMENT;
+    }
+
+    if (this->leftover_size > 0) {
+        memcpy((uint8_t*)buffer + offset, this->leftover_buffer, this->leftover_size);
+        offset += this->leftover_size;
+        // leftover size will be reassigned in the end, but in case the function ends prematurely we will zero it for safety.
+        this->leftover_size = 0;
+    }
+
+    while (offset < offset_end) {
+        size_t transfer_size = offset_end - offset;
+        MemoryView buffer_view(static_cast<uint8_t*>(buffer) + offset, transfer_size);
+        auto expected_bytes_read = this->sync_read_raw_buffer(buffer_view);
+        status = expected_bytes_read.status();
+        if (HAILO_TIMEOUT == status) {
+            return handle_timeout(buffer, offset, initial_offset, frame_size);
+        } else if (HAILO_STREAM_INTERNAL_ABORT == status) {
+            LOGGER__INFO("sync_read_raw_buffer was aborted");
+            return status;
+        } else if (HAILO_SUCCESS != status) {
+            LOGGER__ERROR("read failed");
+            return status;
+        }
+        transfer_size = expected_bytes_read.release();
+        if (is_sync_packet(buffer, offset, transfer_size)) {
+            uint32_t sequence_index = BYTE_ORDER__ntohl(((hailo_output_sync_packet_t*)((uint8_t*)buffer + offset))->sequence_index);
+            if (is_sync_expected(offset, initial_offset, frame_size)) {
+                if (sequence_index != (this->last_seen_sync_index + 1)) {
+                    // Batch is invalid if a frame was skipped
+                    is_batch_invalid = true;
+                    LOGGER__WARNING("Received {} frames. Missed sync packets between them, treating the batch as invalid data", sequence_index - this->last_seen_sync_index);
+                }
+                if (sequence_index == this->last_seen_sync_index) {
+                    LOGGER__ERROR("Got duplicate sync!");
+                    return HAILO_INTERNAL_FAILURE;
+                }
+            } else {
+                size_t number_of_missing_bytes = (frame_size - ((offset - initial_offset) % frame_size));
+                LOGGER__WARNING("Some bytes are missing at frame, padding {} bytes with zeros", number_of_missing_bytes);
+                memset((uint8_t*)buffer + offset, 0, number_of_missing_bytes);
+                offset += number_of_missing_bytes;
+                if (offset == offset_end) {
+                    got_last_sync_early = true;
+                }
+                is_batch_invalid = true;
+            }
+            this->last_seen_sync_index = sequence_index;
+        } else {
+            offset += transfer_size;
+        }
+    }
+
+    status = HAILO_SUCCESS;
+
+    if (!got_last_sync_early) {
+        status = get_last_sync();
+    }
+    if (HAILO_SUCCESS == status && is_batch_invalid) {
+        return HAILO_INVALID_FRAME;
+    }
+
+    return HAILO_SUCCESS;
+}
+
+hailo_status EthernetOutputStream::get_last_sync() {
+    size_t last_packet_size = sizeof(this->leftover_buffer);
+    MemoryView leftover_buffer_view(this->leftover_buffer, last_packet_size);
+    auto expected_bytes_read = sync_read_raw_buffer(leftover_buffer_view);
+    CHECK(HAILO_TIMEOUT != expected_bytes_read.status(), HAILO_INVALID_FRAME, "Got timeout on last sync, marking last frame as invalid");
+    CHECK_EXPECTED_AS_STATUS(expected_bytes_read, "Recv error");
+    last_packet_size = expected_bytes_read.release();
+
+    if (is_sync_packet(this->leftover_buffer, 0, last_packet_size)) {
+        this->leftover_size = 0;
+    } else {
+        LOGGER__WARNING("Received a data packet instead of sync, saving leftover for later frame");
+        this->leftover_size = last_packet_size;
+    }
+
+    return HAILO_SUCCESS;
+}
+
+hailo_status EthernetOutputStream::handle_timeout(const void* buffer, size_t offset,
+                                                       size_t initial_offset, const size_t frame_size) {
+    // In case data a timeout has occurred, and data was received, try filling missing in frame
+    if (this->encountered_timeout || (offset == initial_offset)) {
+        LOGGER__ERROR("Got timeout, unable to complete the frame");
+        return HAILO_TIMEOUT;
+    }
+    LOGGER__ERROR("Received timeout. Continuing logic as if a sync packet was received");
+    size_t number_of_missing_bytes = (frame_size - ((offset - initial_offset) % frame_size));
+    LOGGER__ERROR("padding {} bytes with zeros because of timeout", number_of_missing_bytes);
+    memset((uint8_t*)buffer + offset, 0, number_of_missing_bytes);
+    this->encountered_timeout = true;
+    return HAILO_INVALID_FRAME;
+}
+
+bool EthernetOutputStream::is_sync_expected(size_t offset, size_t initial_offset, const size_t frame_size) {
+    return (((offset - initial_offset) % frame_size) == 0) && (offset > initial_offset);
+}
+
+bool EthernetOutputStream::is_sync_packet(const void* buffer, size_t offset, size_t transfer_size) {
+    return (transfer_size == sizeof(hailo_output_sync_packet_t) &&
+            ((hailo_output_sync_packet_t*)((uint8_t*)buffer + offset))->barker == BYTE_ORDER__ntohl(SYNC_PACKET_BARKER));
+}
+
+hailo_status EthernetOutputStream::read_all(MemoryView &buffer)
+{
+    if ((buffer.size() % HailoRTCommon::HW_DATA_ALIGNMENT) != 0) {
+        LOGGER__ERROR("Size must be aligned to {} (got {})", HailoRTCommon::HW_DATA_ALIGNMENT, buffer.size());
+        return HAILO_INVALID_ARGUMENT;
+    }
+
+    hailo_status status = HAILO_UNINITIALIZED;
+    if (this->configuration.is_sync_enabled) {
+        status = this->read_all_with_sync(buffer.data(), 0, buffer.size());
+    } else {
+        status = this->read_all_no_sync(buffer.data(), 0, buffer.size());
+    }
+    if (HAILO_STREAM_INTERNAL_ABORT == status) {
+        LOGGER__INFO("read_all was aborted!");
+        return status;
+    }
+    CHECK_SUCCESS(status);
+
+    return HAILO_SUCCESS;
+}
+
+Expected<size_t> EthernetOutputStream::sync_read_raw_buffer(MemoryView &buffer)
+{
+    auto status = get_network_group_activated_event()->wait(std::chrono::milliseconds(0));
+    CHECK_AS_EXPECTED(HAILO_TIMEOUT != status, HAILO_NETWORK_GROUP_NOT_ACTIVATED, 
+        "Trying to read on stream before its network_group is activated");
+    CHECK_SUCCESS_AS_EXPECTED(status);
+
+    auto buffer_size = buffer.size();
+    status = m_udp.recv((uint8_t*)buffer.data(),&buffer_size);
+    if (HAILO_STREAM_INTERNAL_ABORT == status) {
+        LOGGER__INFO("Udp recv was aborted!");
+        return make_unexpected(status);
+    }
+    CHECK_SUCCESS_AS_EXPECTED(status);
+
+    return buffer_size;
+}
+
+hailo_status EthernetOutputStream::fill_output_stream_ptr_with_info(const hailo_eth_output_stream_params_t &params, EthernetOutputStream *stream)
+{
+    if ((HAILO_FORMAT_ORDER_HAILO_NMS == stream->m_stream_info.format.order)
+        && (params.is_sync_enabled)) {
+        LOGGER__ERROR("NMS is not supported with sync enabled. Setting sync flag to false");
+        stream->configuration.is_sync_enabled = false;
+    } else {
+        stream->configuration.is_sync_enabled = params.is_sync_enabled;
+    }
+
+    stream->configuration.max_payload_size = params.max_payload_size;
+    stream->configuration.buffers_threshold = params.buffers_threshold;
+
+    stream->m_is_stream_activated = false;
+    return HAILO_SUCCESS;
+}
+
+Expected<std::unique_ptr<EthernetOutputStream>> EthernetOutputStream::create(Device &device,
+    const LayerInfo &edge_layer, const hailo_eth_output_stream_params_t &params, EventPtr network_group_activated_event)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    std::unique_ptr<EthernetOutputStream> local_stream = nullptr;
+    // TODO: try to avoid cast
+    auto eth_device = reinterpret_cast<EthernetDevice*>(&device);
+
+    const auto stream_index = edge_layer.index;
+    auto udp = eth_stream__create_udp(eth_device, params.host_address, stream_index, params.device_port, false);
+    CHECK_EXPECTED(udp);
+    local_stream = std::unique_ptr<EthernetOutputStream>(new (std::nothrow) EthernetOutputStream(device,
+        edge_layer, 
+        udp.release(), std::move(network_group_activated_event), status));
+    CHECK((nullptr != local_stream), make_unexpected(HAILO_OUT_OF_HOST_MEMORY));
+    CHECK_SUCCESS_AS_EXPECTED(status);
+
+    status = fill_output_stream_ptr_with_info(params, local_stream.get());
+    CHECK_SUCCESS_AS_EXPECTED(status);
+    return local_stream;
+}
+
+hailo_status EthernetOutputStream::set_timeout(std::chrono::milliseconds timeout)
+{
+    return m_udp.set_timeout(timeout);
+}
+
+std::chrono::milliseconds EthernetOutputStream::get_timeout() const
+{
+    return std::chrono::milliseconds((MILLISECONDS_IN_SECOND * m_udp.m_timeout.tv_sec) + (m_udp.m_timeout.tv_usec / MICROSECONDS_IN_MILLISECOND));
+}
+
+hailo_status EthernetOutputStream::abort()
+{
+    return m_udp.abort();
+}
+
+hailo_status EthernetInputStream::abort()
+{
+    return m_udp.abort();
+}
+
+} /* namespace hailort */
diff --git a/hailort/libhailort/src/eth_stream.hpp b/hailort/libhailort/src/eth_stream.hpp
new file mode 100644 (file)
index 0000000..241c495
--- /dev/null
@@ -0,0 +1,207 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file eth_stream.hpp
+ * @brief TODO: brief
+ *
+ * TODO: doc
+ **/
+
+#ifndef HAILO_ETH_STREAM_H_
+#define HAILO_ETH_STREAM_H_
+
+#include "stream_internal.hpp"
+#include "hailo/hailort.h"
+#include "token_bucket.hpp"
+#include "udp.hpp"
+#include "hailo/hef.hpp"
+#include "hailo/device.hpp"
+#include "hailo/event.hpp"
+
+#if defined(__GNUC__)
+#include "common/os/posix/traffic_control.hpp"
+#endif
+
+namespace hailort
+{
+
+// TODO: move those structs to hailort.h when implemented
+typedef struct {
+    uint16_t max_payload_size;
+    bool use_dataflow_padding;
+    bool is_sync_enabled;
+    uint32_t frames_per_sync;
+    uint32_t packets_per_frame;
+    uint16_t sync_size;
+    uint32_t buffers_threshold;
+} hailo_stream_eth_input_configuration_t;
+
+typedef struct {
+    uint16_t max_payload_size;
+    bool is_sync_enabled;
+    uint32_t buffers_threshold;
+} hailo_stream_eth_output_configuration_t;
+
+class EthernetInputStream : public InputStreamBase {
+private:
+    hailo_stream_eth_input_configuration_t configuration;
+    Udp m_udp;
+    bool m_is_stream_activated;
+    Device &m_device;
+
+    hailo_status eth_stream__config_input_sync_params(uint32_t frames_per_sync);
+    hailo_status eth_stream__write_all_no_sync(void *buffer, size_t offset, size_t size);
+    hailo_status eth_stream__write_all_with_sync(void *buffer, size_t offset, size_t size);
+    hailo_status set_timeout(std::chrono::milliseconds timeout);
+    void set_max_payload_size(uint16_t size);
+
+protected:
+    virtual hailo_status eth_stream__write_with_remainder(void *buffer, size_t offset, size_t size, size_t remainder_size);
+    virtual Expected<size_t> sync_write_raw_buffer(const MemoryView &buffer) override;
+    virtual hailo_status sync_write_all_raw_buffer_no_transform_impl(void *buffer, size_t offset, size_t size) override;
+
+public:
+    EthernetInputStream(Device &device, Udp &&udp, EventPtr &&network_group_activated_event, const LayerInfo &layer_info, hailo_status &status) :
+        InputStreamBase(layer_info, HAILO_STREAM_INTERFACE_ETH, std::move(network_group_activated_event), status), m_udp(std::move(udp)), m_device(device) {}
+    EthernetInputStream(EthernetInputStream&& other) :
+        InputStreamBase(std::move(other)),
+        configuration(std::move(other.configuration)),
+        m_udp(std::move(other.m_udp)),
+        m_is_stream_activated(std::exchange(other.m_is_stream_activated, false)),
+        m_device(other.m_device)
+    {}
+
+    virtual ~EthernetInputStream();
+
+    static Expected<std::unique_ptr<EthernetInputStream>> create(Device &device,
+        const LayerInfo &edge_layer, const hailo_eth_input_stream_params_t &params, EventPtr network_group_activated_event);
+
+    uint16_t get_remote_port();
+    virtual hailo_status activate_stream() override;
+    virtual hailo_status deactivate_stream() override;
+    virtual hailo_stream_interface_t get_interface() const override { return HAILO_STREAM_INTERFACE_ETH; }
+    virtual std::chrono::milliseconds get_timeout() const override;
+    virtual hailo_status abort() override;
+    virtual hailo_status clear_abort() override {return HAILO_SUCCESS;}; // TODO (HRT-3799): clear abort state in the eth stream
+};
+
+class EthernetInputStreamRateLimited : public EthernetInputStream {
+protected:
+    const uint32_t rate_bytes_per_sec;
+
+public:
+    EthernetInputStreamRateLimited(Device &device, Udp &&udp, EventPtr &&network_group_activated_event,
+        uint32_t rate_bytes_per_sec, const LayerInfo &layer_info, hailo_status &status);
+    EthernetInputStreamRateLimited(EthernetInputStreamRateLimited &&other);
+    virtual ~EthernetInputStreamRateLimited() = default;
+};
+
+class TokenBucketEthernetInputStream : public EthernetInputStreamRateLimited {
+private:
+    DynamicTokenBucket token_bucket;
+    // Note:
+    // * We set the token bucket's burst size to be our MTU. If we'd use larger burst sizes
+    //   we could send packets faster than the desired rate.
+    // * We send packets with at most MAX_UDP_PAYLOAD_SIZE bytes of data. Hence we won't
+    //   consume more than MAX_UDP_PAYLOAD_SIZE tokens from the token bucket.
+    static const uint32_t BURST_SIZE = MAX_UDP_PAYLOAD_SIZE;
+    static const uint32_t MAX_CONSUME_SIZE = MAX_UDP_PAYLOAD_SIZE;
+
+protected:
+    virtual hailo_status eth_stream__write_with_remainder(void *buffer, size_t offset, size_t size, size_t remainder_size);
+
+public:
+    TokenBucketEthernetInputStream(Device &device, Udp &&udp, EventPtr &&network_group_activated_event,
+        uint32_t rate_bytes_per_sec, const LayerInfo &layer_info, hailo_status &status);
+    TokenBucketEthernetInputStream(TokenBucketEthernetInputStream &&other);
+    virtual ~TokenBucketEthernetInputStream() = default;
+};
+
+
+#if defined(__GNUC__)
+class TrafficControlEthernetInputStream : public EthernetInputStreamRateLimited {
+public:
+    static Expected<std::unique_ptr<TrafficControlEthernetInputStream>> create(Device &device, Udp &&udp,
+        EventPtr &&network_group_activated_event, uint32_t rate_bytes_per_sec, const LayerInfo &layer_info);
+    TrafficControlEthernetInputStream(TrafficControlEthernetInputStream&& other) = default;
+    virtual ~TrafficControlEthernetInputStream() = default;
+
+private:
+    TrafficControlEthernetInputStream(Device &device, Udp &&udp, EventPtr &&network_group_activated_event,
+        uint32_t rate_bytes_per_sec, TrafficControl &&tc, const LayerInfo &layer_info, hailo_status &status);
+    static Expected<std::string> get_interface_address(const struct in_addr *addr);
+
+    TrafficControl m_tc;
+};
+#endif
+
+class EthernetOutputStream : public OutputStreamBase {
+private:
+    uint8_t leftover_buffer[MAX_UDP_PAYLOAD_SIZE];
+    size_t leftover_size = 0;
+    uint32_t last_seen_sync_index;
+    bool encountered_timeout;
+    hailo_stream_eth_output_configuration_t configuration;
+    Udp m_udp;
+    bool m_is_stream_activated;
+    Device &m_device;
+
+    EthernetOutputStream(Device &device, const LayerInfo &edge_layer, Udp &&udp, EventPtr &&network_group_activated_event, hailo_status &status) :
+        OutputStreamBase(edge_layer, std::move(network_group_activated_event), status),
+        leftover_buffer(),
+        leftover_size(0),
+        // Firmware starts sending sync sequence from 0, so treating the first previous as max value (that will be overflowed to 0)
+        last_seen_sync_index(std::numeric_limits<uint32_t>::max()),
+        encountered_timeout(false),
+        configuration(),
+        m_udp(std::move(udp)),
+        m_device(device)
+    {}
+
+    hailo_status read_all(MemoryView &buffer) override;
+    hailo_status read_all_with_sync(void *buffer, size_t offset, size_t size);
+    hailo_status read_all_no_sync(void *buffer, size_t offset, size_t size);
+
+    static bool is_sync_packet(const void* buffer, size_t offset, size_t transfer_size);
+    static bool is_sync_expected(size_t offset, size_t initial_offset, size_t frame_size);
+    hailo_status handle_timeout(const void* buffer, size_t offset, size_t initial_offset, size_t frame_size);
+    hailo_status set_timeout(std::chrono::milliseconds timeout);
+    hailo_status get_last_sync();
+
+    static hailo_status fill_output_stream_ptr_with_info(const hailo_eth_output_stream_params_t &params, EthernetOutputStream *stream);
+
+public:
+    EthernetOutputStream(EthernetOutputStream&& other) :
+        OutputStreamBase(std::move(other)),
+        leftover_buffer(),
+        leftover_size(std::move(other.leftover_size)),
+        last_seen_sync_index(std::move(other.last_seen_sync_index)),
+        encountered_timeout(std::move(other.encountered_timeout)),
+        configuration(std::move(other.configuration)),
+        m_udp(std::move(other.m_udp)),
+        m_is_stream_activated(std::exchange(other.m_is_stream_activated, false)),
+        m_device(other.m_device)
+    {
+        memcpy(leftover_buffer, other.leftover_buffer, sizeof(leftover_buffer));
+    }
+
+    virtual ~EthernetOutputStream();
+
+    virtual Expected<size_t> sync_read_raw_buffer(MemoryView &buffer);
+
+    static Expected<std::unique_ptr<EthernetOutputStream>> create(Device &device, const LayerInfo &edge_layer,
+        const hailo_eth_output_stream_params_t &params, EventPtr network_group_activated_event);
+
+    virtual hailo_status activate_stream() override;
+    virtual hailo_status deactivate_stream() override;
+    virtual hailo_stream_interface_t get_interface() const override { return HAILO_STREAM_INTERFACE_ETH; }
+    virtual std::chrono::milliseconds get_timeout() const override;
+    virtual hailo_status abort() override;
+    virtual hailo_status clear_abort() override {return HAILO_SUCCESS;}; // TODO (HRT-3799): clear abort state in the eth stream
+};
+
+} /* namespace hailort */
+
+#endif /* HAILO_ETH_STREAM_H_ */
diff --git a/hailort/libhailort/src/event_internal.hpp b/hailort/libhailort/src/event_internal.hpp
new file mode 100644 (file)
index 0000000..605e176
--- /dev/null
@@ -0,0 +1,85 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file event.hpp
+ * @brief Event and Semaphore wrapper objects used for multithreading
+ **/
+
+#ifndef _EVENT_INTERNAL_HPP_
+#define _EVENT_INTERNAL_HPP_
+
+#include "hailo/hailort.h"
+#include "hailo/expected.hpp"
+
+#include <memory>
+#include <vector>
+#include <array>
+#include <chrono>
+#if defined(__GNUC__)
+#include <poll.h>
+#endif
+
+namespace hailort
+{
+
+// TODO: Replace with a static wait_multiple func belonging to Waitable (SDK-16567).
+//       Will get a vector of pointers as an argument. Can also use variadic
+//       template args for cases with fixed number Waitables
+class WaitOrShutdown final
+{
+public:
+    WaitOrShutdown(WaitablePtr waitable, EventPtr shutdown_event);
+    ~WaitOrShutdown() = default;
+
+    WaitOrShutdown(const WaitOrShutdown &other) = delete;
+    WaitOrShutdown &operator=(const WaitOrShutdown &other) = delete;
+    WaitOrShutdown(WaitOrShutdown &&other) noexcept = default;
+    WaitOrShutdown &operator=(WaitOrShutdown &&other) = delete;
+
+    // Waits on waitable or shutdown_event to be signaled:
+    // * If shutdown_event is signaled:
+    //   - shutdown_event is not reset
+    //   - HAILO_SHUTDOWN_EVENT_SIGNALED is returned
+    // * If waitable is signaled:
+    //   - waitable is reset if waitable->is_auto_reset()
+    //   - HAILO_SUCCESS is returned
+    // * If both waitable and shutdown_event are signaled:
+    //   - shutdown_event is not reset
+    //   - waitable is not reset
+    //   - HAILO_SHUTDOWN_EVENT_SIGNALED is returned
+    // * If neither are signaled, then HAILO_TIMEOUT is returned
+    // * On any failure an appropriate status shall be returned
+    hailo_status wait(std::chrono::milliseconds timeout);
+    hailo_status signal();
+
+private:
+    // Note: We want to guarantee that if the shutdown event is signaled, HAILO_SHUTDOWN_EVENT_SIGNALED will be
+    //       returned.
+    //       * In Unix, using poll this isn't a problem since we'll get all the readable fds in a single call.
+    //       * In Windows, using WaitForMultipleObjects, this works differently (from msdn):
+    //         If bWaitAll is FALSE, the return value minus WAIT_OBJECT_0 indicates the lpHandles array index
+    //         of the object that satisfied the wait. If more than one object became signaled during the call,
+    //         this is the array index of the signaled object with the smallest index value of all the signaled
+    //         objects.
+    //         (https://docs.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-waitformultipleobjects)
+    //       * Hence, SHUTDOWN_INDEX must come before WAITABLE_INDEX!
+    static const size_t SHUTDOWN_INDEX = 0;
+    static const size_t WAITABLE_INDEX = 1;
+    #if defined(_MSC_VER)
+    using WaitHandleArray = std::array<underlying_handle_t, 2>;
+    #else
+    using WaitHandleArray = std::array<struct pollfd, 2>;
+    #endif
+
+    const WaitablePtr m_waitable;
+    const EventPtr m_shutdown_event;
+    WaitHandleArray m_wait_handle_array;
+
+    static WaitHandleArray create_wait_handle_array(WaitablePtr waitable, EventPtr shutdown_event);
+};
+
+} /* namespace hailort */
+
+#endif /* _EVENT_INTERNAL_HPP_ */
diff --git a/hailort/libhailort/src/hailort.cpp b/hailort/libhailort/src/hailort.cpp
new file mode 100644 (file)
index 0000000..c771928
--- /dev/null
@@ -0,0 +1,2089 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file hailort.cpp
+ * @brief HailoRT library.
+ *
+ * Hailo runtime (HailoRT) is a library for neural network inference on Hailo devices.
+ **/
+
+
+#include "hailo/hailort.h"
+#include "hailo/hailort_common.hpp"
+#include "hailo/hef.hpp"
+#include "hailo/stream.hpp"
+#include "hailo/device.hpp"
+#include "hailo/vdevice.hpp"
+#include "hailo/transform.hpp"
+#include "hailo/vstream.hpp"
+#include "hailo/event.hpp"
+#include "hailo/network_rate_calculator.hpp"
+#include "hailo/inference_pipeline.hpp"
+#include "eth_device.hpp"
+#include "pcie_device.hpp"
+#include "pcie_stream.hpp"
+#include "eth_stream.hpp"
+#include "control.hpp"
+#include "sensor_config_utils.hpp"
+#include "common/compiler_extensions_compat.hpp"
+#include "hailort_logger.hpp"
+
+#include <chrono>
+
+using namespace hailort;
+
+COMPAT__INITIALIZER(hailort__initialize_logger)
+{
+    // Init logger singleton if compiling only HailoRT
+    (void) HailoRTLogger::get_instance();
+}
+
+hailo_status hailo_get_library_version(hailo_version_t *version)
+{
+    CHECK_ARG_NOT_NULL(version);
+    version->major = HAILORT_MAJOR_VERSION;
+    version->minor = HAILORT_MINOR_VERSION;
+    version->revision = HAILORT_REVISION_VERSION;
+    return HAILO_SUCCESS;
+}
+
+// TODO(oro): wrap with try/catch over C++
+// TODO: Fill eth_device_infos_length items into pcie_device_infos, 
+//       even if 'scan_results->size() > eth_device_infos_length' (HRT-3163)
+hailo_status hailo_scan_ethernet_devices(const char *interface_name, hailo_eth_device_info_t *eth_device_infos,
+    size_t eth_device_infos_length, size_t *number_of_devices, uint32_t timeout_ms)
+{
+    CHECK_ARG_NOT_NULL(interface_name);
+    CHECK_ARG_NOT_NULL(eth_device_infos);
+    CHECK_ARG_NOT_NULL(number_of_devices);
+
+    auto scan_results = EthernetDevice::scan(interface_name, std::chrono::milliseconds(timeout_ms));
+    CHECK_EXPECTED_AS_STATUS(scan_results);
+
+    CHECK(scan_results->size() <= eth_device_infos_length, HAILO_INSUFFICIENT_BUFFER,
+        "eth_device_infos buffer not large enough (required: {}, buffer_length: {}))",
+            scan_results->size(), eth_device_infos_length);
+
+    memcpy(eth_device_infos, scan_results->data(), sizeof(*eth_device_infos)*scan_results->size());
+    *number_of_devices = scan_results->size();
+    return HAILO_SUCCESS;
+}
+
+hailo_status hailo_create_ethernet_device(hailo_eth_device_info_t *device_info, hailo_device *device_out)
+{
+    CHECK_ARG_NOT_NULL(device_info);
+    CHECK_ARG_NOT_NULL(device_out);
+
+    auto device = EthernetDevice::create(*device_info);
+    CHECK_EXPECTED_AS_STATUS(device);
+    *device_out = reinterpret_cast<hailo_device>(device.release().release());
+    return HAILO_SUCCESS;
+}
+
+hailo_status hailo_identify(hailo_device device, hailo_device_identity_t *device_identity)
+{
+    CHECK_ARG_NOT_NULL(device);
+    CHECK_ARG_NOT_NULL(device_identity);
+
+    auto identity = (reinterpret_cast<Device*>(device))->identify();
+    CHECK_EXPECTED_AS_STATUS(identity);
+    *device_identity = identity.release();
+
+    return HAILO_SUCCESS;
+}
+
+hailo_status hailo_core_identify(hailo_device device, hailo_core_information_t *core_information)
+{
+    CHECK_ARG_NOT_NULL(device);
+    CHECK_ARG_NOT_NULL(core_information);
+
+    auto identity = (reinterpret_cast<Device*>(device))->core_identify();
+    CHECK_EXPECTED_AS_STATUS(identity);
+    *core_information = identity.release();
+
+    return HAILO_SUCCESS;
+}
+
+hailo_status hailo_get_extended_device_information(hailo_device device, hailo_extended_device_information_t *extended_device_information)
+{
+    CHECK_ARG_NOT_NULL(device);
+    CHECK_ARG_NOT_NULL(extended_device_information);
+
+    auto extended_device_info_expected = (reinterpret_cast<Device*>(device))->get_extended_device_information();
+    CHECK_EXPECTED_AS_STATUS(extended_device_info_expected);
+    *extended_device_information = extended_device_info_expected.release();
+
+    return HAILO_SUCCESS;
+}
+
+// TODO: Fill pcie_device_infos_length items into pcie_device_infos, 
+//       even if 'scan_results->size() > pcie_device_infos_length' (HRT-3163)
+hailo_status hailo_scan_pcie_devices(
+    hailo_pcie_device_info_t *pcie_device_infos, size_t pcie_device_infos_length, size_t *number_of_devices)
+{
+    CHECK_ARG_NOT_NULL(pcie_device_infos);
+    CHECK_ARG_NOT_NULL(number_of_devices);
+
+    auto scan_results = PcieDevice::scan();
+    CHECK_EXPECTED_AS_STATUS(scan_results);
+
+    CHECK(scan_results->size() <= pcie_device_infos_length, HAILO_INSUFFICIENT_BUFFER,
+        "pcie_device_infos buffer not large enough (required: {}, buffer_size: {}))", scan_results->size(), pcie_device_infos_length);
+
+    memcpy(pcie_device_infos, scan_results->data(), sizeof(*pcie_device_infos)*scan_results->size());
+    *number_of_devices = scan_results->size();
+    return HAILO_SUCCESS;
+}
+
+hailo_status hailo_parse_pcie_device_info(const char *device_info_str,
+    hailo_pcie_device_info_t *device_info)
+{
+    CHECK_ARG_NOT_NULL(device_info_str);
+    CHECK_ARG_NOT_NULL(device_info);
+
+    auto local_device_info = PcieDevice::parse_pcie_device_info(std::string(device_info_str));
+    CHECK_EXPECTED_AS_STATUS(local_device_info);
+
+    *device_info = local_device_info.value();
+    return HAILO_SUCCESS;
+}
+
+hailo_status hailo_create_pcie_device(hailo_pcie_device_info_t *device_info, hailo_device *device_out)
+{
+    CHECK_ARG_NOT_NULL(device_out);
+
+    auto device = (device_info == nullptr) ? PcieDevice::create() : PcieDevice::create(*device_info);
+    CHECK_EXPECTED_AS_STATUS(device, "Failed creating pcie device");
+    *device_out = reinterpret_cast<hailo_device>(device.release().release());
+
+    return HAILO_SUCCESS;
+}
+
+hailo_status hailo_release_device(hailo_device device_ptr)
+{
+    CHECK_ARG_NOT_NULL(device_ptr);
+    delete reinterpret_cast<Device*>(device_ptr);
+    return HAILO_SUCCESS;
+}
+
+hailo_status hailo_set_fw_logger(hailo_device device, hailo_fw_logger_level_t level, uint32_t interface_mask)
+{
+    CHECK_ARG_NOT_NULL(device);
+    auto status = (reinterpret_cast<Device*>(device))->set_fw_logger(level, interface_mask);
+    CHECK_SUCCESS(status);
+    return HAILO_SUCCESS;
+}
+
+hailo_status hailo_set_throttling_state(hailo_device device, bool should_activate)
+{
+    CHECK_ARG_NOT_NULL(device);
+    auto status = (reinterpret_cast<Device*>(device))->set_throttling_state(should_activate);
+    CHECK_SUCCESS(status);
+    return HAILO_SUCCESS;
+}
+
+hailo_status hailo_get_throttling_state(hailo_device device, bool *is_active)
+{
+    CHECK_ARG_NOT_NULL(device);
+    CHECK_ARG_NOT_NULL(is_active);
+
+    auto is_throttling_enabled_expected = (reinterpret_cast<Device*>(device))->get_throttling_state();
+    CHECK_EXPECTED_AS_STATUS(is_throttling_enabled_expected);
+    *is_active = is_throttling_enabled_expected.release();
+    return HAILO_SUCCESS;
+}
+
+hailo_status hailo_wd_enable(hailo_device device, hailo_cpu_id_t cpu_id)
+{
+    CHECK_ARG_NOT_NULL(device);
+    auto status = (reinterpret_cast<Device*>(device))->wd_enable(cpu_id);
+    CHECK_SUCCESS(status);
+    return HAILO_SUCCESS;
+}
+
+hailo_status hailo_wd_disable(hailo_device device, hailo_cpu_id_t cpu_id)
+{
+    CHECK_ARG_NOT_NULL(device);
+    auto status = (reinterpret_cast<Device*>(device))->wd_disable(cpu_id);
+    CHECK_SUCCESS(status);
+    return HAILO_SUCCESS;
+}
+
+hailo_status hailo_wd_config(hailo_device device, hailo_cpu_id_t cpu_id, uint32_t wd_cycles, hailo_watchdog_mode_t wd_mode)
+{
+    CHECK_ARG_NOT_NULL(device);
+    auto status = (reinterpret_cast<Device*>(device))->wd_config(cpu_id, wd_cycles, wd_mode);
+    CHECK_SUCCESS(status);
+    return HAILO_SUCCESS;
+}
+
+hailo_status hailo_get_previous_system_state(hailo_device device, hailo_cpu_id_t cpu_id, uint32_t *previous_system_state)
+{
+    CHECK_ARG_NOT_NULL(device);
+    CHECK_ARG_NOT_NULL(previous_system_state);
+    auto res = (reinterpret_cast<Device*>(device))->previous_system_state(cpu_id);
+    CHECK_EXPECTED_AS_STATUS(res);
+    *previous_system_state = res.release();
+    return HAILO_SUCCESS;
+}
+
+hailo_status hailo_set_pause_frames(hailo_device device, bool rx_pause_frames_enable)
+{
+    CHECK_ARG_NOT_NULL(device);
+    auto status = (reinterpret_cast<Device*>(device))->set_pause_frames(rx_pause_frames_enable);
+    CHECK_SUCCESS(status);
+    return HAILO_SUCCESS;
+}
+
+hailo_status hailo_get_device_id(hailo_device device, hailo_device_id_t *id)
+{
+    CHECK_ARG_NOT_NULL(device);
+    CHECK_ARG_NOT_NULL(id);
+    auto device_id = (reinterpret_cast<Device*>(device))->get_dev_id();
+    CHECK(strnlen(device_id, HAILO_MAX_DEVICE_ID_LENGTH) < HAILO_MAX_DEVICE_ID_LENGTH, HAILO_INTERNAL_FAILURE,
+        "Device '{}' has a too long name (max is HAILO_MAX_DEVICE_ID_LENGTH)", device_id);
+    strncpy(id->id, device_id, HAILO_MAX_DEVICE_ID_LENGTH - 1);
+    id->id[HAILO_MAX_DEVICE_ID_LENGTH - 1] = 0;
+    return HAILO_SUCCESS;
+}
+
+hailo_status hailo_get_chip_temperature(hailo_device device, hailo_chip_temperature_info_t *temp_info)
+{
+    CHECK_ARG_NOT_NULL(device);
+    CHECK_ARG_NOT_NULL(temp_info);
+    auto res = (reinterpret_cast<Device*>(device))->get_chip_temperature();
+    CHECK_EXPECTED_AS_STATUS(res);
+    *temp_info = res.release();
+    return HAILO_SUCCESS;
+}
+
+hailo_status hailo_reset_device(hailo_device device, hailo_reset_device_mode_t mode)
+{
+    CHECK_ARG_NOT_NULL(device);
+    auto status = (reinterpret_cast<Device*>(device))->reset(mode);
+    CHECK_SUCCESS(status);
+    return HAILO_SUCCESS;
+}
+
+hailo_status hailo_update_firmware(hailo_device device, void *firmware_buffer, uint32_t firmware_buffer_size)
+{
+    CHECK_ARG_NOT_NULL(device);
+    CHECK_ARG_NOT_NULL(firmware_buffer);
+    auto status = reinterpret_cast<Device*>(device)->firmware_update(MemoryView(firmware_buffer, firmware_buffer_size), true);
+    CHECK_SUCCESS(status);
+    return HAILO_SUCCESS;
+}
+
+hailo_status hailo_update_second_stage(hailo_device device, void *second_stage_buffer, uint32_t second_stage_buffer_size)
+{
+    CHECK_ARG_NOT_NULL(device);
+    CHECK_ARG_NOT_NULL(second_stage_buffer);
+    auto status = reinterpret_cast<Device*>(device)->second_stage_update(reinterpret_cast<uint8_t*>(second_stage_buffer), second_stage_buffer_size);
+    CHECK_SUCCESS(status);
+    return HAILO_SUCCESS;
+}
+
+hailo_status hailo_power_measurement(hailo_device device, hailo_dvm_options_t dvm,
+    hailo_power_measurement_types_t measurement_type, float32_t *measurement)
+{
+    CHECK_ARG_NOT_NULL(device);
+    CHECK_ARG_NOT_NULL(measurement);
+    auto status = Control::power_measurement(*reinterpret_cast<Device*>(device), static_cast<CONTROL_PROTOCOL__dvm_options_t>(dvm), static_cast<CONTROL_PROTOCOL__power_measurement_types_t>(measurement_type), measurement);
+    CHECK_SUCCESS(status);
+    return HAILO_SUCCESS;
+}
+
+hailo_status hailo_start_power_measurement(hailo_device device, uint32_t delay_milliseconds,
+    hailo_averaging_factor_t averaging_factor, hailo_sampling_period_t sampling_period)
+{
+    CHECK_ARG_NOT_NULL(device);
+    auto status = Control::start_power_measurement(*reinterpret_cast<Device*>(device), delay_milliseconds, static_cast<CONTROL_PROTOCOL__averaging_factor_t>(averaging_factor), static_cast<CONTROL_PROTOCOL__sampling_period_t>(sampling_period));
+    CHECK_SUCCESS(status);
+    return HAILO_SUCCESS;
+}
+
+hailo_status hailo_set_power_measurement(hailo_device device, uint32_t index,
+    hailo_dvm_options_t dvm, hailo_power_measurement_types_t measurement_type)
+{
+    CHECK_ARG_NOT_NULL(device);
+    auto status = Control::set_power_measurement(*reinterpret_cast<Device*>(device), index, static_cast<CONTROL_PROTOCOL__dvm_options_t>(dvm), static_cast<CONTROL_PROTOCOL__power_measurement_types_t>(measurement_type));
+    CHECK_SUCCESS(status);
+    return HAILO_SUCCESS;
+}
+
+hailo_status hailo_get_power_measurement(hailo_device device, uint32_t index, bool should_clear,
+    hailo_power_measurement_data_t *measurement_data)
+{
+    CHECK_ARG_NOT_NULL(device);
+    CHECK_ARG_NOT_NULL(measurement_data);
+    auto status = Control::get_power_measurement(*reinterpret_cast<Device*>(device), index, should_clear, measurement_data);
+    CHECK_SUCCESS(status);
+    return HAILO_SUCCESS;
+}
+
+hailo_status hailo_stop_power_measurement(hailo_device device)
+{
+    CHECK_ARG_NOT_NULL(device);
+    auto status = Control::stop_power_measurement(*reinterpret_cast<Device*>(device));
+    CHECK_SUCCESS(status);
+    return HAILO_SUCCESS;
+}
+
+hailo_status hailo_reset_sensor(hailo_device device, uint8_t section_index)
+{
+    CHECK_ARG_NOT_NULL(device);
+    auto status = (reinterpret_cast<Device*>(device))->sensor_reset(section_index);
+    CHECK_SUCCESS(status);
+    return HAILO_SUCCESS;
+}
+
+hailo_status hailo_set_sensor_i2c_bus_index(hailo_device device, hailo_sensor_types_t sensor_type, uint8_t bus_index)
+{
+    CHECK_ARG_NOT_NULL(device);
+    auto status = Control::sensor_set_i2c_bus_index(*reinterpret_cast<Device*>(device), sensor_type, bus_index);
+    CHECK_SUCCESS(status);
+    return HAILO_SUCCESS;
+}
+
+hailo_status hailo_load_and_start_sensor(hailo_device device, uint8_t section_index)
+{
+    CHECK_ARG_NOT_NULL(device);
+    auto status = (reinterpret_cast<Device*>(device))->sensor_load_and_start_config(section_index);
+    CHECK_SUCCESS(status);
+    return HAILO_SUCCESS;
+}
+
+hailo_status hailo_i2c_read(hailo_device device, const hailo_i2c_slave_config_t *slave_config, uint32_t register_address, uint8_t *data, uint32_t length)
+{
+    CHECK_ARG_NOT_NULL(device);
+    CHECK_ARG_NOT_NULL(slave_config);
+    CHECK_ARG_NOT_NULL(data);
+    auto status = Control::i2c_read(*reinterpret_cast<Device*>(device), slave_config, register_address, data, length);
+    CHECK_SUCCESS(status);
+    return HAILO_SUCCESS;
+}
+
+hailo_status hailo_i2c_write(hailo_device device, const hailo_i2c_slave_config_t *slave_config, uint32_t register_address, const uint8_t *data, uint32_t length)
+{
+    CHECK_ARG_NOT_NULL(device);
+    CHECK_ARG_NOT_NULL(slave_config);
+    CHECK_ARG_NOT_NULL(data);
+    auto status = Control::i2c_write(*reinterpret_cast<Device*>(device), slave_config, register_address, data, length);
+    CHECK_SUCCESS(status);
+    return HAILO_SUCCESS;
+}
+
+hailo_status hailo_dump_sensor_config(hailo_device device, uint8_t section_index, const char *config_file_path)
+{
+    CHECK_ARG_NOT_NULL(device);
+    CHECK_ARG_NOT_NULL(config_file_path);
+
+    auto status = (reinterpret_cast<Device*>(device))->sensor_dump_config(section_index, config_file_path);
+    CHECK_SUCCESS(status);
+    return HAILO_SUCCESS;
+}
+
+hailo_status hailo_store_sensor_config(hailo_device device, uint32_t section_index, hailo_sensor_types_t sensor_type,
+    uint32_t reset_config_size, uint16_t config_height, uint16_t config_width, uint16_t config_fps,
+    const char *config_file_path, const char *config_name)
+{   
+    CHECK_ARG_NOT_NULL(device);
+    CHECK_ARG_NOT_NULL(config_file_path);
+    CHECK_ARG_NOT_NULL(config_name);
+    
+    auto status = (reinterpret_cast<Device*>(device))->store_sensor_config(section_index, sensor_type, reset_config_size, config_height, config_width,
+        config_fps, config_file_path, config_name);
+    CHECK_SUCCESS(status);
+    return HAILO_SUCCESS;
+}
+
+hailo_status hailo_store_isp_config(hailo_device device, uint32_t reset_config_size, uint16_t config_height, uint16_t config_width,
+    uint16_t config_fps,  const char *isp_static_config_file_path, const char *isp_runtime_config_file_path, const char *config_name)
+{
+    CHECK_ARG_NOT_NULL(device);
+    CHECK_ARG_NOT_NULL(isp_static_config_file_path);
+    CHECK_ARG_NOT_NULL(isp_runtime_config_file_path);
+    CHECK_ARG_NOT_NULL(config_name);
+    
+    auto status = (reinterpret_cast<Device*>(device))->store_isp_config(reset_config_size, config_height, config_width,
+        config_fps, isp_static_config_file_path, isp_runtime_config_file_path, config_name);
+    CHECK_SUCCESS(status);
+    return HAILO_SUCCESS;
+}
+
+hailo_status hailo_create_hef_file(hailo_hef *hef_out, const char *file_name)
+{
+    CHECK_ARG_NOT_NULL(hef_out);
+    CHECK_ARG_NOT_NULL(file_name);
+
+    auto hef = Hef::create(file_name);
+    CHECK_EXPECTED_AS_STATUS(hef);
+
+    auto allocated_hef = new (std::nothrow) Hef(hef.release());
+    CHECK_NOT_NULL(allocated_hef, HAILO_OUT_OF_HOST_MEMORY);
+
+    *hef_out = reinterpret_cast<hailo_hef>(allocated_hef);
+    return HAILO_SUCCESS;
+}
+
+hailo_status hailo_create_hef_buffer(hailo_hef *hef_out, const void *buffer, size_t size)
+{
+    CHECK_ARG_NOT_NULL(hef_out);
+    CHECK_ARG_NOT_NULL(buffer);
+
+    auto hef = Hef::create(MemoryView::create_const(buffer, size));
+    CHECK_EXPECTED_AS_STATUS(hef);
+
+    auto allocated_hef = new (std::nothrow) Hef(hef.release());
+    CHECK_NOT_NULL(allocated_hef, HAILO_OUT_OF_HOST_MEMORY);
+
+    *hef_out = reinterpret_cast<hailo_hef>(allocated_hef);
+    return HAILO_SUCCESS;
+}
+
+hailo_status hailo_release_hef(hailo_hef hef_ptr)
+{
+    CHECK_ARG_NOT_NULL(hef_ptr);
+
+    auto hef = reinterpret_cast<Hef*>(hef_ptr);
+    delete hef;
+    return HAILO_SUCCESS;
+}
+
+static hailo_status hailo_init_network_params(hailo_hef hef, std::string net_group_name, 
+    hailo_network_parameters_by_name_t *network_params, size_t &network_params_by_name_count)
+{
+    CHECK_ARG_NOT_NULL(hef);
+    CHECK_ARG_NOT_NULL(network_params);
+
+    auto network_params_by_name = reinterpret_cast<Hef*>(hef)->create_network_parameters_by_name(net_group_name);
+    CHECK_EXPECTED_AS_STATUS(network_params_by_name);
+    CHECK(HAILO_MAX_NETWORKS_IN_NETWORK_GROUP >= network_params_by_name->size(), HAILO_INTERNAL_FAILURE,
+        "Too many networks in network group {}", net_group_name);
+    network_params_by_name_count = network_params_by_name->size();
+    int network = 0;
+    for (const auto &network_params_pair : network_params_by_name.release()) {
+        CHECK(network_params_pair.first.length() < HAILO_MAX_NETWORK_NAME_SIZE, HAILO_INTERNAL_FAILURE,
+            "network '{}' has a too long name (max is HAILO_MAX_NETWORK_NAME_SIZE)", network_params_pair.first);
+
+        hailo_network_parameters_by_name_t params_by_name = {};
+        strncpy(params_by_name.name, network_params_pair.first.c_str(), network_params_pair.first.length() + 1);
+        params_by_name.network_params = network_params_pair.second;
+        network_params[network] = params_by_name;
+        network++;
+    }
+
+    return HAILO_SUCCESS;
+}
+
+hailo_status hailo_init_configure_params(hailo_hef hef, hailo_stream_interface_t stream_interface,
+    hailo_configure_params_t *params)
+{
+    CHECK_ARG_NOT_NULL(hef);
+    CHECK_ARG_NOT_NULL(params);
+
+    auto network_groups_names = reinterpret_cast<Hef*>(hef)->get_network_groups_names();
+    CHECK(HAILO_MAX_NETWORK_GROUPS >= network_groups_names.size(), HAILO_INVALID_HEF,
+        "Too many network_groups on a given HEF");
+
+    params->network_group_params_count = network_groups_names.size();
+    uint8_t net_group = 0;
+    for (const auto &net_group_name : network_groups_names) {
+        CHECK(HAILO_MAX_NETWORK_GROUP_NAME_SIZE > net_group_name.length(), HAILO_INTERNAL_FAILURE,
+            "Network group '{}' name is too long (max is HAILO_MAX_NETWORK_GROUP_NAME_SIZE, including NULL terminator)",
+            net_group_name);
+        strncpy(params->network_group_params[net_group].name, net_group_name.c_str(), net_group_name.length() + 1);
+
+        auto config_params =  HailoRTDefaults::get_configure_params();
+        params->network_group_params[net_group].batch_size = config_params.batch_size;
+        params->network_group_params[net_group].power_mode = config_params.power_mode;
+
+        auto stream_params_by_name = reinterpret_cast<Hef*>(hef)->create_stream_parameters_by_name(net_group_name, stream_interface);
+        CHECK_EXPECTED_AS_STATUS(stream_params_by_name);
+        CHECK(HAILO_MAX_STREAMS_COUNT >= stream_params_by_name->size(), HAILO_INTERNAL_FAILURE,
+            "Too many streams in HEF");
+        params->network_group_params[net_group].stream_params_by_name_count = stream_params_by_name->size();
+
+        uint8_t stream = 0;
+        for (const auto &stream_params_pair : stream_params_by_name.release()) {
+            CHECK(stream_params_pair.first.length() < HAILO_MAX_STREAM_NAME_SIZE, HAILO_INTERNAL_FAILURE,
+                "Stream '{}' has a too long name (max is HAILO_MAX_STREAM_NAME_SIZE)", stream_params_pair.first);
+
+            hailo_stream_parameters_by_name_t params_by_name = {};
+            strncpy(params_by_name.name, stream_params_pair.first.c_str(), stream_params_pair.first.length() + 1);
+            params_by_name.stream_params = stream_params_pair.second;
+            params->network_group_params[net_group].stream_params_by_name[stream] = params_by_name;
+            stream++;
+        }
+
+        /* Fill network params */
+        auto status = hailo_init_network_params(hef, net_group_name, 
+            params->network_group_params[net_group].network_params_by_name, 
+            params->network_group_params[net_group].network_params_by_name_count);
+        CHECK_SUCCESS(status);
+
+        net_group++;
+    }
+
+    return HAILO_SUCCESS;
+}
+
+hailo_status hailo_init_configure_params_mipi_input(hailo_hef hef, hailo_stream_interface_t output_interface,
+    hailo_mipi_input_stream_params_t *mipi_params, hailo_configure_params_t *params)
+{
+    CHECK_ARG_NOT_NULL(hef);
+    CHECK_ARG_NOT_NULL(mipi_params);
+    CHECK_ARG_NOT_NULL(params);
+
+    auto network_groups_names = reinterpret_cast<Hef*>(hef)->get_network_groups_names();
+    CHECK(HAILO_MAX_NETWORK_GROUPS >= network_groups_names.size(), HAILO_INVALID_HEF,
+        "Too many network_groups on a given HEF");
+
+    params->network_group_params_count = network_groups_names.size();
+    uint8_t net_group = 0;
+    for (const auto &net_group_name : network_groups_names) {
+        auto config_params =  HailoRTDefaults::get_configure_params();
+        strncpy(params->network_group_params[net_group].name, net_group_name.c_str(), net_group_name.length() + 1);
+        params->network_group_params[net_group].power_mode = config_params.power_mode;
+
+        auto stream_params_by_name = reinterpret_cast<Hef*>(hef)->create_stream_parameters_by_name_mipi_input(net_group_name,
+            output_interface, *mipi_params);
+        CHECK_EXPECTED_AS_STATUS(stream_params_by_name);
+        CHECK(HAILO_MAX_STREAMS_COUNT >= stream_params_by_name->size(), HAILO_INTERNAL_FAILURE,
+            "Too many streams in HEF. There are {} streams for network_group {}", stream_params_by_name->size(),
+            net_group_name);
+        params->network_group_params[net_group].stream_params_by_name_count = stream_params_by_name->size();
+
+        uint8_t stream = 0;
+        for (const auto &stream_params_pair : stream_params_by_name.release()) {
+            CHECK(stream_params_pair.first.length() < HAILO_MAX_STREAM_NAME_SIZE, HAILO_INTERNAL_FAILURE,
+                "Stream '{}' has a too long name (max is HAILO_MAX_STREAM_NAME_SIZE)", stream_params_pair.first);
+
+            hailo_stream_parameters_by_name_t params_by_name = {};
+            strncpy(params_by_name.name, stream_params_pair.first.c_str(), stream_params_pair.first.length() + 1);
+            params_by_name.stream_params = stream_params_pair.second;
+            params->network_group_params[net_group].stream_params_by_name[stream] = params_by_name;
+            stream++;
+        }
+
+        /* Fill network params */
+        auto status = hailo_init_network_params(hef, net_group_name, 
+            params->network_group_params[net_group].network_params_by_name, 
+            params->network_group_params[net_group].network_params_by_name_count);
+
+        CHECK_SUCCESS(status);
+        net_group++;
+    }
+
+    return HAILO_SUCCESS;
+}
+
+NetworkGroupsParamsMap get_configure_params_map(hailo_configure_params_t *params)
+{
+    NetworkGroupsParamsMap configure_params;
+    if (nullptr != params) {
+        for (size_t i = 0; i < params->network_group_params_count; i++) {
+            configure_params.emplace(std::string(params->network_group_params[i].name),
+                ConfigureNetworkParams(params->network_group_params[i]));
+        }
+    }
+    return configure_params;
+}
+
+hailo_status hailo_configure_device(hailo_device device, hailo_hef hef, hailo_configure_params_t *params,
+    hailo_configured_network_group *network_groups, size_t *number_of_network_groups)
+{
+    CHECK_ARG_NOT_NULL(device);
+    CHECK_ARG_NOT_NULL(hef);
+    CHECK_ARG_NOT_NULL(network_groups);
+    CHECK_ARG_NOT_NULL(number_of_network_groups);
+
+    auto configure_params = get_configure_params_map(params);
+
+    auto added_net_groups = (reinterpret_cast<Device*>(device))->configure(*reinterpret_cast<Hef*>(hef), configure_params);
+    CHECK_EXPECTED_AS_STATUS(added_net_groups);
+
+    CHECK(added_net_groups->size() <= (*number_of_network_groups), HAILO_INSUFFICIENT_BUFFER,
+        "Can't return all network_groups. HEF file contained {} network_groups, but output array is of size {}",
+        added_net_groups->size(), (*number_of_network_groups));
+
+    for (size_t i = 0; i < added_net_groups->size(); ++i) {
+        network_groups[i] = reinterpret_cast<hailo_configured_network_group>(added_net_groups.value()[i].get());
+    }
+
+    *number_of_network_groups = added_net_groups.value().size();
+    return HAILO_SUCCESS;
+}
+
+hailo_status hailo_get_network_groups_infos(hailo_hef hef, hailo_network_group_info_t *infos,
+    size_t *number_of_infos)
+{
+    CHECK_ARG_NOT_NULL(hef);
+    CHECK_ARG_NOT_NULL(infos);
+    CHECK_ARG_NOT_NULL(number_of_infos);
+
+    auto network_groups_infos = reinterpret_cast<Hef*>(hef)->get_network_groups_infos();
+    CHECK_EXPECTED_AS_STATUS(network_groups_infos);
+    if(*number_of_infos < network_groups_infos->size()) {
+        LOGGER__ERROR(
+            "The given array is to small to contain all network_groups infos. there are {} network_groups in the given HEF.",
+            network_groups_infos->size());
+        *number_of_infos = network_groups_infos->size();
+        return HAILO_INSUFFICIENT_BUFFER;
+    }
+
+    std::copy(network_groups_infos->begin(), network_groups_infos->end(), infos);
+    *number_of_infos = network_groups_infos->size();
+
+    return HAILO_SUCCESS;
+}
+
+static hailo_status convert_stream_infos_vector_to_array(std::vector<hailo_stream_info_t> &&stream_infos_vec, 
+    hailo_stream_info_t *stream_infos, size_t *number_of_streams,
+    bool include_input, bool include_output)
+{
+    stream_infos_vec.erase(std::remove_if(stream_infos_vec.begin(), stream_infos_vec.end(),
+        [include_input, include_output](const hailo_stream_info_t &stream_info) {
+            return (!include_input && (HAILO_H2D_STREAM == stream_info.direction)) ||
+                   (!include_output && (HAILO_D2H_STREAM == stream_info.direction));
+        }),
+        stream_infos_vec.end());
+
+    auto available_streams = *number_of_streams;
+    *number_of_streams = stream_infos_vec.size();
+
+    CHECK(stream_infos_vec.size() <= available_streams, HAILO_INSUFFICIENT_BUFFER,
+          "The given buffer is too small to contain all stream infos. there are {} streams in the given hef, given buffer size is {}",
+          stream_infos_vec.size(), available_streams);
+
+    std::copy(stream_infos_vec.begin(), stream_infos_vec.end(), stream_infos);
+
+    return HAILO_SUCCESS;
+}
+
+hailo_status hailo_network_group_get_all_stream_infos(hailo_configured_network_group network_group,
+    hailo_stream_info_t *stream_infos, size_t stream_infos_length, size_t *number_of_streams)
+{
+    CHECK_ARG_NOT_NULL(network_group);
+    CHECK_ARG_NOT_NULL(stream_infos);
+    CHECK_ARG_NOT_NULL(number_of_streams);
+
+    auto stream_infos_vec = (reinterpret_cast<ConfiguredNetworkGroup*>(network_group))->get_all_stream_infos();
+    CHECK_EXPECTED_AS_STATUS(stream_infos_vec);
+    auto detected_streams = stream_infos_length;
+    auto status = convert_stream_infos_vector_to_array(stream_infos_vec.release(), stream_infos, &detected_streams,
+        true, true);
+    /* Update detected number of stream even in case of error */
+    (*number_of_streams) = detected_streams;
+    CHECK_SUCCESS(status);
+
+    return HAILO_SUCCESS;
+}
+
+hailo_status hailo_network_group_get_input_stream_infos(hailo_configured_network_group network_group,
+    hailo_stream_info_t *stream_infos, size_t stream_infos_length, size_t *number_of_streams)
+{
+    CHECK_ARG_NOT_NULL(network_group);
+    CHECK_ARG_NOT_NULL(stream_infos);
+    CHECK_ARG_NOT_NULL(number_of_streams);
+
+    auto stream_infos_vec = (reinterpret_cast<ConfiguredNetworkGroup*>(network_group))->get_all_stream_infos();
+    CHECK_EXPECTED_AS_STATUS(stream_infos_vec);
+    auto detected_streams = stream_infos_length;
+    auto status = convert_stream_infos_vector_to_array(stream_infos_vec.release(), stream_infos, &detected_streams,
+        true, false);
+    CHECK_SUCCESS(status);
+    
+    (*number_of_streams) = detected_streams;
+
+    return HAILO_SUCCESS;
+}
+
+hailo_status hailo_network_group_get_output_stream_infos(hailo_configured_network_group network_group,
+    hailo_stream_info_t *stream_infos, size_t stream_infos_length, size_t *number_of_streams)
+{
+    CHECK_ARG_NOT_NULL(network_group);
+    CHECK_ARG_NOT_NULL(stream_infos);
+    CHECK_ARG_NOT_NULL(number_of_streams);
+
+    auto stream_infos_vec = (reinterpret_cast<ConfiguredNetworkGroup*>(network_group))->get_all_stream_infos();
+    CHECK_EXPECTED_AS_STATUS(stream_infos_vec);
+    auto detected_streams = stream_infos_length;
+    auto status = convert_stream_infos_vector_to_array(stream_infos_vec.release(), stream_infos, &detected_streams,
+        false, true);
+    CHECK_SUCCESS(status);
+
+    (*number_of_streams) = detected_streams;
+    
+    return HAILO_SUCCESS;
+}
+
+hailo_status hailo_activate_network_group(hailo_configured_network_group network_group,
+    hailo_activate_network_group_params_t *activation_params,
+    hailo_activated_network_group *activated_network_group_out)
+{
+    CHECK_ARG_NOT_NULL(network_group);
+    CHECK_ARG_NOT_NULL(activated_network_group_out);
+
+    hailo_activate_network_group_params_t actual_activation_params = (activation_params != nullptr) ?
+        *activation_params :
+        HailoRTDefaults::get_network_group_params();
+
+    auto net_group_ptr = reinterpret_cast<ConfiguredNetworkGroup*>(network_group);
+    auto activated_net_group = net_group_ptr->activate(actual_activation_params);
+    CHECK_EXPECTED_AS_STATUS(activated_net_group);
+
+    *activated_network_group_out = reinterpret_cast<hailo_activated_network_group>(activated_net_group.release().release());
+
+    return HAILO_SUCCESS;
+}
+
+hailo_status hailo_get_input_stream(hailo_configured_network_group configured_network_group, const char *name, 
+    hailo_input_stream *stream_out)
+{
+    CHECK_ARG_NOT_NULL(configured_network_group);
+    CHECK_ARG_NOT_NULL(name);
+    CHECK_ARG_NOT_NULL(stream_out);
+
+    auto name_string = std::string(name);
+    auto stream = (reinterpret_cast<ConfiguredNetworkGroup *>(configured_network_group))->get_input_stream_by_name(name_string);
+    CHECK_EXPECTED_AS_STATUS(stream);
+
+    *stream_out = reinterpret_cast<hailo_input_stream>(&stream.value().get());
+
+    return HAILO_SUCCESS;
+}
+
+hailo_status hailo_get_output_stream(hailo_configured_network_group configured_network_group, const char *name, 
+    hailo_output_stream *stream_out)
+{
+    CHECK_ARG_NOT_NULL(configured_network_group);
+    CHECK_ARG_NOT_NULL(name);
+    CHECK_ARG_NOT_NULL(stream_out);
+
+    auto name_string = std::string(name);
+    auto stream = (reinterpret_cast<ConfiguredNetworkGroup *>(configured_network_group))->get_output_stream_by_name(name_string);
+    CHECK_EXPECTED_AS_STATUS(stream);
+
+    *stream_out = reinterpret_cast<hailo_output_stream>(&stream.value().get());
+
+    return HAILO_SUCCESS;
+}
+
+const std::string get_name_as_str(const char *name)
+{
+    return (nullptr == name) ? "" : std::string(name);
+}
+
+hailo_status hailo_hef_get_all_stream_infos(hailo_hef hef, const char *name,
+    hailo_stream_info_t *stream_infos, size_t stream_infos_length, size_t *number_of_streams)
+{
+    CHECK_ARG_NOT_NULL(hef);
+    CHECK_ARG_NOT_NULL(stream_infos);
+    CHECK_ARG_NOT_NULL(number_of_streams);
+    const auto name_str = get_name_as_str(name);
+
+    auto stream_infos_expected = (reinterpret_cast<Hef*>(hef))->get_all_stream_infos(name_str);
+    CHECK_EXPECTED_AS_STATUS(stream_infos_expected);
+
+    auto detected_streams = stream_infos_length;
+    auto status = convert_stream_infos_vector_to_array(stream_infos_expected.release(), stream_infos, &detected_streams, 
+        true, true);
+    CHECK_SUCCESS(status);
+
+    (*number_of_streams) = detected_streams;
+
+    return HAILO_SUCCESS;
+}
+
+hailo_status hailo_hef_get_stream_info_by_name(hailo_hef hef, const char *network_group_name, const char *stream_name,
+    hailo_stream_direction_t stream_direction, hailo_stream_info_t *stream_info)
+{
+    CHECK_ARG_NOT_NULL(hef);
+    CHECK_ARG_NOT_NULL(stream_name);
+    CHECK_ARG_NOT_NULL(stream_info);
+    const auto name_str = get_name_as_str(network_group_name);
+
+    auto stream_info_expected = (reinterpret_cast<Hef*>(hef))->get_stream_info_by_name(stream_name,
+        stream_direction, name_str);
+    CHECK_EXPECTED_AS_STATUS(stream_info_expected);
+
+    *stream_info = stream_info_expected.release();
+
+    return HAILO_SUCCESS;
+}
+
+static hailo_status convert_vstream_infos_vector_to_array(std::vector<hailo_vstream_info_t> vstream_infos_vec, 
+    hailo_vstream_info_t *vstream_infos, size_t *vstream_infos_count)
+{
+    size_t vstream_infos_array_entries = *vstream_infos_count;
+    *vstream_infos_count = vstream_infos_vec.size();
+
+    CHECK(vstream_infos_vec.size() <= vstream_infos_array_entries, HAILO_INSUFFICIENT_BUFFER,
+          "The given buffer is too small to contain all vstream infos. there are {} vstreams in the given hef, given buffer size is {}",
+          vstream_infos_vec.size(), vstream_infos_array_entries);
+
+    std::copy(vstream_infos_vec.begin(), vstream_infos_vec.end(), vstream_infos);
+
+    return HAILO_SUCCESS;
+}
+
+hailo_status hailo_hef_get_all_vstream_infos(hailo_hef hef, const char *name,
+    hailo_vstream_info_t *vstream_infos, size_t *vstream_infos_count)
+{
+    CHECK_ARG_NOT_NULL(hef);
+    CHECK_ARG_NOT_NULL(vstream_infos);
+    CHECK_ARG_NOT_NULL(vstream_infos_count);
+    const auto name_str = get_name_as_str(name);
+
+    auto vstream_infos_expected = (reinterpret_cast<Hef*>(hef))->get_all_vstream_infos(name_str);
+    CHECK_EXPECTED_AS_STATUS(vstream_infos_expected);
+
+    auto status = convert_vstream_infos_vector_to_array(vstream_infos_expected.release(), vstream_infos, vstream_infos_count);
+    CHECK_SUCCESS(status);
+    
+    return HAILO_SUCCESS;
+}
+
+hailo_status hailo_get_latency_measurement_from_network_group(hailo_configured_network_group configured_network_group,
+    hailo_latency_measurement_result_t *result)
+{
+    LOGGER__WARNING("'hailo_get_latency_measurement_from_network_group' is deprecated. One shuold use 'hailo_get_latency_measurement()'.");
+    CHECK_ARG_NOT_NULL(configured_network_group);
+    CHECK_ARG_NOT_NULL(result);
+
+    auto latency_result = ((ConfiguredNetworkGroup*)configured_network_group)->get_latency_measurement();
+    CHECK_EXPECTED_AS_STATUS(latency_result);
+
+    hailo_latency_measurement_result_t local_result {};
+    local_result.avg_hw_latency_ms = std::chrono::duration<double, std::milli>(latency_result->avg_hw_latency).count();
+
+    *result = local_result;
+    return HAILO_SUCCESS;
+}
+
+HAILORTAPI hailo_status hailo_get_latency_measurement(hailo_configured_network_group configured_network_group,
+    const char *network_name, hailo_latency_measurement_result_t *result)
+{
+    CHECK_ARG_NOT_NULL(configured_network_group);
+    CHECK_ARG_NOT_NULL(result);
+
+    std::string network_name_str = (nullptr == network_name) ? "" : network_name;
+
+    auto latency_result = ((ConfiguredNetworkGroup*)configured_network_group)->get_latency_measurement(network_name_str);
+    CHECK_EXPECTED_AS_STATUS(latency_result);
+
+    hailo_latency_measurement_result_t local_result {};
+    local_result.avg_hw_latency_ms = std::chrono::duration<double, std::milli>(latency_result->avg_hw_latency).count();
+
+    *result = local_result;
+    return HAILO_SUCCESS;
+}
+
+hailo_status hailo_calculate_eth_input_rate_limits(hailo_hef hef, const char *network_group_name, uint32_t fps,
+    hailo_rate_limit_t *rates, size_t *rates_length)
+{
+    CHECK_ARG_NOT_NULL(hef);
+    CHECK_ARG_NOT_NULL(rates);
+    CHECK_ARG_NOT_NULL(rates_length);
+
+    const auto name_str = get_name_as_str(network_group_name);
+
+    auto rate_calc = NetworkUdpRateCalculator::create(reinterpret_cast<Hef*>(hef), name_str);
+    CHECK_EXPECTED_AS_STATUS(rate_calc);
+    auto calculated_rates = rate_calc->calculate_inputs_bandwith(fps);
+    CHECK_EXPECTED_AS_STATUS(calculated_rates);
+
+    if (*rates_length < calculated_rates->size()) {
+        LOGGER__ERROR("Too many input layers detected. input layers detected: {}, rate_per_name array size: {}",
+            calculated_rates->size(), *rates_length);
+        *rates_length = calculated_rates->size();
+        return HAILO_INSUFFICIENT_BUFFER;
+    }
+    *rates_length = calculated_rates->size();
+
+    int i = 0;
+    for (auto &rate_pair : calculated_rates.release()) {
+        rates[i].rate = rate_pair.second;
+        // + 1 for NULL terminator
+        strncpy(rates[i].stream_name, rate_pair.first.c_str(), rate_pair.first.length() + 1);
+        i++;
+    }
+
+    return HAILO_SUCCESS;
+}
+
+hailo_status hailo_set_input_stream_timeout(hailo_input_stream stream, uint32_t timeout_ms)
+{
+    CHECK_ARG_NOT_NULL(stream);
+
+    auto status = (reinterpret_cast<InputStream*>(stream))->set_timeout(std::chrono::milliseconds(timeout_ms));
+    CHECK_SUCCESS(status);
+    return HAILO_SUCCESS;
+}
+
+hailo_status hailo_set_output_stream_timeout(hailo_output_stream stream, uint32_t timeout_ms)
+{
+    CHECK_ARG_NOT_NULL(stream);
+
+    auto status = (reinterpret_cast<OutputStream*>(stream))->set_timeout(std::chrono::milliseconds(timeout_ms));
+    CHECK_SUCCESS(status);
+    return HAILO_SUCCESS;
+}
+
+size_t hailo_get_input_stream_frame_size(hailo_input_stream stream)
+{
+    return (reinterpret_cast<InputStream*>(stream))->get_frame_size();
+}
+
+size_t hailo_get_output_stream_frame_size(hailo_output_stream stream)
+{
+    return (reinterpret_cast<OutputStream*>(stream))->get_frame_size();
+}
+
+hailo_status hailo_get_input_stream_info(hailo_input_stream stream, hailo_stream_info_t *stream_info)
+{
+    CHECK_ARG_NOT_NULL(stream);
+    CHECK_ARG_NOT_NULL(stream_info);
+    *stream_info = reinterpret_cast<InputStream*>(stream)->get_info();
+    return HAILO_SUCCESS;
+}
+
+hailo_status hailo_get_output_stream_info(hailo_output_stream stream, hailo_stream_info_t *stream_info)
+{
+    CHECK_ARG_NOT_NULL(stream);
+    CHECK_ARG_NOT_NULL(stream_info);
+    *stream_info = reinterpret_cast<OutputStream*>(stream)->get_info();
+    return HAILO_SUCCESS;
+}
+
+hailo_status hailo_stream_read_raw_buffer(hailo_output_stream stream, void *buffer, size_t size)
+{
+    CHECK_ARG_NOT_NULL(stream);
+    CHECK_ARG_NOT_NULL(buffer);
+
+    MemoryView buffer_view(buffer, size);
+    auto status = (reinterpret_cast<OutputStream*>(stream))->read(buffer_view);
+    CHECK_SUCCESS(status);
+    return HAILO_SUCCESS;
+}
+
+hailo_status hailo_stream_write_raw_buffer(hailo_input_stream stream, const void *buffer, size_t size)
+{
+    CHECK_ARG_NOT_NULL(stream);
+    CHECK_ARG_NOT_NULL(buffer);
+
+    auto status = (reinterpret_cast<InputStream*>(stream))->write(MemoryView::create_const(buffer, size));
+    CHECK_SUCCESS(status);
+    return HAILO_SUCCESS;
+}
+
+hailo_status hailo_fuse_nms_frames(const hailo_nms_fuse_input_t *nms_fuse_inputs, uint32_t inputs_count,
+    uint8_t *fused_buffer, size_t fused_buffer_size)
+{
+    CHECK_ARG_NOT_NULL(nms_fuse_inputs);
+    CHECK_ARG_NOT_NULL(fused_buffer);
+
+    std::vector<MemoryView> mem_views;
+    mem_views.reserve(inputs_count);
+    for (uint32_t i = 0; i < inputs_count; i++) {
+        mem_views.emplace_back(nms_fuse_inputs[i].buffer, nms_fuse_inputs[i].size);
+    }
+
+    std::vector<hailo_nms_info_t> params_of_buffers;
+    params_of_buffers.reserve(inputs_count);
+    for (uint32_t i = 0; i < inputs_count; i++) {
+        params_of_buffers.push_back(nms_fuse_inputs[i].nms_info);
+    }
+
+    // TODO: check if passing vectors is hurting performance
+    auto status = fuse_buffers(mem_views, params_of_buffers, MemoryView(fused_buffer, fused_buffer_size));
+    CHECK_SUCCESS(status);
+    return HAILO_SUCCESS;
+}
+
+size_t hailo_get_host_frame_size(const hailo_stream_info_t *stream_info, const hailo_transform_params_t *transform_params)
+{
+    return HailoRTCommon::get_frame_size(*stream_info, *transform_params);
+}
+
+hailo_status hailo_deactivate_network_group(hailo_activated_network_group activated_network_group)
+{
+    CHECK_ARG_NOT_NULL(activated_network_group);
+
+    auto net_group_casted = reinterpret_cast<ActivatedNetworkGroup*>(activated_network_group);
+    delete net_group_casted;
+    
+    return HAILO_SUCCESS;
+}
+
+hailo_status hailo_set_notification_callback(hailo_device device, hailo_notification_callback callback,
+    hailo_notification_id_t notification_id, void *opaque)
+{
+    CHECK_ARG_NOT_NULL(device);
+    CHECK_ARG_NOT_NULL(callback);
+
+    auto status = (reinterpret_cast<Device*>(device))->set_notification_callback(
+        [callback] (Device &device, const hailo_notification_t &notification, void* opaque) {
+            callback(reinterpret_cast<hailo_device>(&device), &notification, opaque);
+        },
+        notification_id, opaque);
+    CHECK_SUCCESS(status);
+    return HAILO_SUCCESS;
+}
+
+hailo_status hailo_remove_notification_callback(hailo_device device, hailo_notification_id_t notification_id)
+{
+    CHECK_ARG_NOT_NULL(device);
+
+    auto status = (reinterpret_cast<Device*>(device))->remove_notification_callback(notification_id);
+    CHECK_SUCCESS(status);
+    return HAILO_SUCCESS;
+}
+
+hailo_status hailo_test_chip_memories(hailo_device device)
+{
+    CHECK_ARG_NOT_NULL(device);
+    auto status = Control::test_chip_memories(*reinterpret_cast<Device*>(device));
+    CHECK_SUCCESS(status);
+    return HAILO_SUCCESS;
+}
+
+hailo_status hailo_create_input_transform_context(const hailo_stream_info_t *stream_info,
+    const hailo_transform_params_t *transform_params, hailo_input_transform_context *transform_context)
+{
+    CHECK_ARG_NOT_NULL(stream_info);
+    CHECK_ARG_NOT_NULL(transform_params);
+    CHECK_ARG_NOT_NULL(transform_context);
+
+    auto local_transform_context = InputTransformContext::create(*stream_info, *transform_params);
+    CHECK_EXPECTED_AS_STATUS(local_transform_context);
+
+    *transform_context = reinterpret_cast<hailo_input_transform_context>(local_transform_context.release().release());
+    return HAILO_SUCCESS;
+}
+
+hailo_status hailo_release_input_transform_context(hailo_input_transform_context transform_context)
+{
+    CHECK_ARG_NOT_NULL(transform_context);
+    delete reinterpret_cast<InputTransformContext*>(transform_context);
+    return HAILO_SUCCESS;
+}
+
+hailo_status hailo_is_input_transformation_required(const hailo_3d_image_shape_t *src_image_shape, const hailo_format_t *src_format,
+    const hailo_3d_image_shape_t *dst_image_shape, const hailo_format_t *dst_format, const hailo_quant_info_t *quant_info,
+    bool *transformation_required)
+{
+    CHECK_ARG_NOT_NULL(src_image_shape);
+    CHECK_ARG_NOT_NULL(src_format);
+    CHECK_ARG_NOT_NULL(dst_image_shape);
+    CHECK_ARG_NOT_NULL(dst_format);
+    CHECK_ARG_NOT_NULL(quant_info);
+    CHECK_ARG_NOT_NULL(transformation_required);
+
+    *transformation_required = InputTransformContext::is_transformation_required(*src_image_shape, *src_format, *dst_image_shape, *dst_format,
+        *quant_info);
+    return HAILO_SUCCESS;
+}
+
+hailo_status hailo_transform_frame_by_input_transform_context(hailo_input_transform_context transform_context,
+    const void *src, size_t src_size, void *dst, size_t dst_size)
+{
+    CHECK_ARG_NOT_NULL(transform_context);
+    CHECK_ARG_NOT_NULL(src);
+    CHECK_ARG_NOT_NULL(dst);
+
+    MemoryView dst_buffer(dst, dst_size);
+    auto status = reinterpret_cast<InputTransformContext*>(transform_context)->transform(
+        MemoryView::create_const(src, src_size), dst_buffer);
+    CHECK_SUCCESS(status);
+    return HAILO_SUCCESS;
+}
+
+hailo_status hailo_create_output_transform_context(const hailo_stream_info_t *stream_info,
+    const hailo_transform_params_t *transform_params, hailo_output_transform_context *transform_context)
+{
+    CHECK_ARG_NOT_NULL(stream_info);
+    CHECK_ARG_NOT_NULL(transform_params);
+    CHECK_ARG_NOT_NULL(transform_context);
+
+    auto local_transform_context = OutputTransformContext::create(*stream_info, *transform_params);
+    CHECK_EXPECTED_AS_STATUS(local_transform_context);
+
+    *transform_context = reinterpret_cast<hailo_output_transform_context>(local_transform_context.release().release());
+    return HAILO_SUCCESS;
+}
+
+hailo_status hailo_release_output_transform_context(hailo_output_transform_context transform_context)
+{
+    CHECK_ARG_NOT_NULL(transform_context);
+    delete reinterpret_cast<OutputTransformContext*>(transform_context);
+    return HAILO_SUCCESS;
+}
+
+hailo_status hailo_is_output_transformation_required(const hailo_3d_image_shape_t *src_image_shape, const hailo_format_t *src_format,
+    const hailo_3d_image_shape_t *dst_image_shape, const hailo_format_t *dst_format, const hailo_quant_info_t *quant_info,
+    bool *transformation_required)
+{
+    CHECK_ARG_NOT_NULL(src_image_shape);
+    CHECK_ARG_NOT_NULL(src_format);
+    CHECK_ARG_NOT_NULL(dst_image_shape);
+    CHECK_ARG_NOT_NULL(dst_format);
+    CHECK_ARG_NOT_NULL(quant_info);
+    CHECK_ARG_NOT_NULL(transformation_required);
+
+    *transformation_required = OutputTransformContext::is_transformation_required(*src_image_shape, *src_format, *dst_image_shape, *dst_format,
+        *quant_info);
+    return HAILO_SUCCESS;
+}
+
+hailo_status hailo_transform_frame_by_output_transform_context(hailo_output_transform_context transform_context,
+    const void *src, size_t src_size, void *dst, size_t dst_size)
+{
+    CHECK_ARG_NOT_NULL(transform_context);
+    CHECK_ARG_NOT_NULL(src);
+    CHECK_ARG_NOT_NULL(dst);
+
+    MemoryView dst_buffer(dst, dst_size);
+    auto status = reinterpret_cast<OutputTransformContext*>(transform_context)->transform(MemoryView::create_const(src,
+        src_size), dst_buffer);
+    CHECK_SUCCESS(status);
+    return HAILO_SUCCESS;
+}
+
+// TODO (HRT-6080): Remove deprecated function
+hailo_status hailo_create_input_transformer(const hailo_stream_info_t *stream_info,
+    const hailo_transform_params_t *transform_params, hailo_input_transform_context *transformer)
+{
+    CHECK_ARG_NOT_NULL(stream_info);
+    CHECK_ARG_NOT_NULL(transform_params);
+    CHECK_ARG_NOT_NULL(transformer);
+
+    LOGGER__WARNING("'hailo_create_input_transformer' is deprecated. One shold use 'hailo_create_input_transform_context'");
+
+    auto local_transformer = InputTransformContext::create(*stream_info, *transform_params);
+    CHECK_EXPECTED_AS_STATUS(local_transformer);
+
+    *transformer = reinterpret_cast<hailo_input_transform_context>(local_transformer.release().release());
+    return HAILO_SUCCESS;
+}
+
+// TODO (HRT-6080): Remove deprecated function
+hailo_status hailo_release_input_transformer(hailo_input_transform_context transformer)
+{
+    CHECK_ARG_NOT_NULL(transformer);
+
+    LOGGER__WARNING("'hailo_release_input_transformer' is deprecated. One shold use 'hailo_release_input_transform_context'");
+
+    delete reinterpret_cast<InputTransformContext*>(transformer);
+    return HAILO_SUCCESS;
+}
+
+// TODO (HRT-6080): Remove deprecated function
+hailo_status hailo_transform_frame_by_input_transformer(hailo_input_transform_context transformer,
+    const void *src, size_t src_size, void *dst, size_t dst_size)
+{
+    CHECK_ARG_NOT_NULL(transformer);
+    CHECK_ARG_NOT_NULL(src);
+    CHECK_ARG_NOT_NULL(dst);
+
+    LOGGER__WARNING("'hailo_transform_frame_by_input_transformer' is deprecated. One shold use 'hailo_transform_frame_by_input_transform_context'");
+
+    MemoryView dst_buffer(dst, dst_size);
+    auto status = reinterpret_cast<InputTransformContext*>(transformer)->transform(
+        MemoryView::create_const(src, src_size), dst_buffer);
+    CHECK_SUCCESS(status);
+    return HAILO_SUCCESS;
+}
+
+// TODO (HRT-6080): Remove deprecated function
+hailo_status hailo_create_output_transformer(const hailo_stream_info_t *stream_info,
+    const hailo_transform_params_t *transform_params, hailo_output_transform_context *transformer)
+{
+    CHECK_ARG_NOT_NULL(stream_info);
+    CHECK_ARG_NOT_NULL(transform_params);
+    CHECK_ARG_NOT_NULL(transformer);
+
+    LOGGER__WARNING("'hailo_create_output_transformer' is deprecated. One shold use 'hailo_create_output_transform_context'");
+
+    auto local_transformer = OutputTransformContext::create(*stream_info, *transform_params);
+    CHECK_EXPECTED_AS_STATUS(local_transformer);
+
+    *transformer = reinterpret_cast<hailo_output_transform_context>(local_transformer.release().release());
+    return HAILO_SUCCESS;
+}
+
+// TODO (HRT-6080): Remove deprecated function
+hailo_status hailo_release_output_transformer(hailo_output_transform_context transformer)
+{
+    CHECK_ARG_NOT_NULL(transformer);
+    LOGGER__WARNING("'hailo_release_output_transformer' is deprecated. One shold use 'hailo_release_output_transform_context'");
+
+    delete reinterpret_cast<OutputTransformContext*>(transformer);
+    return HAILO_SUCCESS;
+}
+
+// TODO (HRT-6080): Remove deprecated function
+hailo_status hailo_transform_frame_by_output_transformer(hailo_output_transform_context transformer,
+    const void *src, size_t src_size, void *dst, size_t dst_size)
+{
+    CHECK_ARG_NOT_NULL(transformer);
+    CHECK_ARG_NOT_NULL(src);
+    CHECK_ARG_NOT_NULL(dst);
+
+    LOGGER__WARNING("'hailo_transform_frame_by_output_transformer' is deprecated. One shold use 'hailo_transform_frame_by_output_transform_context'");
+
+    MemoryView dst_buffer(dst, dst_size);
+    auto status = reinterpret_cast<OutputTransformContext*>(transformer)->transform(MemoryView::create_const(src,
+        src_size), dst_buffer);
+    CHECK_SUCCESS(status);
+    return HAILO_SUCCESS;
+}
+
+hailo_status hailo_create_demuxer_by_stream(hailo_output_stream stream,
+    const hailo_demux_params_t *demux_params, hailo_output_demuxer *demuxer)
+{
+    CHECK_ARG_NOT_NULL(stream);
+    CHECK_ARG_NOT_NULL(demux_params);
+    CHECK_ARG_NOT_NULL(demuxer);
+
+    auto local_demuxer = OutputDemuxer::create(*reinterpret_cast<OutputStream*>(stream));
+    CHECK_EXPECTED_AS_STATUS(local_demuxer);
+
+    auto allocated_demuxer = local_demuxer.release().release();
+
+    *demuxer = reinterpret_cast<hailo_output_demuxer>(allocated_demuxer);
+    return HAILO_SUCCESS;
+}
+
+hailo_status hailo_release_output_demuxer(hailo_output_demuxer demuxer)
+{
+    CHECK_ARG_NOT_NULL(demuxer);
+    delete reinterpret_cast<OutputDemuxer*>(demuxer);
+    return HAILO_SUCCESS;
+}
+
+hailo_status hailo_demux_raw_frame_by_output_demuxer(hailo_output_demuxer demuxer, const void *src, size_t src_size,
+    hailo_stream_raw_buffer_t *raw_buffers, size_t raw_buffers_count)
+{
+    CHECK_ARG_NOT_NULL(src);
+    CHECK_ARG_NOT_NULL(raw_buffers);
+    CHECK_ARG_NOT_NULL(demuxer);
+
+    std::vector<MemoryView> raw_buffers_vector;
+    for (size_t i = 0; i < raw_buffers_count; i++) {
+        raw_buffers_vector.emplace_back(raw_buffers[i].buffer, raw_buffers[i].size);
+    }
+    auto src_memview = MemoryView::create_const(src, src_size);
+    auto status = reinterpret_cast<OutputDemuxer*>(demuxer)->transform_demux(src_memview, raw_buffers_vector);
+    CHECK_SUCCESS(status);
+
+    return HAILO_SUCCESS;
+}
+
+hailo_status hailo_get_mux_infos_by_output_demuxer(hailo_output_demuxer demuxer, hailo_stream_info_t *stream_infos,
+    size_t *number_of_streams)
+{
+    CHECK_ARG_NOT_NULL(demuxer);
+    CHECK_ARG_NOT_NULL(stream_infos);
+    CHECK_ARG_NOT_NULL(number_of_streams);
+
+    const auto &mux_infos = reinterpret_cast<OutputDemuxer*>(demuxer)->get_edges_stream_info();
+    if (*number_of_streams < mux_infos.size()) {
+        LOGGER__ERROR("Too many mux infos detected. Mux infos detected: {}, stream_infos array size: {}",
+            mux_infos.size(), *number_of_streams);
+        *number_of_streams = mux_infos.size();
+        return HAILO_INSUFFICIENT_BUFFER;
+    }
+    *number_of_streams = mux_infos.size();
+
+    for (size_t i = 0; i < mux_infos.size(); i++) {
+        stream_infos[i] = mux_infos[i];
+    }
+    return HAILO_SUCCESS;
+}
+
+hailo_status hailo_hef_get_vstream_name_from_original_name(hailo_hef hef, const char *network_group_name,
+    const char *original_name, hailo_layer_name_t *vstream_name)
+{
+    CHECK_ARG_NOT_NULL(hef);
+    CHECK_ARG_NOT_NULL(original_name);
+    CHECK_ARG_NOT_NULL(vstream_name);
+
+    const auto network_group_name_str = get_name_as_str(network_group_name);
+
+    auto results = reinterpret_cast<Hef*>(hef)->get_vstream_name_from_original_name(original_name, network_group_name_str);
+    CHECK_EXPECTED_AS_STATUS(results);
+
+    CHECK(results->length() < HAILO_MAX_STREAM_NAME_SIZE, HAILO_INTERNAL_FAILURE,
+        "Stream '{}' name is too long. max allowed (including NULL terminator): {}, recived: {}", results.value(),
+        HAILO_MAX_STREAM_NAME_SIZE, results->length() + 1);
+
+    // + 1 for NULL terminator
+    strncpy(vstream_name->name, results->c_str(), results->length() + 1);
+    return HAILO_SUCCESS;
+}
+
+hailo_status hailo_hef_get_original_names_from_vstream_name(hailo_hef hef, const char *network_group_name,
+    const char *vstream_name, hailo_layer_name_t *original_names, size_t *original_names_length)
+{
+    CHECK_ARG_NOT_NULL(hef);
+    CHECK_ARG_NOT_NULL(vstream_name);
+    CHECK_ARG_NOT_NULL(original_names);
+    CHECK_ARG_NOT_NULL(original_names_length);
+
+    const auto network_group_name_str = get_name_as_str(network_group_name);
+
+    auto results = reinterpret_cast<Hef*>(hef)->get_original_names_from_vstream_name(vstream_name, network_group_name_str);
+    CHECK_EXPECTED_AS_STATUS(results);
+
+    if (results->size() > *original_names_length) {
+        LOGGER__ERROR("Too many original names detected. original_names_count: {}, original_names_array_size: {}",
+            results->size(), *original_names_length);
+        *original_names_length = results->size();
+        return HAILO_INSUFFICIENT_BUFFER;
+    }
+    *original_names_length = results->size();
+
+    int i = 0;
+    for (const auto &original_name : results.value()) {
+        CHECK(original_name.length() < HAILO_MAX_STREAM_NAME_SIZE, HAILO_INTERNAL_FAILURE,
+            "Layer '{}' name is too long. max allowed (including NULL terminator): {}, received: {}", original_name,
+            HAILO_MAX_STREAM_NAME_SIZE, original_name.length() + 1);
+
+        // + 1 for NULL terminator
+        strncpy(original_names[i].name, original_name.c_str(), original_name.length() + 1);
+        i++;
+    }
+    return HAILO_SUCCESS;
+}
+
+hailo_status hailo_hef_get_vstream_names_from_stream_name(hailo_hef hef, const char *network_group_name,
+    const char *stream_name, hailo_layer_name_t *vstream_names, size_t *vstream_names_length)
+{
+    CHECK_ARG_NOT_NULL(hef);
+    CHECK_ARG_NOT_NULL(stream_name);
+    CHECK_ARG_NOT_NULL(vstream_names);
+    CHECK_ARG_NOT_NULL(vstream_names_length);
+
+    const auto network_group_name_str = get_name_as_str(network_group_name);
+
+    auto results = reinterpret_cast<Hef*>(hef)->get_vstream_names_from_stream_name(stream_name, network_group_name_str);
+    CHECK_EXPECTED_AS_STATUS(results);
+
+    if (results->size() > *vstream_names_length) {
+        LOGGER__ERROR("Too many vstream names detected. vstream_names_count: {}, vstream_names_array_size: {}",
+            results->size(), *vstream_names_length);
+        *vstream_names_length = results->size();
+        return HAILO_INSUFFICIENT_BUFFER;
+    }
+    *vstream_names_length = results->size();
+
+    int i = 0;
+    for (const auto &vstream_name : results.value()) {
+        CHECK(vstream_name.length() < HAILO_MAX_STREAM_NAME_SIZE, HAILO_INTERNAL_FAILURE,
+            "Layer '{}' name is too long. max allowed (including NULL terminator): {}, received: {}", vstream_name,
+            HAILO_MAX_STREAM_NAME_SIZE, vstream_name.length() + 1);
+
+        // + 1 for NULL terminator
+        strncpy(vstream_names[i].name, vstream_name.c_str(), vstream_name.length() + 1);
+        i++;
+    }
+    return HAILO_SUCCESS;
+}
+
+hailo_status hailo_hef_get_stream_names_from_vstream_name(hailo_hef hef, const char *network_group_name,
+    const char *vstream_name, hailo_layer_name_t *stream_names, size_t *stream_names_length)
+{
+    CHECK_ARG_NOT_NULL(hef);
+    CHECK_ARG_NOT_NULL(vstream_name);
+    CHECK_ARG_NOT_NULL(stream_names);
+    CHECK_ARG_NOT_NULL(stream_names_length);
+
+    const auto network_group_name_str = get_name_as_str(network_group_name);
+
+    auto results = reinterpret_cast<Hef*>(hef)->get_stream_names_from_vstream_name(vstream_name, network_group_name_str);
+    CHECK_EXPECTED_AS_STATUS(results);
+
+    if (results->size() > *stream_names_length) {
+        LOGGER__ERROR("Too many stream names detected. vstream_names_count: {}, vstream_names_array_size: {}",
+            results->size(), *stream_names_length);
+        *stream_names_length = results->size();
+        return HAILO_INSUFFICIENT_BUFFER;
+    }
+    *stream_names_length = results->size();
+
+    int i = 0;
+    for (const auto &stream_name : results.value()) {
+        CHECK(stream_name.length() < HAILO_MAX_STREAM_NAME_SIZE, HAILO_INTERNAL_FAILURE,
+            "Layer '{}' name is too long. max allowed (including NULL terminator): {}, received: {}", stream_name,
+            HAILO_MAX_STREAM_NAME_SIZE, stream_name.length() + 1);
+
+        // + 1 for NULL terminator
+        strncpy(stream_names[i].name, stream_name.c_str(), stream_name.length() + 1);
+        i++;
+    }
+    return HAILO_SUCCESS;
+}
+
+hailo_status hailo_hef_get_sorted_output_names(hailo_hef hef, const char *network_group_name,
+    hailo_layer_name_t *sorted_output_names, size_t *sorted_output_names_count)
+{
+    CHECK_ARG_NOT_NULL(hef);
+    CHECK_ARG_NOT_NULL(sorted_output_names);
+    CHECK_ARG_NOT_NULL(sorted_output_names_count);
+
+    const auto network_group_name_str = get_name_as_str(network_group_name);
+
+    auto results = reinterpret_cast<Hef*>(hef)->get_sorted_output_names(network_group_name_str);
+    CHECK_EXPECTED_AS_STATUS(results);
+
+    if (results->size() > *sorted_output_names_count) {
+        LOGGER__ERROR("Failed to get sorted output names. Results size is {}, given size is {}",
+            results->size(), *sorted_output_names_count);
+        *sorted_output_names_count = results->size();
+        return HAILO_INSUFFICIENT_BUFFER;
+    }
+    *sorted_output_names_count = results->size();
+    
+    int i = 0;
+    for (const auto &name : results.value()) {
+        CHECK(name.length() < HAILO_MAX_STREAM_NAME_SIZE, HAILO_INTERNAL_FAILURE,
+            "Output '{}' name is too long. max allowed (including NULL terminator): {}, received: {}", name,
+            HAILO_MAX_STREAM_NAME_SIZE, name.length() + 1);
+
+        // + 1 for NULL terminator
+        strncpy(sorted_output_names[i].name, name.c_str(), name.length() + 1);
+        i++;
+    }
+    return HAILO_SUCCESS;
+}
+
+hailo_status hailo_hef_get_bottleneck_fps(hailo_hef hef, const char *network_group_name,
+    float64_t *bottleneck_fps)
+{
+    CHECK_ARG_NOT_NULL(hef);
+    CHECK_ARG_NOT_NULL(bottleneck_fps);
+
+    const auto network_group_name_str = get_name_as_str(network_group_name);
+
+    auto bottleneck_fps_expected = reinterpret_cast<Hef*>(hef)->get_bottleneck_fps(network_group_name_str);
+    CHECK_EXPECTED_AS_STATUS(bottleneck_fps_expected);
+
+    *bottleneck_fps = bottleneck_fps_expected.value();
+
+    return HAILO_SUCCESS;
+}
+
+hailo_status hailo_make_input_vstream_params(hailo_configured_network_group network_group, bool quantized,
+    hailo_format_type_t format_type, hailo_input_vstream_params_by_name_t *input_params,
+    size_t *input_params_count)
+{
+    CHECK_ARG_NOT_NULL(network_group);
+    CHECK_ARG_NOT_NULL(input_params);
+    CHECK_ARG_NOT_NULL(input_params_count);
+
+    auto net_group_ptr = reinterpret_cast<ConfiguredNetworkGroup*>(network_group);
+    auto input_params_map = net_group_ptr->make_input_vstream_params(quantized, format_type, 
+        HAILO_DEFAULT_VSTREAM_TIMEOUT_MS , HAILO_DEFAULT_VSTREAM_QUEUE_SIZE);
+    CHECK_EXPECTED_AS_STATUS(input_params_map);
+
+    if (input_params_map->size() > *input_params_count) {
+        LOGGER__ERROR(
+            "The given buffer is too small to contain all input_vstream_params. There are {} input vstreams in the given network_group, given size is {}",
+            input_params_map->size(), *input_params_count);
+        *input_params_count = input_params_map->size();
+        return HAILO_INSUFFICIENT_BUFFER;
+    }
+
+    int i = 0;
+    for (auto &name_pair : input_params_map.value()) {
+        // Adding +1 for NULL terminator
+        CHECK(HAILO_MAX_STREAM_NAME_SIZE >= (name_pair.first.length() + 1), HAILO_INVALID_ARGUMENT,
+            "Name too long (max is {}, received {})", HAILO_MAX_STREAM_NAME_SIZE, name_pair.first);
+        memcpy(input_params[i].name, name_pair.first.c_str(), name_pair.first.length() + 1);
+        input_params[i].params = name_pair.second;
+        i++;
+    }
+    *input_params_count = input_params_map->size();
+
+    return HAILO_SUCCESS;
+}
+
+hailo_status hailo_make_output_vstream_params(hailo_configured_network_group network_group, bool quantized,
+    hailo_format_type_t format_type, hailo_output_vstream_params_by_name_t *output_vstream_params,
+    size_t *output_params_count)
+{
+    CHECK_ARG_NOT_NULL(network_group);
+    CHECK_ARG_NOT_NULL(output_vstream_params);
+    CHECK_ARG_NOT_NULL(output_params_count);
+
+    auto net_group_ptr = reinterpret_cast<ConfiguredNetworkGroup*>(network_group);
+    auto output_params_map = net_group_ptr->make_output_vstream_params(quantized, format_type,
+        HAILO_DEFAULT_VSTREAM_TIMEOUT_MS , HAILO_DEFAULT_VSTREAM_QUEUE_SIZE);
+    CHECK_EXPECTED_AS_STATUS(output_params_map);
+
+    if (output_params_map->size() > *output_params_count) {
+        LOGGER__ERROR(
+            "The given buffer is too small to contain all output_vstream_params. There are {} output vstreams in the given network_group, given size is {}",
+            output_params_map->size(), *output_params_count);
+        *output_params_count = output_params_map->size();
+        return HAILO_INSUFFICIENT_BUFFER;
+    }
+
+    int i = 0;
+    for (auto &name_pair : output_params_map.value()) {
+        // Adding +1 for NULL terminator
+        CHECK(HAILO_MAX_STREAM_NAME_SIZE >= (name_pair.first.length() + 1), HAILO_INVALID_ARGUMENT,
+            "Name too long (max is {}, received {})", HAILO_MAX_STREAM_NAME_SIZE, name_pair.first);
+        memcpy(output_vstream_params[i].name, name_pair.first.c_str(), name_pair.first.length() + 1);
+        output_vstream_params[i].params = name_pair.second;
+        i++;
+    }
+    *output_params_count = output_params_map->size();
+
+    return HAILO_SUCCESS;
+}
+
+hailo_status hailo_get_output_vstream_groups(hailo_configured_network_group network_group,
+    hailo_output_vstream_name_by_group_t *output_name_by_group, size_t *output_name_by_group_count)
+{
+    CHECK_ARG_NOT_NULL(network_group);
+    CHECK_ARG_NOT_NULL(output_name_by_group);
+    CHECK_ARG_NOT_NULL(output_name_by_group_count);
+
+    auto net_group_ptr = reinterpret_cast<ConfiguredNetworkGroup*>(network_group);
+    auto output_vstream_groups = net_group_ptr->get_output_vstream_groups();
+    CHECK_EXPECTED_AS_STATUS(output_vstream_groups);
+
+    
+    size_t number_of_output_vstreams = 0;
+    for (auto &vstreams_group : output_vstream_groups.value()) {
+        number_of_output_vstreams += vstreams_group.size();
+    }
+    if (number_of_output_vstreams > *output_name_by_group_count) {
+        LOGGER__ERROR(
+            "The given buffer is too small to contain all output_vstream_params. There are {} output vstreams in the given network_group, given size is {}",
+            number_of_output_vstreams, *output_name_by_group_count);
+        *output_name_by_group_count = number_of_output_vstreams;
+        return HAILO_INSUFFICIENT_BUFFER;
+    }
+
+    uint8_t pipeline_group_index = 0;
+    int vstream_index = 0;
+    for (auto &vstreams_group : output_vstream_groups.value()) {
+        for (auto &vstream_name : vstreams_group) {
+            // Adding +1 for NULL terminator
+            CHECK(HAILO_MAX_STREAM_NAME_SIZE >= (vstream_name.length() + 1), HAILO_INVALID_ARGUMENT,
+                "Name too long (max is {}, received {})", HAILO_MAX_STREAM_NAME_SIZE, vstream_name);
+            memcpy(output_name_by_group[vstream_index].name, vstream_name.c_str(), vstream_name.length() + 1);
+            output_name_by_group[vstream_index].pipeline_group_index = pipeline_group_index;
+            vstream_index++;
+        }
+        pipeline_group_index++;
+    }
+    *output_name_by_group_count = number_of_output_vstreams;
+
+    return HAILO_SUCCESS;
+}
+
+hailo_status hailo_create_input_vstreams(hailo_configured_network_group configured_network_group,
+    const hailo_input_vstream_params_by_name_t *inputs_params, size_t inputs_count, hailo_input_vstream *input_vstreams)
+{
+    CHECK_ARG_NOT_NULL(configured_network_group);
+    CHECK_ARG_NOT_NULL(inputs_params);
+    CHECK_ARG_NOT_NULL(input_vstreams);
+
+    std::map<std::string, hailo_vstream_params_t> inputs_params_map;
+    for (size_t i = 0; i < inputs_count; i++) {
+        inputs_params_map.emplace(inputs_params[i].name, inputs_params[i].params);
+    }
+
+    auto vstreams_vec_expected = VStreamsBuilder::create_input_vstreams(*reinterpret_cast<ConfiguredNetworkGroup*>(configured_network_group), 
+        inputs_params_map);
+    CHECK_EXPECTED_AS_STATUS(vstreams_vec_expected);
+    auto vstreams_vec = vstreams_vec_expected.release();
+
+    std::vector<std::unique_ptr<InputVStream>> vstreams_ptrs;
+    for (auto &vstream: vstreams_vec) {
+        auto vstream_ptr = new (std::nothrow) InputVStream(std::move(vstream));
+        CHECK_NOT_NULL(vstream_ptr, HAILO_OUT_OF_HOST_MEMORY);
+
+        vstreams_ptrs.emplace_back(vstream_ptr);
+    }
+
+    for (size_t i = 0; i < inputs_count; i++) {
+        input_vstreams[i] = reinterpret_cast<hailo_input_vstream>(vstreams_ptrs[i].release());
+    }
+
+    return HAILO_SUCCESS;
+}
+
+hailo_status hailo_create_output_vstreams(hailo_configured_network_group configured_network_group,
+    const hailo_output_vstream_params_by_name_t *outputs_params, size_t outputs_count, hailo_output_vstream *output_vstreams)
+{
+    CHECK_ARG_NOT_NULL(configured_network_group);
+    CHECK_ARG_NOT_NULL(outputs_params);
+    CHECK_ARG_NOT_NULL(output_vstreams);
+
+    std::map<std::string, hailo_vstream_params_t> outputs_params_map;
+    for (size_t i = 0; i < outputs_count; i++) {
+        outputs_params_map.emplace(outputs_params[i].name, outputs_params[i].params);
+    }
+
+    auto vstreams_vec_expected = VStreamsBuilder::create_output_vstreams(*reinterpret_cast<ConfiguredNetworkGroup*>(configured_network_group), 
+        outputs_params_map);
+    CHECK_EXPECTED_AS_STATUS(vstreams_vec_expected);
+    auto vstreams_vec = vstreams_vec_expected.release();
+
+    std::vector<std::unique_ptr<OutputVStream>> vstreams_ptrs;
+    for (auto &vstream: vstreams_vec) {
+        auto vstream_ptr = new (std::nothrow) OutputVStream(std::move(vstream));
+        CHECK_NOT_NULL(vstream_ptr, HAILO_OUT_OF_HOST_MEMORY);
+
+        vstreams_ptrs.emplace_back(vstream_ptr);
+    }
+
+    for (size_t i = 0; i < outputs_count; i++) {
+        output_vstreams[i] = reinterpret_cast<hailo_output_vstream>(vstreams_ptrs[i].release());
+    }
+
+    return HAILO_SUCCESS;
+}
+
+hailo_status hailo_get_input_vstream_frame_size(hailo_input_vstream input_vstream, size_t *frame_size)
+{
+    CHECK_ARG_NOT_NULL(input_vstream);
+    CHECK_ARG_NOT_NULL(frame_size);
+
+    *frame_size = reinterpret_cast<InputVStream*>(input_vstream)->get_frame_size();
+    return HAILO_SUCCESS;
+}
+
+hailo_status hailo_get_input_vstream_info(hailo_input_vstream input_vstream, hailo_vstream_info_t *vstream_info)
+{
+    CHECK_ARG_NOT_NULL(input_vstream);
+    CHECK_ARG_NOT_NULL(vstream_info);
+
+    *vstream_info = reinterpret_cast<InputVStream*>(input_vstream)->get_info();
+    return HAILO_SUCCESS;
+}
+
+hailo_status hailo_get_input_vstream_user_format(hailo_input_vstream input_vstream, hailo_format_t *user_buffer_format)
+{
+    CHECK_ARG_NOT_NULL(input_vstream);
+    CHECK_ARG_NOT_NULL(user_buffer_format);
+
+    *user_buffer_format = reinterpret_cast<InputVStream*>(input_vstream)->get_user_buffer_format();
+    return HAILO_SUCCESS;
+}
+
+hailo_status hailo_get_output_vstream_frame_size(hailo_output_vstream output_vstream, size_t *frame_size)
+{
+    CHECK_ARG_NOT_NULL(output_vstream);
+    CHECK_ARG_NOT_NULL(frame_size);
+
+    *frame_size = reinterpret_cast<OutputVStream*>(output_vstream)->get_frame_size();
+    return HAILO_SUCCESS;
+}
+
+hailo_status hailo_get_output_vstream_info(hailo_output_vstream output_vstream, hailo_vstream_info_t *vstream_info)
+{
+    CHECK_ARG_NOT_NULL(output_vstream);
+    CHECK_ARG_NOT_NULL(vstream_info);
+
+    *vstream_info = reinterpret_cast<OutputVStream*>(output_vstream)->get_info();
+    return HAILO_SUCCESS;
+}
+
+hailo_status hailo_get_output_vstream_user_format(hailo_output_vstream output_vstream, hailo_format_t *user_buffer_format)
+{
+    CHECK_ARG_NOT_NULL(output_vstream);
+    CHECK_ARG_NOT_NULL(user_buffer_format);
+
+    *user_buffer_format = reinterpret_cast<OutputVStream*>(output_vstream)->get_user_buffer_format();
+    return HAILO_SUCCESS;
+}
+
+hailo_status hailo_get_vstream_frame_size(hailo_vstream_info_t *vstream_info, hailo_format_t *user_buffer_format, size_t *frame_size)
+{
+    CHECK_ARG_NOT_NULL(vstream_info);
+    CHECK_ARG_NOT_NULL(user_buffer_format);
+    CHECK_ARG_NOT_NULL(frame_size);
+
+    *frame_size = HailoRTCommon::get_frame_size(*vstream_info, *user_buffer_format);
+    return HAILO_SUCCESS;
+}
+
+hailo_status hailo_vstream_write_raw_buffer(hailo_input_vstream input_vstream, const void *buffer, size_t buffer_size)
+{
+    CHECK_ARG_NOT_NULL(input_vstream);
+    CHECK_ARG_NOT_NULL(buffer);
+    
+    auto status = reinterpret_cast<InputVStream*>(input_vstream)->write(MemoryView::create_const(buffer, buffer_size));
+    CHECK_SUCCESS(status);
+    return HAILO_SUCCESS;
+}
+
+hailo_status hailo_vstream_read_raw_buffer(hailo_output_vstream output_vstream, void *dst, size_t dst_size)
+{
+    CHECK_ARG_NOT_NULL(output_vstream);
+    CHECK_ARG_NOT_NULL(dst);
+    
+    auto status = reinterpret_cast<OutputVStream*>(output_vstream)->read(MemoryView(dst, dst_size));
+    CHECK_SUCCESS(status);
+    return HAILO_SUCCESS;
+}
+
+hailo_status hailo_release_input_vstreams(const hailo_input_vstream *input_vstreams, size_t inputs_count)
+{
+    CHECK_ARG_NOT_NULL(input_vstreams);
+
+    for (size_t i = 0; i < inputs_count; i++) {
+        delete reinterpret_cast<InputVStream*>(input_vstreams[i]);
+    }
+    return HAILO_SUCCESS;
+}
+
+hailo_status hailo_release_output_vstreams(const hailo_output_vstream *output_vstreams, size_t outputs_count)
+{
+    CHECK_ARG_NOT_NULL(output_vstreams);
+
+    for (size_t i = 0; i < outputs_count; i++) {
+        delete reinterpret_cast<OutputVStream*>(output_vstreams[i]);
+    }
+    return HAILO_SUCCESS;
+}
+
+hailo_status hailo_clear_input_vstreams(const hailo_input_vstream *input_vstreams, size_t inputs_count)
+{
+    CHECK_ARG_NOT_NULL(input_vstreams);
+
+    std::vector<std::reference_wrapper<InputVStream>> vstreams;
+    vstreams.reserve(inputs_count);
+    for (size_t i = 0; i < inputs_count; i++) {
+        vstreams.emplace_back(std::ref(*reinterpret_cast<InputVStream*>(input_vstreams[i])));
+    }
+
+    auto status = InputVStream::clear(vstreams);
+    CHECK_SUCCESS(status);
+    return HAILO_SUCCESS;
+}
+
+hailo_status hailo_clear_output_vstreams(const hailo_output_vstream *output_vstreams, size_t outputs_count)
+{
+    CHECK_ARG_NOT_NULL(output_vstreams);
+
+    std::vector<std::reference_wrapper<OutputVStream>> vstreams;
+    vstreams.reserve(outputs_count);
+    for (size_t i = 0; i < outputs_count; i++) {
+        vstreams.emplace_back(std::ref(*reinterpret_cast<OutputVStream*>(output_vstreams[i])));
+    }
+
+    auto status = OutputVStream::clear(vstreams);
+    CHECK_SUCCESS(status);
+    return HAILO_SUCCESS;
+}
+
+HAILORTAPI hailo_status hailo_flush_input_vstream(hailo_input_vstream input_vstream)
+{
+    CHECK_ARG_NOT_NULL(input_vstream);
+
+    auto status = reinterpret_cast<InputVStream*>(input_vstream)->flush();
+    CHECK_SUCCESS(status);
+
+    return HAILO_SUCCESS;
+}
+
+hailo_status hailo_wait_for_network_group_activation(hailo_configured_network_group network_group,
+    uint32_t timeout_ms)
+{
+    CHECK_ARG_NOT_NULL(network_group);
+
+    auto status = reinterpret_cast<ConfiguredNetworkGroup*>(network_group)->wait_for_activation(std::chrono::milliseconds(timeout_ms));
+    CHECK_SUCCESS(status);
+    return HAILO_SUCCESS;
+}
+
+static const char *hailo_status_msg_format[] =
+{
+#define HAILO_STATUS__X(value, name) #name,
+    HAILO_STATUS_VARIABLES
+#undef HAILO_STATUS__X
+};
+
+const char* hailo_get_status_message(hailo_status status)
+{
+    if (status >= HAILO_STATUS_COUNT) {
+        LOGGER__ERROR("Failed to get hailo_status message because of invalid hailo_status value. Max hailo_status value = {}, given value = {}",
+            (HAILO_STATUS_COUNT-1), static_cast<int>(status));
+        return nullptr;
+    }
+    return hailo_status_msg_format[status];
+}
+
+hailo_status hailo_init_vdevice_params(hailo_vdevice_params_t *params)
+{
+    CHECK_ARG_NOT_NULL(params);
+    *params = HailoRTDefaults::get_vdevice_params();
+    return HAILO_SUCCESS;
+}
+
+hailo_status hailo_create_vdevice(hailo_vdevice_params_t *params, hailo_vdevice *vdevice)
+{
+    CHECK_ARG_NOT_NULL(vdevice);
+
+    auto vdevice_exp = (params == nullptr) ? VDevice::create() : VDevice::create(*params);
+    CHECK_EXPECTED_AS_STATUS(vdevice_exp, "Failed creating vdevice");
+    *vdevice = reinterpret_cast<hailo_vdevice>(vdevice_exp.release().release());
+
+    return HAILO_SUCCESS;
+}
+
+hailo_status hailo_configure_vdevice(hailo_vdevice vdevice, hailo_hef hef,
+    hailo_configure_params_t *params, hailo_configured_network_group *network_groups, size_t *number_of_network_groups)
+{
+    CHECK_ARG_NOT_NULL(vdevice);
+    CHECK_ARG_NOT_NULL(hef);
+    CHECK_ARG_NOT_NULL(network_groups);
+    CHECK_ARG_NOT_NULL(number_of_network_groups);
+
+    auto configure_params = get_configure_params_map(params);
+
+    auto added_net_groups = (reinterpret_cast<VDevice*>(vdevice))->configure(*reinterpret_cast<Hef*>(hef), configure_params);
+    CHECK_EXPECTED_AS_STATUS(added_net_groups);
+
+    CHECK(added_net_groups->size() <= (*number_of_network_groups), HAILO_INSUFFICIENT_BUFFER,
+        "Can't return all network_groups. HEF file contained {} network_groups, but output array is of size {}",
+        added_net_groups->size(), (*number_of_network_groups));
+
+    for (size_t i = 0; i < added_net_groups->size(); ++i) {
+        network_groups[i] = reinterpret_cast<hailo_configured_network_group>(added_net_groups.value()[i].get());
+    }
+
+    *number_of_network_groups = added_net_groups.value().size();
+    return HAILO_SUCCESS;
+}
+
+hailo_status hailo_get_physical_devices(hailo_vdevice vdevice, hailo_device *devices,
+    size_t *number_of_devices)
+{
+    CHECK_ARG_NOT_NULL(devices);
+    CHECK_ARG_NOT_NULL(number_of_devices);
+
+    auto phys_devices_exp = (reinterpret_cast<VDevice *>(vdevice))->get_physical_devices();
+    CHECK_EXPECTED_AS_STATUS(phys_devices_exp);
+
+    if (*number_of_devices < phys_devices_exp->size()) {
+        LOGGER__ERROR("Can't return all physical devices. there are {} physical devices under the vdevice, but output array is of size {}",
+            phys_devices_exp->size(), *number_of_devices);
+        *number_of_devices = phys_devices_exp->size();
+        return HAILO_INSUFFICIENT_BUFFER;
+    }
+    *number_of_devices = phys_devices_exp->size();
+
+    for (size_t i = 0; i < phys_devices_exp->size(); i++) {
+        devices[i] = reinterpret_cast<hailo_device>(&(phys_devices_exp.value()[i].get()));
+    }
+
+    return HAILO_SUCCESS;
+}
+
+hailo_status hailo_get_physical_devices_infos(hailo_vdevice vdevice, hailo_pcie_device_info_t *devices_infos,
+    size_t *number_of_devices)
+{
+    CHECK_ARG_NOT_NULL(devices_infos);
+    CHECK_ARG_NOT_NULL(number_of_devices);
+
+    auto phys_devices_infos = (reinterpret_cast<VDevice *>(vdevice))->get_physical_devices_infos();
+    CHECK_EXPECTED_AS_STATUS(phys_devices_infos);
+
+    if (*number_of_devices < phys_devices_infos->size()) {
+        LOGGER__ERROR("Can't return all physical devices infos. There are {} physical devices infos under the vdevice, but output array is of size {}",
+            phys_devices_infos->size(), *number_of_devices);
+        *number_of_devices = phys_devices_infos->size();
+        return HAILO_INSUFFICIENT_BUFFER;
+    }
+    *number_of_devices = phys_devices_infos->size();
+
+    for (size_t i = 0; i < phys_devices_infos->size(); i++) {
+        devices_infos[i] = phys_devices_infos.value()[i];
+    }
+
+    return HAILO_SUCCESS;
+}
+
+hailo_status hailo_release_vdevice(hailo_vdevice vdevice_ptr)
+{
+    CHECK_ARG_NOT_NULL(vdevice_ptr);
+    delete reinterpret_cast<VDevice*>(vdevice_ptr);
+    return HAILO_SUCCESS;
+}
+
+hailo_status hailo_infer(hailo_configured_network_group network_group,
+    hailo_input_vstream_params_by_name_t *inputs_params,
+    hailo_stream_raw_buffer_by_name_t *input_buffers,
+    size_t inputs_count,
+    hailo_output_vstream_params_by_name_t *outputs_params,
+    hailo_stream_raw_buffer_by_name_t *output_buffers,
+    size_t outputs_count,
+    size_t frames_count)
+{
+    CHECK_ARG_NOT_NULL(network_group);
+    CHECK_ARG_NOT_NULL(inputs_params);
+    CHECK_ARG_NOT_NULL(outputs_params);
+    CHECK_ARG_NOT_NULL(input_buffers);
+    CHECK_ARG_NOT_NULL(output_buffers);
+
+    std::map<std::string, MemoryView> input_data;
+    for (size_t i = 0; i < inputs_count; i++) {
+        input_data.emplace(input_buffers[i].name, MemoryView(input_buffers[i].raw_buffer.buffer,
+            input_buffers[i].raw_buffer.size));
+    }
+
+    std::map<std::string, MemoryView> output_data;
+    for (size_t i = 0; i < outputs_count; i++) {
+        output_data.emplace(output_buffers[i].name, MemoryView(output_buffers[i].raw_buffer.buffer,
+            output_buffers[i].raw_buffer.size));
+    }
+
+    std::map<std::string, hailo_vstream_params_t> inputs_params_map;
+    for (size_t i = 0; i < inputs_count; i++) {
+        inputs_params_map.emplace(inputs_params[i].name, inputs_params[i].params);
+    }
+
+    std::map<std::string, hailo_vstream_params_t> outputs_params_map;
+    for (size_t i = 0; i < outputs_count; i++) {
+        outputs_params_map.emplace(outputs_params[i].name, outputs_params[i].params);
+    }
+
+    auto net_group_ptr = reinterpret_cast<ConfiguredNetworkGroup*>(network_group);
+    auto infer_pipeline = InferVStreams::create(*net_group_ptr, inputs_params_map, outputs_params_map);
+    CHECK_EXPECTED_AS_STATUS(infer_pipeline);
+
+    auto status = infer_pipeline->infer(input_data, output_data, frames_count);
+    CHECK_SUCCESS(status);
+
+    return HAILO_SUCCESS;
+}
+
+/* Multi network API functions */
+static hailo_status convert_network_infos_vector_to_array(std::vector<hailo_network_info_t> &&network_infos_vec, 
+    hailo_network_info_t *network_infos, size_t *number_of_networks)
+{
+    auto max_entries = *number_of_networks;
+    *number_of_networks = network_infos_vec.size();
+
+    CHECK((*number_of_networks) <= max_entries, HAILO_INSUFFICIENT_BUFFER,
+          "The given buffer is too small to contain all network infos. There are {} networks in the given hef, given buffer size is {}",
+          (*number_of_networks), max_entries);
+
+    std::copy(network_infos_vec.begin(), network_infos_vec.end(), network_infos);
+
+    return HAILO_SUCCESS;
+}
+
+HAILORTAPI hailo_status hailo_hef_get_network_infos(hailo_hef hef, const char *network_group_name,
+    hailo_network_info_t *networks_infos, size_t *number_of_networks)
+{
+    CHECK_ARG_NOT_NULL(hef);
+    CHECK_ARG_NOT_NULL(networks_infos);
+    CHECK_ARG_NOT_NULL(number_of_networks);
+    const auto name_str = get_name_as_str(network_group_name);
+
+    auto network_infos_expected = (reinterpret_cast<Hef*>(hef))->get_network_infos(name_str);
+    CHECK_EXPECTED_AS_STATUS(network_infos_expected);
+
+    auto status = convert_network_infos_vector_to_array(network_infos_expected.release(), networks_infos, number_of_networks);
+    CHECK_SUCCESS(status);
+    
+    return HAILO_SUCCESS;
+}
+
+HAILORTAPI hailo_status hailo_get_network_infos(hailo_configured_network_group network_group,
+    hailo_network_info_t *networks_infos, size_t *number_of_networks)
+{
+    CHECK_ARG_NOT_NULL(network_group);
+    CHECK_ARG_NOT_NULL(networks_infos);
+    CHECK_ARG_NOT_NULL(number_of_networks);
+
+    auto network_infos_expected = (reinterpret_cast<ConfiguredNetworkGroup*>(network_group))->get_network_infos();
+    CHECK_EXPECTED_AS_STATUS(network_infos_expected);
+
+    auto status = convert_network_infos_vector_to_array(network_infos_expected.release(), networks_infos, number_of_networks);
+    CHECK_SUCCESS(status);
+
+    return HAILO_SUCCESS;
+}
+
+HAILORTAPI hailo_status hailo_hef_make_input_vstream_params(hailo_hef hef, const char *name, 
+    bool quantized, hailo_format_type_t format_type, 
+    hailo_input_vstream_params_by_name_t *input_params, size_t *input_params_count)
+{
+    CHECK_ARG_NOT_NULL(input_params);
+    CHECK_ARG_NOT_NULL(input_params_count);
+    const auto name_str = get_name_as_str(name);
+
+    auto input_params_map = (reinterpret_cast<Hef*>(hef))->make_input_vstream_params(name_str, quantized, format_type, 
+        HAILO_DEFAULT_VSTREAM_TIMEOUT_MS , HAILO_DEFAULT_VSTREAM_QUEUE_SIZE);
+    CHECK_EXPECTED_AS_STATUS(input_params_map);
+
+    if (input_params_map->size() > *input_params_count) {
+        LOGGER__ERROR(
+            "The given buffer is too small to contain all input_vstream_params. There are {} detected input vstreams in the given name {} , input param length is {}",
+            input_params_map->size(), name, *input_params_count);
+        *input_params_count = input_params_map->size();
+        return HAILO_INSUFFICIENT_BUFFER;
+    }
+
+    int i = 0;
+    for (auto &name_pair : input_params_map.value()) {
+        // Adding +1 for NULL terminator
+        CHECK(HAILO_MAX_STREAM_NAME_SIZE >= (name_pair.first.length() + 1), HAILO_INVALID_ARGUMENT,
+            "Name too long (max is {}, received {})", HAILO_MAX_STREAM_NAME_SIZE, name_pair.first);
+        memcpy(input_params[i].name, name_pair.first.c_str(), name_pair.first.length() + 1);
+        input_params[i].params = name_pair.second;
+        i++;
+    }
+    *input_params_count = input_params_map->size();
+
+    return HAILO_SUCCESS;    
+}
+
+hailo_status hailo_hef_make_output_vstream_params(hailo_hef hef, const char *name,
+    bool quantized, hailo_format_type_t format_type, 
+    hailo_output_vstream_params_by_name_t *output_vstream_params, size_t *output_params_count)
+{
+    CHECK_ARG_NOT_NULL(output_vstream_params);
+    CHECK_ARG_NOT_NULL(output_params_count);
+    const auto name_str = get_name_as_str(name);
+
+    auto output_params_map = (reinterpret_cast<Hef*>(hef))->make_output_vstream_params(name_str, quantized, format_type,
+        HAILO_DEFAULT_VSTREAM_TIMEOUT_MS , HAILO_DEFAULT_VSTREAM_QUEUE_SIZE);
+    CHECK_EXPECTED_AS_STATUS(output_params_map);
+
+    if (output_params_map->size() > *output_params_count) {
+        LOGGER__ERROR(
+            "The given buffer is too small to contain all input_vstream_params. There are {} detected output vstreams in the given name {} , output param length is {}",
+            output_params_map->size(), name, *output_params_count);
+        *output_params_count = output_params_map->size();
+        return HAILO_INSUFFICIENT_BUFFER;
+    }
+
+    int i = 0;
+    for (auto &name_pair : output_params_map.value()) {
+        // Adding +1 for NULL terminator
+        CHECK(HAILO_MAX_STREAM_NAME_SIZE >= (name_pair.first.length() + 1), HAILO_INVALID_ARGUMENT,
+            "Name too long (max is {}, received {})", HAILO_MAX_STREAM_NAME_SIZE, name_pair.first);
+        memcpy(output_vstream_params[i].name, name_pair.first.c_str(), name_pair.first.length() + 1);
+        output_vstream_params[i].params = name_pair.second;
+        i++;
+    }
+    *output_params_count = output_params_map->size();
+
+    return HAILO_SUCCESS;
+}
+/* End of multi network API functions */
diff --git a/hailort/libhailort/src/hailort_common.cpp b/hailort/libhailort/src/hailort_common.cpp
new file mode 100644 (file)
index 0000000..6b6fcbc
--- /dev/null
@@ -0,0 +1,21 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file hailort_common.cpp
+ * @brief Implementation of common hailort utilities
+ **/
+
+#include "hailo/hailort_common.hpp"
+
+namespace hailort
+{
+
+// Needed for the linker
+const uint32_t HailoRTCommon::BBOX_PARAMS;
+const uint32_t HailoRTCommon::MAX_DEFUSED_LAYER_COUNT;
+const size_t HailoRTCommon::HW_DATA_ALIGNMENT;
+const uint64_t HailoRTCommon::NMS_DELIMITER;
+
+} /* namespace hailort */
diff --git a/hailort/libhailort/src/hailort_defaults.hpp b/hailort/libhailort/src/hailort_defaults.hpp
new file mode 100644 (file)
index 0000000..73704fe
--- /dev/null
@@ -0,0 +1,359 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file hailort_defaults.hpp
+ * @brief 
+ **/
+#ifndef HAILORT_DEFAULTS_HPP_
+#define HAILORT_DEFAULTS_HPP_
+
+#include <hailo/hailort.h>
+#include "hailo/expected.hpp"
+#include "hailo/network_group.hpp"
+#include "common/logger_macros.hpp"
+
+namespace hailort
+{
+
+constexpr hailo_format_order_t DEFAULT_FORMAT_ORDER_MAP[] = {
+    // Key is device_format_order, value is default user_format_order
+    HAILO_FORMAT_ORDER_AUTO,                // HAILO_FORMAT_ORDER_AUTO, - Should not be used!
+    HAILO_FORMAT_ORDER_NHWC,                // HAILO_FORMAT_ORDER_NHWC,
+    HAILO_FORMAT_ORDER_NHWC,                // HAILO_FORMAT_ORDER_NHCW,
+    HAILO_FORMAT_ORDER_FCR,                 // HAILO_FORMAT_ORDER_FCR,
+    HAILO_FORMAT_ORDER_F8CR,                // HAILO_FORMAT_ORDER_F8CR,
+    HAILO_FORMAT_ORDER_NHW,                 // HAILO_FORMAT_ORDER_NHW,
+    HAILO_FORMAT_ORDER_NC,                  // HAILO_FORMAT_ORDER_NC,
+    HAILO_FORMAT_ORDER_BAYER_RGB,           // HAILO_FORMAT_ORDER_BAYER_RGB,
+    HAILO_FORMAT_ORDER_12_BIT_BAYER_RGB,    // HAILO_FORMAT_ORDER_12_BIT_BAYER_RGB
+    HAILO_FORMAT_ORDER_HAILO_NMS,           // HAILO_FORMAT_ORDER_HAILO_NMS,
+    HAILO_FORMAT_ORDER_NHWC,                // HAILO_FORMAT_ORDER_RGB888,
+    HAILO_FORMAT_ORDER_NCHW,                // HAILO_FORMAT_ORDER_NCHW,
+    HAILO_FORMAT_ORDER_YUY2                 // HAILO_FORMAT_ORDER_YUY2
+    };
+
+constexpr hailo_format_order_t DEFAULT_FORMAT_ARGMAX_ORDER_MAP[] = {
+    // Key is device_format_order, value is default user_format_order
+    HAILO_FORMAT_ORDER_AUTO,                // HAILO_FORMAT_ORDER_AUTO, - Should not be used!
+    HAILO_FORMAT_ORDER_NHW,                 // HAILO_FORMAT_ORDER_NHWC,
+    HAILO_FORMAT_ORDER_NHW,                 // HAILO_FORMAT_ORDER_NHCW,
+    HAILO_FORMAT_ORDER_NHW,                 // HAILO_FORMAT_ORDER_FCR,
+    HAILO_FORMAT_ORDER_NHW,                 // HAILO_FORMAT_ORDER_F8CR,
+    HAILO_FORMAT_ORDER_NHW,                 // HAILO_FORMAT_ORDER_NHW,
+    HAILO_FORMAT_ORDER_NC,                  // HAILO_FORMAT_ORDER_NC,
+    HAILO_FORMAT_ORDER_NHW,                 // HAILO_FORMAT_ORDER_BAYER_RGB,
+    HAILO_FORMAT_ORDER_NHW,                 // HAILO_FORMAT_ORDER_12_BIT_BAYER_RGB
+    HAILO_FORMAT_ORDER_HAILO_NMS,           // HAILO_FORMAT_ORDER_HAILO_NMS,
+    HAILO_FORMAT_ORDER_NHW,                 // HAILO_FORMAT_ORDER_RGB888,
+    HAILO_FORMAT_ORDER_NHW,                 // HAILO_FORMAT_ORDER_NCHW,
+    HAILO_FORMAT_ORDER_YUY2                 // HAILO_FORMAT_ORDER_YUY2
+};
+
+
+#define HAILO_DEFAULT_PARTIAL_NETWORK_NAME (std::string("default_network_name"))
+#define HAILO_DEFAULT_NETWORK_NAME_QUALIFIER (std::string("/"))
+
+class HailoRTDefaults
+{
+public:
+    HailoRTDefaults() = delete;
+
+    // This func must be aligned to SDK!
+    static Expected<hailo_format_order_t> get_device_format_order(uint32_t allocator_format_order)
+    {
+        switch (allocator_format_order) {
+        case 0:
+            return std::move(HAILO_FORMAT_ORDER_NHWC);
+            break;
+        case 1:
+            return std::move(HAILO_FORMAT_ORDER_NHCW);
+            break;
+        case 2:
+            return std::move(HAILO_FORMAT_ORDER_NC);
+            break;
+        case 3:
+            return std::move(HAILO_FORMAT_ORDER_FCR);
+            break;
+        case 4:
+            return std::move(HAILO_FORMAT_ORDER_BAYER_RGB);
+            break;
+        case 5:
+            return std::move(HAILO_FORMAT_ORDER_NHW);
+            break;
+        case 6:
+            return std::move(HAILO_FORMAT_ORDER_HAILO_NMS);
+            break;
+        case 7:
+            return std::move(HAILO_FORMAT_ORDER_F8CR);
+            break;
+        case 8:
+            return std::move(HAILO_FORMAT_ORDER_RGB888);
+            break;
+        case 11:
+            return std::move(HAILO_FORMAT_ORDER_YUY2);
+            break;
+        default:
+            LOGGER__ERROR("Invalid allocator_format_order ({})", allocator_format_order);
+            return make_unexpected(HAILO_INTERNAL_FAILURE);
+        }
+    }
+
+    static constexpr hailo_format_order_t get_default_host_format_order(const hailo_format_t &device_format)
+    {
+        const bool is_argmax = (0 != (device_format.flags & HAILO_FORMAT_FLAGS_HOST_ARGMAX));
+        if (!is_argmax) {
+            return DEFAULT_FORMAT_ORDER_MAP[device_format.order];
+        } else {
+            return DEFAULT_FORMAT_ARGMAX_ORDER_MAP[device_format.order];
+        }
+    }
+
+    static constexpr struct sockaddr_in get_sockaddr() {
+        struct sockaddr_in address{};
+        address.sin_family = AF_INET;
+        address.sin_port = 0;
+        address.sin_addr.s_addr = INADDR_ANY;
+        // sin_zero is already zeroed
+        return address;
+    }
+
+    static constexpr hailo_format_t get_user_buffer_format()
+    {
+        return get_user_buffer_format(true, HAILO_FORMAT_TYPE_AUTO);
+    }
+
+    static constexpr hailo_format_t get_user_buffer_format(bool quantized, hailo_format_type_t format_type)
+    {
+        hailo_format_t user_buffer_format{};
+        user_buffer_format.type = format_type;
+        user_buffer_format.order = HAILO_FORMAT_ORDER_AUTO;
+
+        hailo_format_flags_t flags = HAILO_FORMAT_FLAGS_NONE;
+        if (quantized) {
+            flags = static_cast<hailo_format_flags_t>(flags | HAILO_FORMAT_FLAGS_QUANTIZED);
+        }
+
+        user_buffer_format.flags = flags;
+        return user_buffer_format;
+    }
+
+    static constexpr hailo_transform_params_t get_transform_params(bool quantized, hailo_format_type_t format_type)
+    {
+        hailo_transform_params_t params{};
+        params.transform_mode = HAILO_STREAM_TRANSFORM_COPY;
+        params.user_buffer_format = get_user_buffer_format(quantized, format_type);
+        return params;
+    }
+
+    static constexpr hailo_transform_params_t get_transform_params()
+    {
+        return get_transform_params(true, HAILO_FORMAT_TYPE_AUTO);
+    }
+
+    static constexpr hailo_vstream_params_t get_vstreams_params()
+    {
+        return get_vstreams_params(true, HAILO_FORMAT_TYPE_AUTO);
+    }
+
+    static constexpr hailo_vstream_params_t get_vstreams_params(bool quantized, hailo_format_type_t format_type)
+    {
+        hailo_vstream_params_t params{};
+        params.user_buffer_format = get_user_buffer_format(quantized, format_type);
+        params.queue_size = HAILO_DEFAULT_VSTREAM_QUEUE_SIZE;
+        params.timeout_ms = HAILO_DEFAULT_VSTREAM_TIMEOUT_MS;
+        params.vstream_stats_flags = HAILO_VSTREAM_STATS_NONE;
+        params.pipeline_elements_stats_flags = HAILO_PIPELINE_ELEM_STATS_NONE;
+        return params;
+    }
+
+    static constexpr hailo_transform_params_t get_transform_params(const hailo_stream_info_t &stream_info)
+    {
+        hailo_transform_params_t params{};
+        params.transform_mode = HAILO_STREAM_TRANSFORM_COPY;
+        params.user_buffer_format.type = stream_info.format.type;
+        params.user_buffer_format.order = get_default_host_format_order(stream_info.format);
+        params.user_buffer_format.flags = static_cast<hailo_format_flags_t>(
+            HAILO_FORMAT_FLAGS_QUANTIZED &
+            ~HAILO_FORMAT_FLAGS_TRANSPOSED);
+        return params;
+    }
+
+    static constexpr hailo_eth_input_stream_params_t get_eth_input_stream_params() {
+        hailo_eth_input_stream_params_t params{};
+        params.host_address = get_sockaddr();
+        params.device_port = HAILO_DEFAULT_ETH_DEVICE_PORT;
+        params.max_payload_size = HAILO_DEFAULT_ETH_MAX_PAYLOAD_SIZE;
+        params.is_sync_enabled = false;
+        params.frames_per_sync = 0;
+        params.rate_limit_bytes_per_sec = 0;
+        params.buffers_threshold = HAILO_DEFAULT_BUFFERS_THRESHOLD;
+        return params;
+    }
+
+    static constexpr hailo_eth_output_stream_params_t get_eth_output_stream_params() {
+        hailo_eth_output_stream_params_t params{};
+        params.host_address = get_sockaddr();
+        params.device_port = HAILO_DEFAULT_ETH_DEVICE_PORT;
+        params.max_payload_size = HAILO_DEFAULT_ETH_MAX_PAYLOAD_SIZE;
+        params.is_sync_enabled = true;
+        return params;
+    }
+
+
+    static constexpr hailo_pcie_input_stream_params_t get_pcie_input_stream_params() {
+        hailo_pcie_input_stream_params_t params{};
+        return params;
+    }
+
+    static constexpr hailo_pcie_output_stream_params_t get_pcie_output_stream_params() {
+        hailo_pcie_output_stream_params_t params{};
+        return params;
+    }
+
+    static constexpr hailo_core_input_stream_params_t get_core_input_stream_params() {
+        hailo_core_input_stream_params_t params{};
+        return params;
+    }
+
+    static constexpr hailo_core_output_stream_params_t get_core_output_stream_params() {
+        hailo_core_output_stream_params_t params{};
+        return params;
+    }
+
+    static constexpr hailo_mipi_input_stream_params_t get_mipi_input_stream_params()
+    {
+        hailo_mipi_input_stream_params_t params = {};
+
+        params.mipi_rx_id = 0;
+        params.data_type = HAILO_MIPI_RX_TYPE_RAW_8;
+
+        params.mipi_common_params.img_width_pixels = 1920;
+        params.mipi_common_params.img_height_pixels = 1080;
+        params.mipi_common_params.pixels_per_clock = HAILO_MIPI_PIXELS_PER_CLOCK_4;
+        params.mipi_common_params.number_of_lanes = 2;
+        params.mipi_common_params.clock_selection = HAILO_MIPI_CLOCK_SELECTION_AUTOMATIC;
+        params.mipi_common_params.data_rate = 260;
+        params.mipi_common_params.virtual_channel_index = 0;
+
+        params.isp_enable = false;
+        params.isp_params.isp_img_in_order = HAILO_MIPI_ISP_IMG_IN_ORDER_GR_FIRST;
+        params.isp_params.isp_img_out_data_type = HAILO_MIPI_IMG_OUT_DATA_TYPE_RGB_888;
+        params.isp_params.isp_crop_enable = false;
+        params.isp_params.isp_crop_output_width_pixels = 1920;
+        params.isp_params.isp_crop_output_height_pixels = 1080;
+        params.isp_params.isp_crop_output_width_start_offset_pixels = 0;
+        params.isp_params.isp_crop_output_height_start_offset_pixels = 0;
+        params.isp_params.isp_test_pattern_enable = true;
+        params.isp_params.isp_configuration_bypass = false;
+        params.isp_params.isp_run_time_ae_enable = true;
+        params.isp_params.isp_run_time_awb_enable = true;
+        params.isp_params.isp_run_time_adt_enable = true;
+        params.isp_params.isp_run_time_af_enable = false;
+        params.isp_params.isp_run_time_calculations_interval_ms = 0;
+        params.isp_params.isp_light_frequency = HAILO_MIPI_ISP_LIGHT_FREQUENCY_50HZ;
+
+        return params;
+    }
+
+    static Expected<hailo_stream_parameters_t> get_stream_parameters(hailo_stream_interface_t interface,
+            hailo_stream_direction_t direction)
+    {
+        hailo_stream_parameters_t params = {};
+        params.stream_interface = interface;
+        params.direction = direction;
+        switch (params.stream_interface) {
+        case HAILO_STREAM_INTERFACE_PCIE:
+            if (HAILO_H2D_STREAM == direction) {
+                params.pcie_input_params = get_pcie_input_stream_params();
+            } else {
+                params.pcie_output_params = get_pcie_output_stream_params();
+            }
+            break;
+        case HAILO_STREAM_INTERFACE_CORE:
+            if (HAILO_H2D_STREAM == direction) {
+                params.core_input_params = get_core_input_stream_params();
+            } else {
+                params.core_output_params = get_core_output_stream_params();
+            }
+            break;
+        case HAILO_STREAM_INTERFACE_ETH:
+            if (HAILO_H2D_STREAM == direction) {
+                params.eth_input_params = get_eth_input_stream_params();
+            } else {
+                params.eth_output_params = get_eth_output_stream_params();
+            }
+            break;
+        default:
+            LOGGER__ERROR("Invalid stream interface");
+            return make_unexpected(HAILO_INVALID_ARGUMENT);
+        }
+        return params;
+    }
+
+    static hailo_activate_network_group_params_t get_network_group_params()
+    {
+        hailo_activate_network_group_params_t params = {};
+        return params;
+    }
+
+    static ConfigureNetworkParams get_configure_params(uint16_t batch_size = HAILO_DEFAULT_BATCH_SIZE,
+        hailo_power_mode_t power_mode = HAILO_POWER_MODE_PERFORMANCE)
+    {
+        ConfigureNetworkParams params = {};
+        params.batch_size = batch_size;
+        if (std::getenv("FORCE_POWER_MODE_ULTRA_PERFORMANCE") != nullptr) {
+            power_mode = HAILO_POWER_MODE_ULTRA_PERFORMANCE;
+        }
+        params.power_mode = power_mode;
+        params.latency = HAILO_LATENCY_NONE;
+        return params;
+    }
+
+    static hailo_network_parameters_t get_network_parameters(uint16_t batch_size = HAILO_DEFAULT_BATCH_SIZE)
+    {
+        hailo_network_parameters_t params = {};
+        params.batch_size = batch_size;
+
+        return params;
+    }    
+
+    static std::string get_network_name(const std::string &net_group_name)
+    {
+        std::string default_network_name = net_group_name + 
+            HAILO_DEFAULT_NETWORK_NAME_QUALIFIER + 
+            HAILO_DEFAULT_PARTIAL_NETWORK_NAME;
+
+        return default_network_name;
+    }
+
+    static std::string get_partial_network_name()
+    {
+        return HAILO_DEFAULT_PARTIAL_NETWORK_NAME;
+    }    
+
+    static hailo_format_t expand_auto_format(const hailo_format_t &host_format, const hailo_format_t &hw_format)
+    {
+        auto host_format_copy = host_format;
+        if (HAILO_FORMAT_TYPE_AUTO == host_format_copy.type) {
+            host_format_copy.type =  hw_format.type;
+        }
+        if (HAILO_FORMAT_ORDER_AUTO == host_format_copy.order) {
+            host_format_copy.order = get_default_host_format_order(hw_format);
+        }
+        return host_format_copy;
+    }
+
+    static hailo_vdevice_params_t get_vdevice_params()
+    {
+        hailo_vdevice_params_t params = {};
+        params.device_count = HAILO_DEFAULT_DEVICE_COUNT;
+        params.device_infos = nullptr;
+        return params;
+    }
+};
+
+} /* namespace hailort */
+
+#endif /* HAILORT_DEFAULTS_HPP_ */
diff --git a/hailort/libhailort/src/hailort_logger.cpp b/hailort/libhailort/src/hailort_logger.cpp
new file mode 100644 (file)
index 0000000..6a5d59c
--- /dev/null
@@ -0,0 +1,176 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file hailort_logger.cpp
+ * @brief Implements logger used by hailort.
+ **/
+
+#include "hailort_logger.hpp"
+#include "common/utils.hpp"
+
+#include <spdlog/sinks/rotating_file_sink.h>
+#include <spdlog/sinks/stdout_color_sinks.h>
+#include <spdlog/sinks/android_sink.h>
+#include <spdlog/sinks/null_sink.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <iostream>
+#ifdef _WIN32
+#include <io.h>
+#include <AclAPI.h>
+#endif
+
+namespace hailort
+{
+
+#define MAX_LOG_FILE_SIZE (1024 * 1024) // 1MB
+
+#define HAILORT_NAME "HailoRT"
+#define HAILORT_LOGGER_FILENAME "hailort.log"
+#define HAILORT_MAX_NUMBER_OF_LOG_FILES (1) // There will be 2 log files - 1 spare
+#define HAILORT_CONSOLE_LOGGER_PATTERN "[%n] [%^%l%$] %v" // Console logger will print: [hailort logger file name] [log level] msg
+#define HAILORT_FILE_LOGGER_PATTERN "[%Y-%m-%d %X.%e] [%n] [%l] [%s:%#] [%!] %v" //File logger will print: [timestamp] [hailort] [log level] [source file:line number] [function name] msg
+#define HAILORT_ANDROID_LOGGER_PATTERN "%v"               // Android logger will print only message (additional info are built-in)
+
+#ifdef _WIN32
+bool is_curr_dir_accesible()
+{
+    // The code is based on examples from: https://cpp.hotexamples.com/examples/-/-/AccessCheck/cpp-accesscheck-function-examples.html
+    bool return_val = false;
+    SECURITY_INFORMATION security_Info = OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION | LABEL_SECURITY_INFORMATION;
+    PSECURITY_DESCRIPTOR security_desc = NULL;
+    DWORD access_mask = GENERIC_WRITE;
+    GENERIC_MAPPING mapping = {0xFFFFFFFF};
+    mapping.GenericRead = FILE_GENERIC_READ;
+    mapping.GenericWrite = FILE_GENERIC_WRITE;
+    mapping.GenericExecute = FILE_GENERIC_EXECUTE;
+    mapping.GenericAll = FILE_ALL_ACCESS;
+    HANDLE h_token = NULL;
+    HANDLE h_impersonated_token = NULL;
+    PRIVILEGE_SET privilege_set = {0};
+    DWORD privilege_set_size = sizeof(privilege_set);
+    DWORD granted_access = 0;
+    BOOL access_status = FALSE;
+
+    // Retrieves a copy of the security descriptor for current dir.
+    DWORD result = GetNamedSecurityInfo(".", SE_FILE_OBJECT, security_Info, NULL, NULL, NULL, NULL, &security_desc);
+    if (result != ERROR_SUCCESS) {
+        std::cerr << "Failed to get security information for local directory with error = " << result << std::endl;
+        return_val = false;
+        goto l_exit;
+    }
+
+    MapGenericMask(&access_mask, &mapping);
+    if (OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &h_token) == 0) {
+        return_val = false;
+        std::cerr << "OpenProcessToken() Failed. Cannot check directory's access permissions, last_error = " << GetLastError() << std::endl;
+        goto l_release_security_desc;
+    }
+
+    // Getting a handle to an impersonation token. It will represent the client that is attempting to gain access. 
+    if (DuplicateToken(h_token, SecurityImpersonation, &h_impersonated_token) == 0) {
+        std::cerr << "DuplicateToken() Failed. Cannot check directory's access permissions, last_error = " << GetLastError() << std::endl;
+        return_val = false;
+        goto l_close_token;
+    }
+
+    if (AccessCheck(security_desc, h_impersonated_token, access_mask, &mapping, &privilege_set, &privilege_set_size, &granted_access, &access_status) == 0) {
+        std::cerr << "AccessCheck Failed. Cannot check directory's access permissions, last_error = " << GetLastError() << std::endl;
+        return_val = false;
+        goto l_close_impersonated_token;
+    }
+
+    return_val = (access_status == TRUE);
+
+l_close_impersonated_token:
+    if (NULL != h_impersonated_token) {
+        (void)CloseHandle(h_impersonated_token);
+    }
+
+l_close_token:
+    if (NULL != h_token) {
+        (void)CloseHandle(h_token);
+    }
+
+l_release_security_desc:
+    if (NULL != security_desc) {
+           (void)LocalFree(security_desc);
+    }    
+l_exit:
+    return return_val;
+}
+#else
+bool is_curr_dir_accesible()
+{
+    auto ret = access(".", W_OK);
+    if (ret == 0) {
+        return true;
+    }
+    else if (EACCES == errno) {
+        return false;
+    }
+    else {
+        std::cerr << "Failed checking directory's access permissions, errno = " << errno << std::endl;
+        return false;
+    }
+}
+#endif
+
+std::shared_ptr<spdlog::sinks::sink> HailoRTLogger::create_file_sink()
+{
+    // TODO: HRT-4937 - Consider creating the hailort.log file in predetermined directory or as a part of SYSLOG
+    if (is_curr_dir_accesible()) {
+        return make_shared_nothrow<spdlog::sinks::rotating_file_sink_mt>(HAILORT_LOGGER_FILENAME, MAX_LOG_FILE_SIZE,
+            HAILORT_MAX_NUMBER_OF_LOG_FILES);
+    }
+    else {
+        std::cerr << "HailoRT warning: Cannot create HailoRT log file! Please check the current directory's write permissions." << std::endl;
+        // Create null sink instead (Will throw away its log)
+        return make_shared_nothrow<spdlog::sinks::null_sink_st>();
+    }
+}
+
+HailoRTLogger::HailoRTLogger() :
+    m_console_sink(make_shared_nothrow<spdlog::sinks::stderr_color_sink_mt>()),
+#ifdef __ANDROID__
+    m_file_sink(make_shared_nothrow<spdlog::sinks::android_sink_mt>(HAILORT_NAME))
+#else
+    m_file_sink(create_file_sink())
+#endif
+{
+
+#ifdef __ANDROID__
+    m_file_sink->set_pattern(HAILORT_ANDROID_LOGGER_PATTERN);
+#else
+    m_file_sink->set_pattern(HAILORT_FILE_LOGGER_PATTERN);
+#endif
+
+    // TODO: Handle null pointers for logger and sinks
+    m_console_sink->set_pattern(HAILORT_CONSOLE_LOGGER_PATTERN);
+    spdlog::sinks_init_list sink_list = { m_console_sink, m_file_sink };
+    m_hailort_logger = make_shared_nothrow<spdlog::logger>(HAILORT_NAME, sink_list.begin(), sink_list.end());
+
+#ifdef NDEBUG
+    set_levels(spdlog::level::warn, spdlog::level::info, spdlog::level::warn);
+#else
+    set_levels(spdlog::level::warn, spdlog::level::debug, spdlog::level::debug);
+#endif
+    spdlog::set_default_logger(m_hailort_logger);
+}
+
+std::shared_ptr<spdlog::logger> HailoRTLogger::logger()
+{
+    return m_hailort_logger;
+}
+
+void HailoRTLogger::set_levels(spdlog::level::level_enum console_level,
+    spdlog::level::level_enum file_level, spdlog::level::level_enum flush_level)
+{
+    m_console_sink->set_level(console_level);
+    m_file_sink->set_level(file_level);
+    m_hailort_logger->flush_on(flush_level);
+}
+
+} /* namespace hailort */
diff --git a/hailort/libhailort/src/hailort_logger.hpp b/hailort/libhailort/src/hailort_logger.hpp
new file mode 100644 (file)
index 0000000..b8544b1
--- /dev/null
@@ -0,0 +1,48 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file logger_macros.hpp
+ * @brief Declares logger used by hailort.
+ **/
+
+#ifndef _HAILORT_LOGGER_HPP_
+#define _HAILORT_LOGGER_HPP_
+
+
+#include <string.h>
+#include <stdint.h>
+#include <ctype.h>
+
+#include "hailo/hailort.h"
+#include "common/logger_macros.hpp"
+
+namespace hailort
+{
+
+class HailoRTLogger {
+public:
+    static HailoRTLogger& get_instance()
+    {
+        static HailoRTLogger instance;
+        return instance;
+    }
+    HailoRTLogger(HailoRTLogger const&) = delete;
+    void operator=(HailoRTLogger const&) = delete;
+
+    std::shared_ptr<spdlog::logger> logger();
+    void set_levels(spdlog::level::level_enum console_level, spdlog::level::level_enum file_level,
+        spdlog::level::level_enum flush_level);
+    static std::shared_ptr<spdlog::sinks::sink> create_file_sink();
+private:
+    HailoRTLogger();
+
+    std::shared_ptr<spdlog::sinks::sink> m_console_sink;
+    std::shared_ptr<spdlog::sinks::sink> m_file_sink;
+    std::shared_ptr<spdlog::logger> m_hailort_logger;
+};
+
+} /* namespace hailort */
+
+#endif /* _HAILORT_LOGGER_HPP_ */
diff --git a/hailort/libhailort/src/hef.cpp b/hailort/libhailort/src/hef.cpp
new file mode 100644 (file)
index 0000000..ba0d276
--- /dev/null
@@ -0,0 +1,3708 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file hef.cpp
+ * @brief TODO: brief
+ *
+ * TODO: doc
+ **/
+
+#include "hailo/hailort.h"
+#include "hailo/hef.hpp"
+#include "hef_internal.hpp"
+#include "hailo/stream.hpp"
+#include "hailo/device.hpp"
+#include "common/utils.hpp"
+#include "hailo/hailort_common.hpp"
+#include "hailort_defaults.hpp"
+
+#include "hlpcie.hpp"
+#include "pcie_device.hpp"
+#include "context_switch/multi_context/vdma_config_manager.hpp"
+#include "context_switch/single_context/hcp_config_manager.hpp"
+#include "context_switch/single_context/hcp_config_network_group.hpp"
+#include "byte_order.h"
+#include "common/logger_macros.hpp"
+#include "context_switch/hef_metadata.hpp"
+#include "common/file_utils.hpp"
+#include "layer_info.hpp"
+#include "context_switch_defs.h"
+
+#include <fstream>
+#include <memory>
+#include <limits>
+#include <stdint.h>
+#include <stdbool.h>
+#include <set>
+#include <algorithm>
+#include <cstring>
+#include <numeric>
+
+namespace hailort
+{
+
+#define HEF__MD5_BUFFER_SIZE (1024)
+#define CCW_BYTES_IN_WORD (4)
+#define CCW_DATA_OFFSET (CCW_BYTES_IN_WORD * 2)
+#define CCW_HEADER_SIZE (CCW_DATA_OFFSET)
+#define DEFAULT_BATCH_SIZE (1)
+
+static const uint8_t ENABLE_LCU_CONTROL_WORD[4] = {1, 0, 0, 0};
+
+// Parse initial_l3 register from old hef
+constexpr uint32_t HAILO8_INITIAL_L3_CUT_MASK = 0x0000007F;
+constexpr uint32_t HAILO8_INITIAL_L3_OFFSET_MASK = 0x0007FF80L;
+constexpr uint32_t HAILO8_INITIAL_L3_OFFSET_SHIFT = 7;
+constexpr uint32_t HAILO8_INITIAL_L3_OFFSET_BYTES_GRANULARITY_SHIFT = 3;
+constexpr uint64_t CCW_NOP = 0x0;
+
+#pragma pack(push, 1)
+typedef struct {
+    uint32_t words_count;
+    uint32_t address;
+} CcwHeader;
+#pragma pack(pop)
+
+typedef struct {
+    std::set<uint8_t> D2H_channels_in_use;
+    std::set<uint8_t> H2D_channels_in_use;
+} ContextSwitchChannelsParsingInfo;
+
+// Note: Can't add the definition in the header. This will lead to the following error:
+//       /usr/include/c++/7/bits/unique_ptr.h: In instantiation of ‘void std::default_delete<_Tp>::operator()(_Tp*) const [with _Tp = Hef::Impl]’:
+//       /usr/include/c++/7/bits/unique_ptr.h:263:17:   required from ‘std::unique_ptr<_Tp, _Dp>::~unique_ptr() [with _Tp = Hef::Impl; _Dp = std::default_delete<Hef::Impl>]’
+//       /local/users/projects/platform-sw/hailort/libhailort/src/../include/hailo/hef.hpp:61:7:   required from ‘Expected<T>::~Expected() [with T = Hef]’
+//       /local/users/projects/platform-sw/hailort/hailortcli/run_command.cpp:705:51:   required from here
+//       /usr/include/c++/7/bits/unique_ptr.h:76:22: error: invalid application of ‘sizeof’ to incomplete type ‘Hef::Impl’
+//         static_assert(sizeof(_Tp)>0,
+Hef::~Hef() = default;
+Hef::Hef(Hef &&) = default;
+Hef &Hef::operator=(Hef &&) = default;
+
+Expected<Hef> Hef::create(const std::string &hef_path)
+{
+    auto impl = Hef::Impl::create(hef_path);
+    CHECK_EXPECTED(impl);
+
+    // TODO: can we do this without the copy ctor here (i.e. make the impl as a unique_ptr to begin with)
+    return Hef(make_unique_nothrow<Impl>(impl.release()));
+}
+
+Expected<Hef> Hef::create(const MemoryView &hef_buffer)
+{
+    auto impl = Hef::Impl::create(hef_buffer);
+    CHECK_EXPECTED(impl);
+
+    // TODO: can we do this without the copy ctor here (i.e. make the impl as a unique_ptr to begin with)
+    return Hef(make_unique_nothrow<Impl>(impl.release()));
+}
+
+Hef::Hef(std::unique_ptr<Impl> pimpl) :
+    pimpl(std::move(pimpl))
+{}
+
+Expected<std::vector<hailo_stream_info_t>> Hef::get_input_stream_infos(const std::string &name)
+{
+    auto network_pair = pimpl->get_network_group_and_network_name(name);
+    CHECK_EXPECTED(network_pair);
+
+    return pimpl->get_input_stream_infos(network_pair.value().first, network_pair.value().second);
+}
+
+Expected<std::vector<hailo_stream_info_t>> Hef::get_output_stream_infos(const std::string &name)
+{
+    auto network_pair = pimpl->get_network_group_and_network_name(name);
+    CHECK_EXPECTED(network_pair);
+
+    return pimpl->get_output_stream_infos(network_pair.value().first, network_pair.value().second);
+}
+
+Expected<std::vector<hailo_stream_info_t>> Hef::get_all_stream_infos(const std::string &name)
+{
+    auto network_pair = pimpl->get_network_group_and_network_name(name);
+    CHECK_EXPECTED(network_pair);
+
+    return pimpl->get_all_stream_infos(network_pair.value().first, network_pair.value().second);
+}
+
+Expected<std::vector<hailo_network_info_t>> Hef::get_network_infos(const std::string &net_group_name)
+{
+    auto names_pair = pimpl->get_network_group_and_network_name(net_group_name);
+    CHECK_EXPECTED(names_pair);
+    return pimpl->get_network_infos(names_pair->first);
+}
+
+Expected<hailo_stream_info_t> Hef::get_stream_info_by_name(const std::string &stream_name,
+    hailo_stream_direction_t stream_direction, const std::string &net_group_name)
+{
+    // Addressing the situation where net_group_name == ""
+    auto net_group_name_pair = pimpl->get_network_group_and_network_name(net_group_name);
+    CHECK_EXPECTED(net_group_name_pair);
+    auto net_group_name_str = net_group_name_pair->first;
+
+    return pimpl->get_stream_info_by_name(stream_name, stream_direction, net_group_name_str);
+}
+
+Expected<std::vector<hailo_vstream_info_t>> Hef::get_input_vstream_infos(const std::string &name)
+{
+    auto network_pair = pimpl->get_network_group_and_network_name(name);
+    CHECK_EXPECTED(network_pair);
+
+    return pimpl->get_input_vstream_infos(network_pair.value().first, network_pair.value().second);
+}
+
+Expected<std::vector<hailo_vstream_info_t>> Hef::get_output_vstream_infos(const std::string &name)
+{
+    auto network_pair = pimpl->get_network_group_and_network_name(name);
+    CHECK_EXPECTED(network_pair);
+
+    return pimpl->get_output_vstream_infos(network_pair.value().first, network_pair.value().second);
+}
+
+Expected<std::vector<hailo_vstream_info_t>> Hef::get_all_vstream_infos(const std::string &name)
+{
+    auto network_pair = pimpl->get_network_group_and_network_name(name);
+    CHECK_EXPECTED(network_pair);
+
+    return pimpl->get_all_vstream_infos(network_pair.value().first, network_pair.value().second);
+}
+
+Expected<std::vector<std::string>> Hef::get_sorted_output_names(const std::string &net_group_name)
+{
+    // Addressing the situation where net_group_name == ""
+    auto net_group_name_pair = pimpl->get_network_group_and_network_name(net_group_name);
+    CHECK_EXPECTED(net_group_name_pair);
+    auto net_group_name_str = net_group_name_pair->first;
+
+    return pimpl->get_sorted_output_names(net_group_name_str);
+}
+
+Expected<size_t> Hef::get_number_of_input_streams(const std::string &net_group_name)
+{
+    // Addressing the situation where net_group_name == ""
+    auto net_group_name_pair = pimpl->get_network_group_and_network_name(net_group_name);
+    CHECK_EXPECTED(net_group_name_pair);
+    auto net_group_name_str = net_group_name_pair->first;
+
+    return pimpl->get_number_of_input_streams(net_group_name_str);
+}
+
+Expected<size_t> Hef::get_number_of_output_streams(const std::string &net_group_name)
+{
+    // Addressing the situation where net_group_name == ""
+    auto net_group_name_pair = pimpl->get_network_group_and_network_name(net_group_name);
+    CHECK_EXPECTED(net_group_name_pair);
+    auto net_group_name_str = net_group_name_pair->first;
+
+    return pimpl->get_number_of_output_streams(net_group_name_str);
+}
+
+Expected<float64_t> Hef::get_bottleneck_fps(const std::string &net_group_name)
+{
+    return pimpl->get_bottleneck_fps(net_group_name);
+}
+
+Expected<std::string> Hef::get_vstream_name_from_original_name(const std::string &original_name,
+    const std::string &net_group_name)
+{
+    return pimpl->get_vstream_name_from_original_name(original_name, net_group_name);
+}
+
+Expected<std::vector<std::string>> Hef::get_original_names_from_vstream_name(const std::string &stream_name,
+    const std::string &net_group_name)
+{
+    return pimpl->get_original_names_from_vstream_name(stream_name, net_group_name);
+}
+
+Expected<std::vector<std::string>> Hef::get_stream_names_from_vstream_name(const std::string &vstream_name,
+    const std::string &net_group_name)
+{
+    auto network_group_name_pair = pimpl->get_network_group_and_network_name(net_group_name);
+    CHECK_EXPECTED(network_group_name_pair);
+    auto net_group_name_str = network_group_name_pair->first;
+
+    return pimpl->get_stream_names_from_vstream_name(vstream_name, net_group_name_str);
+}
+
+Expected<std::vector<std::string>> Hef::get_vstream_names_from_stream_name(const std::string &stream_name,
+    const std::string &net_group_name)
+{
+    auto network_group_name_pair = pimpl->get_network_group_and_network_name(net_group_name);
+    CHECK_EXPECTED(network_group_name_pair);
+    auto net_group_name_str = network_group_name_pair->first;
+
+    return pimpl->get_vstream_names_from_stream_name(stream_name, net_group_name_str);
+}
+
+Expected<Hef::Impl> Hef::Impl::create(const std::string &hef_path)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+
+    Impl hef(hef_path, status);
+    if (HAILO_SUCCESS != status) {
+        LOGGER__ERROR("Failed creating HEF");
+        return make_unexpected(status);
+    }
+
+    return hef;
+}
+
+Expected<Hef::Impl> Hef::Impl::create(const MemoryView &hef_buffer)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+
+    Impl hef(hef_buffer, status);
+    if (HAILO_SUCCESS != status) {
+        LOGGER__ERROR("Failed creating HEF");
+        return make_unexpected(status);
+    }
+
+    return hef;
+}
+
+static hailo_status calc_istream_md5(std::ifstream &s, MD5_SUM_t &calculated_md5)
+{
+    char md5_buffer[HEF__MD5_BUFFER_SIZE] = {};
+    MD5_CTX md5 = {};
+
+    auto beg_pos = s.tellg();
+    CHECK(-1 != beg_pos, HAILO_FILE_OPERATION_FAILURE, "ifstream::tellg() failed");
+
+    MD5_Init(&md5);
+    while (!s.eof()) {
+        s.read(md5_buffer, HEF__MD5_BUFFER_SIZE);
+        CHECK(!s.bad(), HAILO_FILE_OPERATION_FAILURE, "ifstream::read() failed");
+        MD5_Update(&md5, &md5_buffer, static_cast<size_t>(s.gcount()));
+    }
+    MD5_Final(calculated_md5, &md5);
+
+    s.clear();
+    s.seekg(beg_pos, s.beg);
+    CHECK(s.good(), HAILO_FILE_OPERATION_FAILURE, "ifstream::seekg() failed");
+
+    return HAILO_SUCCESS;
+}
+
+hailo_status Hef::Impl::validate_hef_header(const hef__header_t &header, MD5_SUM_t &calculated_md5, size_t proto_size)
+{
+    CHECK(HEADER_MAGIC == BYTE_ORDER__htonl(header.magic), HAILO_INVALID_HEF,
+        "HEF magic does not match. detected magic - {:x}", header.magic);
+
+    CHECK(HEADER_VERSION == BYTE_ORDER__htonl(header.version), HAILO_INVALID_HEF, "HEF version does not match");
+
+    CHECK(proto_size == BYTE_ORDER__htonl(header.hef_proto_length), HAILO_INVALID_HEF,
+        "HEF file length does not match");
+
+    CHECK(0 == memcmp(&calculated_md5, &header.expected_md5, sizeof(MD5_SUM_t)), HAILO_INVALID_HEF,
+        "HEF md5 does not match");
+
+    return HAILO_SUCCESS;
+}
+
+hailo_status Hef::Impl::validate_hef_extensions()
+{
+    std::vector<std::string> unsupported_extensions;
+    for (const auto &extension : m_hef_extensions) {
+        if ((extension.type_index() >= m_supported_extensions_bitset.size()) || !m_supported_extensions_bitset.test(extension.type_index())) {
+            unsupported_extensions.emplace_back(extension.name());
+        }
+    }
+
+    CHECK(unsupported_extensions.empty(), HAILO_INVALID_HEF, "Failed opening non-compatible HEF with the following unsupported extensions: {}",
+        std::accumulate(std::next(unsupported_extensions.begin()), unsupported_extensions.end(), unsupported_extensions[0], 
+        [] (std::string a, std::string b) { return std::move(a) + ", " + b; }));
+
+    return HAILO_SUCCESS;
+}
+
+hailo_status Hef::Impl::parse_hef_file(const std::string &hef_path)
+{
+    auto hef_file = std::ifstream(hef_path, std::ios::in | std::ios::binary);
+    CHECK(hef_file.is_open(), HAILO_OPEN_FILE_FAILURE, "Failed to open HEF file \"{}\". errno: {}", hef_path, errno);
+
+    hef__header_t header = {};
+    hef_file.read((char*)&header, sizeof(header));
+    CHECK(hef_file.good(), HAILO_FILE_OPERATION_FAILURE, "Failed reading HEF header");
+
+    auto proto_size = get_istream_size(hef_file);
+    CHECK_EXPECTED_AS_STATUS(proto_size);
+
+    MD5_SUM_t calculated_md5 = {};
+    auto status = calc_istream_md5(hef_file, calculated_md5);
+    CHECK_SUCCESS(status);
+
+    status = validate_hef_header(header, calculated_md5, proto_size.value());
+    CHECK_SUCCESS(status);
+
+    ProtoHEFHef hef_message;
+    auto rb = hef_message.ParseFromIstream(&hef_file);
+    CHECK(rb, HAILO_INVALID_HEF, "Failed parsing HEF file");
+    status = transfer_protobuf_field_ownership(hef_message);
+    CHECK_SUCCESS(status);
+
+    status = fill_networks_metadata();
+    CHECK_SUCCESS(status);
+
+    // Must be called after fill_networks_metadata
+    status = validate_hef_extensions();
+    CHECK_SUCCESS(status);
+
+    return HAILO_SUCCESS;
+}
+
+hailo_status Hef::Impl::parse_hef_memview(const MemoryView &hef_memview)
+{
+    CHECK(hef_memview.size() >= sizeof(hef__header_t), HAILO_INVALID_HEF, "Invalid HEF header");
+    const hef__header_t &header = reinterpret_cast<const hef__header_t&>(*hef_memview.data());
+
+    auto proto_buffer = (hef_memview.data() + sizeof(header));
+    auto proto_size = (hef_memview.size() - sizeof(header));
+
+    MD5_CTX md5 = {};
+    MD5_SUM_t calculated_md5 = {};
+    MD5_Init(&md5);
+    MD5_Update(&md5, proto_buffer, proto_size);
+    MD5_Final(calculated_md5, &md5);
+
+    auto status = validate_hef_header(header, calculated_md5, proto_size);
+    CHECK_SUCCESS(status);
+
+    ProtoHEFHef hef_message;
+    auto rb = hef_message.ParseFromArray(proto_buffer, static_cast<int>(proto_size));
+    CHECK(rb, HAILO_INVALID_HEF, "Failed parsing HEF buffer");
+    status = transfer_protobuf_field_ownership(hef_message);
+    CHECK_SUCCESS(status);
+
+    status = fill_networks_metadata();
+    CHECK_SUCCESS(status);
+
+    // Must be called after fill_networks_metadata
+    status = validate_hef_extensions();
+    CHECK_SUCCESS(status);
+
+    return HAILO_SUCCESS;
+}
+
+hailo_status Hef::Impl::fill_networks_metadata()
+{
+    fill_extensions_bitset();
+    auto supported_features = get_supported_features(m_header, m_hef_extensions, m_included_features,
+        m_hef_optional_extensions);
+
+    for (auto &network_group : m_groups) {
+
+        CHECK(!contains(m_network_group_metadata,
+            network_group->network_group_metadata().network_group_name()),
+            HAILO_INVALID_OPERATION, "Network group with the name {} is already configured on the device",
+            network_group->network_group_metadata().network_group_name());
+
+        auto layer_infos = HefUtils::get_all_layers_info(*network_group, supported_features);
+        CHECK_EXPECTED_AS_STATUS(layer_infos);
+
+        auto sorted_output_names = HefUtils::get_sorted_output_names(*network_group);
+        CHECK_EXPECTED_AS_STATUS(sorted_output_names);
+
+        std::vector<std::string> sorted_partial_network_names;
+        if (supported_features.multi_network_support) {
+            sorted_partial_network_names.reserve(network_group->networks_names().size());
+            for (auto &partial_network_name : network_group->networks_names()) {
+                sorted_partial_network_names.push_back(partial_network_name);
+            }
+        } else {
+            sorted_partial_network_names.push_back(HAILO_DEFAULT_PARTIAL_NETWORK_NAME);
+        }
+
+        NetworkGroupMetadata metadata(network_group->network_group_metadata().network_group_name(),
+            layer_infos.release(), sorted_output_names.release(), supported_features, sorted_partial_network_names);
+        m_network_group_metadata.emplace(
+            network_group->network_group_metadata().network_group_name(), metadata);
+    }
+
+    return HAILO_SUCCESS;
+}
+
+hailo_status Hef::Impl::transfer_protobuf_field_ownership(ProtoHEFHef &hef_message)
+{
+    m_groups.reserve(hef_message.network_groups().size());
+    while (!hef_message.network_groups().empty()) {
+        // We pass the ownership from protobuf to shared_ptr (it'll call delete when the refcount drops to 0)
+        // Note: Protobuf messages are allocated with new
+        const auto network_group = hef_message.mutable_network_groups()->ReleaseLast();
+        CHECK(nullptr != network_group, HAILO_INTERNAL_FAILURE, "Null network group found while parsing HEF; Unexpected");
+        m_groups.emplace_back(network_group);
+    }
+
+    m_hef_extensions.reserve(hef_message.extensions().size());
+    for (const auto &extension : hef_message.extensions()) {
+        m_hef_extensions.emplace_back(extension);
+    }
+
+    m_header.CopyFrom(hef_message.header());
+    m_included_features.CopyFrom(hef_message.included_features());
+
+    m_hef_optional_extensions.reserve(hef_message.optional_extensions().size());
+    for (const auto &optional_extension : hef_message.optional_extensions()) {
+        m_hef_optional_extensions.emplace_back(optional_extension);
+    }
+
+    return HAILO_SUCCESS;
+}
+
+Hef::Impl::Impl(const std::string &hef_path, hailo_status &status)
+{
+    status = HAILO_UNINITIALIZED;
+    GOOGLE_PROTOBUF_VERIFY_VERSION;
+
+    status = parse_hef_file(hef_path);
+    if (HAILO_SUCCESS != status) {
+        LOGGER__ERROR("Failed parsing HEF file");
+        return;
+    }
+
+    status = HAILO_SUCCESS;
+}
+
+Hef::Impl::Impl(const MemoryView &hef_memview, hailo_status &status)
+{
+    status = HAILO_UNINITIALIZED;
+    GOOGLE_PROTOBUF_VERIFY_VERSION;
+
+    status = parse_hef_memview(hef_memview);
+    if (HAILO_SUCCESS != status) {
+        LOGGER__ERROR("Failed parsing HEF buffer");
+        return;
+    }
+
+    status = HAILO_SUCCESS;
+}
+
+void Hef::Impl::fill_extensions_bitset()
+{
+    for (auto extension : SUPPORTED_EXTENSIONS) {
+        m_supported_extensions_bitset[extension] = 1;
+    }
+}
+
+NetworkGroupSupportedFeatures Hef::Impl::get_supported_features(const ProtoHEFHeader &header,
+        const std::vector<ProtoHEFExtension> &hef_extensions, const ProtoHEFIncludedFeatures &included_features,
+        const std::vector<ProtoHEFOptionalExtension> &hef_optional_extensions)
+{
+    NetworkGroupSupportedFeatures supported_features = {};
+    supported_features.padded_ddr_buffers = check_hef_extension(ProtoHEFExtensionType::PADDED_DDR_BUFFERS, header,
+        hef_extensions, included_features);
+    supported_features.multi_network_support = check_hef_optional_extension(ProtoHEFExtensionType::MULTI_NETWORK_VARIABLE_BATCH_SIZE,
+        header, hef_optional_extensions);
+   supported_features.multi_context = check_hef_extension(ProtoHEFExtensionType::IS_MULTI_CONTEXTS, header,
+        hef_extensions, included_features);
+
+    return supported_features;
+}
+
+hailo_status get_hw_padding_params(hailo_format_order_t format_order, uint32_t width, uint32_t features, uint32_t hw_data_bytes, 
+    uint16_t &feature_padding_payload, uint16_t &periph_bytes_per_buffer)
+{
+    uint32_t feature_padding_payload_32bit = 0; 
+    uint32_t periph_bytes_per_buffer_32bit = 0;
+
+    // TODO: HRT-3278 dont assume core_buffers_per_frame == height    
+    switch (format_order)
+    {
+    case HAILO_FORMAT_ORDER_NHCW:
+    case HAILO_FORMAT_ORDER_NHW:
+        feature_padding_payload_32bit = width * hw_data_bytes;
+        periph_bytes_per_buffer_32bit = feature_padding_payload_32bit * features;
+        break;
+    case HAILO_FORMAT_ORDER_NHWC:
+    case HAILO_FORMAT_ORDER_FCR:
+    case HAILO_FORMAT_ORDER_F8CR:
+    case HAILO_FORMAT_ORDER_NC:
+    case HAILO_FORMAT_ORDER_BAYER_RGB:
+    case HAILO_FORMAT_ORDER_12_BIT_BAYER_RGB:
+    case HAILO_FORMAT_ORDER_RGB888:
+        feature_padding_payload_32bit = features * hw_data_bytes;
+        periph_bytes_per_buffer_32bit = feature_padding_payload_32bit * width;
+        break;
+    default:
+        LOGGER__ERROR("unsupported format for HW padding");
+        return HAILO_INTERNAL_FAILURE;
+    }
+
+    CHECK(IS_FIT_IN_UINT16(feature_padding_payload_32bit), HAILO_INVALID_HEF, 
+        "frame width {} is too big", feature_padding_payload_32bit);
+    CHECK(IS_FIT_IN_UINT16(periph_bytes_per_buffer_32bit), HAILO_INVALID_HEF,
+        "unpadded bytes per buffer {} is too big", periph_bytes_per_buffer_32bit);
+
+    feature_padding_payload = static_cast<uint16_t>(feature_padding_payload_32bit);
+    periph_bytes_per_buffer = static_cast<uint16_t>(periph_bytes_per_buffer_32bit);
+
+    return HAILO_SUCCESS;
+}
+
+Expected<CONTROL_PROTOCOL__nn_stream_config_t> HefConfigurator::parse_nn_stream_config(hailo_format_order_t format_order, uint32_t width, uint32_t features,
+    uint32_t hw_data_bytes, uint16_t core_buffers_per_frame, uint16_t core_bytes_per_buffer, bool hw_padding_supported, bool is_ddr)
+{
+    CONTROL_PROTOCOL__nn_stream_config_t stream_config = {};
+
+    stream_config.core_buffers_per_frame = core_buffers_per_frame;
+    stream_config.core_bytes_per_buffer = core_bytes_per_buffer;
+
+    /* For DDR buffering - core buffers is depended on the amount of buffers per PCIe interrupt. No HW padding required */
+    if (is_ddr) {
+        stream_config.core_buffers_per_frame = DDR_NUMBER_OF_ROWS_PER_INTERRUPT;
+        stream_config.feature_padding_payload = 0;
+        stream_config.periph_bytes_per_buffer = stream_config.core_bytes_per_buffer;
+    } else {
+        if (hw_padding_supported) {
+            auto status = get_hw_padding_params(format_order, width, features, hw_data_bytes,
+                stream_config.feature_padding_payload, stream_config.periph_bytes_per_buffer);
+            CHECK_SUCCESS_AS_EXPECTED(status);
+        } else {
+            stream_config.feature_padding_payload = 0;
+            stream_config.periph_bytes_per_buffer = stream_config.core_bytes_per_buffer;
+        }
+        /* For now, no support for buffer padding */
+        stream_config.buffer_padding_payload = 0;
+        stream_config.buffer_padding = 0;
+    }
+    return stream_config;
+}
+
+Expected<CONTROL_PROTOCOL__nn_stream_config_t> HefConfigurator::parse_nn_stream_config(const ProtoHEFEdgeLayerBase &edge_layer,
+    bool hw_padding_supported, const ProtoHEFEdgeConnectionType &edge_connection_type)
+{
+    CHECK_AS_EXPECTED(IS_FIT_IN_UINT16(edge_layer.core_bytes_per_buffer()), HAILO_INVALID_HEF,
+        "core_bytes_per_buffer is too big");
+    CHECK_AS_EXPECTED(IS_FIT_IN_UINT16(edge_layer.core_buffers_per_frame()), HAILO_INVALID_HEF,
+        "core_buffers_per_frame is too big");
+
+    auto format_order_exp = HailoRTDefaults::get_device_format_order(edge_layer.format());
+    CHECK_EXPECTED(format_order_exp);
+    auto format_order = format_order_exp.release();
+    auto is_ddr = ProtoHEFEdgeConnectionType::PROTO__EDGE_CONNECTION_TYPE__DDR == edge_connection_type;
+
+    // Width and features only used in case hw_padding is supported. In that case, they represent the HW shape (without padding)
+    return parse_nn_stream_config(format_order, edge_layer.width(), edge_layer.features(),
+        edge_layer.data_bytes(), static_cast<uint16_t>(edge_layer.core_buffers_per_frame()),
+        static_cast<uint16_t>(edge_layer.core_bytes_per_buffer()), hw_padding_supported, is_ddr);
+}
+
+Expected<CONTROL_PROTOCOL__nn_stream_config_t> HefConfigurator::parse_nn_stream_config(const LayerInfo &edge_layer, bool hw_padding_supported)
+{
+    auto is_ddr = false; // This function is called only on boundary layers, so no DDR
+    return parse_nn_stream_config(edge_layer.format.order, edge_layer.hw_shape.width, edge_layer.hw_shape.features,
+        edge_layer.hw_data_bytes, edge_layer.core_buffers_per_frame, edge_layer.core_bytes_per_buffer, hw_padding_supported,
+        is_ddr);
+}
+
+
+bool HefConfigurator::is_hw_padding_supported(bool is_boundary, bool is_mux, hailo_format_order_t format_order,
+    uint16_t core_buffers_per_frame, uint32_t height, uint32_t width, uint32_t features, uint32_t hw_data_bytes)
+{
+    if (!is_boundary || is_mux) {
+        return false;
+    }
+
+    // TODO: HRT-4462 support more orders
+    switch (format_order)
+    {
+    case HAILO_FORMAT_ORDER_NHCW:
+        break;
+    default:
+        LOGGER__DEBUG("HW padding is not supported for format {} ", format_order);
+        return false;
+    }
+
+    if (core_buffers_per_frame != height) {
+        // TODO: HRT-3278
+        LOGGER__DEBUG("HW padding is supported only on layers with core_buffers_per_frame == height");
+        return false;
+    }
+
+    if (((width * features) % 8) != 0) {
+        // TODO: HRT-963 support chunks
+        LOGGER__DEBUG("HW padding is supported only when periph_bytes_per_buffer is a multiple of 8");
+        return false;
+    }
+
+    if((width * features * hw_data_bytes) > 
+        (HAILO8_INBOUND_DATA_STREAM_SIZE - 1)) {
+        // TODO: HRT-4177
+        LOGGER__DEBUG("HW padding is supported only on layers with features * width * data size > stream size");
+        return false;
+    }
+    return true;
+}
+
+bool HefConfigurator::is_hw_padding_supported(const LayerInfo &layer_info)
+{
+    /* If the network is transposed, the width and height are swapped in LayerInfo c'tor, so need to swap it again for calculations */
+    auto height = layer_info.shape.height;
+    auto width = layer_info.shape.width;
+    if (layer_info.format.flags & HAILO_FORMAT_FLAGS_TRANSPOSED) {
+        std::swap(height, width);
+    }
+
+    auto is_boundary = true; // This function is called only on boundary layers
+    return is_hw_padding_supported(is_boundary, layer_info.is_mux, layer_info.format.order,layer_info.core_buffers_per_frame,
+        height, width, layer_info.shape.features, layer_info.hw_data_bytes);
+}
+
+bool HefConfigurator::is_hw_padding_supported(const ProtoHEFEdgeLayer &edge_layer)
+{
+    auto is_boundary = (ProtoHEFEdgeConnectionType::PROTO__EDGE_CONNECTION_TYPE__BOUNDARY == edge_layer.context_switch_info().edge_connection_type());
+    auto is_mux = (ProtoHEFEdgeLayerType::PROTO__EDGE_LAYER_TYPE__MUX == edge_layer.edge_layer_type());
+    auto edge_layer_base = edge_layer.layer_info().edge_layer_base();
+    auto format_order_exp = HailoRTDefaults::get_device_format_order(edge_layer_base.format());
+    if (!format_order_exp) {
+        LOGGER__DEBUG("Failed to get format order. Not enabling hw padding");
+        return false;
+    }
+    CHECK_EXPECTED_AS_STATUS(format_order_exp);
+    auto format_order = format_order_exp.release();
+
+    if (!IS_FIT_IN_UINT16(edge_layer_base.core_buffers_per_frame())) {
+        LOGGER__DEBUG("Invalid core_buffers_per_frame. Not enabling hw padding");
+        return false;
+    }
+
+    return is_hw_padding_supported(is_boundary, is_mux, format_order, static_cast<uint16_t>(edge_layer_base.core_buffers_per_frame()),
+        edge_layer_base.height(), edge_layer_base.width(), edge_layer_base.features(), edge_layer_base.data_bytes());
+}
+
+Expected<std::vector<hailo_stream_info_t>> Hef::Impl::get_input_stream_infos(const std::string &net_group_name,
+    const std::string &partial_network_name)
+{
+    auto network_group_metadata = get_network_group_metadata(net_group_name);
+    CHECK_EXPECTED(network_group_metadata);
+    return network_group_metadata->get_input_stream_infos(partial_network_name);
+}
+
+Expected<std::vector<hailo_stream_info_t>> Hef::Impl::get_output_stream_infos(const std::string &net_group_name,
+    const std::string &partial_network_name)
+{
+    auto network_group_metadata = get_network_group_metadata(net_group_name);
+    CHECK_EXPECTED(network_group_metadata);
+    return network_group_metadata->get_output_stream_infos(partial_network_name);
+}
+
+Expected<std::vector<hailo_stream_info_t>> Hef::Impl::get_all_stream_infos(const std::string &net_group_name,
+    const std::string &partial_network_name)
+{
+    auto network_group_metadata = get_network_group_metadata(net_group_name);
+    CHECK_EXPECTED(network_group_metadata);
+    return network_group_metadata->get_all_stream_infos(partial_network_name);
+}
+
+Expected<std::vector<hailo_network_info_t>> Hef::Impl::get_network_infos(const std::string &net_group_name)
+{
+    auto network_group_metadata = get_network_group_metadata(net_group_name);
+    CHECK_EXPECTED(network_group_metadata);
+    return network_group_metadata->get_network_infos();
+}
+
+Expected<hailo_stream_info_t> Hef::Impl::get_stream_info_by_name(const std::string &stream_name,
+    hailo_stream_direction_t stream_direction, const std::string &net_group_name)
+{
+    auto network_group_metadata = get_network_group_metadata(net_group_name);
+    CHECK_EXPECTED(network_group_metadata);
+
+    if (HAILO_H2D_STREAM == stream_direction) {
+        auto stream_infos = network_group_metadata->get_input_stream_infos();
+        CHECK_EXPECTED(stream_infos);
+        for (auto &stream_info : stream_infos.value()) {
+            if (stream_name == stream_info.name) {
+                return std::move(stream_info);
+            }
+        }
+    } else {
+        auto stream_infos = network_group_metadata->get_output_stream_infos();
+        CHECK_EXPECTED(stream_infos);
+        for (auto &stream_info : stream_infos.value()) {
+            if (stream_name == stream_info.name) {
+                return std::move(stream_info);
+            }
+        }
+    }
+
+    return make_unexpected(HAILO_NOT_FOUND);
+}
+
+Expected<std::vector<hailo_vstream_info_t>> Hef::Impl::get_input_vstream_infos(const std::string &net_group_name,
+    const std::string &partial_network_name)
+{
+    auto network_group_metadata = get_network_group_metadata(net_group_name);
+    CHECK_EXPECTED(network_group_metadata);
+    return network_group_metadata->get_input_vstream_infos(partial_network_name);
+}
+
+Expected<std::vector<hailo_vstream_info_t>> Hef::Impl::get_output_vstream_infos(const std::string &net_group_name,
+    const std::string &partial_network_name)
+{
+    auto network_group_metadata = get_network_group_metadata(net_group_name);
+    CHECK_EXPECTED(network_group_metadata);
+    return network_group_metadata->get_output_vstream_infos(partial_network_name);
+}
+
+Expected<std::vector<hailo_vstream_info_t>> Hef::Impl::get_all_vstream_infos(const std::string &net_group_name,
+    const std::string &partial_network_name)
+{
+    auto network_group_metadata = get_network_group_metadata(net_group_name);
+    CHECK_EXPECTED(network_group_metadata);
+    return network_group_metadata->get_all_vstream_infos(partial_network_name);
+}
+
+const std::vector<ProtoHEFNetworkGroupPtr>& Hef::Impl::network_groups() const
+{
+    return m_groups;
+};
+
+bool Hef::Impl::check_hef_extension(const ProtoHEFExtensionType &extension, const ProtoHEFHeader &header,
+    const std::vector<ProtoHEFExtension> &hef_extensions, const ProtoHEFIncludedFeatures &included_features)
+{
+    if (header.version() > 0) {
+        return std::find_if(hef_extensions.begin(), hef_extensions.end(),
+            [extension] (const ProtoHEFExtension &extended_feature) { return ((ProtoHEFExtensionType)extended_feature.type_index()) == extension; }) != hef_extensions.end();
+    }
+
+    // ProtoHEFIncludedFeature is deprecated
+    switch (extension) {
+        case ProtoHEFExtensionType::ABBALE:
+            return included_features.abbale();
+        case ProtoHEFExtensionType::POSTED_WRITES:
+            return included_features.posted_writes();
+        case ProtoHEFExtensionType::DDR:
+            return included_features.ddr();
+        case ProtoHEFExtensionType::IS_MULTI_CONTEXTS:
+            return (included_features.number_of_contexts() > 0);
+        case ProtoHEFExtensionType::COMPRESSED_PARAMS:
+            return included_features.compressed_params();
+        case ProtoHEFExtensionType::TRANSPOSE_COMPONENT:
+            return included_features.transpose_component();
+        case ProtoHEFExtensionType::PADDED_DDR_BUFFERS:
+            return included_features.padded_ddr_buffers();
+        default:
+            return false;
+    }
+}
+
+bool Hef::Impl::check_hef_optional_extension(const ProtoHEFExtensionType &extension, const ProtoHEFHeader &header,
+    const std::vector<ProtoHEFOptionalExtension> &hef_optional_extensions)
+{
+    if (header.version() > 0) {
+        return std::find_if(hef_optional_extensions.begin(), hef_optional_extensions.end(),
+            [extension] (const ProtoHEFOptionalExtension &extended_feature) { return ((ProtoHEFExtensionType)extended_feature.type_index()) == extension; }) != hef_optional_extensions.end();
+    }
+
+    /* optional extensions are only for m_header.version() > 0. 
+       For lower version, those features are not supported */
+    return false;
+}
+
+Expected<std::pair<std::string, std::string>> Hef::Impl::get_network_group_and_network_name(const std::string &name)
+{
+    std::string network_group_name;
+    if (name.empty()) {
+        // Name is not given - addressing all networks in the first network_group
+        network_group_name = m_groups[0]->network_group_metadata().network_group_name();
+        LOGGER__INFO("No name was given. Addressing all networks of default network_group: {}",
+            network_group_name);
+        return std::make_pair(network_group_name, HAILO_DEFAULT_PARTIAL_NETWORK_NAME);
+    } else if (HAILO_DEFAULT_PARTIAL_NETWORK_NAME == name) {
+        /* HAILO_DEFAULT_PARTIAL_NETWORK_NAME is passed for HEFs that doesnt support network groups.
+           We know that multiple networks_groups in single HEF is'nt supported for those HEFs. */
+        network_group_name = m_groups[0]->network_group_metadata().network_group_name();
+        return std::make_pair(network_group_name, HAILO_DEFAULT_PARTIAL_NETWORK_NAME);
+    } else {
+        for (const auto &network_group : m_groups) {
+            network_group_name = network_group->network_group_metadata().network_group_name();
+            // Look for network_group with the given name
+            if (name == network_group_name) {
+                return std::make_pair(network_group_name, HAILO_DEFAULT_PARTIAL_NETWORK_NAME);
+            }
+            // Look for network with the given name
+            for (const auto &partial_network_name : network_group->networks_names()) {
+                auto full_network_name = network_group_name + HAILO_DEFAULT_NETWORK_NAME_QUALIFIER + partial_network_name;
+                if (name == full_network_name) {
+                    return std::make_pair(network_group_name, partial_network_name);
+                }
+            }
+            // Handle case of deafult_network_name
+            if (name == network_group_name + HAILO_DEFAULT_NETWORK_NAME_QUALIFIER + HAILO_DEFAULT_PARTIAL_NETWORK_NAME) {
+                return std::make_pair(network_group_name, HAILO_DEFAULT_PARTIAL_NETWORK_NAME);
+            }
+        }
+    }
+
+    LOGGER__ERROR("Failed to find network or network_group with the name {}",
+        name);
+    return make_unexpected(HAILO_NOT_FOUND);
+}
+
+Expected<ProtoHEFNetworkGroupPtr> Hef::Impl::get_net_group_by_name(const std::string &net_group_name)
+{
+    if ("" == net_group_name) {
+        LOGGER__INFO("No network_group name was given. Addressing default network_group: {}",
+            m_groups[0]->network_group_metadata().network_group_name());
+        return Expected<ProtoHEFNetworkGroupPtr>(m_groups[0]);
+    }
+    for (auto const &net_group : m_groups) {
+        CHECK_AS_EXPECTED(nullptr != net_group, HAILO_INTERNAL_FAILURE, "null netwrok group");
+        if (net_group_name == net_group->network_group_metadata().network_group_name()) {
+            return Expected<ProtoHEFNetworkGroupPtr>(net_group);
+        }
+    }
+    LOGGER__ERROR("HEF does not contain network_group with name {}", net_group_name);
+    return make_unexpected(HAILO_NOT_FOUND);
+}
+
+Expected<size_t> Hef::Impl::get_number_of_input_streams(const std::string &net_group_name)
+{
+    auto network_group_metadata = get_network_group_metadata(net_group_name);
+    CHECK_EXPECTED(network_group_metadata);
+
+    auto input_layer_infos = network_group_metadata->get_input_layer_infos();
+    CHECK_EXPECTED(input_layer_infos);
+    return input_layer_infos->size();
+}
+
+Expected<size_t> Hef::Impl::get_number_of_output_streams(const std::string &net_group_name)
+{
+    auto network_group_metadata = get_network_group_metadata(net_group_name);
+    CHECK_EXPECTED(network_group_metadata);
+
+    auto output_layer_infos = network_group_metadata->get_output_layer_infos();
+    CHECK_EXPECTED(output_layer_infos);
+    return output_layer_infos->size();
+}
+
+hailo_status HefUtils::fill_layer_info_with_base_info(const ProtoHEFEdgeLayerBase &base_info, 
+    const ProtoHEFEdgeConnectionType &edge_connection_type, const ProtoHEFNetworkGroupMetadata &network_group_proto, 
+    bool hw_padding_supported, bool transposed, LayerInfo &layer_info)
+{
+    auto format_order_exp = HailoRTDefaults::get_device_format_order(base_info.format());
+    CHECK_EXPECTED_AS_STATUS(format_order_exp);
+
+    auto format_oder = format_order_exp.release();
+
+    if (HEF__FORMAT__NMS != base_info.format()) {
+        layer_info.shape.height = base_info.height();
+        layer_info.shape.width = base_info.width();
+        layer_info.shape.features = base_info.features();
+    } else {
+        layer_info.shape.height = static_cast<uint32_t>(base_info.additional_info().nms_info().number_of_classes());
+        layer_info.shape.width = HailoRTCommon::BBOX_PARAMS;
+        layer_info.shape.features = static_cast<uint32_t>(base_info.additional_info().nms_info().max_output_size() *
+            base_info.additional_info().nms_info().input_division_factor());
+    }
+
+    if (hw_padding_supported) {
+        layer_info.hw_shape.height = base_info.height();
+        layer_info.hw_shape.width = base_info.width();
+        layer_info.hw_shape.features = base_info.features();
+    }
+    else {
+        layer_info.hw_shape.height = base_info.padded_height();
+        layer_info.hw_shape.width = base_info.padded_width();
+        layer_info.hw_shape.features = base_info.padded_features();
+    }
+    layer_info.hw_data_bytes = base_info.data_bytes();
+
+    // TODO: remove duplications with stream info parse
+    layer_info.format.order = format_oder;
+    layer_info.format.flags = HAILO_FORMAT_FLAGS_QUANTIZED;
+
+    // The check network_group_proto.transposed_net() is for supporting backward compatability for old hefs
+    if ((network_group_proto.transposed_net() || transposed) && (layer_info.format.order != HAILO_FORMAT_ORDER_NC))  {
+        std::swap(layer_info.shape.height, layer_info.shape.width);
+        layer_info.format.flags |= HAILO_FORMAT_FLAGS_TRANSPOSED;
+    }
+
+    if (base_info.host_argmax()) {
+        layer_info.format.flags |= HAILO_FORMAT_FLAGS_HOST_ARGMAX;
+        layer_info.shape.features = 1;
+    }
+
+    auto type = HailoRTCommon::get_format_type(layer_info.hw_data_bytes);
+    CHECK_EXPECTED_AS_STATUS(type);
+    layer_info.format.type = type.value();
+
+    auto nn_stream_config = HefConfigurator::parse_nn_stream_config(base_info, hw_padding_supported, 
+        edge_connection_type);
+    CHECK_EXPECTED_AS_STATUS(nn_stream_config, "Failed parse nn stream config");
+    layer_info.core_bytes_per_buffer = nn_stream_config->core_bytes_per_buffer;
+    layer_info.core_buffers_per_frame = nn_stream_config->core_buffers_per_frame;
+
+    if (!IS_FIT_IN_UINT8(base_info.sys_index())) {
+        LOGGER__ERROR("Failed to parse HEF. Invalid sys_index {}.", base_info.sys_index());
+        return HAILO_INVALID_HEF;
+    }
+    layer_info.index = static_cast<uint8_t>(base_info.sys_index());
+
+    if (HAILO_FORMAT_ORDER_HAILO_NMS == layer_info.format.order) {
+        auto expected_nms_info = parse_proto_nms_info(base_info.additional_info().nms_info());
+        CHECK_EXPECTED_AS_STATUS(expected_nms_info);
+        layer_info.nms_info = expected_nms_info.release();
+    }
+
+    return HAILO_SUCCESS;
+}
+
+hailo_status HefUtils::fill_layer_info(const ProtoHEFEdgeLayerInfo &info, 
+    const ProtoHEFEdgeConnectionType &edge_connection_type, 
+    const ProtoHEFNetworkGroup &net_group, hailo_stream_direction_t direction,
+    bool hw_padding_supported, const std::string &partial_network_name, LayerInfo &layer_info)
+{
+    auto status = fill_layer_info_with_base_info(info.edge_layer_base(), edge_connection_type, net_group.network_group_metadata(),
+        hw_padding_supported, info.transposed(), layer_info);
+    CHECK_SUCCESS(status);
+
+    if (HAILO_MAX_STREAM_NAME_SIZE < (info.name().length() + 1)) {
+        LOGGER__ERROR("The edge layer '{}' has a too long name (max is HAILO_MAX_STREAM_NAME_SIZE)", info.name());
+        return HAILO_INTERNAL_FAILURE;
+    }
+    if (HAILO_MAX_NETWORK_NAME_SIZE < (partial_network_name.length() + 1)) {
+        LOGGER__ERROR("The network '{}' has a too long name (max is HAILO_MAX_NETWORK_NAME_SIZE)", partial_network_name);
+        return HAILO_INTERNAL_FAILURE;
+    }
+    layer_info.name = info.name();
+
+    layer_info.partial_network_name = partial_network_name;
+    layer_info.is_mux = false;
+    layer_info.direction = direction;
+    layer_info.quant_info.limvals_max = info.numeric_info().limvals_max();
+    layer_info.quant_info.limvals_min = info.numeric_info().limvals_min();
+    layer_info.quant_info.qp_scale = info.numeric_info().qp_scale();
+    layer_info.quant_info.qp_zp = info.numeric_info().qp_zp();
+    // Simulation info
+    assert (1 == info.edge_layer_base().buffer_indices_size());
+    layer_info.buffer_indices.cluster_index = info.edge_layer_base().buffer_indices(0).cluster_index();
+    layer_info.buffer_indices.index = info.edge_layer_base().buffer_indices(0).index();
+
+    layer_info.is_defused_nms = net_group.has_fused_layers_metadata() &&
+        (HAILO_FORMAT_ORDER_HAILO_NMS == layer_info.format.order) && layer_info.nms_info.is_defused;
+
+    if (layer_info.is_defused_nms) {
+        for (const auto &fused_layer : net_group.fused_layers_metadata().fused_layers()) {
+            if (fused_layer.layer_info().name() == layer_info.nms_info.defuse_info.original_name) {
+                // This creates a new LayerInfo for the fused layer *for each defused layer*, even though they all share the same fused layer.
+                // TODO Make it so all defused layer reference the same LayerInfo of the fused layer.
+                LayerInfo fused_layer_info = {};
+                status = fill_fused_nms_info(fused_layer, fused_layer_info, layer_info.quant_info, partial_network_name);
+                CHECK_SUCCESS(status);
+                layer_info.fused_nms_layer.push_back(fused_layer_info);
+                break;
+            }
+        }
+        CHECK(0 != layer_info.fused_nms_layer.size(), HAILO_NOT_FOUND, "Could not find the fused layer {}", layer_info.nms_info.defuse_info.original_name);
+    }
+
+    return HAILO_SUCCESS;
+}
+
+hailo_status HefUtils::fill_fused_nms_info(const ProtoHEFEdgeLayerFused &info, LayerInfo &layer_info,
+    hailo_quant_info_t &defuse_quant_info, const std::string &partial_network_name)
+{
+    auto base_info = info.layer_info().edge_layer_base();
+    auto format_order_exp = HailoRTDefaults::get_device_format_order(base_info.format());
+    CHECK_EXPECTED_AS_STATUS(format_order_exp);
+    layer_info.format.order = format_order_exp.release();
+    layer_info.format.flags = HAILO_FORMAT_FLAGS_QUANTIZED;
+
+    layer_info.shape.height = static_cast<uint32_t>(info.nms_info().number_of_classes());
+    layer_info.shape.width = HailoRTCommon::BBOX_PARAMS;
+    layer_info.shape.features = static_cast<uint32_t>(info.nms_info().max_output_size() *
+        info.nms_info().input_division_factor());
+
+    layer_info.hw_data_bytes = base_info.data_bytes();
+
+    auto type = HailoRTCommon::get_format_type(layer_info.hw_data_bytes);
+    CHECK_EXPECTED_AS_STATUS(type);
+    layer_info.format.type = type.value();
+
+    auto expected_nms_info = parse_proto_nms_info(info.nms_info());
+    CHECK_EXPECTED_AS_STATUS(expected_nms_info);
+    layer_info.nms_info = expected_nms_info.release();
+
+    if (HAILO_MAX_STREAM_NAME_SIZE < (info.layer_info().name().length() + 1)) {
+        LOGGER__ERROR("The edge layer '{}' has a too long name (max is HAILO_MAX_STREAM_NAME_SIZE)", info.layer_info().name());
+        return HAILO_INTERNAL_FAILURE;
+    }
+    layer_info.name = info.layer_info().name();
+    layer_info.partial_network_name = partial_network_name;
+    layer_info.is_mux = false;
+    layer_info.direction = HAILO_D2H_STREAM;
+    // Due to bug in SDK quant info of fused layer is empty, so we use the quant info of  the defused layer
+    layer_info.quant_info = defuse_quant_info;
+
+    // Simulation info
+    assert (1 == info.layer_info().edge_layer_base().buffer_indices_size());
+    layer_info.buffer_indices.cluster_index = info.layer_info().edge_layer_base().buffer_indices(0).cluster_index();
+    layer_info.buffer_indices.index = info.layer_info().edge_layer_base().buffer_indices(0).index();
+
+    return HAILO_SUCCESS;
+}
+
+hailo_status HefUtils::fill_mux_info(const ProtoHEFEdgeLayerMux &info,
+    const ProtoHEFEdgeConnectionType &edge_connection_type, 
+    const ProtoHEFNetworkGroup &net_group, hailo_stream_direction_t direction,
+    bool hw_padding_supported, const std::string &partial_network_name, LayerInfo &layer_info)
+{
+    const bool transposed = false;
+    auto status = fill_layer_info_with_base_info(info.edge_layer_base(), edge_connection_type, net_group.network_group_metadata(),
+        hw_padding_supported, transposed, layer_info);
+    CHECK_SUCCESS(status);
+
+    if (HAILO_MAX_STREAM_NAME_SIZE < (info.name().length() + 1)) {
+        LOGGER__ERROR("The edge layer '{}' has a too long name (max is HAILO_MAX_STREAM_NAME_SIZE)", info.name());
+        return HAILO_INTERNAL_FAILURE;
+    }
+    if (HAILO_MAX_NETWORK_NAME_SIZE < (partial_network_name.length() + 1)) {
+        LOGGER__ERROR("The network '{}' has a too long name (max is HAILO_MAX_NETWORK_NAME_SIZE)", partial_network_name);
+        return HAILO_INTERNAL_FAILURE;
+    }
+    layer_info.name = info.name();
+
+    layer_info.partial_network_name = partial_network_name;
+    layer_info.is_mux = true;
+    layer_info.predecessor.reserve(info.mux_data().number_of_predecessors());
+    layer_info.height_gcd = info.mux_data().height_gcd();
+    layer_info.height_ratios.reserve(info.mux_data().height_ratios_list_len());
+    for (const auto &height_ratio : info.mux_data().height_ratios_list()) {
+        layer_info.height_ratios.emplace_back(height_ratio);
+    }
+    // Simulation info
+    assert (1 == info.edge_layer_base().buffer_indices_size());
+    layer_info.buffer_indices.cluster_index = info.edge_layer_base().buffer_indices(0).cluster_index();
+    layer_info.buffer_indices.index = info.edge_layer_base().buffer_indices(0).index();
+
+    for (uint32_t i = 0; i < info.mux_data().number_of_predecessors(); i++) {
+        LayerInfo temp_layer = {};
+        switch (info.predecessors(i).edge_case()) {
+            case ProtoHefEdge::kLayerInfo:
+                status = fill_layer_info(info.predecessors(i).layer_info(), edge_connection_type, net_group,
+                    direction, hw_padding_supported, partial_network_name, temp_layer);
+                if (HAILO_SUCCESS != status) {
+                    return status;
+                }
+                layer_info.predecessor.push_back(temp_layer);
+                break;
+            case ProtoHefEdge::kLayerMux:
+                status = fill_mux_info(info.predecessors(i).layer_mux(), edge_connection_type, net_group,
+                    direction, hw_padding_supported, partial_network_name, temp_layer);
+                if (HAILO_SUCCESS != status) {
+                    return status;
+                }
+                layer_info.predecessor.push_back(temp_layer);
+                break;
+            default:
+                LOGGER__ERROR("Invalid layer type");
+                return HAILO_INTERNAL_FAILURE;
+                break;
+        }
+    }
+
+    return HAILO_SUCCESS;
+}
+
+Expected<std::vector<LayerInfo>> HefUtils::get_all_layers_info(const ProtoHEFNetworkGroup &network_group_proto,
+    const NetworkGroupSupportedFeatures &supported_features)
+{
+    std::vector<LayerInfo> layers_info;
+    for (uint8_t context_index = 0; context_index < network_group_proto.contexts_size(); context_index++) {
+        auto &context_metadata = network_group_proto.contexts(context_index).metadata();
+        for (int i = 0; i < context_metadata.edge_layers_size(); i++) {
+            // We parse only boundary layers for user usage
+            if (ProtoHEFEdgeConnectionType::PROTO__EDGE_CONNECTION_TYPE__BOUNDARY !=
+                context_metadata.edge_layers(i).context_switch_info().edge_connection_type()) {
+                    continue;
+                }
+            auto layer_info = get_layer_info(network_group_proto, context_metadata.edge_layers(i), supported_features);
+            CHECK_EXPECTED(layer_info);
+
+            // Validate unique layer names
+            for (const auto &layer : layers_info) {
+                CHECK_AS_EXPECTED((layer.name != layer_info->name), HAILO_INVALID_HEF,
+                    "Layer name should be unique. name '{}' appears more than once", layer_info->name);
+            }
+
+            layers_info.emplace_back(layer_info.release());
+        }
+    }
+    return layers_info;
+}
+
+Expected<hailo_nms_info_t> HefUtils::parse_proto_nms_info(const ProtoHEFNmsInfo &proto_nms_info)
+{
+    hailo_nms_info_t nms_info = {};
+    nms_info.number_of_classes = static_cast<uint32_t>(proto_nms_info.number_of_classes());
+    nms_info.bbox_size = static_cast<uint32_t>(proto_nms_info.bbox_size());
+    nms_info.max_bboxes_per_class = static_cast<uint32_t>(proto_nms_info.max_output_size());
+    nms_info.chunks_per_frame = static_cast<uint32_t>(proto_nms_info.input_division_factor());
+    if (nms_info.chunks_per_frame == 0) {
+        // Old hef, use default value 1
+        nms_info.chunks_per_frame = 1;
+    }
+    nms_info.is_defused = static_cast<bool>(proto_nms_info.is_defused());
+    nms_info.defuse_info.class_group_index =
+        static_cast<uint32_t>(proto_nms_info.defuse_info().class_group_index());
+
+    CHECK_AS_EXPECTED(nms_info.defuse_info.class_group_index < HailoRTCommon::MAX_DEFUSED_LAYER_COUNT,
+        HAILO_INVALID_HEF, "class_group_index from HEF is bigger than {}!", HailoRTCommon::MAX_DEFUSED_LAYER_COUNT);
+
+    const std::string &original_name = proto_nms_info.defuse_info().original_name();
+    CHECK_AS_EXPECTED(HAILO_MAX_STREAM_NAME_SIZE >= (original_name.length() + 1), HAILO_INTERNAL_FAILURE,
+        "original_name field '{}' has a too long name (max is HAILO_MAX_STREAM_NAME_SIZE including the null terminated character)",
+        original_name);
+    strncpy(nms_info.defuse_info.original_name, original_name.c_str(), original_name.length() + 1);
+    return nms_info;
+}
+
+Expected<LayerInfo> HefUtils::get_layer_info(const ProtoHEFNetworkGroup &net_group, const ProtoHEFEdgeLayer &layer,
+    const NetworkGroupSupportedFeatures &supported_features)
+{
+    // We parse only boundary layers for user usage
+    CHECK_AS_EXPECTED(
+        ProtoHEFEdgeConnectionType::PROTO__EDGE_CONNECTION_TYPE__BOUNDARY == layer.context_switch_info().edge_connection_type(),
+        HAILO_INTERNAL_FAILURE, "get_layer_info can be called only on boundary layers");
+
+    LayerInfo result = {};
+    const auto direction = 
+        (ProtoHEFEdgeLayerDirection::PROTO__EDGE_LAYER_DIRECTION__DEVICE_TO_HOST == layer.direction()) ?
+        HAILO_D2H_STREAM : HAILO_H2D_STREAM;
+    auto support_multi_networks = supported_features.multi_network_support;
+    auto network_index = static_cast<uint8_t>((support_multi_networks) ? layer.network_index() : 0);
+    auto partial_network_name = HefUtils::get_partial_network_name_by_index(net_group, network_index, supported_features);
+    CHECK_EXPECTED(partial_network_name);
+    const bool hw_padding_supported = HefConfigurator::is_hw_padding_supported(layer);
+    if (ProtoHEFEdgeLayerType::PROTO__EDGE_LAYER_TYPE__INFO == layer.edge_layer_type()) {
+        // TODO: return LayerInfo
+        auto status = fill_layer_info(layer.layer_info(), layer.context_switch_info().edge_connection_type(),
+            net_group, direction, hw_padding_supported, partial_network_name.value(), result);
+        CHECK_SUCCESS_AS_EXPECTED(status);
+    } else if (ProtoHEFEdgeLayerType::PROTO__EDGE_LAYER_TYPE__MUX == layer.edge_layer_type()) {
+        // TODO: return LayerInfo
+        auto status = fill_mux_info(layer.layer_mux(), layer.context_switch_info().edge_connection_type(), 
+            net_group, direction, hw_padding_supported, partial_network_name.value(), result);
+        CHECK_SUCCESS_AS_EXPECTED(status);
+    } else {
+        LOGGER__ERROR("Invalid layer type");
+        return make_unexpected(HAILO_INTERNAL_FAILURE);
+    }
+
+    result.direction = (ProtoHEFEdgeLayerDirection::PROTO__EDGE_LAYER_DIRECTION__DEVICE_TO_HOST ==
+            layer.direction()) ? HAILO_D2H_STREAM : HAILO_H2D_STREAM;
+
+    return result;
+}
+
+Expected<std::vector<std::string>> HefUtils::get_sorted_output_names(const ProtoHEFNetworkGroup &net_group)
+{
+    if (net_group.fused_layers_metadata().network_has_fused_layers()) {
+        return std::vector<std::string>(std::begin(net_group.fused_layers_metadata().updated_sorted_output_names()),
+            std::end(net_group.fused_layers_metadata().updated_sorted_output_names()));
+    } else if (0 != net_group.sorted_outputs_order_size()) {
+        // For backwards compatibility before we've added updated_sorted_output_names
+        return std::vector<std::string>(std::begin(net_group.sorted_outputs_order()),
+            std::end(net_group.sorted_outputs_order()));
+    } else {
+        // For backwards compatibility before we've added this field
+        uint32_t number_of_contexts = net_group.contexts_size();
+        const auto& context_metadata = net_group.contexts(number_of_contexts - 1).metadata();
+
+        CHECK_AS_EXPECTED(0 < context_metadata.sorted_outputs_order_size(), HAILO_INVALID_HEF,
+            "Sorted output names is not set up in the HEF.");
+
+        return std::vector<std::string>(std::begin(context_metadata.sorted_outputs_order()),
+            std::end(context_metadata.sorted_outputs_order()));
+    }
+}
+
+/* VdmaConfigNetworkGroup funcs */
+
+inline std::pair<uint8_t, uint16_t> old_hef_parse_initial_l3(uint32_t initial_l3)
+{
+    // parse initial l3 as written in hailo8 initial_l3 format -
+    //      7 bits of initial_l3_cut
+    //      12 bits of initial_l3_offset, offset in 256 bits (8 bytes) granularity.
+    const uint8_t initial_l3_cut = static_cast<uint8_t>(initial_l3 & HAILO8_INITIAL_L3_CUT_MASK);
+    const uint32_t initial_l3_offset_256 = (initial_l3 & HAILO8_INITIAL_L3_OFFSET_MASK) >> HAILO8_INITIAL_L3_OFFSET_SHIFT;
+    const uint16_t initial_l3_offset = static_cast<uint16_t>(initial_l3_offset_256 << HAILO8_INITIAL_L3_OFFSET_BYTES_GRANULARITY_SHIFT);
+    return std::make_pair(initial_l3_cut, initial_l3_offset);
+}
+
+static hailo_status fill_boundary_input_layer(CONTROL_PROTOCOL__context_switch_context_info_t *context_info, 
+        uint8_t **context_meta_data_head_pointer, uint8_t stream_index, const CONTROL_PROTOCOL__nn_stream_config_t &nn_stream_config,
+        ContextSwitchChannelsParsingInfo &channels_parsing_info, ResourcesManager &resources_manager,
+        const std::string &layer_name, uint8_t network_index)
+{
+    auto channel_index = resources_manager.get_available_channel_index(
+        channels_parsing_info.H2D_channels_in_use, ChannelInfo::Type::BOUNDARY, VdmaChannel::Direction::H2D, layer_name);
+    CHECK_EXPECTED_AS_STATUS(channel_index);
+
+    // Mark the channel info with the stream_index
+    auto channel_info = resources_manager.get_channel_info(channel_index.value());
+    CHECK_EXPECTED_AS_STATUS(channel_info);
+    channel_info->get().set_pcie_stream_index(stream_index);
+
+    // Lock the channel for further use in this net_group
+    channels_parsing_info.H2D_channels_in_use.insert(channel_index.value());
+
+    LOGGER__DEBUG("Boundary input stream: {} h2d_pcie_channel: {}.", stream_index, channel_index.value());
+
+    /* Update metadata */
+    auto status = HEF_METADATA__add_network_boundary_input_edge_layer(context_info, 
+        context_meta_data_head_pointer, stream_index, channel_index.value(), network_index, nn_stream_config,
+        DEFAULT_DESC_PAGE_SIZE);
+    CHECK_SUCCESS(status);
+
+    return HAILO_SUCCESS;
+}
+
+static hailo_status fill_inter_context_input_layer(CONTROL_PROTOCOL__context_switch_context_info_t *context_info, 
+        uint8_t **context_meta_data_head_pointer, ResourcesManager &resources_manager, uint8_t dst_context, 
+        uint8_t stream_index, const CONTROL_PROTOCOL__nn_stream_config_t &nn_stream_config,
+        const ProtoHEFEdgeLayer *edge_layer_info, ContextSwitchChannelsParsingInfo &channels_parsing_info,
+        std::set<uint8_t> &channels_to_unlock,
+        uint8_t network_index)
+{
+    uint8_t src_context = 0;
+    uint8_t src_stream_index = 0;
+
+    // Used to log layer info - debug only
+    (void)dst_context;
+
+    if (!(IS_FIT_IN_UINT8(edge_layer_info->context_switch_info().connected_context_index()))) {
+        LOGGER__ERROR("Failed to parse HEF. Invalid connected_context index: {}.",
+            edge_layer_info->context_switch_info().connected_context_index());
+        return HAILO_INVALID_HEF;
+    }
+    src_context = static_cast<uint8_t>(edge_layer_info->context_switch_info().connected_context_index());
+
+    if(!(IS_FIT_IN_UINT8(edge_layer_info->context_switch_info().connected_sys_index()))) {
+        LOGGER__ERROR("Failed to parse HEF. Invalid connected_sys index: {}.",
+            edge_layer_info->context_switch_info().connected_sys_index());
+        return HAILO_INVALID_HEF;
+    }
+    src_stream_index = static_cast<uint8_t>(edge_layer_info->context_switch_info().connected_sys_index());
+
+    /* Find next available h2d_channel, and mark it to unlock at the end of the context */
+    auto h2d_channel_index = resources_manager.get_available_channel_index(channels_parsing_info.H2D_channels_in_use,
+        ChannelInfo::Type::INTER_CONTEXT, VdmaChannel::Direction::H2D);
+    CHECK_EXPECTED_AS_STATUS(h2d_channel_index);
+
+    channels_parsing_info.H2D_channels_in_use.insert(h2d_channel_index.value());
+    channels_to_unlock.insert(h2d_channel_index.value());
+
+    /* Get inter context buffer previously created */
+    auto intermediate_buffer_key = std::make_pair(src_context, src_stream_index);
+    auto intermediate_buffer_exp = resources_manager.get_intermediate_buffer(intermediate_buffer_key);
+    CHECK_EXPECTED_AS_STATUS(intermediate_buffer_exp, "Failed to find intermediate buffer for src context {}, src_stream_index {}",
+        src_context, src_stream_index);
+    auto &intermediate_buffer = intermediate_buffer_exp->get();
+
+    LOGGER__DEBUG("Intermediate input stream {}, src_context:{}, dst_context: {}, h2d_pcie_channel {}.",
+        stream_index, src_context, dst_context, h2d_channel_index.value());
+
+    /* Update metadata */
+    return HEF_METADATA__add_inter_context_input_edge_layer(context_info, context_meta_data_head_pointer,
+        stream_index, h2d_channel_index.value(), network_index, nn_stream_config,
+        intermediate_buffer.descriptors_in_frame(), intermediate_buffer.dma_address(), intermediate_buffer.depth(),
+        intermediate_buffer.desc_page_size());
+}
+
+static hailo_status fill_boundary_output_layer(CONTROL_PROTOCOL__context_switch_context_info_t *context_info,
+        uint8_t **context_meta_data_head_pointer, ResourcesManager &resources_manager, uint8_t stream_index,
+        const CONTROL_PROTOCOL__nn_stream_config_t &nn_stream_config, uint32_t frame_credits_in_bytes,
+        ContextSwitchChannelsParsingInfo &channels_parsing_info, const std::string &layer_name, uint8_t network_index, 
+        const std::string &partial_network_name)
+{
+   auto channel_index = resources_manager.get_available_channel_index(channels_parsing_info.D2H_channels_in_use,
+       ChannelInfo::Type::BOUNDARY, VdmaChannel::Direction::D2H, layer_name);
+    CHECK_EXPECTED_AS_STATUS(channel_index);
+
+    // Mark the channel info with the stream_index
+    auto channel_info = resources_manager.get_channel_info(channel_index.value());
+    CHECK_EXPECTED_AS_STATUS(channel_info);
+    channel_info->get().set_pcie_stream_index(stream_index);
+
+    // Lock the channel for further use in this net_group
+    channels_parsing_info.D2H_channels_in_use.insert(channel_index.value());
+
+    auto desc_sizes_pair = resources_manager.get_desc_buffer_sizes_for_boundary_channel(frame_credits_in_bytes,
+        partial_network_name);
+    CHECK_EXPECTED_AS_STATUS(desc_sizes_pair);
+
+    LOGGER__DEBUG("Boundary output stream: {} d2h_pcie_channel: {}.", stream_index, channel_index.value());
+
+    /* Update metadata */
+    auto status = HEF_METADATA__add_network_boundary_output_edge_layer(context_info, 
+            context_meta_data_head_pointer, stream_index, channel_index.value(), network_index,
+            nn_stream_config, frame_credits_in_bytes, desc_sizes_pair->first);
+    CHECK_SUCCESS(status);
+
+    return HAILO_SUCCESS;
+}
+
+static hailo_status fill_inter_context_output_layer(CONTROL_PROTOCOL__context_switch_context_info_t *context_info, 
+        uint8_t **context_meta_data_head_pointer, ResourcesManager &resources_manager, uint8_t src_context,
+        uint8_t stream_index, const CONTROL_PROTOCOL__nn_stream_config_t &nn_stream_config, uint32_t frame_credits_in_bytes,
+        const ProtoHEFEdgeLayer *edge_layer_info, ContextSwitchChannelsParsingInfo &channels_parsing_info,
+        std::set<uint8_t> &channels_to_unlock, uint8_t network_index, const std::string &partial_network_name)
+{
+    std::vector<uint8_t> connected_h2d_channels;
+
+    CHECK(IS_FIT_IN_UINT8(edge_layer_info->context_switch_info().connected_context_index()),
+        HAILO_INVALID_HEF, "Failed to parse HEF. Invalid connected_context index: {}.",
+        edge_layer_info->context_switch_info().connected_context_index());
+
+    /* Find next available d2h_channel, and mark it to unlock at the end of the context */
+    auto d2h_channel_index = resources_manager.get_available_channel_index(
+        channels_parsing_info.D2H_channels_in_use, ChannelInfo::Type::INTER_CONTEXT, VdmaChannel::Direction::D2H);
+    CHECK_EXPECTED_AS_STATUS(d2h_channel_index);
+
+    channels_parsing_info.D2H_channels_in_use.insert(d2h_channel_index.value());
+    channels_to_unlock.insert(d2h_channel_index.value());
+
+    auto intermediate_buffer_exp = resources_manager.create_inter_context_buffer(frame_credits_in_bytes,
+        stream_index, src_context, partial_network_name);
+    CHECK_EXPECTED_AS_STATUS(intermediate_buffer_exp);
+    auto &intermediate_buffer = intermediate_buffer_exp->get();
+
+    LOGGER__DEBUG("Intermediate output stream {}, src_context:{}, d2h_pcie_channel {}.",
+        stream_index, src_context, d2h_channel_index.value());
+
+    /* Update metadata */
+    auto status = HEF_METADATA__add_inter_context_output_edge_layer(context_info, context_meta_data_head_pointer,
+        stream_index, d2h_channel_index.value(), network_index, nn_stream_config, frame_credits_in_bytes, 
+        intermediate_buffer.dma_address(), intermediate_buffer.descriptors_in_frame(),
+        intermediate_buffer.desc_page_size(), intermediate_buffer.depth());
+    CHECK_SUCCESS(status);
+
+    return HAILO_SUCCESS;
+}
+
+static hailo_status fill_ddr_layer_multi_context(CONTROL_PROTOCOL__context_switch_context_info_t *context_switch_info,
+    uint8_t **context_meta_data_head_pointer, ResourcesManager &resources_manager, uint8_t context_index,
+    const ProtoHEFEdgeLayer &edge_layer_proto, const CONTROL_PROTOCOL__nn_stream_config_t &nn_stream_config,
+    hailo_stream_direction_t direction, ContextSwitchChannelsParsingInfo &channels_parsing_info, uint32_t frame_credits_in_bytes,
+    std::set<uint8_t> &channels_to_unlock, uint8_t network_index)
+{
+    /* Find out if the connected layer has already been parsed */
+    uint8_t channel_index = 0;
+    if (HAILO_H2D_STREAM == direction) {
+        auto channel_index_expected = resources_manager.get_available_channel_index(channels_parsing_info.H2D_channels_in_use,
+            ChannelInfo::Type::DDR, VdmaChannel::Direction::H2D);
+        CHECK_EXPECTED_AS_STATUS(channel_index_expected);
+        channel_index = channel_index_expected.value();
+        channels_parsing_info.H2D_channels_in_use.insert(channel_index);
+        /* If managed by the FW - allow reuse of the channel in between contexts */
+        if (resources_manager.get_supported_features().padded_ddr_buffers) {
+            channels_to_unlock.insert(channel_index);
+        }
+    } else if (HAILO_D2H_STREAM == direction) {
+        auto channel_index_expected = resources_manager.get_available_channel_index(channels_parsing_info.D2H_channels_in_use,
+            ChannelInfo::Type::DDR, VdmaChannel::Direction::D2H);
+        CHECK_EXPECTED_AS_STATUS(channel_index_expected);
+        channel_index = channel_index_expected.value();
+        channels_parsing_info.D2H_channels_in_use.insert(channel_index);
+        /* If managed by the FW - allow reuse of the channel in between contexts */
+        if (resources_manager.get_supported_features().padded_ddr_buffers) {
+            channels_to_unlock.insert(channel_index);
+        }
+    } else {
+        LOGGER__ERROR("Invalid layer direction");
+        return HAILO_INVALID_ARGUMENT;
+    }
+
+    auto fw_managed_channel = resources_manager.get_supported_features().padded_ddr_buffers;
+    for (auto &ddr_info : resources_manager.ddr_infos()) {
+        if (HAILO_H2D_STREAM == direction) {
+            /* we have the info already, just validate saved info, add ch_index, and return success! */
+            if ((ddr_info.row_size == edge_layer_proto.layer_info().edge_layer_base().core_bytes_per_buffer())
+                && (ddr_info.context_index == context_index)
+                && (ddr_info.d2h_stream_index == edge_layer_proto.context_switch_info().connected_sys_index())
+                && (ddr_info.h2d_stream_index == static_cast<uint8_t>(edge_layer_proto.layer_info().edge_layer_base().sys_index()))) {
+                ddr_info.h2d_channel_index = channel_index;
+                LOGGER__DEBUG("DDR layer: input stream_index: {}, output stream_index: {}, h2d_pcie_channel {}, d2h_pcie_channel: {}.",
+                    ddr_info.h2d_stream_index, ddr_info.d2h_stream_index, ddr_info.h2d_channel_index, ddr_info.d2h_channel_index);
+                return HEF_METADATA__add_ddr_buffer_input_edge_layer(context_switch_info,
+                    context_meta_data_head_pointer, ddr_info.h2d_stream_index, ddr_info.h2d_channel_index, network_index,
+                    nn_stream_config, ddr_info.intermediate_buffer->dma_address(), ddr_info.intermediate_buffer->depth(), fw_managed_channel);
+            }
+        } else if (HAILO_D2H_STREAM == direction) {
+            /* we have the info already, just validate saved info, add ch_index, and return success! */
+            if ((ddr_info.row_size == edge_layer_proto.layer_info().edge_layer_base().core_bytes_per_buffer())
+                && (ddr_info.context_index == context_index)
+                && (ddr_info.h2d_stream_index == edge_layer_proto.context_switch_info().connected_sys_index())
+                && (ddr_info.d2h_stream_index == static_cast<uint8_t>(edge_layer_proto.layer_info().edge_layer_base().sys_index()))) {
+                ddr_info.d2h_channel_index = channel_index;
+                LOGGER__DEBUG("DDR layer: input stream_index: {}, output stream_index: {}, h2d_pcie_channel {}, d2h_pcie_channel: {}.",
+                    ddr_info.h2d_stream_index, ddr_info.d2h_stream_index, ddr_info.h2d_channel_index, ddr_info.d2h_channel_index);
+                return HEF_METADATA__add_ddr_buffer_output_edge_layer(context_switch_info,
+                    context_meta_data_head_pointer, ddr_info.d2h_stream_index, ddr_info.d2h_channel_index, network_index,
+                    nn_stream_config, frame_credits_in_bytes, ddr_info.intermediate_buffer->dma_address(), 
+                    static_cast<uint16_t>(ddr_info.intermediate_buffer->descs_count() - 1), ddr_info.intermediate_buffer->desc_page_size(), 
+                    ddr_info.intermediate_buffer->depth(), fw_managed_channel);
+            }
+        } else {
+            LOGGER__ERROR("Invalid layer direction");
+            return HAILO_INVALID_ARGUMENT;
+        }
+    }
+
+    /* Allocate resources and prepare ddr_info */
+    DdrChannelsInfo local_info = {};
+    local_info.context_index = context_index;
+    CHECK(IS_FIT_IN_UINT8(edge_layer_proto.context_switch_info().connected_sys_index()),
+        HAILO_INVALID_HEF, "Failed to parse HEF. Invalid connected_sys_index: {}.",
+        edge_layer_proto.context_switch_info().connected_sys_index());
+    auto connected_sys_index = static_cast<uint8_t>(edge_layer_proto.context_switch_info().connected_sys_index());
+    if (HAILO_H2D_STREAM == direction) {
+        local_info.h2d_channel_index = channel_index;
+        local_info.h2d_stream_index = static_cast<uint8_t>(edge_layer_proto.layer_info().edge_layer_base().sys_index());
+        local_info.d2h_stream_index = connected_sys_index;
+    } else if (HAILO_D2H_STREAM == direction) {
+        local_info.h2d_stream_index = connected_sys_index;
+        local_info.d2h_channel_index = channel_index;
+        local_info.d2h_stream_index = static_cast<uint8_t>(edge_layer_proto.layer_info().edge_layer_base().sys_index());
+    } else {
+        LOGGER__ERROR("Invalid layer direction");
+        return HAILO_INVALID_ARGUMENT;
+    }
+
+    local_info.row_size = static_cast<uint16_t>(edge_layer_proto.layer_info().edge_layer_base().core_bytes_per_buffer());
+    // We count on local_info.min_buffered_rows to be aligned to DDR_NUMBER_OF_ROWS_PER_INTERRUPT in the ddr threads
+    local_info.min_buffered_rows = edge_layer_proto.context_switch_info().buffers();
+    auto leftover = (local_info.min_buffered_rows % DDR_NUMBER_OF_ROWS_PER_INTERRUPT);
+    if (0 != leftover) {
+        local_info.min_buffered_rows += (DDR_NUMBER_OF_ROWS_PER_INTERRUPT - leftover);
+    }
+
+    /* Create descs list */
+    auto ddr_buffer = resources_manager.create_ddr_buffer(local_info, context_index);
+    CHECK_EXPECTED_AS_STATUS(ddr_buffer);
+
+    local_info.intermediate_buffer = &ddr_buffer->get();
+
+    if (resources_manager.get_supported_features().padded_ddr_buffers) {
+        CHECK(0 == (DEFAULT_DESC_PAGE_SIZE % local_info.intermediate_buffer->desc_page_size()), HAILO_INTERNAL_FAILURE,
+            "In padded DDR buffers, desc list page size must be dividor of {}", DEFAULT_DESC_PAGE_SIZE);
+        CHECK(0 == (local_info.row_size % local_info.intermediate_buffer->desc_page_size()), HAILO_INTERNAL_FAILURE, 
+            "If HEF supports padded DDR buffers, row size must be a multiple of descriptor page size");
+        local_info.descriptors_per_frame = (local_info.row_size / local_info.intermediate_buffer->desc_page_size()) * 
+            edge_layer_proto.layer_info().edge_layer_base().core_buffers_per_frame();
+
+        auto programed_descs = ddr_buffer->get().program_ddr();
+        CHECK_EXPECTED_AS_STATUS(programed_descs);
+        local_info.initial_programed_descs = programed_descs.release();
+    } else {
+        LOGGER__WARNING("HEF from an old version detected. For optimal perfomance, please consider re-compile the HEF");
+        auto programed_descs = ddr_buffer->get().program_host_managed_ddr(local_info.row_size,
+            local_info.min_buffered_rows * DDR_THREADS_MIN_BUFFERED_ROWS_INITIAL_SCALE, 0);
+        CHECK_EXPECTED_AS_STATUS(programed_descs);
+        local_info.initial_programed_descs = programed_descs.release();
+        local_info.descriptors_per_frame = 0; // unused for host controlled ddr buffering
+    }
+    local_info.desc_list_size_mask = static_cast<uint32_t>(local_info.intermediate_buffer->descs_count() - 1);
+
+    // Add layer to metadata
+    if (HAILO_H2D_STREAM == direction) {
+        auto status = HEF_METADATA__add_ddr_buffer_input_edge_layer(context_switch_info,
+            context_meta_data_head_pointer, local_info.h2d_stream_index, local_info.h2d_channel_index, network_index,
+            nn_stream_config, local_info.intermediate_buffer->dma_address(), local_info.intermediate_buffer->depth(),
+            fw_managed_channel);
+        CHECK_SUCCESS(status);
+    } else if (HAILO_D2H_STREAM == direction) {
+        auto status = HEF_METADATA__add_ddr_buffer_output_edge_layer(context_switch_info,
+            context_meta_data_head_pointer, local_info.d2h_stream_index, local_info.d2h_channel_index,
+            network_index, nn_stream_config, frame_credits_in_bytes, local_info.intermediate_buffer->dma_address(), 
+            static_cast<uint16_t>(local_info.intermediate_buffer->descs_count() - 1), local_info.intermediate_buffer->desc_page_size(), 
+            local_info.intermediate_buffer->depth(), fw_managed_channel);
+        CHECK_SUCCESS(status);
+    } else {
+        LOGGER__ERROR("Invalid layer direction");
+        return HAILO_INVALID_ARGUMENT;
+    }
+
+    resources_manager.ddr_infos().push_back(local_info);
+    return HAILO_SUCCESS;
+}
+
+Expected<std::string> HefUtils::get_partial_network_name_by_index(const ProtoHEFNetworkGroup &network_group_proto, uint8_t network_index, 
+    const NetworkGroupSupportedFeatures &supported_features)
+{
+    if (supported_features.multi_network_support) {
+        CHECK_AS_EXPECTED(network_index < network_group_proto.networks_names_size(), HAILO_INVALID_ARGUMENT,
+            "Requested name for network_index={}, however there are only {} networks in the network group",
+            network_index, network_group_proto.networks_names_size());
+        return std::string(network_group_proto.networks_names(network_index));
+    } else {
+        return HailoRTDefaults::get_partial_network_name();
+    }
+}
+
+static hailo_status parse_and_fill_h2d_layer_multi_context(
+    CONTROL_PROTOCOL__context_switch_context_info_t *context_info,
+    uint8_t **context_meta_data_head_pointer, const ProtoHEFEdgeLayer &edge_layer_proto,
+    ContextSwitchChannelsParsingInfo &channels_parsing_info, ResourcesManager &resources_manager,
+    uint8_t context_index, std::set<uint8_t> &channels_to_unlock)
+{
+    uint8_t stream_index = 0;
+    uint32_t frame_credits_in_bytes = 0;
+
+    CHECK(ProtoHEFEdgeLayerType::PROTO__EDGE_LAYER_TYPE__INFO == edge_layer_proto.edge_layer_type(), HAILO_INVALID_HEF,
+        "H2D layers must be info_layer");
+
+    CHECK(IS_FIT_IN_UINT8(edge_layer_proto.layer_info().edge_layer_base().sys_index()), HAILO_INVALID_HEF,
+        "Failed to parse HEF. Invalid sys_index: {}.", edge_layer_proto.layer_info().edge_layer_base().sys_index());
+    stream_index = static_cast<uint8_t>(edge_layer_proto.layer_info().edge_layer_base().sys_index());
+
+    const bool hw_padding_supported = HefConfigurator::is_hw_padding_supported(edge_layer_proto);
+
+    auto nn_stream_config = HefConfigurator::parse_nn_stream_config(edge_layer_proto.layer_info().edge_layer_base(), hw_padding_supported,
+        edge_layer_proto.context_switch_info().edge_connection_type());
+    CHECK_EXPECTED_AS_STATUS(nn_stream_config, "Failed parse nn stream config");
+    auto layer_name = edge_layer_proto.layer_info().name();
+    auto support_multi_networks = resources_manager.get_supported_features().multi_network_support;
+    auto network_index = static_cast<uint8_t>((support_multi_networks) ? edge_layer_proto.network_index() : 0);
+
+    /* credits work on periph bytes */
+    frame_credits_in_bytes = (nn_stream_config->periph_bytes_per_buffer * nn_stream_config->core_buffers_per_frame);
+
+    switch (edge_layer_proto.context_switch_info().edge_connection_type()) {
+        case ProtoHEFEdgeConnectionType::PROTO__EDGE_CONNECTION_TYPE__BOUNDARY:
+            return fill_boundary_input_layer(context_info, context_meta_data_head_pointer, stream_index,
+                *nn_stream_config, channels_parsing_info, resources_manager, layer_name, network_index);
+
+        case ProtoHEFEdgeConnectionType::PROTO__EDGE_CONNECTION_TYPE__INTERMEDIATE:
+            return fill_inter_context_input_layer(context_info, context_meta_data_head_pointer, resources_manager,
+                context_index, stream_index, *nn_stream_config, &edge_layer_proto, channels_parsing_info,
+                channels_to_unlock, network_index);
+
+        case ProtoHEFEdgeConnectionType::PROTO__EDGE_CONNECTION_TYPE__DDR:
+            return fill_ddr_layer_multi_context(context_info, context_meta_data_head_pointer, resources_manager, context_index,
+                edge_layer_proto, *nn_stream_config, HAILO_H2D_STREAM, channels_parsing_info, frame_credits_in_bytes, 
+                channels_to_unlock, network_index);
+
+        default:
+            LOGGER__ERROR("Invalid edge connection type");
+            return HAILO_INTERNAL_FAILURE;
+    }
+}
+
+static hailo_status parse_and_fill_d2h_layer_multi_context(ProtoHEFNetworkGroupPtr network_group_proto, 
+        CONTROL_PROTOCOL__context_switch_context_info_t *context_info, 
+        uint8_t **context_meta_data_head_pointer, const ProtoHEFEdgeLayer &edge_layer_proto,
+        ContextSwitchChannelsParsingInfo &channels_parsing_info, ResourcesManager &resources_manager,
+        uint8_t context_index, std::set<uint8_t> &channels_to_unlock)
+{
+    uint8_t stream_index = 0;
+    uint32_t frame_credits_in_bytes = 0;
+    bool is_mux = false;
+    std::string layer_name;
+    CONTROL_PROTOCOL__nn_stream_config_t nn_stream_config = {};
+
+    const bool hw_padding_supported = HefConfigurator::is_hw_padding_supported(edge_layer_proto);
+
+    if (ProtoHEFEdgeLayerType::PROTO__EDGE_LAYER_TYPE__INFO == edge_layer_proto.edge_layer_type()) {
+        CHECK(IS_FIT_IN_UINT8(edge_layer_proto.layer_info().edge_layer_base().sys_index()), HAILO_INVALID_HEF,
+            "Failed to parse HEF. Invalid sys_index: {}.", edge_layer_proto.layer_info().edge_layer_base().sys_index());
+        stream_index = static_cast<uint8_t>(edge_layer_proto.layer_info().edge_layer_base().sys_index());
+        auto nn_stream_config_expected = HefConfigurator::parse_nn_stream_config(edge_layer_proto.layer_info().edge_layer_base(), 
+            hw_padding_supported, edge_layer_proto.context_switch_info().edge_connection_type());
+        CHECK_EXPECTED_AS_STATUS(nn_stream_config_expected, "Failed parse nn stream config");
+        nn_stream_config = nn_stream_config_expected.release();
+        frame_credits_in_bytes = (nn_stream_config.periph_bytes_per_buffer * nn_stream_config.core_buffers_per_frame);
+        is_mux = false;
+        layer_name = edge_layer_proto.layer_info().name();
+    } else if (ProtoHEFEdgeLayerType::PROTO__EDGE_LAYER_TYPE__MUX == edge_layer_proto.edge_layer_type()) {
+        CHECK(IS_FIT_IN_UINT8(edge_layer_proto.layer_mux().edge_layer_base().sys_index()), HAILO_INVALID_HEF,
+        "Failed to parse HEF. Invalid sys_index: {}.", edge_layer_proto.layer_mux().edge_layer_base().sys_index());
+        stream_index = static_cast<uint8_t>(edge_layer_proto.layer_mux().edge_layer_base().sys_index());
+        auto nn_stream_config_expected = HefConfigurator::parse_nn_stream_config(edge_layer_proto.layer_mux().edge_layer_base(), 
+            hw_padding_supported, edge_layer_proto.context_switch_info().edge_connection_type());
+        CHECK_EXPECTED_AS_STATUS(nn_stream_config_expected, "Failed parse nn stream config");
+        nn_stream_config = nn_stream_config_expected.release();
+        frame_credits_in_bytes = (nn_stream_config.periph_bytes_per_buffer * nn_stream_config.core_buffers_per_frame);
+        is_mux = true;
+        layer_name = edge_layer_proto.layer_mux().name();
+    } else {
+        LOGGER__ERROR("Invalid layer type.");
+        return HAILO_INVALID_HEF;
+    }
+
+    auto support_multi_networks = resources_manager.get_supported_features().multi_network_support;
+    auto network_index = static_cast<uint8_t>((support_multi_networks) ? edge_layer_proto.network_index() : 0);
+    auto partial_network_name = HefUtils::get_partial_network_name_by_index(*network_group_proto, network_index, 
+        resources_manager.get_supported_features());
+    CHECK_EXPECTED_AS_STATUS(partial_network_name);
+    
+    switch (edge_layer_proto.context_switch_info().edge_connection_type()) {
+        case ProtoHEFEdgeConnectionType::PROTO__EDGE_CONNECTION_TYPE__BOUNDARY:
+            return fill_boundary_output_layer(context_info, context_meta_data_head_pointer, resources_manager,
+                stream_index, nn_stream_config, frame_credits_in_bytes, channels_parsing_info, layer_name,
+                network_index, partial_network_name.value());
+
+        case ProtoHEFEdgeConnectionType::PROTO__EDGE_CONNECTION_TYPE__INTERMEDIATE:
+            CHECK(!is_mux, HAILO_INVALID_HEF, "Inter-context layer can't be mux.");
+            return fill_inter_context_output_layer(context_info, context_meta_data_head_pointer, resources_manager,
+                context_index, stream_index, nn_stream_config, frame_credits_in_bytes,
+                &edge_layer_proto, channels_parsing_info, channels_to_unlock, network_index, partial_network_name.value());
+
+        case ProtoHEFEdgeConnectionType::PROTO__EDGE_CONNECTION_TYPE__DDR:
+            return fill_ddr_layer_multi_context(context_info, context_meta_data_head_pointer, resources_manager, context_index,
+                edge_layer_proto, nn_stream_config, HAILO_D2H_STREAM, channels_parsing_info, frame_credits_in_bytes, 
+                channels_to_unlock, network_index);
+
+        default:
+            LOGGER__ERROR("Invalid edge connection type");
+            return HAILO_INTERNAL_FAILURE;
+    }
+}
+
+static hailo_status parse_and_fill_layers_mapping_multi_context(ProtoHEFNetworkGroupPtr network_group_proto, 
+        CONTROL_PROTOCOL__context_switch_context_info_t *context_info, 
+        uint8_t **context_meta_data_head_pointer, const ProtoHEFContextMetadata *context_metadata, 
+        ContextSwitchChannelsParsingInfo &channels_parsing_info, ResourcesManager &resources_manager, uint8_t context_index)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    // We use those sets to unlock resources at the end of each context parsing to prevent reuse within the same context
+    std::set<uint8_t> channels_to_unlock = {};
+
+    CHECK(0 < context_metadata->edge_layers_size(), HAILO_INVALID_HEF, "No edge layers in this context");
+
+    for (const auto &edge_layer_proto : context_metadata->edge_layers()) {
+        if (ProtoHEFEdgeLayerDirection::PROTO__EDGE_LAYER_DIRECTION__HOST_TO_DEVICE == edge_layer_proto.direction()) {
+            status = parse_and_fill_h2d_layer_multi_context(context_info, context_meta_data_head_pointer,
+                edge_layer_proto, channels_parsing_info, resources_manager, context_index, channels_to_unlock);
+            CHECK_SUCCESS(status);
+        } else if (ProtoHEFEdgeLayerDirection::PROTO__EDGE_LAYER_DIRECTION__DEVICE_TO_HOST == edge_layer_proto.direction()) {
+            status = parse_and_fill_d2h_layer_multi_context(network_group_proto, context_info, context_meta_data_head_pointer, 
+                edge_layer_proto, channels_parsing_info, resources_manager, context_index, channels_to_unlock);
+            CHECK_SUCCESS(status);
+        } else {
+            LOGGER__ERROR("Invalid argument: stream_direction");
+            return HAILO_INVALID_ARGUMENT;
+        }
+    }
+
+    /* UN-Lock resources at the end of the context - 
+        d2h inter-context and DDR buffer channels, h2d inter-context and DDR buffer channels */
+    for (auto& channel_index : channels_to_unlock) {
+        if (contains(channels_parsing_info.D2H_channels_in_use, channel_index)) {
+            channels_parsing_info.D2H_channels_in_use.erase(channel_index);
+        } else if (contains(channels_parsing_info.H2D_channels_in_use, channel_index)) {
+            channels_parsing_info.H2D_channels_in_use.erase(channel_index);
+        } else {
+            LOGGER__ERROR("channel_index {} was marked for unlocking, but is not marked as in use in context {}",
+                channel_index, context_index);
+            return HAILO_INTERNAL_FAILURE;
+        }
+    }
+
+    return HAILO_SUCCESS;
+}
+
+static hailo_status fill_ddr_buffers_info(CONTROL_PROTOCOL__context_switch_context_info_t *context_info, 
+    uint8_t **context_meta_data_head_pointer, ResourcesManager &resources_manager, uint8_t context_index)
+{
+    const CONTROL_PROTOCOL__TRIGGER_t none_trigger = HEF_METADATA__build_none_trigger();
+    bool found_ddr_pair_for_context = false;
+    // See: HRT-5373
+    static const bool NOT_REPEATED = false;
+
+    for (auto& ddr_info : resources_manager.ddr_infos()) {
+        if (context_index == ddr_info.context_index) {
+            /* Any action must have a trigger */
+            auto status = HEF_METADATA__add_trigger_to_trigger_group(context_info, context_meta_data_head_pointer, 
+                &none_trigger);
+            CHECK_SUCCESS(status, "failed to add NONE trigger before ddr buffer pair infos");
+            /* Add ddr pair info action */
+            status = HEF_METADATA__add_ddr_pair_info(context_info, context_meta_data_head_pointer, 
+                ddr_info.h2d_channel_index, ddr_info.d2h_channel_index, ddr_info.descriptors_per_frame, 
+                ddr_info.initial_programed_descs, NOT_REPEATED);
+            CHECK_SUCCESS(status,"failed to add ddr pair info");
+            found_ddr_pair_for_context = true;
+        }
+    }
+
+    if (found_ddr_pair_for_context) {
+        /* No need to add NONE trigger. This action can be inside the last none trigger of the last DDR pair */
+        auto status = HEF_METADATA__add_ddr_buffering_start(context_info, context_meta_data_head_pointer, NOT_REPEATED);
+        CHECK_SUCCESS(status,"failed to add ddr buffer start action");
+    }
+
+    return HAILO_SUCCESS;
+}
+
+// Returns pairs of form [start, end] (inclusive) of repeated 'ContextSwitchConfigAction's in the given vector
+static std::vector<std::pair<uint32_t, uint32_t>> get_repreated_actions_boundary_indices(
+    const std::vector<ContextSwitchConfigActionPtr> &actions)
+{
+    const uint32_t num_actions = static_cast<uint32_t>(actions.size());
+
+    std::vector<std::pair<uint32_t, uint32_t>> repeated_indexes;
+    uint32_t start_index = 0;
+    while (start_index < num_actions) {
+        auto end_index = start_index + 1;
+        do
+        {
+            if (end_index == num_actions) {
+                break;
+            }
+            if (actions[start_index]->get_type() != actions[end_index]->get_type()) {
+                break;
+            }
+            end_index++;
+        } while (true);
+        
+
+        repeated_indexes.emplace_back(start_index, end_index - 1);
+        start_index = end_index;
+    }
+
+    return repeated_indexes;
+}
+
+// Returns a map from start indexes of repeated actions to the size of the chunk (number of repeated actions)
+static std::map<uint32_t, uint8_t> get_start_indexes_of_repeated_actions(
+    const std::vector<ContextSwitchConfigActionPtr> &actions,
+    const std::vector<std::pair<uint32_t, uint32_t>> &repeated_indexes,
+    // TODO: get this from HardCoded config (HRT-5352)
+    const std::set<ContextSwitchConfigAction::Type> &action_types_denylist = {})
+{
+    std::map<uint32_t, uint8_t> result;
+    for (const auto &index_pair : repeated_indexes) {
+        if (!actions[index_pair.first]->supports_repeated_block()) {
+            continue;
+        }
+
+        if (contains(action_types_denylist, actions[index_pair.first]->get_type())) {
+            continue;
+        }
+
+        // TODO: Move merge calculation to HRT-5352
+        // Merge calculation (see also - CONTEXT_SWITCH_DEFS__repeated_action_header_t in common/include/context_switch_defs.h):
+        // * Assume there are x repeated actions that can be merged
+        // * Let a := sizeof(action_to_be_merged) [without CONTEXT_SWITCH_DEFS__common_action_header_t]
+        // * sizeof(CONTEXT_SWITCH_DEFS__common_action_header_t) is 5
+        // * sizeof(CONTEXT_SWITCH_DEFS__repeated_action_header_t) is 3
+        // Then:
+        // * original_size = x * (5 + a) = 5x + ax
+        // * new_size = 5 + 3 + ax = 8 + ax
+        // * new_size < original_size <=> 8 + ax < 5x + ax <=> 8 < 5x <=> 1.6 < x
+        // Hence we merge for x >= 2
+        static_assert(sizeof(CONTEXT_SWITCH_DEFS__common_action_header_t) == 5,
+            "Merge calculation assumes that 'sizeof(CONTEXT_SWITCH_DEFS__common_action_header_t) == 5'");
+        static_assert(sizeof(CONTEXT_SWITCH_DEFS__repeated_action_header_t) == 3,
+            "Merge calculation assumes that 'sizeof(CONTEXT_SWITCH_DEFS__repeated_action_header_t) == 3'");
+        static const uint32_t MIN_REQUIRED_FOR_MERGING = 2;
+
+        uint32_t start_index = index_pair.first;
+        const uint32_t end_index = index_pair.second;
+        while (start_index < end_index) {
+            const auto curr_chunk_size = static_cast<uint8_t>(std::min(
+                static_cast<uint32_t>(std::numeric_limits<uint8_t>::max()),
+                end_index - start_index + 1));
+            if (curr_chunk_size < MIN_REQUIRED_FOR_MERGING) {
+                break;
+            }
+
+            result.emplace(start_index, curr_chunk_size);
+
+            start_index += curr_chunk_size;
+        }
+    }
+
+    return result;
+}
+
+static std::set<uint32_t> get_end_indexes_of_write_ccw_actions(
+    const std::vector<ContextSwitchConfigActionPtr> &actions,
+    const std::vector<std::pair<uint32_t, uint32_t>> &repeated_indexes)
+{
+    std::set<uint32_t> result;
+    for (const auto &index_pair : repeated_indexes) {
+        const auto curr_action_type = actions[index_pair.first]->get_type();
+        if (ContextSwitchConfigAction::Type::WriteDataCcw != curr_action_type) {
+            continue;
+        }
+
+        result.insert(index_pair.second);
+    }
+
+    return result;
+}
+
+static hailo_status proccess_write_ccw_action(const ContextSwitchConfigActionPtr &configuration_action,
+    std::vector<ConfigResources> &config_resources, std::set<uint8_t> &pending_cfg_ch_buffer,
+    std::vector<uint16_t> &total_ccw_bursts, const std::set<uint32_t> &end_indexes_of_write_ccw_actions, 
+    const uint32_t &action_index, const bool support_pre_fetch, 
+    std::vector<ContextSwitchConfigActionPtr> &processed_configuration_actions)
+{
+    // Add the config channel index of the current WriteDataCcwAction
+    const auto cfg_channel_index = configuration_action->get_proto_action().write_data_ccw().cfg_channel_index();
+    CHECK(IS_FIT_IN_UINT8(cfg_channel_index), HAILO_INVALID_HEF, 
+        "Failed to parse HEF. Invalid cfg_channel_index: {}.", cfg_channel_index);
+    pending_cfg_ch_buffer.insert(static_cast<uint8_t>(cfg_channel_index));
+            
+    /* TODO - get CCW headers from proto (need to add it into the proto) */
+    //const auto ccw_bursts = configuration_action->get_proto_action().write_data_ccw().ccw_bursts();
+    const uint16_t ccw_bursts = 1;
+    auto accum_ccw_bursts = total_ccw_bursts[cfg_channel_index] + ccw_bursts;
+    CHECK(IS_FIT_IN_UINT16(accum_ccw_bursts), HAILO_INTERNAL_FAILURE,
+        "Failed to parse HEF. action fetch ccw burst supports only to 2^16 bursts.");
+    total_ccw_bursts[cfg_channel_index] = static_cast<uint16_t>(accum_ccw_bursts);
+
+    // At the end of a consecutive group of WriteDataCcwActions, we program the
+    // descriptors for all the config channels used.
+    if (contains(end_indexes_of_write_ccw_actions, action_index)) {
+        /* Add the last CCW write into the buffer */
+        processed_configuration_actions.emplace_back(configuration_action);
+
+        /* Build descriptor list for this buffer */
+        auto create_desc_action = CreateConfigDescAndFetchAction::create(config_resources, pending_cfg_ch_buffer, 
+            total_ccw_bursts, support_pre_fetch);
+        CHECK_EXPECTED_AS_STATUS(create_desc_action);
+        processed_configuration_actions.emplace_back(create_desc_action.release());
+
+        /* Cleanups */
+        pending_cfg_ch_buffer.clear();
+        for (uint8_t cleanup_ch_index = 0; cleanup_ch_index < total_ccw_bursts.size(); cleanup_ch_index++) {
+            total_ccw_bursts[cleanup_ch_index] = 0;
+        }
+    } else {
+        processed_configuration_actions.emplace_back(configuration_action);
+    }
+
+    return HAILO_SUCCESS;
+}
+
+
+// Adds context switch configuration actions that don't appear in the HEF:
+// * If groups of consecutive actions can be "merged" as repeated actions (saving room the FW's
+//   action list) a RepeatedHeaderAction is placed before the relevant actions.
+//   See also: CONTROL_PROTOCOL__REPEATED_ACTION_t's documnetion in control_protocol.h.
+// * At the end of each consecutive group of WriteDataCcwAction, a CreateConfigDescAndFetchAction is added.
+static Expected<std::vector<ContextSwitchConfigActionPtr>> process_configuration_actions(
+    std::vector<ContextSwitchConfigActionPtr> &input_configuration_actions,
+    std::vector<ConfigResources> &config_resources, const bool support_pre_fetch)
+{
+    std::vector<ContextSwitchConfigActionPtr> processed_configuration_actions;
+    
+    std::set<uint8_t> pending_cfg_ch_buffer;
+    std::vector<uint16_t> total_ccw_bursts(config_resources.size(), 0);
+
+    const auto repeated_indexes = get_repreated_actions_boundary_indices(input_configuration_actions);
+    const auto start_indexes_of_repeated_actions = get_start_indexes_of_repeated_actions(input_configuration_actions, repeated_indexes);
+    const auto end_indexes_of_write_ccw_actions = get_end_indexes_of_write_ccw_actions(input_configuration_actions, repeated_indexes);
+    for (uint32_t action_index = 0; action_index < input_configuration_actions.size(); action_index++) {
+        // A group of actions can be "merged" as repeated actions.
+        // Hence we add a RepeatedHeaderAction and mark all the actions in this group as "reapted"
+        if (contains(start_indexes_of_repeated_actions, action_index)) {
+            const auto num_repeates = start_indexes_of_repeated_actions.at(action_index);
+            auto create_repeated_action = RepeatedHeaderAction::create(
+                input_configuration_actions[action_index]->get_action_list_type(), num_repeates);
+            CHECK_EXPECTED(create_repeated_action);
+            processed_configuration_actions.emplace_back(create_repeated_action.release());
+            for (uint32_t repeated_offset = 0; repeated_offset < num_repeates; repeated_offset++) {
+                input_configuration_actions[action_index + repeated_offset]->set_is_in_repeated_block(true);
+            }
+        }
+
+        // Add the current action
+        const auto &configuration_action = input_configuration_actions[action_index];
+
+        if (ContextSwitchConfigAction::Type::WriteDataCcw == configuration_action->get_type()) {
+            auto status = proccess_write_ccw_action(configuration_action, config_resources, pending_cfg_ch_buffer,
+                total_ccw_bursts, end_indexes_of_write_ccw_actions, action_index, 
+                support_pre_fetch, processed_configuration_actions);
+            CHECK_SUCCESS_AS_EXPECTED(status);
+        } else {
+            // Add the current action
+            processed_configuration_actions.emplace_back(configuration_action);
+        }
+    }
+
+    return processed_configuration_actions;
+}
+
+static bool is_mercury_device_type(const ProtoHEFHwArch &hw_arch) 
+{
+    /* TODO - HRT-5067 - use one hw_arch for mercury */    
+    return (PROTO__HW_ARCH__MERCURY == hw_arch) || (PROTO__HW_ARCH__GINGER == hw_arch) ||
+        (PROTO__HW_ARCH__LAVENDER == hw_arch);
+}
+
+static hailo_status parse_actions_in_operation(const ProtoHEFOperation &operation,
+    Device &device, const ProtoHEFHwArch &hw_arch, std::vector<ConfigResources> &config_resources, 
+    const ResourcesManager &resources_manager, ProtoHEFNetworkGroupPtr network_group_proto, 
+    CONTROL_PROTOCOL__context_switch_context_info_t &context_info, uint8_t **context_meta_data_head_pointer)
+{
+    auto support_pre_fetch = is_mercury_device_type(hw_arch);
+
+    // First, the context switch configuration actions from the HEF are added in their order of
+    // appearance (which is chronological).
+    std::vector<ContextSwitchConfigActionPtr> configuration_actions;
+    for (const auto &proto_action : operation.actions()) {
+        auto configuration_action = ContextSwitchConfigAction::create(proto_action, device, config_resources,
+            resources_manager, *network_group_proto, support_pre_fetch);
+        CHECK_EXPECTED_AS_STATUS(configuration_action);
+        configuration_actions.emplace_back(configuration_action.release());
+    }
+
+    // Next, we process the actions from the HEF, adding 'CreateConfigDescAndFetchAction's and 'RepeatedHeaderAction's.
+    // The resulting vector contains the configuration actions to be executed in chronological order.
+    const auto processed_configuration_actions = process_configuration_actions(configuration_actions, config_resources, 
+        support_pre_fetch);
+    CHECK_EXPECTED_AS_STATUS(processed_configuration_actions);
+
+    // Finally, we execute the context switch configuration actions.
+    for (const auto &configuration_action : processed_configuration_actions.value()) {
+        const auto status = configuration_action->execute(&context_info, context_meta_data_head_pointer);
+        CHECK_SUCCESS(status);
+    }
+
+    return HAILO_SUCCESS;
+}
+
+static hailo_status fill_context_recepies_for_multi_context(ProtoHEFNetworkGroupPtr network_group_proto, 
+    const ProtoHEFHwArch &hw_arch, CONTROL_PROTOCOL__context_switch_context_info_t &context_info, 
+    ResourcesManager &resources_manager, uint8_t context_index, const ProtoHEFContext &proto_context, 
+    Device &device, ContextSwitchChannelsParsingInfo &channels_parsing_info)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    uint8_t *context_meta_data_head_pointer = context_info.context_network_data;
+
+    // Add edge layers mapping
+    status = parse_and_fill_layers_mapping_multi_context(network_group_proto, &context_info, &context_meta_data_head_pointer, 
+        &proto_context.metadata(), channels_parsing_info, resources_manager, context_index);
+    CHECK_SUCCESS(status);
+
+    auto number_of_edge_layers = proto_context.metadata().edge_layers_size();
+    CHECK(IS_FIT_IN_UINT8(number_of_edge_layers), HAILO_INVALID_HEF, 
+        "Failed to parse HEF. Invalid edge_layers_size: {}.", number_of_edge_layers);
+
+    // For the case of DDR buffers in FW - add DDR info after parsing all edge layers and before parsing all other actions
+    if (resources_manager.get_supported_features().padded_ddr_buffers) {
+        status = fill_ddr_buffers_info(&context_info, &context_meta_data_head_pointer, resources_manager, context_index);
+        CHECK_SUCCESS(status);
+    }
+
+    CHECK(IS_FIT_IN_UINT8(proto_context.operations_size()), HAILO_INVALID_HEF,
+        "Failed to parse HEF. Invalid operations_count: {}.", proto_context.operations_size());
+
+    context_info.context_stream_remap_data.should_use_stream_remap = static_cast<uint8_t>(proto_context.metadata().shmiglue_info().should_use_shmiglue());
+
+    /* Parse context */
+    for (const auto &operation : proto_context.operations()) {
+        const auto operation_trigger = ContextSwitchTrigger::create(operation.trigger());
+        CHECK_EXPECTED_AS_STATUS(operation_trigger);
+        operation_trigger->add_to_trigger_group(&context_info, &context_meta_data_head_pointer);
+
+        status = parse_actions_in_operation(operation, device, hw_arch, 
+            resources_manager.dynamic_config(context_index), resources_manager, network_group_proto, context_info, 
+            &context_meta_data_head_pointer);
+        CHECK_SUCCESS(status);
+    }
+
+    // update context_network_data_length per context, and dynamic_contexts_descriptors count in main header
+    context_info.context_network_data_length =
+        static_cast<uint32_t>(context_meta_data_head_pointer - context_info.context_network_data);
+
+    return HAILO_SUCCESS;
+}
+
+static hailo_status fill_preliminary_config_recepies_for_multi_context(const ProtoHEFHwArch &hw_arch,
+    CONTROL_PROTOCOL__context_switch_context_info_t &context_info, ResourcesManager &resources_manager,
+    ProtoHEFNetworkGroupPtr network_group_proto, const ProtoHEFPreliminaryConfig &proto_preliminary_config, Device &device)
+{
+    uint8_t *context_meta_data_head_pointer = context_info.context_network_data;
+
+    CHECK(IS_FIT_IN_UINT8(proto_preliminary_config.operation_size()), HAILO_INVALID_HEF,
+        "Failed to parse HEF. Invalid operations_count: {}.", proto_preliminary_config.operation_size());
+
+    /* Parse preliminary config */
+    for (const auto &operation_proto : proto_preliminary_config.operation()) {
+        const auto operation_trigger = ContextSwitchTrigger::create(operation_proto.trigger());
+        CHECK_EXPECTED_AS_STATUS(operation_trigger);
+        operation_trigger->add_to_trigger_group(&context_info, &context_meta_data_head_pointer);
+
+        const auto status = parse_actions_in_operation(operation_proto, device, hw_arch, 
+            resources_manager.preliminary_config(), resources_manager, network_group_proto, context_info, 
+            &context_meta_data_head_pointer);
+        CHECK_SUCCESS(status);
+    }
+
+    // update context_network_data_length per context, and preliminary_context_descriptors count in main header
+    context_info.context_network_data_length =
+        static_cast<uint32_t>(context_meta_data_head_pointer - context_info.context_network_data);
+
+    return HAILO_SUCCESS;
+}
+
+Expected<std::shared_ptr<ResourcesManager>> Hef::Impl::create_resources_manager(
+    ProtoHEFNetworkGroupPtr network_group_proto, uint8_t net_group_index,
+    VdmaDevice &device, HailoRTDriver &driver, const ConfigureNetworkParams &config_params, 
+    std::shared_ptr<NetworkGroupMetadata> network_group_metadata,
+    const ProtoHEFHwArch &hw_arch)
+{
+    CHECK_ARG_NOT_NULL_AS_EXPECTED(network_group_proto);
+
+    CHECK(network_group_proto->contexts_size() <= MAX_CONTEXTS_COUNT, make_unexpected(HAILO_INVALID_HEF),
+        "App '{}' contains more contexts than allowed ({} > {})", network_group_proto->network_group_metadata().network_group_name(),
+        network_group_proto->contexts_size(), MAX_CONTEXTS_COUNT);
+    
+    for (auto &network_params : config_params.network_params_by_name) {
+        CHECK(HAILO_MAX_BATCH_SIZE >= network_params.second.batch_size, make_unexpected(HAILO_INVALID_ARGUMENT),
+            "Given batch size ({}) for network group {}, network {} is bigger than max allowed ({})", network_params.second.batch_size,
+            network_group_proto->network_group_metadata().network_group_name(), network_params.first, HAILO_MAX_BATCH_SIZE);
+    }
+
+    auto parsing_info = Hef::Impl::get_parsing_info(network_group_proto);
+    CHECK_EXPECTED(parsing_info);
+
+    auto resources_manager = ResourcesManager::create(device, driver, config_params, network_group_proto, network_group_metadata,
+        parsing_info.release(), net_group_index);
+    CHECK_EXPECTED(resources_manager);
+
+    auto preliminary_context = resources_manager->add_new_context();
+    CHECK_EXPECTED(preliminary_context);
+
+    auto status = fill_preliminary_config_recepies_for_multi_context(hw_arch, preliminary_context.value().get(),
+        resources_manager.value(), network_group_proto, network_group_proto->preliminary_config(), device);
+    CHECK_SUCCESS_AS_EXPECTED(status);
+    resources_manager->update_preliminary_config_buffer_info();
+
+    ContextSwitchChannelsParsingInfo channels_parsing_info = {};
+
+    for (uint8_t context_index = 0; context_index < network_group_proto->contexts_size(); ++context_index) {
+        auto new_context = resources_manager->add_new_context();
+        CHECK_EXPECTED(new_context);
+
+        status = fill_context_recepies_for_multi_context(network_group_proto, hw_arch, new_context.value().get(), resources_manager.value(),
+            context_index, network_group_proto->contexts(context_index), device, channels_parsing_info);
+        CHECK_SUCCESS_AS_EXPECTED(status);
+    }
+    resources_manager->update_dynamic_contexts_buffer_info();
+
+    status = resources_manager->create_vdma_channels();
+    CHECK_SUCCESS_AS_EXPECTED(status);
+
+    auto resources_manager_ptr = make_shared_nothrow<ResourcesManager>(resources_manager.release());
+    CHECK_NOT_NULL_AS_EXPECTED(resources_manager_ptr, HAILO_OUT_OF_HOST_MEMORY);
+
+    return resources_manager_ptr;
+}
+
+Expected<std::vector<std::string>> Hef::Impl::get_sorted_output_names(const std::string &net_group_name)
+{
+    auto network_group_metadata = get_network_group_metadata(net_group_name);
+    CHECK_EXPECTED(network_group_metadata);
+
+    auto res = network_group_metadata->get_sorted_output_names();
+    return res;
+}
+
+static Expected<WriteMemoryInfo> parse_ccw_buffer(const std::string &ccw_buffer)
+{
+    WriteMemoryInfo write_memory_info = {};
+    CHECK_AS_EXPECTED(ccw_buffer.size() > CCW_DATA_OFFSET, HAILO_INVALID_HEF, "ccw buffer is too small");
+    CcwHeader *header = (CcwHeader*)(ccw_buffer.data());
+
+    uint32_t words_count = header->words_count + 1;
+    auto data_length = words_count * CCW_BYTES_IN_WORD;
+    write_memory_info.address = header->address;
+
+    // Validation for ccw size
+    size_t expected_ccw_data_length = (ccw_buffer.length() - CCW_DATA_OFFSET);
+    if (0 != (words_count % 2)) {
+        expected_ccw_data_length -= CCW_BYTES_IN_WORD;
+    }
+    CHECK_AS_EXPECTED(data_length == expected_ccw_data_length, HAILO_INVALID_HEF,
+        "Invalid ccw buffer was parsed from HEF");
+
+    auto data_buff = Buffer::create(reinterpret_cast<const uint8_t*>(ccw_buffer.data() + CCW_DATA_OFFSET), data_length);
+    CHECK_EXPECTED(data_buff);
+    write_memory_info.data = data_buff.release();
+
+    return write_memory_info;
+}
+
+/* HcpConfigNetworkGroup funcs */
+
+Expected<std::vector<WriteMemoryInfo>> Hef::Impl::create_single_context_network_group_config(const ProtoHEFPreliminaryConfig& proto_config)
+{
+    std::vector<WriteMemoryInfo> config_buffers;
+
+    for (const auto &operation : proto_config.operation()) {
+        switch (operation.trigger().trigger_case()) {
+            case ProtoHEFTrigger::kTriggerNone: {
+                break;
+            }
+            default: {
+                LOGGER__ERROR("Triggers different from 'ProtoHEFTriggerNone' are not supported");
+                return make_unexpected(HAILO_INTERNAL_FAILURE);
+            }
+        }
+
+        for (const auto &action : operation.actions()) {
+            switch (action.action_case()) {
+                case ProtoHEFAction::kNone: {
+                    break;
+                }
+                case ProtoHEFAction::kWriteData: {
+                    WriteMemoryInfo write_memory_info = {};
+                    write_memory_info.address = static_cast<uint32_t>(action.write_data().address());
+                    auto data_buff = Buffer::create(
+                        reinterpret_cast<const uint8_t*>(action.write_data().data().data()),
+                        action.write_data().data().length());
+                    CHECK_EXPECTED(data_buff);
+                    write_memory_info.data = data_buff.release();
+                    config_buffers.emplace_back(std::move(write_memory_info));
+                    break;
+                }
+                case ProtoHEFAction::kWriteDataCcw: {
+                    auto config_buffer = parse_ccw_buffer(action.write_data_ccw().data());
+                    CHECK_EXPECTED(config_buffer);
+                    config_buffers.emplace_back(config_buffer.release());
+                    break;
+                }
+                case ProtoHEFAction::kDisableLcu: {
+                    // We ignore this action. the lcu_disable will happen in the nn_core reset before configuring specific network_group
+                    break;
+                }
+                case ProtoHEFAction::kEnableLcu: {
+                    WriteMemoryInfo write_memory_info = {};
+                    write_memory_info.address = action.enable_lcu().lcu_enable_address();
+                    auto data_buff = Buffer::create(ENABLE_LCU_CONTROL_WORD, sizeof(ENABLE_LCU_CONTROL_WORD));
+                    CHECK_EXPECTED(data_buff);
+                    write_memory_info.data = data_buff.release();
+                    config_buffers.emplace_back(std::move(write_memory_info));
+                    break;
+                }
+                case ProtoHEFAction::kAllowInputDataflow: {
+                case ProtoHEFAction::kWaitForModuleConfigDone:
+                    // We ignore the 'wait_for_interrupt' actions. After writing the configurations we can be sure everything is configured and dont need to wait for interrupts
+                    break;
+                }
+                case ProtoHEFAction::kWaitForSeqeuncer: {
+                case ProtoHEFAction::kEnableSequencer:
+                    LOGGER__ERROR("Parsing error. Sequencer related actions are not supported over Ethernet. "
+                        "If you use the Ethernet interface, please disable the Sequencer in the Dataflow Compiler (SDK) and then re-create the HEF. "
+                        "Disabling the Sequencer is done using the hef_param command in the model script (ALLS file). "
+                        "See the Dataflow Compiler user guide for more information.");
+                    return make_unexpected(HAILO_INVALID_HEF);
+                }
+                default: {
+                    LOGGER__ERROR("Invalid action");
+                    return make_unexpected(HAILO_INTERNAL_FAILURE);
+                }
+            }
+        }
+    }
+
+    return config_buffers;
+}
+
+ProtoHEFHwArch Hef::Impl::get_device_arch()
+{
+    return m_header.hw_arch();
+}
+
+Expected<float64_t> Hef::Impl::get_bottleneck_fps(const std::string &net_group_name)
+{
+    auto net_group = get_net_group_by_name(net_group_name);
+    CHECK_EXPECTED(net_group);
+    return net_group.value()->network_group_metadata().bottleneck_fps();
+}
+
+bool Hef::Impl::contains_ddr_layers(const ProtoHEFNetworkGroup& net_group)
+{
+    for (auto &context : net_group.contexts()) {
+        for (auto &layer : context.metadata().edge_layers()) {
+            if (ProtoHEFEdgeConnectionType::PROTO__EDGE_CONNECTION_TYPE__DDR ==
+                layer.context_switch_info().edge_connection_type()) {
+                return true;
+            }
+        }
+    }
+    return false;
+}
+
+bool is_edge_under_mux(const LayerInfo &info, const std::string &edge_name)
+{
+    if (!info.is_mux) {
+        return edge_name == info.name;
+    }
+    for (const auto &pred : info.predecessor) {
+        if (info.is_mux) {
+            if (is_edge_under_mux(pred, edge_name)) {
+                return true;
+            }
+        } else {
+            if (edge_name == pred.name) {
+                return true;
+            }
+        }
+    }
+    return false;
+}
+
+Expected<std::vector<std::string>> Hef::Impl::get_stream_names_from_vstream_name(const std::string &vstream_name,
+    const std::string &net_group_name)
+{
+    auto network_group_metadata = get_network_group_metadata(net_group_name);
+    CHECK_EXPECTED(network_group_metadata);
+
+    return network_group_metadata->get_stream_names_from_vstream_name(vstream_name);
+}
+
+void get_demuxes_names_impl(const LayerInfo &info, std::vector<std::string> &res)
+{
+    if (!info.is_mux) {
+        res.push_back(info.name);
+    } else {
+        for (auto &pred : info.predecessor) {
+            get_demuxes_names_impl(pred, res);
+        }
+    }
+}
+
+std::vector<std::string> get_demuxes_names(const LayerInfo &info)
+{
+    std::vector<std::string> res;
+    get_demuxes_names_impl(info, res);
+    return res;
+}
+
+Expected<std::vector<std::string>> Hef::Impl::get_vstream_names_from_stream_name(const std::string &stream_name,
+    const std::string &net_group_name)
+{
+    auto network_group_metadata = get_network_group_metadata(net_group_name);
+    CHECK_EXPECTED(network_group_metadata);
+
+    return network_group_metadata->get_vstream_names_from_stream_name(stream_name);
+}
+
+Expected<CONTROL_PROTOCOL__TRIGGER_t> ContextSwitchTrigger::serialize(const ProtoHEFTrigger &proto_trigger)
+{
+    switch (proto_trigger.trigger_case()) {
+        case ProtoHEFTrigger::kTriggerNone:
+            return HEF_METADATA__build_none_trigger();
+        case ProtoHEFTrigger::kTriggerAllDataWasReceived:
+        {
+            const auto stream_index = proto_trigger.trigger_all_data_was_received().shmifo_index();
+            CHECK_AS_EXPECTED(IS_FIT_IN_UINT8(stream_index), HAILO_INVALID_HEF,
+                "Failed to parse HEF. Invalid stream_index: {}.", stream_index);
+            return HEF_METADATA__build_input_stream_trigger(static_cast<uint8_t>(stream_index));
+        }
+        case ProtoHEFTrigger::kTriggerAllDataWasSent:
+        {
+            const auto stream_index = proto_trigger.trigger_all_data_was_sent().shmifo_index();
+            CHECK_AS_EXPECTED(IS_FIT_IN_UINT8(stream_index), HAILO_INVALID_HEF,
+                "Failed to parse HEF. Invalid stream_index: {}.", stream_index);
+            return HEF_METADATA__build_output_stream_trigger(static_cast<uint8_t>(stream_index));
+        }
+        case ProtoHEFTrigger::kTriggerLcu:
+        {
+            const auto cluster_index = proto_trigger.trigger_lcu().cluster_index();
+            const auto lcu_index = proto_trigger.trigger_lcu().lcu_index();
+            CHECK_AS_EXPECTED(IS_FIT_IN_UINT8(cluster_index), HAILO_INVALID_HEF,
+                "Failed to parse HEF. Invalid cluster_index: {}.", cluster_index);
+            CHECK_AS_EXPECTED(IS_FIT_IN_UINT8(lcu_index), HAILO_INVALID_HEF,
+                "Failed to parse HEF. Invalid lcu_index: {}.", lcu_index);
+            return HEF_METADATA__build_lcu_trigger(static_cast<uint8_t>(cluster_index),
+                static_cast<uint8_t>(lcu_index));
+        }
+        case ProtoHEFTrigger::kTriggerNms:
+        {
+            const auto aggregator_index = proto_trigger.trigger_nms().aggregator_index();
+            const auto pred_cluster_ob_index = proto_trigger.trigger_nms().pred_cluster_ob_index();
+            const auto pred_cluster_ob_cluster_index = proto_trigger.trigger_nms().pred_cluster_ob_cluster_index();
+            const auto pred_cluster_ob_interface = proto_trigger.trigger_nms().pred_cluster_ob_interface();
+            const auto succ_prepost_ob_index = proto_trigger.trigger_nms().succ_prepost_ob_index();
+            const auto succ_prepost_ob_interface = proto_trigger.trigger_nms().succ_prepost_ob_interface();
+            CHECK_AS_EXPECTED(IS_FIT_IN_UINT8(aggregator_index), HAILO_INVALID_HEF,
+                "Failed to parse HEF. Invalid aggregator_index: {}.", aggregator_index);
+            CHECK_AS_EXPECTED(IS_FIT_IN_UINT8(pred_cluster_ob_index), HAILO_INVALID_HEF,
+                "Failed to parse HEF. Invalid pred_cluster_ob_index: {}.", pred_cluster_ob_index);
+            CHECK_AS_EXPECTED(IS_FIT_IN_UINT8(pred_cluster_ob_cluster_index), HAILO_INVALID_HEF,
+                "Failed to parse HEF. Invalid pred_cluster_ob_cluster_index: {}.", pred_cluster_ob_cluster_index);
+            CHECK_AS_EXPECTED(IS_FIT_IN_UINT8(pred_cluster_ob_interface), HAILO_INVALID_HEF,
+                "Failed to parse HEF. Invalid pred_cluster_ob_interface: {}.", pred_cluster_ob_interface);
+            CHECK_AS_EXPECTED(IS_FIT_IN_UINT8(succ_prepost_ob_index), HAILO_INVALID_HEF,
+                "Failed to parse HEF. Invalid succ_prepost_ob_index: {}.", succ_prepost_ob_index);
+            CHECK_AS_EXPECTED(IS_FIT_IN_UINT8(succ_prepost_ob_interface), HAILO_INVALID_HEF,
+                "Failed to parse HEF. Invalid succ_prepost_ob_interface: {}.", succ_prepost_ob_interface);
+            
+            return HEF_METADATA__build_nms_trigger(static_cast<uint8_t>(aggregator_index),
+                static_cast<uint8_t>(pred_cluster_ob_index), static_cast<uint8_t>(pred_cluster_ob_cluster_index),
+                static_cast<uint8_t>(pred_cluster_ob_interface), static_cast<uint8_t>(succ_prepost_ob_index),
+                static_cast<uint8_t>(succ_prepost_ob_interface));
+        }
+        case ProtoHEFTrigger::kTriggerDmaIdle:
+        {
+            const auto stream_index = proto_trigger.trigger_dma_idle().shmifo_index();
+            CHECK_AS_EXPECTED(IS_FIT_IN_UINT8(stream_index), HAILO_INVALID_HEF,
+                "Failed to parse HEF. Invalid stream_index: {}.", stream_index);
+            return HEF_METADATA__build_dma_idle_trigger(static_cast<uint8_t>(stream_index));
+        }
+        default:
+            LOGGER__ERROR("Invalid trigger");
+            break;
+    }
+
+    // Deafult case
+    return make_unexpected(HAILO_INTERNAL_FAILURE);
+}
+
+Expected<ContextSwitchTrigger> ContextSwitchTrigger::create(const ProtoHEFTrigger &proto_trigger)
+{
+    auto serialized_trigger = serialize(proto_trigger);
+    CHECK_EXPECTED(serialized_trigger);
+    return ContextSwitchTrigger(serialized_trigger.release());
+}
+
+ContextSwitchTrigger::ContextSwitchTrigger(CONTROL_PROTOCOL__TRIGGER_t &&serialized_trigger) :
+    m_serialized_trigger(std::move(serialized_trigger))
+{}
+
+CONTROL_PROTOCOL__TRIGGER_t ContextSwitchTrigger::get_serialized() const
+{
+    return m_serialized_trigger;
+}
+
+hailo_status ContextSwitchTrigger::add_to_trigger_group(CONTROL_PROTOCOL__context_switch_context_info_t *context_info,
+    uint8_t **context_meta_data_head_pointer) const
+{
+    return HEF_METADATA__add_trigger_to_trigger_group(context_info, context_meta_data_head_pointer,
+        &m_serialized_trigger);
+}
+
+Expected<ContextSwitchConfigActionPtr> ContextSwitchConfigAction::create(const ProtoHEFAction &proto_action, Device &device,
+    std::vector<ConfigResources> &config, const ResourcesManager &resources_manager, const ProtoHEFNetworkGroup &net_group,
+    bool support_pre_fetch)
+{
+    switch (proto_action.action_case()) {
+        case ProtoHEFAction::kWriteDataCcw:
+            return WriteDataCcwAction::create(proto_action, config, support_pre_fetch);
+
+        case ProtoHEFAction::kWriteData:
+            return WriteDataAction::create(proto_action, device);
+
+        case ProtoHEFAction::kDisableLcu:
+            return DisableLcuAction::create(proto_action);
+
+        case ProtoHEFAction::kEnableLcu:
+            return EnableLcuAction::create(proto_action, resources_manager, net_group);
+
+        case ProtoHEFAction::kEnableSequencer:
+            return EnableSequencerAction::create(proto_action);
+        
+        case ProtoHEFAction::kNone:
+            return NoneAction::create();
+
+        case ProtoHEFAction::kWaitForSeqeuncer:
+            return WaitForSeqeuncerAction::create(proto_action);
+
+        case ProtoHEFAction::kWriteCompressedData:
+            LOGGER__ERROR("Action 'WriteCompressedData' is not supported");
+            return make_unexpected(HAILO_NOT_IMPLEMENTED);
+
+        case ProtoHEFAction::kAllowInputDataflow:
+            return AllowInputDataflowAction::create(proto_action);
+
+        case ProtoHEFAction::kWaitForModuleConfigDone:
+            return WaitForModuleConfigDoneAction::create(proto_action);
+
+        default:
+            LOGGER__ERROR("Invalid action");
+            break;
+    }
+
+    // Default case
+    return make_unexpected(HAILO_INTERNAL_FAILURE);
+}
+
+ContextSwitchConfigAction::ContextSwitchConfigAction(Type type) :
+    ContextSwitchConfigAction(type, CONTROL_PROTOCOL__CONTEXT_SWITCH_ACTION_COUNT, ProtoHEFAction::default_instance())
+{}
+
+ContextSwitchConfigAction::ContextSwitchConfigAction(Type type, const ProtoHEFAction& proto_action) :
+    ContextSwitchConfigAction(type, CONTROL_PROTOCOL__CONTEXT_SWITCH_ACTION_COUNT, proto_action)
+{}
+
+ContextSwitchConfigAction::ContextSwitchConfigAction(Type type, CONTROL_PROTOCOL__ACTION_TYPE_t action_list_type) :
+    ContextSwitchConfigAction(type, action_list_type, ProtoHEFAction::default_instance())
+{}
+
+ContextSwitchConfigAction::ContextSwitchConfigAction(Type type,
+                                                     CONTROL_PROTOCOL__ACTION_TYPE_t action_list_type,
+                                                     const ProtoHEFAction& proto_action) :
+    m_type(type),
+    m_action_list_type(action_list_type),
+    m_is_in_repeated_block(false),
+    m_proto_action(proto_action)
+{}
+
+ContextSwitchConfigAction::Type ContextSwitchConfigAction::get_type() const
+{
+    return m_type;
+}
+
+CONTROL_PROTOCOL__ACTION_TYPE_t ContextSwitchConfigAction::get_action_list_type() const
+{
+    return m_action_list_type;
+}
+
+const ProtoHEFAction &ContextSwitchConfigAction::get_proto_action() const
+{
+    return m_proto_action;
+}
+
+void ContextSwitchConfigAction::set_is_in_repeated_block(bool is_repeated)
+{
+    m_is_in_repeated_block = is_repeated;
+}
+
+Expected<ContextSwitchConfigActionPtr> NoneAction::create()
+{
+    auto result = ContextSwitchConfigActionPtr(new (std::nothrow) NoneAction());
+    CHECK_AS_EXPECTED((nullptr != result), HAILO_OUT_OF_HOST_MEMORY);
+    return result;
+}
+
+NoneAction::NoneAction() :
+    ContextSwitchConfigAction(Type::None)
+{}
+
+hailo_status NoneAction::execute(CONTROL_PROTOCOL__context_switch_context_info_t *, uint8_t **)
+{
+    // Do nothing
+    return HAILO_SUCCESS;
+}
+
+bool NoneAction::supports_repeated_block() const
+{
+    // None actions are ignored and aren't written to the FW's action list. Hence they can't be part of a repeated block.
+    return false;
+}
+
+Expected<ContextSwitchConfigActionPtr> WriteDataAction::create(const ProtoHEFAction& proto_action, Device &device)
+{
+    auto result = ContextSwitchConfigActionPtr(new (std::nothrow) WriteDataAction(proto_action, device));
+    CHECK_AS_EXPECTED((nullptr != result), HAILO_OUT_OF_HOST_MEMORY);
+    return result;
+}
+
+WriteDataAction::WriteDataAction(const ProtoHEFAction& proto_action, Device &device) :
+    ContextSwitchConfigAction(Type::WriteData, proto_action),
+    m_device(device)
+{}
+
+hailo_status WriteDataAction::execute(CONTROL_PROTOCOL__context_switch_context_info_t *, uint8_t **)
+{
+    return m_device.write_memory(static_cast<uint32_t>(m_proto_action.write_data().address()),
+        MemoryView::create_const(m_proto_action.write_data().data().data(), m_proto_action.write_data().data().length()));
+}
+
+bool WriteDataAction::supports_repeated_block() const
+{
+    // WriteDataActions aren't written to the FW's action list. Hence they can't be part of a repeated block.
+    return false;
+}
+
+Expected<ContextSwitchConfigActionPtr> WriteDataCcwAction::create(const ProtoHEFAction& proto_action,
+    std::vector<ConfigResources> &config, bool support_pre_fetch)
+{
+    // Add buffer to cfg_ch buffer without making descriptors (saving offset as the total data so far)
+    CHECK_AS_EXPECTED(proto_action.write_data_ccw().cfg_channel_index() < config.size(), HAILO_INVALID_HEF,
+        "cfg_channel index {} is bigger than config channels count {}",
+        proto_action.write_data_ccw().cfg_channel_index(), config.size());
+    CHECK_AS_EXPECTED(IS_FIT_IN_UINT8(proto_action.write_data_ccw().cfg_channel_index()), HAILO_INVALID_HEF,
+        "Invalid cfg channel index");
+    
+    auto result = ContextSwitchConfigActionPtr(new (std::nothrow) WriteDataCcwAction(proto_action, config,
+        support_pre_fetch));
+    CHECK_AS_EXPECTED((nullptr != result), HAILO_OUT_OF_HOST_MEMORY);
+    return result;
+}
+
+WriteDataCcwAction::WriteDataCcwAction(const ProtoHEFAction& proto_action, std::vector<ConfigResources> &config,
+    bool support_pre_fetch) :
+    ContextSwitchConfigAction(Type::WriteDataCcw, proto_action),
+    m_config(config),
+    m_support_pre_fetch(support_pre_fetch)
+{}
+
+bool WriteDataCcwAction::should_pad_with_nops()
+{
+    if (!m_support_pre_fetch) {
+        return false;
+    }
+
+    auto write_size = m_proto_action.write_data_ccw().data().length();
+    auto cfg_channel = m_proto_action.write_data_ccw().cfg_channel_index();
+    /* If last operation in context - Add nops to fill the buffer */
+    if ((write_size + m_config[cfg_channel].get_current_buffer_size()) != m_config[cfg_channel].get_total_cfg_size()) {
+        return false;
+    }
+
+    return true;
+}
+
+hailo_status WriteDataCcwAction::pad_with_nops(uint8_t cfg_channel_index)
+{
+    auto page_size = m_config[cfg_channel_index].get_page_size();
+    auto buffer_size = m_config[cfg_channel_index].get_total_cfg_size();
+    auto buffer_residue = buffer_size % page_size;
+    if (0 != buffer_residue % CCW_HEADER_SIZE) {
+        LOGGER__ERROR("CFG channel buffer size must be a multiple of CCW header size ({})", CCW_HEADER_SIZE);
+        return HAILO_INTERNAL_FAILURE;
+    }
+    /* If buffer does not fit info descriptor, the host must pad the buffer with CCW NOPs. */
+    auto nop_count = (buffer_residue == 0) ? 0 : ((page_size - buffer_residue) / CCW_HEADER_SIZE);
+    for (uint8_t nop_index = 0; nop_index < nop_count; nop_index++) {
+        /* Generate nop tranaction.
+           CCW of all zeros (64’h0) should be treated as NOP – ignore CCW and expect CCW in next 64b word. 
+           When CSM recognize it is a NOP it pops it from the channel FIFO without forward any address/data/command, 
+           does not contribute to CRC calculations but return credits to the peripheral as usual. */
+        m_config[cfg_channel_index].write(reinterpret_cast<const void *>(&CCW_NOP), sizeof(CCW_NOP));
+    }
+
+    return HAILO_SUCCESS;
+
+}
+
+hailo_status WriteDataCcwAction::execute(CONTROL_PROTOCOL__context_switch_context_info_t *, uint8_t **)
+{
+    if (should_pad_with_nops()) {
+        auto status = pad_with_nops(static_cast<uint8_t>(m_proto_action.write_data_ccw().cfg_channel_index()));
+        CHECK_SUCCESS(status);
+    }
+
+    return m_config[m_proto_action.write_data_ccw().cfg_channel_index()].write(
+        m_proto_action.write_data_ccw().data().data(), m_proto_action.write_data_ccw().data().length());
+}
+
+bool WriteDataCcwAction::supports_repeated_block() const
+{
+    // WriteDataCcwActions aren't written to the FW's action list. Hence they can't be part of a repeated block.
+    return false;
+}
+
+Expected<ContextSwitchConfigActionPtr> CreateConfigDescAndFetchAction::create(
+    std::vector<ConfigResources> &config_resources, const std::set<uint8_t> &pending_cfg_ch_buffer, 
+    const std::vector<uint16_t> &ccw_bursts, bool support_pre_fetch)
+{
+    CHECK_AS_EXPECTED(pending_cfg_ch_buffer.size() > 0, HAILO_INTERNAL_FAILURE,
+        "Expected 1 or more pending_cfg_ch_buffer, however 0 were found");
+    CHECK_AS_EXPECTED(ccw_bursts.size() == config_resources.size(), HAILO_INTERNAL_FAILURE,
+        "CCW burst vector size must match the config resources size");
+
+    auto result = ContextSwitchConfigActionPtr(new (std::nothrow) CreateConfigDescAndFetchAction(config_resources, 
+        pending_cfg_ch_buffer, ccw_bursts, support_pre_fetch));
+    CHECK_AS_EXPECTED((nullptr != result), HAILO_OUT_OF_HOST_MEMORY);
+    return result;
+}
+
+CreateConfigDescAndFetchAction::CreateConfigDescAndFetchAction(std::vector<ConfigResources> &config, const std::set<uint8_t> &pending_cfg_ch_buffer,
+        const std::vector<uint16_t> &ccw_bursts, bool support_pre_fetch) :
+    ContextSwitchConfigAction(Type::CreateDescForCcw),
+    m_config(config),
+    m_pending_cfg_ch_buffer(pending_cfg_ch_buffer),
+    m_ccw_bursts(ccw_bursts),
+    m_support_pre_fetch(support_pre_fetch)
+{}
+
+hailo_status CreateConfigDescAndFetchAction::execute(CONTROL_PROTOCOL__context_switch_context_info_t *context_info,
+    uint8_t **context_meta_data_head_pointer)
+{
+    for (auto cfg_channel_index : m_pending_cfg_ch_buffer) {
+
+        CHECK(cfg_channel_index < m_ccw_bursts.size(), HAILO_INTERNAL_FAILURE,
+            "cfg_channel_index vector size must then m_ccw_burst size");
+
+        const auto desc_count = m_config[cfg_channel_index].program_descriptors();
+        CHECK_EXPECTED_AS_STATUS(desc_count);
+
+        if (m_support_pre_fetch) {
+            auto status = HEF_METADATA__add_ccw_bursts_action(context_info, context_meta_data_head_pointer, 
+                static_cast<uint16_t>(m_ccw_bursts[cfg_channel_index]), cfg_channel_index, m_is_in_repeated_block);
+            CHECK_SUCCESS(status);
+        } else {
+            auto status = HEF_METADATA__add_read_vdma_action(context_info, context_meta_data_head_pointer, desc_count.value(),
+                cfg_channel_index, m_is_in_repeated_block);
+            CHECK_SUCCESS(status);
+        }
+    }
+
+    return HAILO_SUCCESS;
+}
+
+bool CreateConfigDescAndFetchAction::supports_repeated_block() const
+{
+    // TODO: Each CreateConfigDescAndFetchAction may contain multiple HEF_METADATA__add_read_vdma_action.
+    //       They could be part of a repated block, but the curent logic in the hef module assumes that
+    //       only one context switch action is written to the fw per ContextSwitchConfigAction instance.
+    //       Hence this isn't supported.
+    return false;
+}
+
+Expected<ContextSwitchConfigActionPtr> RepeatedHeaderAction::create(
+    CONTROL_PROTOCOL__ACTION_TYPE_t sub_action_type, uint8_t num_actions)
+{
+    CHECK_AS_EXPECTED(sub_action_type != CONTROL_PROTOCOL__CONTEXT_SWITCH_ACTION_ADD_REPEATED, HAILO_INVALID_HEF,
+        "Invalid repeated sub-action type (can't have sub-action with type CONTROL_PROTOCOL__CONTEXT_SWITCH_ACTION_ADD_REPEATED)");
+    CHECK_AS_EXPECTED(sub_action_type != CONTROL_PROTOCOL__CONTEXT_SWITCH_ACTION_COUNT, HAILO_INVALID_HEF,
+        "Invalid repeated sub-action type (can't have sub-action with type CONTROL_PROTOCOL__CONTEXT_SWITCH_ACTION_COUNT)");
+    CHECK_AS_EXPECTED(num_actions != 0, HAILO_INVALID_HEF, "Invalid sub-action count (must be greater than zero)");
+    
+    auto result = ContextSwitchConfigActionPtr(new (std::nothrow) RepeatedHeaderAction(
+        sub_action_type, num_actions));
+    CHECK_AS_EXPECTED((nullptr != result), HAILO_OUT_OF_HOST_MEMORY);
+    return result;
+}
+
+RepeatedHeaderAction::RepeatedHeaderAction(CONTROL_PROTOCOL__ACTION_TYPE_t sub_action_type,
+                                           uint8_t num_actions) :
+    ContextSwitchConfigAction(Type::AddRrepeated, CONTROL_PROTOCOL__CONTEXT_SWITCH_ACTION_ADD_REPEATED),
+    m_sub_action_type(sub_action_type),
+    m_num_actions(num_actions)
+{}
+
+hailo_status RepeatedHeaderAction::execute(CONTROL_PROTOCOL__context_switch_context_info_t *context_info,
+    uint8_t **context_meta_data_head_pointer)
+{
+    return HEF_METADATA__add_repeated_header_action(context_info, context_meta_data_head_pointer,
+        m_sub_action_type, m_num_actions);
+}
+
+bool RepeatedHeaderAction::supports_repeated_block() const
+{
+    // RepeatedHeaderActions can't be part of a repated block themselves
+    return false;
+}
+
+Expected<ContextSwitchConfigActionPtr> DisableLcuAction::create(const ProtoHEFAction& proto_action)
+{
+    CHECK_AS_EXPECTED(IS_FIT_IN_UINT8(proto_action.disable_lcu().cluster_index()), HAILO_INVALID_HEF,
+        "Failed to parse HEF. Invalid cluster_index: {}.", proto_action.disable_lcu().cluster_index());
+    CHECK_AS_EXPECTED(IS_FIT_IN_UINT8(proto_action.disable_lcu().lcu_index()), HAILO_INVALID_HEF,
+        "Failed to parse HEF. Invalid lcu_index: {}", proto_action.disable_lcu().lcu_index());
+    
+    auto result = ContextSwitchConfigActionPtr(new (std::nothrow) DisableLcuAction(proto_action));
+    CHECK_AS_EXPECTED((nullptr != result), HAILO_OUT_OF_HOST_MEMORY);
+    return result;
+}
+
+DisableLcuAction::DisableLcuAction(const ProtoHEFAction& proto_action) :
+    ContextSwitchConfigAction(Type::DisableLcu, CONTROL_PROTOCOL__CONTEXT_SWITCH_ACTION_DISABLE_LCU, proto_action)
+{}
+
+hailo_status DisableLcuAction::execute(CONTROL_PROTOCOL__context_switch_context_info_t *context_info,
+    uint8_t **context_meta_data_head_pointer)
+{
+    return HEF_METADATA__add_disable_lcu_action(context_info, context_meta_data_head_pointer,
+        static_cast<uint8_t>(m_proto_action.disable_lcu().cluster_index()),
+        static_cast<uint8_t>(m_proto_action.disable_lcu().lcu_index()), m_is_in_repeated_block);
+}
+
+bool DisableLcuAction::supports_repeated_block() const
+{
+    return true;
+}
+
+Expected<ContextSwitchConfigActionPtr> EnableLcuAction::create(const ProtoHEFAction& proto_action,
+    const ResourcesManager &resources_manager, const ProtoHEFNetworkGroup &net_group)
+{
+    CHECK_AS_EXPECTED(IS_FIT_IN_UINT8(proto_action.enable_lcu().cluster_index()), HAILO_INVALID_HEF,
+        "Failed to parse HEF. Invalid cluster_index: {}.", proto_action.enable_lcu().cluster_index());
+    CHECK_AS_EXPECTED(IS_FIT_IN_UINT8(proto_action.enable_lcu().lcu_index()), HAILO_INVALID_HEF,
+        "Failed to parse HEF. Invalid lcu_index: {}.", proto_action.enable_lcu().lcu_index());
+    CHECK_AS_EXPECTED(IS_FIT_IN_UINT16(proto_action.enable_lcu().lcu_kernel_done_address()), HAILO_INVALID_HEF,
+        "Failed to parse HEF. Invalid lcu_kernel_done_address: {}.", proto_action.enable_lcu().lcu_kernel_done_address());
+
+    auto support_multi_networks = resources_manager.get_supported_features().multi_network_support;
+    auto network_index = static_cast<uint8_t>((support_multi_networks) ? proto_action.enable_lcu().network_index() : 0);
+    auto partial_network_name = HefUtils::get_partial_network_name_by_index(net_group, network_index,
+        resources_manager.get_supported_features());
+    CHECK_EXPECTED(partial_network_name);
+    auto batch_size = resources_manager.get_network_batch_size_from_partial_name(partial_network_name.value());
+    CHECK_EXPECTED(batch_size);
+
+    const auto kernel_done_address = static_cast<uint16_t>(proto_action.enable_lcu().lcu_kernel_done_address());
+    const auto kernel_done_count = static_cast<uint32_t>(proto_action.enable_lcu().lcu_kernel_done_count());
+    const auto is_default = (CONTEXT_SWITCH_DEFS__ENABLE_LCU_DEFAULT_KERNEL_ADDRESS == kernel_done_address) &&
+        (CONTEXT_SWITCH_DEFS__ENABLE_LCU_DEFAULT_KERNEL_COUNT == kernel_done_count) &&
+        (CONTEXT_SWITCH_DEFS__ENABLE_LCU_DEFAULT_BATCH_SIZE == batch_size.value());
+    
+    auto result = ContextSwitchConfigActionPtr(new (std::nothrow) EnableLcuAction(proto_action, is_default, network_index));
+    CHECK_AS_EXPECTED((nullptr != result), HAILO_OUT_OF_HOST_MEMORY);
+    return result;
+}
+
+CONTROL_PROTOCOL__ACTION_TYPE_t EnableLcuAction::get_enable_lcu_action_type(bool is_default)
+{
+    return is_default ? static_cast<CONTROL_PROTOCOL__ACTION_TYPE_t>(CONTROL_PROTOCOL__CONTEXT_SWITCH_ACTION_ENABLE_LCU_DEFAULT) :
+        static_cast<CONTROL_PROTOCOL__ACTION_TYPE_t>(CONTROL_PROTOCOL__CONTEXT_SWITCH_ACTION_ENABLE_LCU_NON_DEFAULT);
+}
+
+ContextSwitchConfigAction::Type EnableLcuAction::get_enable_lcu_type(bool is_default)
+{
+    return is_default ? Type::EnableLcuDefault : Type::EnableLcuNonDefault;
+}
+
+EnableLcuAction::EnableLcuAction(const ProtoHEFAction& proto_action, bool is_default, uint8_t network_index) :
+    ContextSwitchConfigAction(get_enable_lcu_type(is_default), get_enable_lcu_action_type(is_default), proto_action),
+    m_network_index(network_index),
+    m_is_default(is_default)
+{}
+
+hailo_status EnableLcuAction::execute(CONTROL_PROTOCOL__context_switch_context_info_t *context_info,
+    uint8_t **context_meta_data_head_pointer)
+{
+    const auto cluster_index = static_cast<uint8_t>(m_proto_action.enable_lcu().cluster_index());
+    const auto lcu_index = static_cast<uint8_t>(m_proto_action.enable_lcu().lcu_index());
+    if (m_is_default) {
+        return HEF_METADATA__add_enable_lcu_default_action(context_info, context_meta_data_head_pointer,
+            cluster_index, lcu_index, m_is_in_repeated_block);
+    } else {
+        const auto kernel_done_address = static_cast<uint16_t>(m_proto_action.enable_lcu().lcu_kernel_done_address());
+        const auto kernel_done_count = static_cast<uint32_t>(m_proto_action.enable_lcu().lcu_kernel_done_count());
+        return HEF_METADATA__add_enable_lcu_non_default_action(context_info, context_meta_data_head_pointer,
+            cluster_index, lcu_index, kernel_done_address, kernel_done_count, m_network_index, m_is_in_repeated_block);
+    }
+}
+
+bool EnableLcuAction::supports_repeated_block() const
+{
+    return true;
+}
+
+Expected<ContextSwitchConfigActionPtr> EnableSequencerAction::create(const ProtoHEFAction& proto_action)
+{
+    CHECK_AS_EXPECTED(IS_FIT_IN_UINT8(proto_action.enable_sequencer().cluster_index()), HAILO_INVALID_HEF,
+        "Failed to parse HEF. Invalid cluster_index: {}.", proto_action.enable_sequencer().cluster_index());
+
+    // TODO: Remove when impolemeted in the hef.proto
+    uint64_t l2_offset_0 = 0;
+    uint64_t l2_offset_1 = 0;
+    // TODO: Change the CONTEXT_SWITCH__add_enable_sequencer_proto_action func to receive 4 'l2_offset' params
+    l2_offset_0 |= (uint64_t)(proto_action.enable_sequencer().l2_write_0());
+    l2_offset_0 |= ((uint64_t)(proto_action.enable_sequencer().l2_write_1()) << 32);
+    l2_offset_1 |= (uint64_t)(proto_action.enable_sequencer().l2_write_2());
+    l2_offset_1 |= ((uint64_t)(proto_action.enable_sequencer().l2_write_3()) << 32);
+
+    uint8_t initial_l3_cut = 0;
+    uint16_t initial_l3_offset = 0;
+    if (proto_action.enable_sequencer().initial_l3_info().includes_initial_l3_info()) {
+        const auto &initial_l3_info = proto_action.enable_sequencer().initial_l3_info();
+        CHECK_AS_EXPECTED(IS_FIT_IN_UINT8(initial_l3_info.initial_l3_index()), HAILO_INVALID_HEF,
+            "Initial l3 cut {} is out of range", initial_l3_info.initial_l3_index());
+        CHECK_AS_EXPECTED(IS_FIT_IN_UINT16(initial_l3_info.initial_l3_offset()), HAILO_INVALID_HEF,
+            "Initial l3 offset {} is out of range", initial_l3_info.initial_l3_offset());
+        initial_l3_cut = static_cast<uint8_t>(initial_l3_info.initial_l3_index());
+        initial_l3_offset = static_cast<uint16_t>(initial_l3_info.initial_l3_offset());
+    }
+    else {
+        // Legacy mode should work only on hailo8
+        std::tie(initial_l3_cut, initial_l3_offset) = old_hef_parse_initial_l3(proto_action.enable_sequencer().initial_l3_legacy());
+    }
+
+    auto result = ContextSwitchConfigActionPtr(new (std::nothrow) EnableSequencerAction(
+        proto_action,
+        initial_l3_cut,
+        initial_l3_offset,
+        l2_offset_0,
+        l2_offset_1
+    ));
+    CHECK_AS_EXPECTED((nullptr != result), HAILO_OUT_OF_HOST_MEMORY);
+    return result;
+}
+
+EnableSequencerAction::EnableSequencerAction(const ProtoHEFAction& proto_action, uint8_t initial_l3_cut, uint16_t initial_l3_offset,
+                                             uint64_t l2_offset_0, uint64_t l2_offset_1) :
+    ContextSwitchConfigAction(Type::TriggerSequencer, CONTROL_PROTOCOL__CONTEXT_SWITCH_ACTION_TRIGGER_SEQUENCER, proto_action),
+    m_initial_l3_cut(initial_l3_cut),
+    m_initial_l3_offset(initial_l3_offset),
+    m_l2_offset_0(l2_offset_0),
+    m_l2_offset_1(l2_offset_1)
+{}
+
+hailo_status EnableSequencerAction::execute(CONTROL_PROTOCOL__context_switch_context_info_t *context_info,
+    uint8_t **context_meta_data_head_pointer)
+{
+    const auto cluster_index = static_cast<uint8_t>(m_proto_action.enable_sequencer().cluster_index());
+    const auto active_apu_bitmap = static_cast<uint32_t>(m_proto_action.enable_sequencer().active_apu_bitmap());
+    const auto active_ia_bitmap = static_cast<uint32_t>(m_proto_action.enable_sequencer().active_ia_bitmap());
+    const auto active_sc_bitmap = static_cast<uint64_t>(m_proto_action.enable_sequencer().active_sc_bitmap());
+    const auto active_l2_bitmap = static_cast<uint64_t>(m_proto_action.enable_sequencer().active_l2_bitmap());
+    return HEF_METADATA__add_enable_sequencer_action(context_info, context_meta_data_head_pointer, 
+        cluster_index, m_initial_l3_cut, m_initial_l3_offset, active_apu_bitmap, active_ia_bitmap,
+        active_sc_bitmap, active_l2_bitmap, m_l2_offset_0, m_l2_offset_1, m_is_in_repeated_block);
+}
+
+bool EnableSequencerAction::supports_repeated_block() const
+{
+    return true;
+}
+
+Expected<ContextSwitchConfigActionPtr> WaitForSeqeuncerAction::create(const ProtoHEFAction& proto_action)
+{
+    CHECK_AS_EXPECTED(IS_FIT_IN_UINT8(proto_action.wait_for_seqeuncer().cluster_index()), HAILO_INVALID_HEF,
+        "Failed to parse HEF. Invalid cluster_index: {}.", proto_action.wait_for_seqeuncer().cluster_index());
+    
+    auto result = ContextSwitchConfigActionPtr(new (std::nothrow) WaitForSeqeuncerAction(proto_action));
+    CHECK_AS_EXPECTED((nullptr != result), HAILO_OUT_OF_HOST_MEMORY);
+    return result;
+}
+
+WaitForSeqeuncerAction::WaitForSeqeuncerAction(const ProtoHEFAction& proto_action) :
+    ContextSwitchConfigAction(Type::WaitForSequencerDone, CONTROL_PROTOCOL__CONTEXT_SWITCH_ACTION_WAIT_FOR_SEQUENCER_DONE, proto_action)
+{}
+
+hailo_status WaitForSeqeuncerAction::execute(CONTROL_PROTOCOL__context_switch_context_info_t *context_info,
+    uint8_t **context_meta_data_head_pointer)
+{
+    return HEF_METADATA__add_wait_for_sequencer_action(context_info, context_meta_data_head_pointer,
+        static_cast<uint8_t>(m_proto_action.wait_for_seqeuncer().cluster_index()), m_is_in_repeated_block);
+}
+
+bool WaitForSeqeuncerAction::supports_repeated_block() const
+{
+    // Wait actions shouldn't be repeated (for easier debugging)
+    return false;
+}
+
+Expected<ContextSwitchConfigActionPtr> AllowInputDataflowAction::create(const ProtoHEFAction& proto_action)
+{
+    CHECK_AS_EXPECTED(IS_FIT_IN_UINT8(proto_action.allow_input_dataflow().sys_index()), HAILO_INVALID_HEF,
+        "Failed to parse HEF. Invalid sys_index: {}.", proto_action.allow_input_dataflow().sys_index());
+    
+    auto result = ContextSwitchConfigActionPtr(new (std::nothrow) AllowInputDataflowAction(proto_action));
+    CHECK_AS_EXPECTED((nullptr != result), HAILO_OUT_OF_HOST_MEMORY);
+    return result;
+}
+
+ContextSwitchConfigAction::Type AllowInputDataflowAction::get_input_dataflow_action_type(const ProtoHEFAction& proto_action)
+{
+    if (ProtoHEFEdgeConnectionType::PROTO__EDGE_CONNECTION_TYPE__DDR == proto_action.allow_input_dataflow().connection_type()) {
+        return Type::TriggerNewDataFromDataInputDdr;
+    }
+    return Type::TriggerNewDataFromDataInput;
+}
+
+AllowInputDataflowAction::AllowInputDataflowAction(const ProtoHEFAction& proto_action) :
+    ContextSwitchConfigAction(get_input_dataflow_action_type(proto_action),
+                              CONTROL_PROTOCOL__CONTEXT_SWITCH_ACTION_TRIGGER_NEW_DATA_FROM_DATA_INPUT, proto_action)
+{}
+
+hailo_status AllowInputDataflowAction::execute(CONTROL_PROTOCOL__context_switch_context_info_t *context_info,
+    uint8_t **context_meta_data_head_pointer)
+{
+    // DDR threads are implemented on HailoRT so no FW action is required
+    if (Type::TriggerNewDataFromDataInputDdr == m_type) {
+        return HAILO_SUCCESS;
+    }
+
+    return HEF_METADATA__add_fetch_new_data_action(context_info, context_meta_data_head_pointer,
+        static_cast<uint8_t>(m_proto_action.allow_input_dataflow().sys_index()), m_is_in_repeated_block);
+}
+
+bool AllowInputDataflowAction::supports_repeated_block() const
+{
+    // DDR threads are implemented on HailoRT so no FW action is required. Hence they can't be part of a repeated block.
+    if (Type::TriggerNewDataFromDataInputDdr == m_type) {
+        return false;
+    }
+
+    return true;
+}
+
+Expected<ContextSwitchConfigActionPtr> WaitForModuleConfigDoneAction::create(const ProtoHEFAction& proto_action)
+{
+    CHECK_AS_EXPECTED(IS_FIT_IN_UINT8(proto_action.wait_for_module_config_done().index()), HAILO_INVALID_HEF,
+        "Failed to parse HEF. Invalid index: {}", proto_action.wait_for_module_config_done().index());
+
+    auto result = ContextSwitchConfigActionPtr(new (std::nothrow) WaitForModuleConfigDoneAction(proto_action));
+    CHECK_AS_EXPECTED((nullptr != result), HAILO_OUT_OF_HOST_MEMORY);
+    return result;
+}
+
+WaitForModuleConfigDoneAction::WaitForModuleConfigDoneAction(const ProtoHEFAction& proto_action) :
+    ContextSwitchConfigAction(Type::WaitForModuleConfigDone, CONTROL_PROTOCOL__CONTEXT_SWITCH_ACTION_WAIT_FOR_MODULE_CONFIG_DONE, proto_action)
+{}
+
+hailo_status WaitForModuleConfigDoneAction::execute(CONTROL_PROTOCOL__context_switch_context_info_t *context_info,
+    uint8_t **context_meta_data_head_pointer)
+{
+    return HEF_METADATA__add_wait_for_module_config_done_action(context_info, context_meta_data_head_pointer,
+        static_cast<uint8_t>(m_proto_action.wait_for_module_config_done().index()), m_is_in_repeated_block);
+}
+
+bool WaitForModuleConfigDoneAction::supports_repeated_block() const
+{
+    // Wait actions shouldn't be repeated (for easier debugging)
+    return false;
+}
+
+Expected<std::string> Hef::Impl::get_vstream_name_from_original_name_mux(const std::string &original_name, const ProtoHefEdge &layer)
+{
+    switch (layer.edge_case()) {
+        case ProtoHefEdge::kLayerInfo:
+            for (const auto &name : layer.layer_info().original_names()) {
+                if (original_name == name) {
+                    return std::string(layer.layer_info().name());
+                }
+            }
+            return make_unexpected(HAILO_NOT_FOUND);
+        case ProtoHefEdge::kLayerMux:
+            for (const auto &pred : layer.layer_mux().predecessors()) {
+                auto res = get_vstream_name_from_original_name_mux(original_name, pred);
+                if (res) {
+                    return std::move(res.value());
+                }
+            }
+            return make_unexpected(HAILO_NOT_FOUND);
+        default:
+            LOGGER__ERROR("Invalid layer type");
+            return make_unexpected(HAILO_INTERNAL_FAILURE);
+    }
+}
+
+Expected<std::string> Hef::Impl::get_vstream_name_from_original_name(const std::string &original_name,
+    const std::string &net_group_name)
+{
+    auto net_group = get_net_group_by_name(net_group_name);
+    CHECK_EXPECTED(net_group);
+
+    std::string results;
+
+    for (const auto &context : net_group.value()->contexts()) {
+        for (const auto &layer_info : context.metadata().edge_layers()) {
+            if ((is_h2d_boundary_info_layer(layer_info)) || (is_d2h_boundary_info_layer(layer_info))) {
+                for (auto &name : layer_info.layer_info().original_names()) {
+                    if (original_name == name) {
+                        CHECK_AS_EXPECTED(results.empty(), HAILO_INVALID_HEF, "Original name {} appears more than once in the HEF.", original_name);
+                        results = std::string(layer_info.layer_info().name());
+                    }
+                }
+            } else if(is_d2h_boundary_mux_layer(layer_info)) {
+                for (auto &pred : layer_info.layer_mux().predecessors()) {
+                    auto stream_name = get_vstream_name_from_original_name_mux(original_name, pred);
+                    if (stream_name) {
+                        CHECK_AS_EXPECTED(results.empty(), HAILO_INVALID_HEF, "Original name {} appears more than once in the HEF.", original_name);
+                        results = stream_name.value();
+                    }
+                }
+            }
+        }
+    }
+    CHECK_AS_EXPECTED(!results.empty(), HAILO_NOT_FOUND);
+    return results;
+}
+
+Expected<std::vector<std::string>> Hef::Impl::get_original_names_from_vstream_name_mux(const std::string &vstream_name, const ProtoHefEdge &layer)
+{
+    switch (layer.edge_case()) {
+    case ProtoHefEdge::kLayerInfo:
+    {
+        if (vstream_name == layer.layer_info().name()) {
+            std::vector<std::string> results;
+            for (const auto &name : layer.layer_info().original_names()) {
+                results.push_back(name);
+            }
+            return results;
+        }
+        return make_unexpected(HAILO_NOT_FOUND);
+    }
+    case ProtoHefEdge::kLayerMux:
+        for (const auto &pred : layer.layer_mux().predecessors()) {
+            auto res = get_original_names_from_vstream_name_mux(vstream_name, pred);
+            if (res) {
+                return std::move(res.value());
+            }
+        }
+        return make_unexpected(HAILO_NOT_FOUND);
+    default:
+        LOGGER__ERROR("Invalid layer type");
+        return make_unexpected(HAILO_INTERNAL_FAILURE);
+    }
+}
+
+Expected<std::vector<std::string>> Hef::Impl::get_original_names_from_vstream_name(const std::string &vstream_name,
+    const std::string &net_group_name)
+{
+    auto net_group = get_net_group_by_name(net_group_name);
+    CHECK_EXPECTED(net_group);
+
+    std::vector<std::string> results;
+
+    for (const auto &context : net_group.value()->contexts()) {
+        for (const auto &layer_info : context.metadata().edge_layers()) {
+            if ((is_h2d_boundary_info_layer(layer_info)) || (is_d2h_boundary_info_layer(layer_info))) {
+                if (vstream_name == layer_info.layer_info().name()) {
+                    for (const auto &name : layer_info.layer_info().original_names()) {
+                        results.push_back(name);
+                    }
+                    return results;
+                }
+            } else if(is_d2h_boundary_mux_layer(layer_info)) {
+                for (const auto &pred : layer_info.layer_mux().predecessors()) {
+                    auto names = get_original_names_from_vstream_name_mux(vstream_name, pred);
+                    if (names) {
+                        return std::move(names.value());
+                    }
+                }
+            }
+        }
+    }
+    return make_unexpected(HAILO_NOT_FOUND);
+}
+
+hailo_status Hef::Impl::validate_net_group_unique_layer_names(ProtoHEFNetworkGroupPtr net_group)
+{
+    CHECK_ARG_NOT_NULL(net_group);
+
+    std::set<std::string> edge_layer_names;
+    std::string layer_name;
+    for (auto &context : net_group->contexts()) {
+        for (auto &layer : context.metadata().edge_layers()) {
+            // TODO: remove check for boundary layer after fix will be pushed in SDK
+            if (ProtoHEFEdgeConnectionType::PROTO__EDGE_CONNECTION_TYPE__BOUNDARY ==
+                layer.context_switch_info().edge_connection_type()) {
+                if (ProtoHEFEdgeLayerType::PROTO__EDGE_LAYER_TYPE__INFO == layer.edge_layer_type()) {
+                    layer_name = layer.layer_info().name();
+                } else if (ProtoHEFEdgeLayerType::PROTO__EDGE_LAYER_TYPE__MUX == layer.edge_layer_type()) {
+                    layer_name = layer.layer_mux().name();
+                } else {
+                    LOGGER__ERROR("Invalid layer type.");
+                    return HAILO_INVALID_HEF;
+                }
+                CHECK(!contains(edge_layer_names, layer_name), HAILO_INVALID_HEF,
+                    "layer_name should be unique. {} appears more than once in the given network_group.",
+                    layer_name);
+                edge_layer_names.insert(layer_name);
+            }
+        }
+    }
+    return HAILO_SUCCESS;
+}
+
+hailo_status update_parsing_info(uint8_t cfg_index, uint32_t data_length, ConfigBufferInfoMap &results)
+{
+    CHECK(cfg_index < CONTROL_PROTOCOL__MAX_CFG_CHANNELS, HAILO_INVALID_HEF, "Invalid cfg_index");
+
+    if (contains(results, cfg_index)) {
+        results.at(cfg_index).push_back(data_length);
+        return HAILO_SUCCESS;
+    }
+
+    // If we got here, the current cfg_index's info is parsed for the first time
+    results.emplace(cfg_index, std::vector<uint32_t>(1, data_length));
+    return HAILO_SUCCESS;
+}
+
+Expected<ConfigBufferInfoMap> get_config_buffer_info(
+    const google::protobuf::RepeatedPtrField<ProtoHEFOperation> &operations)
+{
+    auto status = HAILO_UNINITIALIZED;
+    ConfigBufferInfoMap results;
+
+    for (const auto &operation : operations) {
+        for (const auto &action : operation.actions()) {
+            if (ProtoHEFAction::kWriteDataCcw == action.action_case()) {
+                CHECK_AS_EXPECTED(IS_FIT_IN_UINT8(action.write_data_ccw().cfg_channel_index()), HAILO_INVALID_HEF,
+                    "Invalid cfg index {}", action.write_data_ccw().cfg_channel_index());
+                status = update_parsing_info(static_cast<uint8_t>(action.write_data_ccw().cfg_channel_index()),
+                    static_cast<uint32_t>(action.write_data_ccw().data().length()), results);
+                CHECK_SUCCESS_AS_EXPECTED(status);
+            }
+        }
+    }
+    return results;
+}
+
+Expected<HefParsingInfo> Hef::Impl::get_parsing_info(ProtoHEFNetworkGroupPtr net_group)
+{
+    assert(nullptr != net_group);
+
+    // Parse preliminary config
+    auto preliminary_config_buffer_infos = get_config_buffer_info(net_group->preliminary_config().operation());
+    CHECK_EXPECTED(preliminary_config_buffer_infos);
+
+    HefParsingInfo parsing_info;
+    parsing_info.cfg_infos_preliminary_config = preliminary_config_buffer_infos.release();
+
+    // Parse dynamic contexts
+    for (const auto &context : net_group->contexts()) {
+        auto dynamic_ctxt_config_buffer_infos = get_config_buffer_info(context.operations());
+        CHECK_EXPECTED(dynamic_ctxt_config_buffer_infos);
+
+        parsing_info.cfg_infos_per_context.emplace_back(dynamic_ctxt_config_buffer_infos.release());
+    }
+
+    return parsing_info;
+}
+
+std::vector<std::string> Hef::get_network_groups_names()
+{
+    return pimpl->get_network_groups_names();
+}
+
+Expected<NetworkGroupsParamsMap> Hef::create_configure_params(hailo_stream_interface_t stream_interface)
+{
+    NetworkGroupsParamsMap results;
+    for (const auto &name : pimpl->get_network_groups_names()) {
+        auto params = create_configure_params(stream_interface, name);
+        CHECK_EXPECTED(params);
+        results.emplace(std::make_pair(name, params.release()));
+    }
+    return results;
+}
+
+Expected<ConfigureNetworkParams> Hef::create_configure_params(hailo_stream_interface_t stream_interface, const std::string &network_group_name)
+{
+    return pimpl->create_configure_params(stream_interface, network_group_name);
+}
+
+Expected<NetworkGroupsParamsMap> Hef::create_configure_params_mipi_input(hailo_stream_interface_t output_interface,
+    const hailo_mipi_input_stream_params_t &mipi_params)
+{
+    NetworkGroupsParamsMap results;
+    for (const auto &name : pimpl->get_network_groups_names()) {
+        auto params = create_configure_params_mipi_input(output_interface, mipi_params, name);
+        CHECK_EXPECTED(params);
+        results.emplace(std::make_pair(name, params.release()));
+    }
+    return results;
+}
+
+
+Expected<ConfigureNetworkParams> Hef::create_configure_params_mipi_input(hailo_stream_interface_t output_interface,
+    const hailo_mipi_input_stream_params_t &mipi_params, const std::string &network_group_name)
+{
+    return pimpl->create_configure_params_mipi_input(output_interface, mipi_params, network_group_name);
+}
+
+std::vector<std::string> Hef::Impl::get_network_groups_names()
+{
+    std::vector<std::string> results;
+    results.reserve(m_groups.size());
+
+    for (const auto &net_group : m_groups) {
+        results.push_back(net_group->network_group_metadata().network_group_name());
+    }
+    return results;
+}
+
+Expected<std::vector<hailo_network_group_info_t>> Hef::get_network_groups_infos()
+{
+    return pimpl->get_network_groups_infos();
+}
+
+Expected<std::vector<hailo_network_group_info_t>> Hef::Impl::get_network_groups_infos()
+{
+    std::vector<hailo_network_group_info_t> results;
+    results.reserve(m_groups.size());
+
+    for (const auto &net_group : m_groups) {
+        hailo_network_group_info_t info = {};
+        auto &network_group_name = net_group->network_group_metadata().network_group_name();
+        CHECK_AS_EXPECTED(HAILO_MAX_NETWORK_GROUP_NAME_SIZE >= (network_group_name.length() + 1), HAILO_INTERNAL_FAILURE,
+            "The network group '{}' has a too long name (max is HAILO_MAX_NETWORK_GROUP_NAME_SIZE)", network_group_name);
+        strncpy(info.name, network_group_name.c_str(), network_group_name.length() + 1);
+        info.is_multi_context = (1 < net_group->contexts_size());
+        results.push_back(info);
+    }
+    return results;
+}
+
+Expected<std::map<std::string, hailo_vstream_params_t>> Hef::make_input_vstream_params(
+    const std::string &name, bool quantized, hailo_format_type_t format_type, uint32_t timeout_ms,
+    uint32_t queue_size)
+{
+    auto network_pair = pimpl->get_network_group_and_network_name(name);
+    CHECK_EXPECTED(network_pair);
+    
+    return pimpl->make_input_vstream_params(network_pair.value().first, network_pair.value().second, quantized, format_type, 
+        timeout_ms, queue_size);
+}
+
+Expected<std::map<std::string, hailo_vstream_params_t>> Hef::Impl::make_input_vstream_params(
+    const std::string &net_group_name, const std::string &partial_network_name, bool quantized, 
+    hailo_format_type_t format_type, uint32_t timeout_ms, uint32_t queue_size)
+{
+    std::map<std::string, hailo_vstream_params_t> input_vstreams_params;
+    auto status = fill_missing_input_vstream_params_with_default(net_group_name,
+        partial_network_name, input_vstreams_params, quantized, format_type, timeout_ms, queue_size);
+    CHECK_SUCCESS_AS_EXPECTED(status);
+
+    return input_vstreams_params;
+}
+
+Expected<std::map<std::string, hailo_vstream_params_t>> Hef::make_output_vstream_params(
+    const std::string &name, bool quantized, hailo_format_type_t format_type, uint32_t timeout_ms,
+    uint32_t queue_size)
+{
+    auto network_pair = pimpl->get_network_group_and_network_name(name);
+    CHECK_EXPECTED(network_pair);
+
+    return pimpl->make_output_vstream_params(network_pair.value().first, network_pair.value().second, quantized, format_type, 
+        timeout_ms, queue_size);
+}
+
+Expected<std::map<std::string, hailo_vstream_params_t>> Hef::Impl::make_output_vstream_params(
+    const std::string &net_group_name, const std::string &partial_network_name, bool quantized, 
+    hailo_format_type_t format_type, uint32_t timeout_ms, uint32_t queue_size)
+{
+    std::map<std::string, hailo_vstream_params_t> output_vstreams_params;
+    auto status = fill_missing_output_vstream_params_with_default(net_group_name,
+        partial_network_name, output_vstreams_params, quantized, format_type, timeout_ms, queue_size);
+    CHECK_SUCCESS_AS_EXPECTED(status);
+
+    return output_vstreams_params;
+}
+
+hailo_status Hef::Impl::fill_missing_input_vstream_params_with_default(const std::string &net_group_name,
+    const std::string &partial_network_name, std::map<std::string, hailo_vstream_params_t> &input_vstreams_params,
+    bool quantized, hailo_format_type_t format_type, uint32_t timeout_ms, uint32_t queue_size)
+{
+    auto network_group_metadata = get_network_group_metadata(net_group_name);
+    CHECK_EXPECTED_AS_STATUS(network_group_metadata);
+    auto input_vstream_infos = network_group_metadata->get_input_vstream_infos(partial_network_name);
+    CHECK_EXPECTED_AS_STATUS(input_vstream_infos);
+
+    return fill_missing_vstream_params_with_default(input_vstreams_params, input_vstream_infos.value(),
+        quantized, format_type, timeout_ms, queue_size);
+}
+
+hailo_status Hef::Impl::fill_missing_output_vstream_params_with_default(const std::string &net_group_name,
+    const std::string &partial_network_name, std::map<std::string, hailo_vstream_params_t> &output_vstream_params,
+    bool quantized, hailo_format_type_t format_type, uint32_t timeout_ms, uint32_t queue_size)
+{
+    auto network_group_metadata = get_network_group_metadata(net_group_name);
+    CHECK_EXPECTED_AS_STATUS(network_group_metadata);
+    auto output_vstream_infos =  network_group_metadata->get_output_vstream_infos(partial_network_name);
+    CHECK_EXPECTED_AS_STATUS(output_vstream_infos);
+
+    return fill_missing_vstream_params_with_default(output_vstream_params, output_vstream_infos.value(),
+        quantized, format_type, timeout_ms, queue_size);
+}
+
+hailo_status Hef::Impl::fill_missing_vstream_params_with_default(std::map<std::string, hailo_vstream_params_t> &vstream_params,
+    std::vector<hailo_vstream_info_t> &vstream_infos, bool quantized, hailo_format_type_t format_type, uint32_t timeout_ms,
+    uint32_t queue_size)
+{
+    hailo_format_flags_t flags = static_cast<hailo_format_flags_t>(HAILO_FORMAT_FLAGS_NONE);
+    if (quantized) {
+        flags = static_cast<hailo_format_flags_t>(flags | HAILO_FORMAT_FLAGS_QUANTIZED);
+    }
+    for (const auto &vstream_info : vstream_infos) {
+        std::string vstream_name(vstream_info.name);
+        if (contains(vstream_params, vstream_name)) {
+            continue;
+        }
+        hailo_vstream_params_t params{};
+        params.user_buffer_format.order = HAILO_FORMAT_ORDER_AUTO;
+        params.user_buffer_format.type = format_type;
+        params.user_buffer_format.flags = flags;
+        params.timeout_ms = timeout_ms;
+        params.queue_size = queue_size;
+        vstream_params.insert(std::make_pair(vstream_name, params));
+    }
+    return HAILO_SUCCESS;
+}
+
+Expected<ConfigureNetworkParams> Hef::Impl::create_configure_params(hailo_stream_interface_t stream_interface, const std::string &network_group_name)
+{
+    auto params = HailoRTDefaults::get_configure_params();
+    auto stream_params_by_name = create_stream_parameters_by_name(network_group_name, stream_interface);
+    CHECK_EXPECTED(stream_params_by_name);
+    params.stream_params_by_name = stream_params_by_name.release();
+    auto network_params_by_name = create_network_parameters_by_name(network_group_name);
+    CHECK_EXPECTED(network_params_by_name);
+    params.network_params_by_name = network_params_by_name.release();
+
+    return params;
+}
+
+Expected<ConfigureNetworkParams> Hef::Impl::create_configure_params_mipi_input(hailo_stream_interface_t output_interface,
+    const hailo_mipi_input_stream_params_t &mipi_params, const std::string &network_group_name)
+{
+    auto params = HailoRTDefaults::get_configure_params();
+    auto stream_params_by_name = create_stream_parameters_by_name_mipi_input(network_group_name, output_interface, mipi_params);
+    CHECK_EXPECTED(stream_params_by_name);
+    params.stream_params_by_name = stream_params_by_name.release();
+    auto network_params_by_name = create_network_parameters_by_name(network_group_name);
+    CHECK_EXPECTED(network_params_by_name);
+    params.network_params_by_name = network_params_by_name.release();
+
+    return params;
+}
+
+Expected<std::map<std::string, hailo_stream_parameters_t>> Hef::create_stream_parameters_by_name(
+    const std::string &net_group_name, hailo_stream_interface_t stream_interface)
+{
+    auto network_group_name_pair = pimpl->get_network_group_and_network_name(net_group_name);
+    CHECK_EXPECTED(network_group_name_pair);
+    auto net_group_name_str = network_group_name_pair->first;
+
+    return pimpl->create_stream_parameters_by_name(net_group_name_str, stream_interface);
+}
+
+Expected<std::map<std::string, hailo_stream_parameters_t>> Hef::Impl::create_stream_parameters_by_name(
+    const std::string &net_group_name, hailo_stream_interface_t stream_interface)
+{
+    auto network_group_metadata = get_network_group_metadata(net_group_name);
+    CHECK_EXPECTED(network_group_metadata);
+
+    std::map<std::string, hailo_stream_parameters_t> results;
+    auto input_layers_info = network_group_metadata->get_input_layer_infos();
+    CHECK_EXPECTED(input_layers_info);
+    for (auto &input_layer : input_layers_info.value()) {
+        auto params = HailoRTDefaults::get_stream_parameters(stream_interface, HAILO_H2D_STREAM);
+        CHECK_EXPECTED(params);
+        results.emplace(std::make_pair(input_layer.name, params.release()));
+    }
+    auto output_layers_info = network_group_metadata->get_output_layer_infos();
+    CHECK_EXPECTED(output_layers_info);
+    for (auto &output_layer : output_layers_info.value()) {
+        auto params = HailoRTDefaults::get_stream_parameters(stream_interface, HAILO_D2H_STREAM);
+        CHECK_EXPECTED(params);
+        results.emplace(std::make_pair(output_layer.name, params.release()));
+    }
+
+    return results;
+}
+
+Expected<std::map<std::string, hailo_network_parameters_t>> Hef::create_network_parameters_by_name(
+    const std::string &net_group_name)
+{
+    return pimpl->create_network_parameters_by_name(net_group_name);
+}
+
+Expected<std::map<std::string, hailo_network_parameters_t>> Hef::Impl::create_network_parameters_by_name(
+    const std::string &net_group_name)
+{
+    auto net_group = get_net_group_by_name(net_group_name);
+    CHECK_EXPECTED(net_group);
+
+    auto network_gorup_metadata = get_network_group_metadata(net_group_name);
+    CHECK_EXPECTED(network_gorup_metadata);
+
+    std::map<std::string, hailo_network_parameters_t> results;
+
+    if (network_gorup_metadata->supported_features().multi_network_support) {
+        CHECK_AS_EXPECTED((net_group.value()->networks_names_size() != 0), HAILO_INTERNAL_FAILURE, 
+        "Hef support multiple networks, but no networks found in the proto");
+        for (const auto &network : net_group.value()->networks_names()) {
+            auto network_name = net_group_name + HAILO_DEFAULT_NETWORK_NAME_QUALIFIER + network;
+            auto params = HailoRTDefaults::get_network_parameters();
+            results.emplace(std::make_pair(network_name, params));
+        }
+    } else {
+        /* For hefs without the "networks_names" field, build default network name with default params */
+        auto params = HailoRTDefaults::get_network_parameters();
+        auto network = HailoRTDefaults::get_network_name(net_group_name);
+        results.emplace(std::make_pair(network, params));
+    }
+
+    return results;
+}
+
+Expected<std::map<std::string, hailo_stream_parameters_t>> Hef::create_stream_parameters_by_name_mipi_input(
+    const std::string &net_group_name, hailo_stream_interface_t output_interface,
+    const hailo_mipi_input_stream_params_t &mipi_params)
+{
+    auto network_group_name_pair = pimpl->get_network_group_and_network_name(net_group_name);
+    CHECK_EXPECTED(network_group_name_pair);
+    auto net_group_name_str = network_group_name_pair->first;
+
+    return pimpl->create_stream_parameters_by_name_mipi_input(net_group_name_str, output_interface, mipi_params);
+}
+
+Expected<std::map<std::string, hailo_stream_parameters_t>> Hef::Impl::create_stream_parameters_by_name_mipi_input(
+    const std::string &net_group_name, hailo_stream_interface_t output_interface,
+    const hailo_mipi_input_stream_params_t &mipi_params)
+{
+    auto network_group_metadata = get_network_group_metadata(net_group_name);
+    CHECK_EXPECTED(network_group_metadata);
+
+    std::map<std::string, hailo_stream_parameters_t> results;
+    auto input_layers_info = network_group_metadata->get_input_layer_infos();
+    CHECK_EXPECTED(input_layers_info);
+    for (auto &input_layer : input_layers_info.value()) {
+        hailo_stream_parameters_t params = {};
+        params.direction = HAILO_H2D_STREAM;
+        params.stream_interface = HAILO_STREAM_INTERFACE_MIPI;
+        params.mipi_input_params = mipi_params;
+        results.emplace(std::make_pair(input_layer.name, params));
+    }
+    auto output_layers_info = network_group_metadata->get_output_layer_infos();
+    CHECK_EXPECTED(output_layers_info);
+    for (auto &output_layer : output_layers_info.value()) {
+        auto params = HailoRTDefaults::get_stream_parameters(output_interface, HAILO_D2H_STREAM);
+        CHECK_EXPECTED(params);
+        results.emplace(std::make_pair(output_layer.name, params.release()));
+    }
+
+    return results;
+}
+
+NetworkGroupMetadata::NetworkGroupMetadata(const std::string &network_group_name, std::vector<LayerInfo> &&layer_infos,
+    std::vector<std::string> &&sorted_output_names, NetworkGroupSupportedFeatures &supported_features,
+    const std::vector<std::string> &sorted_partial_network_names)
+    : m_network_group_name(network_group_name), m_sorted_output_names(std::move(sorted_output_names)), m_supported_features(supported_features),
+        m_sorted_partial_network_names(sorted_partial_network_names)
+{
+    for (auto &layer_info : layer_infos) {
+        if (HAILO_H2D_STREAM == layer_info.direction) {
+            m_input_layer_infos[layer_info.partial_network_name].push_back(layer_info);
+        } else {
+            m_output_layer_infos[layer_info.partial_network_name].push_back(layer_info);
+        }
+    }
+}
+
+Expected<std::vector<LayerInfo>> NetworkGroupMetadata::get_input_layer_infos(const std::string &partial_network_name) const
+{
+    CHECK_AS_EXPECTED((partial_network_name == HAILO_DEFAULT_PARTIAL_NETWORK_NAME) || contains(m_input_layer_infos, partial_network_name),
+        HAILO_NOT_FOUND, "Network name {} is not found in networks metadata", partial_network_name);
+    std::vector<LayerInfo> res;
+    for (auto &layer_infos_pair : m_input_layer_infos) {
+        if ((partial_network_name == layer_infos_pair.first) || (partial_network_name == HAILO_DEFAULT_PARTIAL_NETWORK_NAME)) {
+            res.insert(res.end(), layer_infos_pair.second.begin(), layer_infos_pair.second.end());
+        }
+    }
+    return res;
+}
+
+Expected<std::vector<LayerInfo>> NetworkGroupMetadata::get_output_layer_infos(const std::string &partial_network_name) const
+{
+    CHECK_AS_EXPECTED((partial_network_name == HAILO_DEFAULT_PARTIAL_NETWORK_NAME) || contains(m_output_layer_infos, partial_network_name),
+        HAILO_NOT_FOUND, "Network name {} is not found in networks metadata", partial_network_name);
+    std::vector<LayerInfo> res;
+    for (auto &layer_infos_pair : m_output_layer_infos) {
+        if ((partial_network_name == layer_infos_pair.first) || (partial_network_name == HAILO_DEFAULT_PARTIAL_NETWORK_NAME)) {
+            res.insert(res.end(), layer_infos_pair.second.begin(), layer_infos_pair.second.end());
+        }
+    }
+    return res;
+}
+
+Expected<std::vector<LayerInfo>> NetworkGroupMetadata::get_all_layer_infos(const std::string &partial_network_name) const
+{
+    auto input_layer_infos = get_input_layer_infos(partial_network_name);
+    CHECK_EXPECTED(input_layer_infos);
+
+    auto output_layer_infos = get_output_layer_infos(partial_network_name);
+    CHECK_EXPECTED(output_layer_infos);
+
+    std::vector<LayerInfo> res;
+    res.reserve(input_layer_infos->size() + output_layer_infos->size());
+    res.insert(res.end(), input_layer_infos->begin(), input_layer_infos->end());
+    res.insert(res.end(), output_layer_infos->begin(), output_layer_infos->end());
+
+    return res;
+}
+
+Expected<std::vector<hailo_stream_info_t>> NetworkGroupMetadata::get_input_stream_infos(const std::string &partial_network_name) const
+{
+    auto input_layer_infos = get_input_layer_infos(partial_network_name);
+    CHECK_EXPECTED(input_layer_infos);
+
+    return convert_layer_infos_to_stream_infos(input_layer_infos.value());
+}
+
+Expected<std::vector<hailo_stream_info_t>> NetworkGroupMetadata::get_output_stream_infos(const std::string &partial_network_name) const
+{
+    auto output_layer_infos = get_output_layer_infos(partial_network_name);
+    CHECK_EXPECTED(output_layer_infos);
+
+    return convert_layer_infos_to_stream_infos(output_layer_infos.value());
+}
+
+Expected<std::vector<hailo_stream_info_t>> NetworkGroupMetadata::get_all_stream_infos(const std::string &partial_network_name) const
+{
+    auto input_stream_infos = get_input_stream_infos(partial_network_name);
+    CHECK_EXPECTED(input_stream_infos);
+
+    auto output_stream_infos = get_output_stream_infos(partial_network_name);
+    CHECK_EXPECTED(output_stream_infos);
+
+    std::vector<hailo_stream_info_t> res;
+    res.reserve(input_stream_infos->size() + output_stream_infos->size());
+    res.insert(res.end(), input_stream_infos->begin(), input_stream_infos->end());
+    res.insert(res.end(), output_stream_infos->begin(), output_stream_infos->end());
+
+    return res;
+}
+
+Expected<std::vector<hailo_vstream_info_t>> NetworkGroupMetadata::get_input_vstream_infos(const std::string &partial_network_name) const
+{
+    auto input_layer_infos = get_input_layer_infos(partial_network_name);
+    CHECK_EXPECTED(input_layer_infos);
+
+    return convert_layer_infos_to_vstream_infos(input_layer_infos.value());
+}
+
+Expected<std::vector<hailo_vstream_info_t>> NetworkGroupMetadata::get_output_vstream_infos(const std::string &partial_network_name) const
+{
+    auto output_layer_infos = get_output_layer_infos(partial_network_name);
+    CHECK_EXPECTED(output_layer_infos);
+    
+    auto res = convert_layer_infos_to_vstream_infos(output_layer_infos.value());
+
+    hailo_status status = HAILO_SUCCESS;
+    std::sort(res.begin(), res.end(),
+        [this, &status](const auto &info1, const auto &info2)
+    {
+        const auto index1 = std::find(m_sorted_output_names.begin(), m_sorted_output_names.end(), std::string(info1.name));
+        const auto index2 = std::find(m_sorted_output_names.begin(), m_sorted_output_names.end(), std::string(info2.name));
+
+        if (m_sorted_output_names.end() == index1) {
+            LOGGER__ERROR("Stream {} not found in sorted output names", info1.name);
+            status = HAILO_INTERNAL_FAILURE;
+            return false;
+        }
+
+        if (m_sorted_output_names.end() == index2) {
+            LOGGER__ERROR("Stream {} not found in sorted output names", info2.name);
+            status = HAILO_INTERNAL_FAILURE;
+            return false;
+        }
+
+        return index1 < index2;
+    });
+    CHECK_SUCCESS_AS_EXPECTED(status);
+
+    return res;
+}
+
+Expected<std::vector<hailo_vstream_info_t>> NetworkGroupMetadata::get_all_vstream_infos(const std::string &partial_network_name) const
+{
+    auto input_vstream_infos = get_input_vstream_infos(partial_network_name);
+    CHECK_EXPECTED(input_vstream_infos);
+
+    auto output_vstream_infos = get_output_vstream_infos(partial_network_name);
+    CHECK_EXPECTED(output_vstream_infos);
+
+    std::vector<hailo_vstream_info_t> res;
+    res.reserve(input_vstream_infos->size() + output_vstream_infos->size());
+    res.insert(res.end(), input_vstream_infos->begin(), input_vstream_infos->end());
+    res.insert(res.end(), output_vstream_infos->begin(), output_vstream_infos->end());
+
+    return res;
+}
+
+Expected<std::vector<std::string>> NetworkGroupMetadata::get_vstream_names_from_stream_name(const std::string &stream_name) const
+{
+    std::vector<std::string> results;
+    auto all_layer_infos =  get_all_layer_infos();
+    CHECK_EXPECTED(all_layer_infos);
+
+    for (auto &layer_info : all_layer_infos.value()) {
+        if (stream_name == layer_info.name) {
+            if (layer_info.is_defused_nms) {
+                return std::vector<std::string> (1, layer_info.fused_nms_layer[0].name);
+            } else if (layer_info.is_mux) {
+                return get_demuxes_names(layer_info);
+            } else {
+                return std::vector<std::string> (1, layer_info.name);
+            }
+        }
+    }
+    return make_unexpected(HAILO_NOT_FOUND);
+}
+
+Expected<std::vector<std::string>> NetworkGroupMetadata::get_stream_names_from_vstream_name(const std::string &vstream_name) const
+{
+    std::vector<std::string> results;
+    auto all_layer_infos =  get_all_layer_infos();
+    CHECK_EXPECTED(all_layer_infos);
+
+    for (auto &layer_info : all_layer_infos.value()) {
+        if (layer_info.is_mux) {
+            if (is_edge_under_mux(layer_info, vstream_name)) {
+                // vstream_name is a demux of the layer info
+                results.push_back(layer_info.name);
+            }
+        } else if (layer_info.is_defused_nms) {
+            if (vstream_name == layer_info.fused_nms_layer[0].name) {
+                // vstream_name is the fused-layer of the layer info
+                results.push_back(layer_info.name);
+            }
+        } else if (vstream_name == layer_info.name) {
+            // vstream_name is a regular stream
+            results.push_back(layer_info.name);
+        }
+    }
+    CHECK_AS_EXPECTED(0 < results.size(), HAILO_NOT_FOUND);
+    return results;
+}
+
+std::vector<hailo_stream_info_t> NetworkGroupMetadata::convert_layer_infos_to_stream_infos(const std::vector<LayerInfo> &layer_infos) const
+{
+    std::vector<hailo_stream_info_t> res;
+    for (auto &layer_info : layer_infos) {
+        res.push_back(LayerInfoUtils::get_stream_info_from_layer_info(layer_info));
+    }
+    return res;
+}
+
+std::vector<hailo_vstream_info_t> NetworkGroupMetadata::convert_layer_infos_to_vstream_infos(const std::vector<LayerInfo> &layer_infos) const
+{
+    std::vector<hailo_vstream_info_t> res;
+    for (auto &layer_info : layer_infos) {
+        auto vstream_infos = LayerInfoUtils::get_vstream_infos_from_layer_info(layer_info, m_network_group_name);
+        for (const auto &vstream_info : vstream_infos) {
+            // In case of fused nms layers, several LayerInfos will contain data about the same fused layer
+            if (!LayerInfoUtils::vstream_info_already_in_vector(res, vstream_info.name)) {
+                res.push_back(vstream_info);
+            }
+        }
+    }
+    return res;
+}
+
+Expected<std::string> NetworkGroupMetadata::get_partial_network_name(const std::string &network_name) const
+{
+    if (network_name.empty()) {
+        return HAILO_DEFAULT_PARTIAL_NETWORK_NAME;
+    }
+
+    for (const auto &partial_network_name : m_sorted_partial_network_names) {
+        auto found = network_name.find(HAILO_DEFAULT_NETWORK_NAME_QUALIFIER + partial_network_name);
+        if (found != std::string::npos) {
+            return Expected<std::string>(partial_network_name);
+        }
+    }
+
+    LOGGER__ERROR("Found no partial network name for network name {}", network_name);
+    return make_unexpected(HAILO_NOT_FOUND);
+}
+
+Expected<std::vector<hailo_network_info_t>> NetworkGroupMetadata::get_network_infos() const
+{
+    std::vector<hailo_network_info_t> network_infos;
+    auto net_group_name = network_group_name();
+    network_infos.reserve(m_sorted_partial_network_names.size());
+    for (auto const &partial_network_name : m_sorted_partial_network_names) {
+        hailo_network_info_t network_info = {};
+        auto network_name = net_group_name + HAILO_DEFAULT_NETWORK_NAME_QUALIFIER + partial_network_name;
+        CHECK_AS_EXPECTED(HAILO_MAX_NETWORK_NAME_SIZE >= (network_name.length() + 1), HAILO_INTERNAL_FAILURE,
+            "The network '{}' has a too long name (max is HAILO_MAX_NETWORK_NAME_SIZE)", network_name);  
+        memcpy(network_info.name, network_name.c_str(), network_name.length() + 1);
+
+        network_infos.push_back(network_info);
+    }
+
+    return network_infos;
+}
+
+} /* namespace hailort */
diff --git a/hailort/libhailort/src/hef_internal.hpp b/hailort/libhailort/src/hef_internal.hpp
new file mode 100644 (file)
index 0000000..a0a0ca4
--- /dev/null
@@ -0,0 +1,724 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file hef_internal.hpp
+ * @brief Internal definition of Hef class Impl
+ **/
+
+#ifndef _HEF_INTERNAL_HPP_
+#define _HEF_INTERNAL_HPP_
+
+// https://github.com/protocolbuffers/protobuf/tree/master/cmake#notes-on-compiler-warnings
+#if defined(_MSC_VER)
+#pragma warning(push)
+#pragma warning(disable: 4244 4267 4127)
+#else
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wconversion"
+#endif
+#include "hef.pb.h"
+#if defined(_MSC_VER)
+#pragma warning( pop ) 
+#else
+#pragma GCC diagnostic pop
+#endif
+
+#include "hailo/hailort.h"
+#include "hailo/expected.hpp"
+#include "hailo/hef.hpp"
+#include "hailo/network_group.hpp"
+#include "context_switch/active_network_group_holder.hpp"
+#include "layer_info.hpp"
+#include "hailort_defaults.hpp"
+#include "control_protocol.h"
+
+#include "control_protocol.hpp"
+
+#include <functional>
+#include <bitset>
+#include <memory>
+
+extern "C" {
+#include "md5.h"
+}
+
+namespace hailort
+{
+
+class ResourcesManager;
+class ConfigResources;
+class VdmaConfigActivatedNetworkGroup;
+using VdmaConfigActiveAppHolder = ActiveNetworkGroupHolder<VdmaConfigActivatedNetworkGroup>;
+using ProtoHEFNetworkGroupPtr = std::shared_ptr<ProtoHEFNetworkGroup>;
+
+#pragma pack(push, 1)
+typedef struct {
+    uint32_t magic;
+    uint32_t version;
+    uint32_t hef_proto_length;
+    uint32_t reserved;
+    MD5_SUM_t expected_md5;
+} hef__header_t;
+#pragma pack(pop)
+
+typedef enum {
+    HEF__FORMAT__TF_RGB = 0,
+    HEF__FORMAT__FRAMES,
+    HEF__FORMAT__FLAT,
+    HEF__FORMAT__FCR,
+    HEF__FORMAT__BAYER_RGB,
+    HEF__FORMAT__ARGMAX,
+    HEF__FORMAT__NMS,
+    HEF__FORMAT__F8CR,
+} HEF__net_io_formatter_type_t;
+
+
+using ConfigBufferInfoMap = std::unordered_map<uint8_t, std::vector<uint32_t>>;
+const static uint32_t SUPPORTED_EXTENSIONS_BITSET_SIZE = 1000;
+static const std::vector<ProtoHEFExtensionType> SUPPORTED_EXTENSIONS = {
+    ABBALE, 
+    POSTED_WRITES, 
+    DDR, 
+    PADDED_DDR_BUFFERS, 
+    IS_MULTI_CONTEXTS, 
+    COMPRESSED_PARAMS, 
+    TRANSPOSE_COMPONENT,
+    IS_NMS_MULTI_CONTEXT,
+    OFFLOAD_ARGMAX
+};
+
+struct HefParsingInfo
+{
+    ConfigBufferInfoMap cfg_infos_preliminary_config;
+    // cfg_count_per_context[ctxt_index][cfg_index]
+    std::vector<ConfigBufferInfoMap> cfg_infos_per_context;
+    // TODO: add information about boundary channels here, so they can be allocated in init time instead of activation time
+    // TODO: Save this struct inside network_group class
+};
+
+struct NetworkGroupSupportedFeatures {
+    bool padded_ddr_buffers;
+    bool multi_network_support;
+    bool multi_context;
+};
+
+static inline bool is_h2d_boundary_info_layer(const ProtoHEFEdgeLayer& layer)
+{
+    return ((ProtoHEFEdgeLayerDirection::PROTO__EDGE_LAYER_DIRECTION__HOST_TO_DEVICE == layer.direction()) &&
+        (ProtoHEFEdgeConnectionType::PROTO__EDGE_CONNECTION_TYPE__BOUNDARY ==
+            layer.context_switch_info().edge_connection_type()) &&
+        (ProtoHEFEdgeLayerType::PROTO__EDGE_LAYER_TYPE__INFO == layer.edge_layer_type()));
+}
+
+static inline bool is_d2h_boundary_info_layer(const ProtoHEFEdgeLayer& layer)
+{
+    return ((ProtoHEFEdgeLayerDirection::PROTO__EDGE_LAYER_DIRECTION__DEVICE_TO_HOST == layer.direction()) &&
+        (ProtoHEFEdgeConnectionType::PROTO__EDGE_CONNECTION_TYPE__BOUNDARY ==
+            layer.context_switch_info().edge_connection_type()) &&
+        (ProtoHEFEdgeLayerType::PROTO__EDGE_LAYER_TYPE__INFO == layer.edge_layer_type()));
+}
+
+static inline bool is_h2d_boundary_mux_layer(const ProtoHEFEdgeLayer& layer)
+{
+    return ((ProtoHEFEdgeLayerDirection::PROTO__EDGE_LAYER_DIRECTION__HOST_TO_DEVICE == layer.direction()) &&
+        (ProtoHEFEdgeConnectionType::PROTO__EDGE_CONNECTION_TYPE__BOUNDARY ==
+            layer.context_switch_info().edge_connection_type()) &&
+        (ProtoHEFEdgeLayerType::PROTO__EDGE_LAYER_TYPE__MUX == layer.edge_layer_type()));
+}
+
+static inline bool is_d2h_boundary_mux_layer(const ProtoHEFEdgeLayer& layer)
+{
+    return ((ProtoHEFEdgeLayerDirection::PROTO__EDGE_LAYER_DIRECTION__DEVICE_TO_HOST == layer.direction()) &&
+        (ProtoHEFEdgeConnectionType::PROTO__EDGE_CONNECTION_TYPE__BOUNDARY ==
+            layer.context_switch_info().edge_connection_type()) &&
+        (ProtoHEFEdgeLayerType::PROTO__EDGE_LAYER_TYPE__MUX == layer.edge_layer_type()));
+}
+
+// TODO: Fix the circular dependency (with HRT-2899, InputStream/OutputStream related code will move elsewhere)
+class InputStreamBase;
+class OutputStreamBase;
+
+// Forward declerations
+struct WriteMemoryInfo;
+class Device;
+class VdmaConfigNetworkGroup;
+class VdmaDevice;
+class HailoRTDriver;
+
+class NetworkGroupMetadata {
+public:
+    NetworkGroupMetadata(const std::string &network_group_name, std::vector<LayerInfo> &&layer_infos, std::vector<std::string> &&sorted_output_names,
+        NetworkGroupSupportedFeatures &supported_features, const std::vector<std::string> &sorted_partial_network_names);
+
+    Expected<std::vector<LayerInfo>> get_input_layer_infos(const std::string &partial_network_name = HAILO_DEFAULT_PARTIAL_NETWORK_NAME) const;
+    Expected<std::vector<LayerInfo>> get_output_layer_infos(const std::string &partial_network_name = HAILO_DEFAULT_PARTIAL_NETWORK_NAME) const;
+    Expected<std::vector<LayerInfo>> get_all_layer_infos(const std::string &partial_network_name = HAILO_DEFAULT_PARTIAL_NETWORK_NAME) const;
+
+    Expected<std::vector<hailo_stream_info_t>> get_input_stream_infos(const std::string &partial_network_name = HAILO_DEFAULT_PARTIAL_NETWORK_NAME) const;
+    Expected<std::vector<hailo_stream_info_t>> get_output_stream_infos(const std::string &partial_network_name = HAILO_DEFAULT_PARTIAL_NETWORK_NAME) const;
+    Expected<std::vector<hailo_stream_info_t>> get_all_stream_infos(const std::string &partial_network_name = HAILO_DEFAULT_PARTIAL_NETWORK_NAME) const;
+
+    Expected<std::vector<hailo_vstream_info_t>> get_input_vstream_infos(const std::string &partial_network_name = HAILO_DEFAULT_PARTIAL_NETWORK_NAME) const;
+    Expected<std::vector<hailo_vstream_info_t>> get_output_vstream_infos(const std::string &partial_network_name = HAILO_DEFAULT_PARTIAL_NETWORK_NAME) const;
+    Expected<std::vector<hailo_vstream_info_t>> get_all_vstream_infos(const std::string &partial_network_name = HAILO_DEFAULT_PARTIAL_NETWORK_NAME) const;
+
+    Expected<std::vector<std::string>> get_vstream_names_from_stream_name(const std::string &stream_name) const;
+    Expected<std::vector<std::string>> get_stream_names_from_vstream_name(const std::string &vstream_name) const;
+
+    // Note - network name is build in the following way - "network_group_name / partial_network name"
+    // get partial network name returns the part of the name after the network group name and the "/" qualifier.
+    // The protobuf hold the partial network name.
+    Expected<std::string> get_partial_network_name(const std::string &network_name) const;
+
+    Expected<std::vector<hailo_network_info_t>> get_network_infos() const;
+
+    const std::string &network_group_name() const
+    {
+        return m_network_group_name;
+    }
+
+    const std::vector<std::string> get_sorted_output_names() const
+    {
+        return m_sorted_output_names;
+    }
+
+    const NetworkGroupSupportedFeatures &supported_features() const
+    {
+        return m_supported_features;
+    }
+
+    const std::vector<std::string> get_partial_network_names() const
+    {
+        return m_sorted_partial_network_names;
+    }
+
+private:
+    std::vector<hailo_stream_info_t> convert_layer_infos_to_stream_infos(const std::vector<LayerInfo> &layer_infos) const;
+    std::vector<hailo_vstream_info_t> convert_layer_infos_to_vstream_infos(const std::vector<LayerInfo> &layer_infos) const;
+
+    // Per network
+    std::map<std::string, std::vector<LayerInfo>> m_input_layer_infos;
+    std::map<std::string, std::vector<LayerInfo>> m_output_layer_infos;
+
+    std::string m_network_group_name;
+    std::vector<std::string> m_sorted_output_names;
+    NetworkGroupSupportedFeatures m_supported_features;
+    std::vector<std::string> m_sorted_partial_network_names;
+};
+
+class Hef::Impl final
+{
+public:
+    static const uint32_t HEADER_MAGIC = 0x01484546;
+    static const uint32_t HEADER_VERSION = 0;
+
+    static Expected<Impl> create(const std::string &hef_path);
+    static Expected<Impl> create(const MemoryView &hef_buffer);
+
+    const std::vector<ProtoHEFNetworkGroupPtr>& network_groups() const;
+
+    Expected<std::pair<std::string, std::string>> get_network_group_and_network_name(const std::string &name);
+
+    Expected<ProtoHEFNetworkGroupPtr> get_net_group_by_name(const std::string &net_group_name="");
+    Expected<std::vector<hailo_network_info_t>> get_network_infos(const std::string &net_group_name="");
+
+    Expected<std::vector<hailo_stream_info_t>> get_input_stream_infos(const std::string &net_group_name="",
+        const std::string &partial_network_name="");
+    Expected<std::vector<hailo_stream_info_t>> get_output_stream_infos(const std::string &net_group_name="",
+        const std::string &partial_network_name="");
+    Expected<std::vector<hailo_stream_info_t>> get_all_stream_infos(const std::string &net_group_name="",
+        const std::string &partial_network_name="");
+    Expected<hailo_stream_info_t> get_stream_info_by_name(const std::string &stream_name,
+        hailo_stream_direction_t stream_direction, const std::string &net_group_name="");
+
+    Expected<std::vector<hailo_vstream_info_t>> get_input_vstream_infos(const std::string &net_group_name="",
+        const std::string &partial_network_name="");
+    Expected<std::vector<hailo_vstream_info_t>> get_output_vstream_infos(const std::string &net_group_name="",
+        const std::string &partial_network_name="");
+    Expected<std::vector<hailo_vstream_info_t>> get_all_vstream_infos(const std::string &net_group_name="",
+        const std::string &partial_network_name="");
+    Expected<std::vector<std::string>> get_sorted_output_names(const std::string &net_group_name="");
+    Expected<size_t> get_number_of_input_streams(const std::string &net_group_name="");
+    Expected<size_t> get_number_of_output_streams(const std::string &net_group_name="");
+    ProtoHEFHwArch get_device_arch();
+    Expected<float64_t> get_bottleneck_fps(const std::string &net_group_name="");
+    static bool contains_ddr_layers(const ProtoHEFNetworkGroup& net_group);
+    static hailo_status validate_net_group_unique_layer_names(ProtoHEFNetworkGroupPtr net_group);
+    Expected<std::vector<hailo_vstream_info_t>> get_network_input_vstream_infos(const std::string &net_group_name="",
+        const std::string &partial_network_name="");
+
+    Expected<std::vector<std::string>> get_stream_names_from_vstream_name(const std::string &vstream_name,
+        const std::string &net_group_name="");
+    Expected<std::vector<std::string>> get_vstream_names_from_stream_name(const std::string &stream_name,
+        const std::string &net_group_name="");
+
+    Expected<std::string> get_vstream_name_from_original_name(const std::string &original_name,
+        const std::string &net_group_name="");
+    Expected<std::vector<std::string>> get_original_names_from_vstream_name(const std::string &stream_name,
+        const std::string &net_group_name="");
+
+    std::vector<std::string> get_network_groups_names();
+    Expected<std::vector<hailo_network_group_info_t>> get_network_groups_infos();
+
+    Expected<ConfigureNetworkParams> create_configure_params(hailo_stream_interface_t stream_interface, const std::string &network_gorup_name);
+    Expected<ConfigureNetworkParams> create_configure_params_mipi_input(hailo_stream_interface_t output_interface,
+        const hailo_mipi_input_stream_params_t &mipi_params, const std::string &network_gorup_name);
+
+    static Expected<std::vector<WriteMemoryInfo>> create_single_context_network_group_config(
+        const ProtoHEFPreliminaryConfig& proto_config);
+
+    /* TODO HRT-5067 - work with hailo_device_architecture_t instead of ProtoHEFHwArch */
+    static Expected<std::shared_ptr<ResourcesManager>> create_resources_manager(
+        ProtoHEFNetworkGroupPtr network_group_proto, uint8_t net_group_index,
+        VdmaDevice &device, HailoRTDriver &driver, const ConfigureNetworkParams &network_group_params,
+        std::shared_ptr<NetworkGroupMetadata> network_group_metadata,
+        const ProtoHEFHwArch &hw_arch);
+
+    Expected<std::map<std::string, hailo_stream_parameters_t>> create_stream_parameters_by_name(
+        const std::string &net_group_name, hailo_stream_interface_t stream_interface);
+
+    Expected<std::map<std::string, hailo_network_parameters_t>> create_network_parameters_by_name(
+        const std::string &net_group_name);
+
+    Expected<std::map<std::string,hailo_stream_parameters_t>> create_stream_parameters_by_name_mipi_input(
+        const std::string &net_group_name, hailo_stream_interface_t output_interface,
+        const hailo_mipi_input_stream_params_t &mipi_params);
+
+    Expected<std::map<std::string, hailo_vstream_params_t>> make_input_vstream_params(
+        const std::string &net_group_name, const std::string &partial_network_name, bool quantized, 
+        hailo_format_type_t format_type, uint32_t timeout_ms, uint32_t queue_size);
+    hailo_status fill_missing_input_vstream_params_with_default(const std::string &net_group_name,
+        const std::string &partial_network_name, std::map<std::string, hailo_vstream_params_t> &input_vstreams_params,
+        bool quantized, hailo_format_type_t format_type, uint32_t timeout_ms, uint32_t queue_size);
+    Expected<std::map<std::string, hailo_vstream_params_t>> make_output_vstream_params(
+        const std::string &net_group_name, const std::string &partial_network_name, bool quantized, 
+        hailo_format_type_t format_type, uint32_t timeout_ms, uint32_t queue_size);
+    hailo_status fill_missing_output_vstream_params_with_default(const std::string &net_group_name,
+        const std::string &partial_network_name, std::map<std::string, hailo_vstream_params_t> &output_vstream_params,
+        bool quantized, hailo_format_type_t format_type, uint32_t timeout_ms, uint32_t queue_size);
+    static hailo_status fill_missing_vstream_params_with_default(std::map<std::string, hailo_vstream_params_t> &vstream_params,
+        std::vector<hailo_vstream_info_t> &name_to_format_info, bool quantized, hailo_format_type_t format_type, uint32_t timeout_ms,
+        uint32_t queue_size);
+
+    Expected<NetworkGroupMetadata> get_network_group_metadata(const std::string &network_group_name)
+    {
+        CHECK_AS_EXPECTED(contains(m_network_group_metadata, network_group_name), HAILO_NOT_FOUND,
+            "Network group with name {} wasn't found", network_group_name);
+        auto metadata = m_network_group_metadata.at(network_group_name);
+        return metadata;
+    }
+
+private:
+    Impl(const std::string &hef_path, hailo_status &status);
+    Impl(const MemoryView &hef_memview, hailo_status &status);
+
+    hailo_status parse_hef_file(const std::string &hef_path);
+    hailo_status parse_hef_memview(const MemoryView &hef_memview);
+    hailo_status transfer_protobuf_field_ownership(ProtoHEFHef &hef_message);
+    hailo_status fill_networks_metadata();
+    void fill_extensions_bitset();
+
+    static bool check_hef_extension(const ProtoHEFExtensionType &extension, const ProtoHEFHeader &header,
+        const std::vector<ProtoHEFExtension> &hef_extensions, const ProtoHEFIncludedFeatures &included_features);
+    // Note: If the network group is found, i.e has_value() is true on the returned object, then the underlying pointer is not null
+    static bool check_hef_optional_extension(const ProtoHEFExtensionType &extension, const ProtoHEFHeader &header,
+        const std::vector<ProtoHEFOptionalExtension> &hef_optional_extensions);
+    static NetworkGroupSupportedFeatures get_supported_features(const ProtoHEFHeader &header,
+        const std::vector<ProtoHEFExtension> &hef_extensions, const ProtoHEFIncludedFeatures &included_features,
+        const std::vector<ProtoHEFOptionalExtension> &hef_optional_extensions);
+
+    hailo_status validate_hef_extensions();
+    static hailo_status validate_hef_header(const hef__header_t &header, MD5_SUM_t &calculated_md5, size_t proto_size);
+    static Expected<HefParsingInfo> get_parsing_info(ProtoHEFNetworkGroupPtr net_group);
+
+    Expected<std::map<std::string, hailo_format_t>> get_inputs_vstream_names_and_format_info(
+        const std::string &net_group_name, const std::string &partial_network_name);
+    Expected<std::map<std::string, hailo_format_t>> get_outputs_vstream_names_and_format_info(
+        const std::string &net_group_name, const std::string &partial_network_name);
+
+    static Expected<std::string> get_vstream_name_from_original_name_mux(const std::string &original_name, const ProtoHefEdge &layer);
+    static Expected<std::vector<std::string>> get_original_names_from_vstream_name_mux(const std::string &vstream_name, const ProtoHefEdge &layer);
+
+    // Hef information
+    ProtoHEFHeader m_header;
+    ProtoHEFIncludedFeatures m_included_features;
+    std::vector<ProtoHEFNetworkGroupPtr> m_groups;
+    std::vector<ProtoHEFExtension> m_hef_extensions;
+    std::vector<ProtoHEFOptionalExtension> m_hef_optional_extensions;
+    std::bitset<SUPPORTED_EXTENSIONS_BITSET_SIZE> m_supported_extensions_bitset;
+
+    // NetworkGroups information
+    std::map<std::string, NetworkGroupMetadata> m_network_group_metadata;
+};
+
+// TODO: Make this part of a namespace? (HRT-2881)
+/* TODO: Create LayerInfo for all layers in the HEF (including inter-context and DDR), and use it for parsing additional info without proto dependency
+    After this will be done, this class should move to layer_info.hpp */
+class HefConfigurator final
+{
+public:
+    HefConfigurator() = delete;
+
+    static Expected<CONTROL_PROTOCOL__nn_stream_config_t> parse_nn_stream_config(const ProtoHEFEdgeLayerBase &edge_layer,
+        bool hw_padding_supported, const ProtoHEFEdgeConnectionType &edge_connection_type);
+    static Expected<CONTROL_PROTOCOL__nn_stream_config_t> parse_nn_stream_config(const LayerInfo &edge_layer,
+        bool hw_padding_supported);
+
+    static bool is_hw_padding_supported(const ProtoHEFEdgeLayer &edge_layer);
+    static bool is_hw_padding_supported(const LayerInfo &layer_info);
+private:
+    static Expected<CONTROL_PROTOCOL__nn_stream_config_t> parse_nn_stream_config(hailo_format_order_t format_order,
+        uint32_t width, uint32_t features, uint32_t hw_data_bytes, uint16_t core_buffers_per_frame,
+        uint16_t core_bytes_per_buffer, bool hw_padding_supported, bool is_ddr);
+
+    static bool is_hw_padding_supported(bool is_boundary, bool is_mux, hailo_format_order_t format_order,
+        uint16_t core_buffers_per_frame, uint32_t height, uint32_t width, uint32_t features, uint32_t hw_data_bytes);
+};
+
+class HefUtils final
+{
+public:
+    HefUtils() = delete;
+
+    static Expected<std::vector<LayerInfo>> get_all_layers_info(const ProtoHEFNetworkGroup &network_group_proto,
+        const NetworkGroupSupportedFeatures &supported_features);
+    static Expected<hailo_nms_info_t> parse_proto_nms_info(const ProtoHEFNmsInfo &proto_nms_info);
+    static Expected<LayerInfo> get_layer_info(const ProtoHEFNetworkGroup &net_group, const ProtoHEFEdgeLayer &layer,
+        const NetworkGroupSupportedFeatures &supported_features);
+    static Expected<std::vector<std::string>> get_sorted_output_names(const ProtoHEFNetworkGroup &net_group);
+    
+    static Expected<std::string> get_partial_network_name_by_index(const ProtoHEFNetworkGroup &network_group_proto, uint8_t network_index, 
+        const NetworkGroupSupportedFeatures &supported_features);
+
+    static Expected<std::vector<hailo_network_info_t>> get_network_infos(const ProtoHEFNetworkGroup &net_group,
+        const std::string &net_group_name, const NetworkGroupSupportedFeatures &supported_features);
+private:
+    static hailo_status fill_layer_info_with_base_info(const ProtoHEFEdgeLayerBase &base_info,
+        const ProtoHEFEdgeConnectionType &edge_connection_type,
+        const ProtoHEFNetworkGroupMetadata &network_group_proto, bool hw_padding_supported, bool transposed, LayerInfo &layer_info);
+    static hailo_status fill_layer_info(const ProtoHEFEdgeLayerInfo &info,
+        const ProtoHEFEdgeConnectionType &edge_connection_type,
+        const ProtoHEFNetworkGroup &net_group, hailo_stream_direction_t direction,
+        bool hw_padding_supported, const std::string &partial_network_name, LayerInfo &layer_info);
+    static hailo_status fill_fused_nms_info(const ProtoHEFEdgeLayerFused &info,
+            LayerInfo &layer_info, hailo_quant_info_t &defuse_quant_info, const std::string &partial_network_name);
+    static hailo_status fill_mux_info(const ProtoHEFEdgeLayerMux &info,
+        const ProtoHEFEdgeConnectionType &edge_connection_type,
+        const ProtoHEFNetworkGroup &net_group, hailo_stream_direction_t direction,
+        bool hw_padding_supported, const std::string &partial_network_name, LayerInfo &layer_info);
+};
+
+class ContextSwitchTrigger final
+{
+public:
+    static Expected<ContextSwitchTrigger> create(const ProtoHEFTrigger &proto_trigger);
+    ContextSwitchTrigger(ContextSwitchTrigger &&) = default;
+    ContextSwitchTrigger(const ContextSwitchTrigger &) = delete;
+    ContextSwitchTrigger &operator=(ContextSwitchTrigger &&) = delete;
+    ContextSwitchTrigger &operator=(const ContextSwitchTrigger &) = delete;
+    ~ContextSwitchTrigger() = default;
+
+    CONTROL_PROTOCOL__TRIGGER_t get_serialized() const;
+    hailo_status add_to_trigger_group(CONTROL_PROTOCOL__context_switch_context_info_t *context_info,
+        uint8_t **context_meta_data_head_pointer) const;
+
+private:
+    static Expected<CONTROL_PROTOCOL__TRIGGER_t> serialize(const ProtoHEFTrigger &proto_trigger);
+    ContextSwitchTrigger(CONTROL_PROTOCOL__TRIGGER_t &&serialized_trigger);
+
+    const CONTROL_PROTOCOL__TRIGGER_t m_serialized_trigger;
+};
+
+class ContextSwitchConfigAction;
+using ContextSwitchConfigActionPtr = std::shared_ptr<ContextSwitchConfigAction>;
+class ContextSwitchConfigAction
+{
+public:
+    enum class Type
+    {
+        None,
+        WriteData,
+        WriteDataCcw,
+        CreateDescForCcw,
+        ReadVdma,
+        TriggerSequencer,
+        WaitForSequencerDone,
+        TriggerNewDataFromDataInput,
+        TriggerNewDataFromDataInputDdr,
+        EnableLcuNonDefault,
+        EnableLcuDefault,
+        DisableLcu,
+        WaitForModuleConfigDone,
+        AddDdrPairInfo,
+        AddDdrBufferingStart,
+        AddRrepeated
+    };
+
+    static Expected<ContextSwitchConfigActionPtr> create(const ProtoHEFAction &proto_action, Device &device,
+        std::vector<ConfigResources> &config, const ResourcesManager &resources_manager, const ProtoHEFNetworkGroup &net_group,
+        bool support_pre_fetch);
+
+    ContextSwitchConfigAction(ContextSwitchConfigAction &&) = default;
+    ContextSwitchConfigAction(const ContextSwitchConfigAction &) = delete;
+    ContextSwitchConfigAction &operator=(ContextSwitchConfigAction &&) = delete;
+    ContextSwitchConfigAction &operator=(const ContextSwitchConfigAction &) = delete;
+    virtual ~ContextSwitchConfigAction() = default;
+
+    virtual hailo_status execute(CONTROL_PROTOCOL__context_switch_context_info_t *context_info,
+        uint8_t **context_meta_data_head_pointer) = 0;
+    virtual bool supports_repeated_block() const = 0;
+    void set_is_in_repeated_block(bool is_repeated);
+    Type get_type() const;
+    CONTROL_PROTOCOL__ACTION_TYPE_t get_action_list_type() const;
+    const ProtoHEFAction &get_proto_action() const;
+
+protected:
+    ContextSwitchConfigAction(Type type);
+    ContextSwitchConfigAction(Type type, const ProtoHEFAction& proto_action);
+    ContextSwitchConfigAction(Type type, CONTROL_PROTOCOL__ACTION_TYPE_t action_list_type);
+    ContextSwitchConfigAction(Type type, CONTROL_PROTOCOL__ACTION_TYPE_t action_list_type, const ProtoHEFAction& proto_action);
+
+    const Type m_type;
+    const CONTROL_PROTOCOL__ACTION_TYPE_t m_action_list_type;
+    bool m_is_in_repeated_block;
+    // Note: This references the action in the hef Protobuf
+    const ProtoHEFAction &m_proto_action;
+};
+
+class NoneAction : public ContextSwitchConfigAction
+{
+public:
+    static Expected<ContextSwitchConfigActionPtr> create();
+    NoneAction(NoneAction &&) = default;
+    NoneAction(const NoneAction &) = delete;
+    NoneAction &operator=(NoneAction &&) = delete;
+    NoneAction &operator=(const NoneAction &) = delete;
+    virtual ~NoneAction() = default;
+
+    virtual hailo_status execute(CONTROL_PROTOCOL__context_switch_context_info_t *context_info,
+        uint8_t **context_meta_data_head_pointer) override;
+    virtual bool supports_repeated_block() const override;
+
+private:
+    NoneAction();
+};
+
+class WriteDataAction : public ContextSwitchConfigAction
+{
+public:
+    static Expected<ContextSwitchConfigActionPtr> create(const ProtoHEFAction& proto_action, Device &device);
+    WriteDataAction(WriteDataAction &&) = default;
+    WriteDataAction(const WriteDataAction &) = delete;
+    WriteDataAction &operator=(WriteDataAction &&) = delete;
+    WriteDataAction &operator=(const WriteDataAction &) = delete;
+    virtual ~WriteDataAction() = default;
+
+    virtual hailo_status execute(CONTROL_PROTOCOL__context_switch_context_info_t *context_info,
+        uint8_t **context_meta_data_head_pointer) override;
+    virtual bool supports_repeated_block() const override;
+
+private:
+    WriteDataAction(const ProtoHEFAction& proto_action, Device &device);
+
+    Device &m_device;
+};
+
+class WriteDataCcwAction : public ContextSwitchConfigAction
+{
+public:
+    static Expected<ContextSwitchConfigActionPtr> create(const ProtoHEFAction& proto_action,
+        std::vector<ConfigResources> &config, bool support_pre_fetch);
+    WriteDataCcwAction(WriteDataCcwAction &&) = default;
+    WriteDataCcwAction(const WriteDataCcwAction &) = delete;
+    WriteDataCcwAction &operator=(WriteDataCcwAction &&) = delete;
+    WriteDataCcwAction &operator=(const WriteDataCcwAction &) = delete;
+    virtual ~WriteDataCcwAction() = default;
+
+    virtual hailo_status execute(CONTROL_PROTOCOL__context_switch_context_info_t *context_info,
+        uint8_t **context_meta_data_head_pointer) override;
+    virtual bool supports_repeated_block() const override;
+
+private:
+    WriteDataCcwAction(const ProtoHEFAction& proto_action, std::vector<ConfigResources> &config, 
+        bool support_pre_fetch);
+
+    bool should_pad_with_nops();
+    hailo_status pad_with_nops(uint8_t cfg_channel_index);
+    
+    std::vector<ConfigResources> &m_config;
+    const bool m_support_pre_fetch;
+};
+
+class CreateConfigDescAndFetchAction : public ContextSwitchConfigAction
+{
+public:
+    static Expected<ContextSwitchConfigActionPtr> create(std::vector<ConfigResources> &config_resources,
+        const std::set<uint8_t> &pending_cfg_ch_buffer, const std::vector<uint16_t> &ccw_burst, bool support_pre_fetch);
+    CreateConfigDescAndFetchAction(CreateConfigDescAndFetchAction &&) = default;
+    CreateConfigDescAndFetchAction(const CreateConfigDescAndFetchAction &) = delete;
+    CreateConfigDescAndFetchAction &operator=(CreateConfigDescAndFetchAction &&) = delete;
+    CreateConfigDescAndFetchAction &operator=(const CreateConfigDescAndFetchAction &) = delete;
+    virtual ~CreateConfigDescAndFetchAction() = default;
+
+    virtual hailo_status execute(CONTROL_PROTOCOL__context_switch_context_info_t *context_info,
+        uint8_t **context_meta_data_head_pointer) override;
+    virtual bool supports_repeated_block() const override;
+
+private:
+    CreateConfigDescAndFetchAction(std::vector<ConfigResources> &config, const std::set<uint8_t> &pending_cfg_ch_buffer,
+        const std::vector<uint16_t> &ccw_burst, bool support_pre_fetch);
+        
+    std::vector<ConfigResources> &m_config;
+    const std::set<uint8_t> m_pending_cfg_ch_buffer;
+    const std::vector<uint16_t> m_ccw_bursts;
+    const bool m_support_pre_fetch;
+};
+
+class RepeatedHeaderAction : public ContextSwitchConfigAction
+{
+public:
+    static Expected<ContextSwitchConfigActionPtr> create(CONTROL_PROTOCOL__ACTION_TYPE_t sub_action_type, uint8_t num_actions);
+    RepeatedHeaderAction(RepeatedHeaderAction &&) = default;
+    RepeatedHeaderAction(const RepeatedHeaderAction &) = delete;
+    RepeatedHeaderAction &operator=(RepeatedHeaderAction &&) = delete;
+    RepeatedHeaderAction &operator=(const RepeatedHeaderAction &) = delete;
+    virtual ~RepeatedHeaderAction() = default;
+
+    virtual hailo_status execute(CONTROL_PROTOCOL__context_switch_context_info_t *context_info,
+        uint8_t **context_meta_data_head_pointer) override;
+    virtual bool supports_repeated_block() const override;
+
+private:
+    RepeatedHeaderAction(CONTROL_PROTOCOL__ACTION_TYPE_t sub_action_type, uint8_t num_actions);
+
+    const CONTROL_PROTOCOL__ACTION_TYPE_t m_sub_action_type;
+    const uint8_t m_num_actions;
+};
+
+class DisableLcuAction : public ContextSwitchConfigAction
+{
+public:
+    static Expected<ContextSwitchConfigActionPtr> create(const ProtoHEFAction& proto_action);
+    DisableLcuAction(DisableLcuAction &&) = default;
+    DisableLcuAction(const DisableLcuAction &) = delete;
+    DisableLcuAction &operator=(DisableLcuAction &&) = delete;
+    DisableLcuAction &operator=(const DisableLcuAction &) = delete;
+    virtual ~DisableLcuAction() = default;
+
+    virtual hailo_status execute(CONTROL_PROTOCOL__context_switch_context_info_t *context_info,
+        uint8_t **context_meta_data_head_pointer) override;
+    virtual bool supports_repeated_block() const override;
+
+private:
+    DisableLcuAction(const ProtoHEFAction& proto_action);
+};
+
+class EnableLcuAction : public ContextSwitchConfigAction
+{
+public:
+    static Expected<ContextSwitchConfigActionPtr> create(const ProtoHEFAction& proto_action,
+        const ResourcesManager &resources_manager, const ProtoHEFNetworkGroup &net_group);
+    EnableLcuAction(EnableLcuAction &&) = default;
+    EnableLcuAction(const EnableLcuAction &) = delete;
+    EnableLcuAction &operator=(EnableLcuAction &&) = delete;
+    EnableLcuAction &operator=(const EnableLcuAction &) = delete;
+    virtual ~EnableLcuAction() = default;
+
+    virtual hailo_status execute(CONTROL_PROTOCOL__context_switch_context_info_t *context_info,
+        uint8_t **context_meta_data_head_pointer) override;
+    virtual bool supports_repeated_block() const override;
+
+private:
+    static CONTROL_PROTOCOL__ACTION_TYPE_t get_enable_lcu_action_type(bool is_default);
+    static Type get_enable_lcu_type(bool is_default);
+
+    EnableLcuAction(const ProtoHEFAction& proto_action, bool is_default, uint8_t network_index);
+    const uint8_t m_network_index;
+    const bool m_is_default;
+};
+
+class EnableSequencerAction : public ContextSwitchConfigAction
+{
+public:
+    static Expected<ContextSwitchConfigActionPtr> create(const ProtoHEFAction& proto_action);
+    EnableSequencerAction(EnableSequencerAction &&) = default;
+    EnableSequencerAction(const EnableSequencerAction &) = delete;
+    EnableSequencerAction &operator=(EnableSequencerAction &&) = delete;
+    EnableSequencerAction &operator=(const EnableSequencerAction &) = delete;
+    virtual ~EnableSequencerAction() = default;
+
+    virtual hailo_status execute(CONTROL_PROTOCOL__context_switch_context_info_t *context_info,
+        uint8_t **context_meta_data_head_pointer) override;
+    virtual bool supports_repeated_block() const override;
+
+private:
+    EnableSequencerAction(const ProtoHEFAction& proto_action, uint8_t initial_l3_cut,
+        uint16_t initial_l3_offset, uint64_t l2_offset_0, uint64_t l2_offset_1);
+
+    const uint8_t m_initial_l3_cut;
+    const uint16_t m_initial_l3_offset;
+    const uint64_t m_l2_offset_0;
+    const uint64_t m_l2_offset_1;
+};
+
+class WaitForSeqeuncerAction : public ContextSwitchConfigAction
+{
+public:
+    static Expected<ContextSwitchConfigActionPtr> create(const ProtoHEFAction& proto_action);
+    WaitForSeqeuncerAction(WaitForSeqeuncerAction &&) = default;
+    WaitForSeqeuncerAction(const WaitForSeqeuncerAction &) = delete;
+    WaitForSeqeuncerAction &operator=(WaitForSeqeuncerAction &&) = delete;
+    WaitForSeqeuncerAction &operator=(const WaitForSeqeuncerAction &) = delete;
+    virtual ~WaitForSeqeuncerAction() = default;
+
+    virtual hailo_status execute(CONTROL_PROTOCOL__context_switch_context_info_t *context_info,
+        uint8_t **context_meta_data_head_pointer) override;
+    virtual bool supports_repeated_block() const override;
+
+private:
+    WaitForSeqeuncerAction(const ProtoHEFAction& proto_action);
+};
+
+class AllowInputDataflowAction : public ContextSwitchConfigAction
+{
+public:
+    static Expected<ContextSwitchConfigActionPtr> create(const ProtoHEFAction& proto_action);
+    AllowInputDataflowAction(AllowInputDataflowAction &&) = default;
+    AllowInputDataflowAction(const AllowInputDataflowAction &) = delete;
+    AllowInputDataflowAction &operator=(AllowInputDataflowAction &&) = delete;
+    AllowInputDataflowAction &operator=(const AllowInputDataflowAction &) = delete;
+    virtual ~AllowInputDataflowAction() = default;
+
+    virtual hailo_status execute(CONTROL_PROTOCOL__context_switch_context_info_t *context_info,
+        uint8_t **context_meta_data_head_pointer) override;
+    virtual bool supports_repeated_block() const override;
+
+private:
+    static ContextSwitchConfigAction::Type get_input_dataflow_action_type(const ProtoHEFAction& proto_action);
+
+    AllowInputDataflowAction(const ProtoHEFAction& proto_action);
+};
+
+class WaitForModuleConfigDoneAction : public ContextSwitchConfigAction
+{
+public:
+    static Expected<ContextSwitchConfigActionPtr> create(const ProtoHEFAction& proto_action);
+    WaitForModuleConfigDoneAction(WaitForModuleConfigDoneAction &&) = default;
+    WaitForModuleConfigDoneAction(const WaitForModuleConfigDoneAction &) = delete;
+    WaitForModuleConfigDoneAction &operator=(WaitForModuleConfigDoneAction &&) = delete;
+    WaitForModuleConfigDoneAction &operator=(const WaitForModuleConfigDoneAction &) = delete;
+    virtual ~WaitForModuleConfigDoneAction() = default;
+
+    virtual hailo_status execute(CONTROL_PROTOCOL__context_switch_context_info_t *context_info,
+        uint8_t **context_meta_data_head_pointer) override;
+    virtual bool supports_repeated_block() const override;
+
+private:
+    WaitForModuleConfigDoneAction(const ProtoHEFAction& proto_action);
+};
+
+} /* namespace hailort */
+
+#endif /* _HEF_INTERNAL_HPP_ */
diff --git a/hailort/libhailort/src/hlpcie.cpp b/hailort/libhailort/src/hlpcie.cpp
new file mode 100644 (file)
index 0000000..634f68c
--- /dev/null
@@ -0,0 +1,405 @@
+#include <stdlib.h>
+#include <stddef.h>
+#include <time.h>
+#include <assert.h>
+#include <errno.h>
+#include "hw_consts.hpp"
+#include "hlpcie.hpp"
+#include "common/circular_buffer.hpp"
+#include "common/logger_macros.hpp"
+#include "d2h_events.h"
+#include "firmware_header.h"
+#include "os/hailort_driver.hpp"
+#include "md5.h"
+
+namespace hailort
+{
+
+/*******************************************************************************
+ * pcie control configurations
+********************************************************************************/
+typedef enum {
+    ATR_PARAM_SIZE_4KB = 11,
+    ATR_PARAM_SIZE_8KB
+} ATR_PARAM_SIZE_t;
+
+typedef enum {
+    ATR0 = 0,
+    ATR1
+} ATR_TABLES_t;
+
+typedef enum {
+    TRSL_ID_AXI4_MASTER_0 = 4,
+    TRSL_ID_AXI4_MASTER_1,
+    TRSL_ID_AXI4_MASTER_2,
+    TRSL_ID_AXI4_MASTER_3,
+} TRSL_ID_t;
+
+#define PCIE_SRAM_TABLE_SIZE (4*1024)
+#define PCIE_SRAM_BAR_4_OFFSET (0)
+#define PCIE_BLOCK_BAR_4_OFFSET (PCIE_SRAM_BAR_4_OFFSET + PCIE_SRAM_TABLE_SIZE)
+
+#define PCIE_REQUEST_SIZE  (0x640)
+#define PCIE_RESPONSE_SIZE (0x640)
+#define PCIE_D2H_EVENT_MESSAGE_SRAM_OFFSET (PCIE_REQUEST_SIZE + PCIE_RESPONSE_SIZE) 
+
+/* SRC_ADDR needs to be shifted by 12 since it takes only bits [31:12] from it. */
+#define PCI_BIT_SHIFT_FOR_4KB_GRANULARITY (12)
+#define PCIE_SRAM_BAR_4_SRC_ADDR (PCIE_SRAM_BAR_4_OFFSET >> PCI_BIT_SHIFT_FOR_4KB_GRANULARITY)
+#define PCIE_BLOCK_BAR_4_SRC_ADDR (PCIE_BLOCK_BAR_4_OFFSET >> PCI_BIT_SHIFT_FOR_4KB_GRANULARITY)
+
+#define ATR0_TABLE_SIZE (PCIE_SRAM_TABLE_SIZE)
+#define ATR1_OFFSET     (ATR0_TABLE_SIZE)
+#define ATR0_PCIE_BRIDGE_OFFSET (0x700)
+#define ATR1_PCIE_BRIDGE_OFFSET (ATR0_PCIE_BRIDGE_OFFSET + 0x20)
+
+/* atr0 table is configured to the SRAM */
+#define ATR0_SRC_ADDR       (0x0)
+#define HAILO_MERCURY_FW_CONTROL_ADDRESS (0x000BE000)
+#define HAILO8_FW_CONTROL_ADDRESS        (0x60000000)
+#define ATR0_TRSL_ADDR2     (0x0)
+
+/* atr1 table is configured to the PCIe block */
+#define ATR1_SRC_ADDR       (0x0)
+// The address macro uses __VA_ARGS__ that doesn't expand correctly with C++ if no args were given, so we must pass the default values explicitly.
+#define ATR1_TRSL_ADDR1     (PCIE_CONFIG_BASE_ADDRESS)
+#define ATR1_TRSL_ADDR2     (0x0)
+
+typedef struct {
+    uint32_t atr_param;
+    uint32_t atr_src;
+    uint32_t atr_trsl_addr_1;
+    uint32_t atr_trsl_addr_2;
+    uint32_t atr_trsl_param;
+} hailo_pcie_atr_config_t;
+
+static uint32_t fw_control_ram_address_by_board_type(HailoRTDriver::BoardType board_type) {
+    switch (board_type) {
+    case HailoRTDriver::BoardType::HAILO8:
+        return HAILO8_FW_CONTROL_ADDRESS;
+    case HailoRTDriver::BoardType::MERCURY:
+        return HAILO_MERCURY_FW_CONTROL_ADDRESS;
+    default:
+        assert(true);
+        return 0;
+    }
+}
+
+// TODO HRT-6392: validate atr in driver, remove hw-consts
+inline void get_atr_param_config(HailoRTDriver &driver, ATR_TABLES_t atr_table, hailo_pcie_atr_config_t *atr_config)
+ {
+    uint64_t param = 0;
+    assert(atr_config != NULL);
+    memset(atr_config, 0, sizeof(hailo_pcie_atr_config_t));
+
+    switch(atr_table) {
+        case ATR0:
+            PCIE_BRIDGE_CONFIG__ATR_PARAM_ATR0_PCIE_WIN1__ATR_IMPL__SET(param);
+            PCIE_BRIDGE_CONFIG__ATR_PARAM_ATR0_PCIE_WIN1__ATR_SIZE__MODIFY(param, ATR_PARAM_SIZE_4KB);
+            PCIE_BRIDGE_CONFIG__ATR_PARAM_ATR0_PCIE_WIN1__SOURCE_ADDR__MODIFY(param, PCIE_SRAM_BAR_4_SRC_ADDR);
+            atr_config->atr_param = (uint32_t)param;
+            atr_config->atr_src = ATR0_SRC_ADDR;
+            atr_config->atr_trsl_addr_1 = fw_control_ram_address_by_board_type(driver.board_type());
+            atr_config->atr_trsl_addr_2 = ATR0_TRSL_ADDR2;
+            atr_config->atr_trsl_param = TRSL_ID_AXI4_MASTER_2;
+            break;
+
+        case ATR1:
+            PCIE_BRIDGE_CONFIG__ATR_PARAM_ATR1_PCIE_WIN1__ATR_IMPL__SET(param);
+            PCIE_BRIDGE_CONFIG__ATR_PARAM_ATR1_PCIE_WIN1__ATR_SIZE__MODIFY(param, ATR_PARAM_SIZE_4KB);
+            PCIE_BRIDGE_CONFIG__ATR_PARAM_ATR1_PCIE_WIN1__SOURCE_ADDR__MODIFY(param, PCIE_BLOCK_BAR_4_SRC_ADDR);
+            atr_config->atr_param = (uint32_t)param;
+            atr_config->atr_src = ATR1_SRC_ADDR;
+            atr_config->atr_trsl_addr_1 = ATR1_TRSL_ADDR1;
+            atr_config->atr_trsl_addr_2 = ATR1_TRSL_ADDR2;
+            atr_config->atr_trsl_param = TRSL_ID_AXI4_MASTER_2;
+            break;
+
+        default:
+            LOGGER__ERROR("table param configuration not supoorted");
+    }
+   
+}
+/*******************************************************************************
+ * Private Functions
+*******************************************************************************/
+// TODO HRT-5358 - Unify MD5 functions. Use by pcie and core driver (and FW)
+void hailo_pcie__set_MD5( const uint8_t  *buffer, uint32_t buffer_length, unsigned char expected_md5[16])
+{
+    MD5_CTX ctx;
+
+    MD5_Init(&ctx);
+    MD5_Update(&ctx, buffer, buffer_length);
+    MD5_Final(expected_md5, &ctx);
+}
+
+hailo_status hailo_pcie__check_atr_configuration(HailoRTDriver &driver,  hailo_pcie_atr_config_t *atr_config_to_validate, ATR_TABLES_t atr_table)
+{
+    uint32_t offset = 0;
+    hailo_pcie_atr_config_t pcie_atr_table;
+
+    if (NULL == atr_config_to_validate) {
+        return HAILO_INVALID_ARGUMENT;
+    }
+
+    switch(atr_table) {
+        case ATR0:
+            offset = ATR0_PCIE_BRIDGE_OFFSET;
+            break;
+
+        case ATR1:
+            offset = ATR1_PCIE_BRIDGE_OFFSET;
+            break;
+
+        default:
+            LOGGER__ERROR("table param configuration not supported");
+    }
+
+    hailo_status status = driver.read_bar(PciBar::bar0, offset , sizeof(pcie_atr_table), &pcie_atr_table);
+    if (HAILO_SUCCESS != status) {
+        LOGGER__ERROR("Reading SRAM table Failed");
+        return status;
+    }
+    /* Check that ATR was configured to as the wanted configuration */
+    if (0 != memcmp(atr_config_to_validate, &pcie_atr_table, sizeof(pcie_atr_table))){
+        LOGGER__ERROR("|==================+================+===============|");
+        LOGGER__ERROR("|              ATR{} is misconfigured                |", atr_table);
+        LOGGER__ERROR("|------------------+----------------+---------------|");
+        LOGGER__ERROR("|  Field           +  Expected      +  Read         |");
+        LOGGER__ERROR("|------------------+----------------+---------------|");
+        LOGGER__ERROR("|  ATR_PARM        |  0x{:08X}    |  0x{:08X}   |", atr_config_to_validate->atr_param, pcie_atr_table.atr_param);
+        LOGGER__ERROR("|  ATR_SRC_ADDR    |  0x{:08X}    |  0x{:08X}   |", atr_config_to_validate->atr_src, pcie_atr_table.atr_src);
+        LOGGER__ERROR("|  ATR_TRSL_ADDR1  |  0x{:08X}    |  0x{:08X}   |", atr_config_to_validate->atr_trsl_addr_1, pcie_atr_table.atr_trsl_addr_1);
+        LOGGER__ERROR("|  ATR_TRSL_ADDR2  |  0x{:08X}    |  0x{:08X}   |", atr_config_to_validate->atr_trsl_addr_2, pcie_atr_table.atr_trsl_addr_2);
+        LOGGER__ERROR("|  ATR_TRSL_PARAM  |  0x{:08X}    |  0x{:08X}   |", atr_config_to_validate->atr_trsl_param, pcie_atr_table.atr_trsl_param);
+        LOGGER__ERROR("|==================+================+===============|");
+        return HAILO_ATR_TABLES_CONF_VALIDATION_FAIL;
+    }
+    return HAILO_SUCCESS;
+}
+
+hailo_status restore_atr_config(HailoRTDriver &driver, hailo_pcie_atr_config_t *old_atr_config)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+
+    status = driver.write_bar(PciBar::bar0, ATR0_PCIE_BRIDGE_OFFSET, sizeof(*old_atr_config), old_atr_config);
+    if (HAILO_SUCCESS != status) {
+        goto l_exit;
+    }
+    status = hailo_pcie__check_atr_configuration(driver, old_atr_config, ATR0);
+    if (HAILO_SUCCESS != status) {
+        LOGGER__ERROR("BAR_4 wasn't configured correctly");
+        goto l_exit;
+    }
+l_exit:
+    return status;
+}
+
+hailo_status config_atr_for_direct_memory_access(HailoRTDriver &driver, uint32_t base_address)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    hailo_pcie_atr_config_t new_atr_config = {};
+
+    get_atr_param_config(driver, ATR0, &new_atr_config);
+    new_atr_config.atr_trsl_addr_1 = base_address;
+
+    // Config BAR0 to the new ATR-configuration, and validate configuration
+    status = driver.write_bar(PciBar::bar0, ATR0_PCIE_BRIDGE_OFFSET, sizeof(new_atr_config), &new_atr_config);
+    if (HAILO_SUCCESS != status) {
+        goto l_exit;
+    }
+    
+    status = hailo_pcie__check_atr_configuration(driver, &new_atr_config, ATR0);
+    if (HAILO_SUCCESS != status) {
+        LOGGER__ERROR("BAR_4 wasn't configured correctly");
+        goto l_exit;
+    }
+
+    status = HAILO_SUCCESS;
+l_exit:
+    return status;
+}
+
+// HRT-6393 move read/write memory to driver
+hailo_status hailo_pcie__write_memory(HailoRTDriver &driver, uint32_t base_address, uint32_t offset,
+    const void *buffer, uint32_t size)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+
+    assert(0 == (base_address & (ATR0_TABLE_SIZE - 1)));
+    assert((offset + size) <= ATR0_TABLE_SIZE);
+
+    status = config_atr_for_direct_memory_access(driver, base_address);
+    if (HAILO_SUCCESS != status) {
+        goto l_exit;
+    }
+
+    status = driver.write_bar(PciBar::bar4, offset, size, buffer);
+    if (HAILO_SUCCESS != status) {
+        goto l_exit;
+    }
+
+    status = HAILO_SUCCESS;
+l_exit:
+    return status;
+}
+
+hailo_status HAILO_PCIE__write_memory(HailoRTDriver &driver, hailo_ptr_t address, const void *buffer, uint32_t size)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    uint32_t offset = 0;
+    uint32_t chunk_size = 0;
+    hailo_pcie_atr_config_t old_atr_config = {};
+    uint32_t base_address = address & ~(ATR0_TABLE_SIZE - 1);
+
+
+    // Save the old ATR-configuration to old_atr_config
+    status = driver.read_bar(PciBar::bar0, ATR0_PCIE_BRIDGE_OFFSET , sizeof(old_atr_config), &old_atr_config);
+    if (HAILO_SUCCESS != status) {
+        goto l_exit;
+    }
+
+    // Write memory
+    offset = 0;
+    if (base_address != address) {
+        chunk_size = MIN(base_address + ATR0_TABLE_SIZE - address, size);
+        status = hailo_pcie__write_memory(driver, base_address, address - base_address, buffer, chunk_size);
+        if (HAILO_SUCCESS != status) {
+            goto l_cleanup;
+        }
+        offset += chunk_size;
+    }
+    while (offset < size) {
+        chunk_size = MIN((size - offset), ATR0_TABLE_SIZE);
+        status = hailo_pcie__write_memory(driver, address + offset, 0, (void*)((uintptr_t)buffer + offset), chunk_size);
+        if (HAILO_SUCCESS != status) {
+            goto l_cleanup;
+        }
+        offset += chunk_size;
+    }
+
+    status = HAILO_SUCCESS;
+l_cleanup:
+    status = restore_atr_config(driver, &old_atr_config);
+    if (HAILO_SUCCESS != status) {
+        LOGGER__ERROR("Failed to reconfigure BAR_4 after direct memory access");
+    }
+l_exit:
+    return status;
+}
+
+hailo_status HAILO_PCIE__read_atr_to_validate_fw_is_up(HailoRTDriver &driver, bool *is_fw_up)
+{
+    hailo_pcie_atr_config_t atr_config = {};
+
+    hailo_status status = driver.read_bar(PciBar::bar0, ATR0_PCIE_BRIDGE_OFFSET , sizeof(atr_config), &atr_config);
+    if (HAILO_SUCCESS != status) {
+        LOGGER__ERROR("Reading SRAM table Failed\n");
+        *is_fw_up = false;
+        return status;
+    }
+
+    *is_fw_up = (fw_control_ram_address_by_board_type(driver.board_type()) == atr_config.atr_trsl_addr_1);
+    return HAILO_SUCCESS;
+}
+
+hailo_status hailo_pcie__read_memory(HailoRTDriver &driver, uint32_t base_address, uint32_t offset, void *buffer,
+    uint32_t size)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+
+    assert(0 == (base_address & (ATR0_TABLE_SIZE - 1)));
+    assert((offset + size) <= ATR0_TABLE_SIZE);
+
+    status = config_atr_for_direct_memory_access(driver, base_address);
+    if (HAILO_SUCCESS != status) {
+        goto l_exit;
+    }
+
+    status = driver.read_bar(PciBar::bar4, offset, size, buffer);
+    if (HAILO_SUCCESS != status) {
+        goto l_exit;
+    }
+
+    status = HAILO_SUCCESS;
+l_exit:
+    return status;
+}
+
+hailo_status HAILO_PCIE__read_memory(HailoRTDriver &driver, hailo_ptr_t address, void *buffer, uint32_t size)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    uint32_t offset = 0;
+    uint32_t chunk_size = 0;
+    uint32_t base_address = address & ~(ATR0_TABLE_SIZE - 1);
+    hailo_pcie_atr_config_t old_atr_config = {};
+
+    // Save the old ATR-configuration to old_atr_config
+    status = driver.read_bar(PciBar::bar0, ATR0_PCIE_BRIDGE_OFFSET , sizeof(old_atr_config),
+        &old_atr_config);
+    if (HAILO_SUCCESS != status) {
+        goto l_exit;
+    }
+
+    // Read memory
+    offset = 0;
+    if (base_address != address) {
+        chunk_size = MIN(base_address + ATR0_TABLE_SIZE - address, size);
+        status = hailo_pcie__read_memory(driver, base_address, address - base_address, buffer, chunk_size);
+        if (HAILO_SUCCESS != status) {
+            goto l_cleanup;
+        }
+        offset += chunk_size;
+    }
+    while (offset < size) {
+        chunk_size = MIN((size - offset), ATR0_TABLE_SIZE);
+        status = hailo_pcie__read_memory(driver, address + offset, 0, (void*)((uintptr_t)buffer + offset), chunk_size);
+        if (HAILO_SUCCESS != status) {
+            goto l_cleanup;
+        }
+        offset += chunk_size;
+    }
+
+    status = HAILO_SUCCESS;
+l_cleanup:
+    status = restore_atr_config(driver, &old_atr_config);
+    if (HAILO_SUCCESS != status) {
+        LOGGER__ERROR("Failed to reconfigure BAR_4 after direct memory access");
+    }
+l_exit:
+    return status;
+}
+
+hailo_status HAILO_PCIE__fw_interact(HailoRTDriver &driver, const void *request, size_t request_len,
+    void *response_buf, size_t *response_buf_size, uint32_t timeout_ms, hailo_cpu_id_t cpu_id)
+{
+    /* Validate ATR0 configuration before writing to BAR4 SRAM*/
+    hailo_pcie_atr_config_t atr_config;
+    get_atr_param_config(driver, ATR0, &atr_config);
+    auto status = hailo_pcie__check_atr_configuration(driver, &atr_config, ATR0);
+    CHECK_SUCCESS(status, "Validate address translation tables Failed, For FW control use.");
+
+    /* Validate ATR1 configuration before writing to BAR4 PCIe bridge block */
+    get_atr_param_config(driver, ATR1, &atr_config);
+    status = hailo_pcie__check_atr_configuration(driver, &atr_config, ATR1);
+    CHECK_SUCCESS(status, "Validate address translation tables Failed, For FW control use.");
+
+    /* Send control */
+    uint8_t request_md5[PCIE_EXPECTED_MD5_LENGTH];
+    hailo_pcie__set_MD5((uint8_t *)request, static_cast<uint32_t>(request_len), request_md5);
+    uint8_t response_md5[PCIE_EXPECTED_MD5_LENGTH];
+    status = driver.fw_control(request, request_len, request_md5,
+        response_buf, response_buf_size, response_md5,
+        std::chrono::milliseconds(timeout_ms), cpu_id);
+    CHECK_SUCCESS(status, "Failed to send fw control");
+
+    /* Validate response MD5 */
+    uint8_t calculated_md5_sum[PCIE_EXPECTED_MD5_LENGTH];
+    hailo_pcie__set_MD5((uint8_t *)response_buf, static_cast<uint32_t>(*response_buf_size), calculated_md5_sum);
+    auto memcmp_result = memcmp(calculated_md5_sum, response_md5, sizeof(calculated_md5_sum));
+    CHECK(0 == memcmp_result, HAILO_CONTROL_RESPONSE_MD5_MISMATCH, "Control response md5 not valid");
+
+    return HAILO_SUCCESS;
+}
+
+} /* namespace hailort */
diff --git a/hailort/libhailort/src/hlpcie.hpp b/hailort/libhailort/src/hlpcie.hpp
new file mode 100644 (file)
index 0000000..7136342
--- /dev/null
@@ -0,0 +1,71 @@
+/**
+ * @file pcie.h
+ */
+
+#ifndef __HLPCIE_HEADER__
+#define __HLPCIE_HEADER__
+
+#include <sys/types.h>
+#include <stdint.h>
+#include <stddef.h>
+
+#include "hailo/hailort.h"
+#include "hailo/device.hpp"
+#include "control_protocol.h"
+#include "d2h_event_queue.hpp"
+#include "os/hailort_driver.hpp"
+
+namespace hailort
+{
+
+#define FW_CODE_MAX_SIZE (0x40000)
+#define FW_CODE_MIN_SIZE (20*4)
+#define FW_KEY_CERTIFICATE_SIZE (0x348)
+#define FW_CONTENT_CERTIFICATE_SIZE (0x5f0)
+
+
+typedef uint32_t hailo_ptr_t; //Core address space is 32bit
+
+/**
+ * Writes data to device memory.
+ * 
+ * @param[in] dev     - A handle to the PCIe device.
+ * @param[in] address - The device address to write to.
+ * @param[in] buffer  - A pointer to the buffer containing the data to transfer.
+ * @param[in] size    - The amount of bytes to write.
+ * @return hailo_status
+ */
+hailo_status HAILO_PCIE__write_memory(HailoRTDriver &driver, hailo_ptr_t address, const void *buffer, uint32_t size);
+
+/**
+ * Reads data from device memory.
+ * 
+ * @param[in] dev      - A handle to the PCIe device.
+ * @param[in] address  - The device address to read from.
+ * @param[out] buffer  - A pointer to the buffer that will contain the transferred data.
+ * @param[in] size     - The amount of bytes to be read.
+ * @return hailo_status
+ */
+hailo_status HAILO_PCIE__read_memory(HailoRTDriver &driver, hailo_ptr_t address, void *buffer, uint32_t size);
+
+hailo_status HAILO_PCIE__read_atr_to_validate_fw_is_up(HailoRTDriver &driver, bool *is_fw_up);
+
+/**
+ * Interact with the firmware.
+ * 
+ * @param[in]     dev               - A handle to the PCIe device to interact with.
+ * @param[in]     request_buf       - A pointer to the request buffer to send to the firmware.
+ * @param[in]     request_buf_size  - The size in bytes of the request buffer.
+ * @param[out]    response_buf      - A pointer to the response buffer to recv from the firmware.//TODO: limitations?
+ * @param[in/out] response_buf_size - A pointer to the size in bytes to recv. The number of bytes received may be less than @response_buf_size.
+ *                                    Upon success, receives the number of bytes that was read; otherwise, untouched.
+ * @param[in]     timeout_ms       -  Time in milliseconds to wait till response will be recived from the firmware
+ * @param[in]     cpu_id           -  The CPU that will handle the control
+ * @return hailo_status
+ */
+hailo_status HAILO_PCIE__fw_interact(HailoRTDriver &driver, const void *request, size_t request_len,  void *response_buf, 
+                                     size_t *response_buf_size, uint32_t timeout_ms, hailo_cpu_id_t cpu_id);
+
+} /* namespace hailort */
+
+#endif /* __HLPCIE_HEADER__ */
diff --git a/hailort/libhailort/src/hw_consts.hpp b/hailort/libhailort/src/hw_consts.hpp
new file mode 100644 (file)
index 0000000..86e87c5
--- /dev/null
@@ -0,0 +1,42 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file hw_consts.hpp
+ * @brief Hardware constants
+ **/
+
+#ifndef _HAILO_HW_CONSTS_HPP_
+#define _HAILO_HW_CONSTS_HPP_
+
+/** stable constants **************************************************/
+
+/** Package constants *********************************************************/
+#define HAILO8_INBOUND_DATA_STREAM_SIZE                                     (0x00010000L)
+
+/** PCIe constants and macors ************************************************/
+#define PCIE_CONFIG_BASE_ADDRESS                                                        (0x00200000L)                                                                   // <hw_base_addresses_macros.h>::HW_BASE_ADDRESSES__PCIE_CONFIG(0, 0, 0)
+#define PCIE_BRIDGE_CONFIG__ATR_PARAM_ATR0_PCIE_WIN1__ATR_IMPL__SET(dst) (dst) =        ((dst) & ~0x00000001L) | ((uint32_t)(1) << 0)                                   // <pcie_bridge_config_macros.h>::PCIE_BRIDGE_CONFIG__ATR_PARAM_ATR0_PCIE_WIN1__ATR_IMPL__SET
+#define PCIE_BRIDGE_CONFIG__ATR_PARAM_ATR0_PCIE_WIN1__ATR_SIZE__MODIFY(dst, src)        (dst) = ((dst) & ~0x0000007EL) | (((uint32_t)(src) << 1) & 0x0000007EL)         // <pcie_bridge_config_macros.h>::PCIE_BRIDGE_CONFIG__ATR_PARAM_ATR0_PCIE_WIN1__ATR_SIZE__MODIFY
+#define PCIE_BRIDGE_CONFIG__ATR_PARAM_ATR0_PCIE_WIN1__SOURCE_ADDR__MODIFY(dst, src)     (dst) = ((dst) & ~0xFFFFF000L) | (((uint32_t)(src) << 12) & 0xFFFFF000L)        // <pcie_bridge_config_macros.h>::PCIE_BRIDGE_CONFIG__ATR_PARAM_ATR0_PCIE_WIN1__SOURCE_ADDR__MODIFY
+#define PCIE_BRIDGE_CONFIG__ATR_PARAM_ATR1_PCIE_WIN1__ATR_IMPL__SET(dst) (dst) =        ((dst) & ~0x00000001L) | ((uint32_t)(1) << 0)                                   // <pcie_bridge_config_macros.h>::PCIE_BRIDGE_CONFIG__ATR_PARAM_ATR1_PCIE_WIN1__ATR_IMPL__SET
+#define PCIE_BRIDGE_CONFIG__ATR_PARAM_ATR1_PCIE_WIN1__ATR_SIZE__MODIFY(dst, src)        (dst) = ((dst) & ~0x0000007EL) | (((uint32_t)(src) << 1) & 0x0000007EL)         // <pcie_bridge_config_macros.h>::PCIE_BRIDGE_CONFIG__ATR_PARAM_ATR1_PCIE_WIN1__ATR_SIZE__MODIFY
+#define PCIE_BRIDGE_CONFIG__ATR_PARAM_ATR1_PCIE_WIN1__SOURCE_ADDR__MODIFY(dst, src)     (dst) = ((dst) & ~0xFFFFF000L) | (((uint32_t)(src) << 12) & 0xFFFFF000L)        // <pcie_bridge_config_macros.h>::PCIE_BRIDGE_CONFIG__ATR_PARAM_ATR1_PCIE_WIN1__SOURCE_ADDR__MODIFY
+
+/** Vdma Channel registers ***************************************************/
+#define VDMA_CHANNEL_OFFSET(ch, is_src)     (((ch) << 5) + ((is_src) ? (0x00) : 0x10))
+#define VDMA_CHANNEL_CONTROL_OFFSET         (0x00)
+#define VDMA_CHANNEL_DEPTH_ID_OFFSET        (0x01)
+#define VDMA_CHANNEL_NUM_AVAIL_OFFSET       (0x02)
+#define VDMA_CHANNEL_NUM_PROC_OFFSET        (0x04)
+#define VDMA_CHANNEL_NUM_ONGOING_OFFSET     (0x04)
+#define VDMA_CHANNEL_ERROR_OFFSET           (0x08)
+#define VDMA_CHANNEL_ADDRESS_L_OFFSET       (0x0A)
+#define VDMA_CHANNEL_ADDRESS_H_OFFSET       (0x0C)
+
+#define VDMA_CHANNEL_ID_AXI                 (0)
+#define VDMA_CHANNEL_ID_PCIE                (4)
+
+
+#endif /* _HAILO_HW_CONSTS_HPP_ */
diff --git a/hailort/libhailort/src/inference_pipeline.cpp b/hailort/libhailort/src/inference_pipeline.cpp
new file mode 100644 (file)
index 0000000..897cbf7
--- /dev/null
@@ -0,0 +1,289 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file inference_pipeline.cpp
+ * @brief Implemention of inference pipeline
+ **/
+
+#include "hailo/inference_pipeline.hpp"
+#include "common/async_thread.hpp"
+#include "vstream_internal.hpp"
+#include "hailort_defaults.hpp"
+#include "context_switch/network_group_internal.hpp"
+
+#include <sstream>
+
+namespace hailort
+{
+
+InferVStreams::InferVStreams(std::vector<InputVStream> &&inputs, std::vector<OutputVStream> &&outputs, bool is_multi_context) :
+    m_inputs(std::move(inputs)),
+    m_outputs(std::move(outputs)),
+    m_is_multi_context(is_multi_context)
+{
+    for (auto &input : m_inputs) {
+        if (contains(m_network_name_to_input_count, input.network_name())) {
+            ++m_network_name_to_input_count[input.network_name()];
+        } else {
+            m_network_name_to_input_count.emplace(input.network_name(), 1);
+        }
+    }
+    for (auto &output : m_outputs) {
+        if (contains(m_network_name_to_output_count, output.network_name())) {
+            ++m_network_name_to_output_count[output.network_name()];
+        } else {
+            m_network_name_to_output_count.emplace(output.network_name(), 1);
+        }
+    }
+}
+
+hailo_status InferVStreams::verify_network_inputs_and_outputs(const std::map<std::string, MemoryView>& inputs_name_mem_view_map,
+                                                   const std::map<std::string, MemoryView>& outputs_name_mem_view_map)
+{
+    std::map<std::string, std::pair<size_t, size_t>> input_output_count_per_network;
+
+    for (const auto &input_name_to_memview : inputs_name_mem_view_map) {
+        auto input_vstream = get_input_by_name(input_name_to_memview.first);
+        CHECK_EXPECTED_AS_STATUS(input_vstream);
+        auto network_name = input_vstream->get().network_name();
+        if (contains(input_output_count_per_network, network_name)) {
+            ++input_output_count_per_network[network_name].first;
+        } else {
+            input_output_count_per_network.emplace(network_name, std::pair<size_t, size_t>(1, 0));
+        }
+    }
+    for (const auto &output_name_to_memview : outputs_name_mem_view_map) {
+        auto output_vstream = get_output_by_name(output_name_to_memview.first);
+        CHECK_EXPECTED_AS_STATUS(output_vstream);
+        auto network_name = output_vstream->get().network_name();
+        if (contains(input_output_count_per_network, network_name)) {
+            ++input_output_count_per_network[network_name].second;
+        } else {
+            input_output_count_per_network.emplace(network_name, std::pair<size_t, size_t>(0, 1));
+        }
+    }
+    CHECK(!m_is_multi_context || (input_output_count_per_network.size() == m_network_name_to_input_count.size()), HAILO_INVALID_ARGUMENT,
+        "For multi-context network groups, inference is only supported on all available networks");
+
+    for (const auto &network_to_input_output_count : input_output_count_per_network) {
+        CHECK(network_to_input_output_count.second.first == m_network_name_to_input_count[network_to_input_output_count.first],
+            HAILO_INVALID_ARGUMENT, "Not all inputs have been provided for network {}", network_to_input_output_count.first);
+        CHECK(network_to_input_output_count.second.second == m_network_name_to_output_count[network_to_input_output_count.first],
+            HAILO_INVALID_ARGUMENT, "Not all outputs have been provided for network {}", network_to_input_output_count.first);
+    }
+    return HAILO_SUCCESS;
+}
+
+static hailo_status verify_vstream_params_in_vstream_infos(const std::map<std::string, hailo_vstream_params_t> &params,
+    const std::vector<hailo_vstream_info_t> &vstream_infos)
+{
+    for (const auto &name_to_param : params) {
+        const auto &name = name_to_param.first;
+        bool found = false;
+        for (const auto &vstream_info : vstream_infos) {
+            if (vstream_info.name == name) {
+                found = true;
+                break;
+            }
+        }
+        CHECK(found, HAILO_NOT_FOUND, "Could not find vstream {}", name);
+    }
+    return HAILO_SUCCESS;
+}
+
+Expected<InferVStreams> InferVStreams::create(ConfiguredNetworkGroup &net_group,
+        const std::map<std::string, hailo_vstream_params_t> &input_params,
+        const std::map<std::string, hailo_vstream_params_t> &output_params)
+{
+    auto network_infos = net_group.get_network_infos();
+    CHECK_EXPECTED(network_infos);
+    auto is_multi_context = (dynamic_cast<ConfiguredNetworkGroupBase&>(net_group)).get_supported_features().multi_context;
+    std::map<std::string, std::pair<size_t, size_t>> input_param_count_per_network;
+    size_t total_inputs_found = 0;
+    size_t total_outputs_found = 0;
+    for (const auto &network_info : network_infos.value()) {
+        auto input_vstream_infos_per_network = net_group.get_input_vstream_infos(network_info.name);
+        CHECK_EXPECTED(input_vstream_infos_per_network);
+        size_t input_counter = 0;
+        for (const auto &vstream_info : input_vstream_infos_per_network.value()) {
+            if (contains(input_params, std::string(vstream_info.name))) {
+                ++input_counter;
+                ++total_inputs_found;
+            }
+        }
+
+        auto output_vstream_infos_per_network = net_group.get_output_vstream_infos(network_info.name);
+        CHECK_EXPECTED(output_vstream_infos_per_network);
+        size_t output_counter = 0;
+        for (const auto &vstream_info : output_vstream_infos_per_network.value()) {
+            if (contains(output_params, std::string(vstream_info.name))) {
+                ++output_counter;
+                ++total_outputs_found;
+            }
+        }
+
+        if (0 != input_counter || 0 != output_counter) {
+            CHECK_AS_EXPECTED(input_counter == input_vstream_infos_per_network->size(), HAILO_INVALID_ARGUMENT,
+                "Found only partial inputs for network {}", network_info.name);
+            CHECK_AS_EXPECTED(output_counter == output_vstream_infos_per_network->size(), HAILO_INVALID_ARGUMENT,
+                "Found only partial outputs for network {}", network_info.name);
+        } else {
+            CHECK_AS_EXPECTED(!is_multi_context, HAILO_INVALID_ARGUMENT,
+                "For multi-context network groups, the pipeline must be created for all available networks");
+        }
+    }
+
+    if (total_inputs_found != input_params.size()) {
+        auto all_input_vstream_infos = net_group.get_input_vstream_infos();
+        CHECK_EXPECTED(all_input_vstream_infos);
+        auto status = verify_vstream_params_in_vstream_infos(input_params, all_input_vstream_infos.release());
+        CHECK_SUCCESS_AS_EXPECTED(status);
+    }
+    if (total_outputs_found != output_params.size()) {
+        auto all_output_vstream_infos = net_group.get_input_vstream_infos();
+        CHECK_EXPECTED(all_output_vstream_infos);
+        auto status = verify_vstream_params_in_vstream_infos(output_params, all_output_vstream_infos.release());
+        CHECK_SUCCESS_AS_EXPECTED(status);
+    }
+
+    auto input_vstreams = VStreamsBuilder::create_input_vstreams(net_group, input_params);
+    CHECK_EXPECTED(input_vstreams);
+    auto output_vstreams = VStreamsBuilder::create_output_vstreams(net_group, output_params);
+    CHECK_EXPECTED(output_vstreams);
+
+    return InferVStreams(input_vstreams.release(), output_vstreams.release(), is_multi_context);
+}
+
+hailo_status InferVStreams::infer(const std::map<std::string, MemoryView>& input_data,
+    std::map<std::string, MemoryView>& output_data, size_t batch_size)
+{
+    auto status = verify_network_inputs_and_outputs(input_data, output_data);
+    CHECK_SUCCESS(status);
+    status = verify_memory_view_size(input_data, output_data, batch_size);
+    CHECK_SUCCESS(status);
+
+    std::vector<AsyncThreadPtr<hailo_status>> results;
+
+    // Launch async read/writes
+    for (auto &input_name_to_data_pair : input_data) {
+        auto input_vstream_exp = get_input_by_name(input_name_to_data_pair.first);
+        CHECK_EXPECTED_AS_STATUS(input_vstream_exp);
+        auto &input_vstream = input_vstream_exp.release().get();
+        results.emplace_back(std::make_unique<AsyncThread<hailo_status>>(
+            [&input_vstream, &input_name_to_data_pair, batch_size]() -> hailo_status {
+                const auto &input_buffer = input_name_to_data_pair.second;
+                for (uint32_t i = 0; i < batch_size; i++) {
+                    const size_t offset = i * input_vstream.get_frame_size();
+                    auto status = input_vstream.write(MemoryView::create_const(
+                        input_buffer.data() + offset,
+                        input_vstream.get_frame_size()));
+                    if (HAILO_STREAM_INTERNAL_ABORT == status) {
+                        LOGGER__DEBUG("Input stream was aborted!");
+                        return status;
+                    }
+                    CHECK_SUCCESS(status);
+                }
+                return HAILO_SUCCESS;
+            }
+        ));
+    }
+    for (auto &output_name_to_data_pair : output_data) {
+        auto output_vstream_exp = get_output_by_name(output_name_to_data_pair.first);
+        CHECK_EXPECTED_AS_STATUS(output_vstream_exp);
+        auto &output_vstream = output_vstream_exp.release().get();
+        results.emplace_back(std::make_unique<AsyncThread<hailo_status>>(
+            [&output_vstream, &output_name_to_data_pair, batch_size]() {
+                for (size_t i = 0; i < batch_size; i++) {
+                    auto status = output_vstream.read(MemoryView(output_name_to_data_pair.second.data() + i * output_vstream.get_frame_size(), output_vstream.get_frame_size()));
+                    if (HAILO_SUCCESS != status) {
+                        return status;
+                    }
+                }
+                return HAILO_SUCCESS;
+            }
+        ));
+    }
+
+    // Wait for all results
+    auto error_status = HAILO_SUCCESS;
+    for (auto& result : results) {
+        status = result->get();
+        if (HAILO_STREAM_INTERNAL_ABORT == status) {
+            continue;
+        }
+        if (HAILO_SUCCESS != status) {
+            error_status = status;
+            LOGGER__ERROR("Failed waiting for threads with status {}", error_status);
+        }
+    }
+    if (HAILO_SUCCESS != error_status) {
+        return error_status;
+    }
+
+    return HAILO_SUCCESS;
+}
+
+hailo_status InferVStreams::verify_memory_view_size(const std::map<std::string, MemoryView>& inputs_name_mem_view_map,
+    const std::map<std::string, MemoryView>& outputs_name_mem_view_map, size_t batch_count)
+{
+    for (const auto &input_name_to_memview : inputs_name_mem_view_map) {
+        auto input_vstream_exp = get_input_by_name(input_name_to_memview.first);
+        CHECK_EXPECTED_AS_STATUS(input_vstream_exp);
+        auto &input_vstream = input_vstream_exp.release().get();
+        CHECK(batch_count * input_vstream.get_frame_size() == input_name_to_memview.second.size(), HAILO_INVALID_ARGUMENT,
+            "Memory size of vstream {} does not match the frame count! (Expected {}, got {})",
+            input_vstream.name(), batch_count * input_vstream.get_frame_size(), input_name_to_memview.second.size());
+    }
+    for (const auto &output_name_to_memview : outputs_name_mem_view_map) {
+        auto output_vstream_exp = get_output_by_name(output_name_to_memview.first);
+        CHECK_EXPECTED_AS_STATUS(output_vstream_exp);
+        auto &output_vstream = output_vstream_exp.release().get();
+        CHECK(batch_count * output_vstream.get_frame_size() == output_name_to_memview.second.size(), HAILO_INVALID_ARGUMENT,
+            "Memory size of vstream {} does not match the frame count! (Expected {}, got {})",
+            output_vstream.name(), batch_count * output_vstream.get_frame_size(), output_name_to_memview.second.size());
+    }
+
+    return HAILO_SUCCESS;
+}
+
+Expected<std::reference_wrapper<InputVStream>> InferVStreams::get_input_by_name(const std::string &name)
+{
+    for (auto &input_vstream : m_inputs) {
+        if (input_vstream.name() == name) {
+            return std::ref(input_vstream);
+        }
+    }
+    return make_unexpected(HAILO_NOT_FOUND);
+}
+
+Expected<std::reference_wrapper<OutputVStream>> InferVStreams::get_output_by_name(const std::string &name)
+{
+    for (auto &ouput_vstream : m_outputs) {
+        if (ouput_vstream.name() == name) {
+            return std::ref(ouput_vstream);
+        }
+    }
+    return make_unexpected(HAILO_NOT_FOUND);
+}
+
+std::vector<std::reference_wrapper<InputVStream>> InferVStreams::get_input_vstreams()
+{
+    std::vector<std::reference_wrapper<InputVStream>> vsterams_refs;
+    for (auto &input_vstream : m_inputs) {
+        vsterams_refs.push_back(std::ref(input_vstream));
+    }
+    return vsterams_refs;
+}
+
+std::vector<std::reference_wrapper<OutputVStream>> InferVStreams::get_output_vstreams()
+{
+    std::vector<std::reference_wrapper<OutputVStream>> vsterams_refs;
+    for (auto &ouput_vstream : m_outputs) {
+        vsterams_refs.push_back(std::ref(ouput_vstream));
+    }
+    return vsterams_refs;
+}
+
+} /* namespace hailort */
diff --git a/hailort/libhailort/src/intermediate_buffer.cpp b/hailort/libhailort/src/intermediate_buffer.cpp
new file mode 100644 (file)
index 0000000..e34c1e5
--- /dev/null
@@ -0,0 +1,100 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file intermediate_buffer.cpp
+ * @brief Manages intermediate buffer for inter-context or ddr channels.
+ */
+
+#include "intermediate_buffer.hpp"
+#include "context_switch/multi_context/resource_manager.hpp"
+
+namespace hailort
+{
+
+Expected<std::unique_ptr<IntermediateBuffer>> IntermediateBuffer::create(Type type, HailoRTDriver &driver,
+    const uint32_t transfer_size, const uint16_t batch_size)
+{
+    switch (type) {
+    case Type::EXTERNAL_DESC:
+        return ExternalDescIntermediateBuffer::create(driver, transfer_size, batch_size);
+    default:
+        LOGGER__ERROR("Invalid intermediate buffer type {}\n", static_cast<int>(type));
+        return make_unexpected(HAILO_INVALID_OPERATION);
+    }
+}
+
+Expected<std::unique_ptr<IntermediateBuffer>> ExternalDescIntermediateBuffer::create(HailoRTDriver &driver,
+    const uint32_t transfer_size, const uint16_t transfers_count)
+{
+    auto desc_sizes_pair = VdmaDescriptorList::get_desc_buffer_sizes_for_single_transfer(driver,
+        transfers_count, transfers_count, transfer_size);
+    CHECK_EXPECTED(desc_sizes_pair);
+    auto desc_page_size = desc_sizes_pair->first;
+    auto descs_count = desc_sizes_pair->second;
+
+    auto buffer = VdmaBuffer::create((descs_count * desc_page_size), HailoRTDriver::DmaDirection::BOTH, driver);
+    CHECK_EXPECTED(buffer);
+
+    auto desc_buffer = VdmaDescriptorList::create(descs_count, desc_page_size, driver);
+    CHECK_EXPECTED(desc_buffer);
+
+    // On dram-dma all descriptors should have channel index, until we implement CCB,
+    // we use some fake channel index. Currently the channel_index is not in used by
+    // the hw. TODO HRT-5835: remove channel_index. 
+    const uint8_t channel_index = 0;
+    auto status = desc_buffer->configure_to_use_buffer(buffer.value(), channel_index);
+    CHECK_SUCCESS_AS_EXPECTED(status);
+
+    auto intermediate_buffer = make_unique_nothrow<ExternalDescIntermediateBuffer>(buffer.release(), desc_buffer.release(),
+        transfer_size, transfers_count);
+    CHECK_NOT_NULL_AS_EXPECTED(intermediate_buffer, HAILO_OUT_OF_HOST_MEMORY);
+
+    return std::unique_ptr<IntermediateBuffer>(intermediate_buffer.release());
+}
+
+hailo_status ExternalDescIntermediateBuffer::program_inter_context()
+{
+    size_t acc_offset = 0;
+    for (uint16_t i = 0; i < m_transfers_count; i++) {
+        auto first_desc_interrupts_domain = VdmaInterruptsDomain::NONE;
+        auto last_desc_interrupts_domain = ((m_transfers_count - 1) == i) ? VdmaInterruptsDomain::DEVICE : 
+                                                                     VdmaInterruptsDomain::NONE;
+        auto desc_count_local = m_desc_list.program_descriptors(m_transfer_size, first_desc_interrupts_domain,
+            last_desc_interrupts_domain, acc_offset, false);
+        CHECK_EXPECTED_AS_STATUS(desc_count_local,
+            "Failed to program descs for intermediate channels. Given batch_size is too big.");
+        acc_offset += desc_count_local.value();
+    }
+    return HAILO_SUCCESS;
+}
+
+Expected<uint16_t> ExternalDescIntermediateBuffer::program_ddr()
+{
+    return m_desc_list.program_descs_for_ddr_transfers(m_desc_list.desc_page_size(), false, 1, 
+        static_cast<uint32_t>(m_desc_list.count()), 0, true);
+}
+
+Expected<uint16_t> ExternalDescIntermediateBuffer::program_host_managed_ddr(uint16_t row_size, uint32_t buffered_rows,
+    uint16_t initial_desc_offset)
+{
+    return m_desc_list.program_descs_for_ddr_transfers(row_size, true, DDR_NUMBER_OF_ROWS_PER_INTERRUPT,
+        buffered_rows, initial_desc_offset, true);
+}
+
+Expected<Buffer> ExternalDescIntermediateBuffer::read()
+{
+    const auto size = m_transfer_size * m_transfers_count;
+    assert(size <= m_buffer.size());
+
+    auto res = Buffer::create(size);
+    CHECK_EXPECTED(res);
+
+    auto status = m_buffer.read(res->data(), size, 0);
+    CHECK_SUCCESS_AS_EXPECTED(status);
+
+    return res.release();
+}
+
+} /* namespace hailort */
diff --git a/hailort/libhailort/src/intermediate_buffer.hpp b/hailort/libhailort/src/intermediate_buffer.hpp
new file mode 100644 (file)
index 0000000..2020242
--- /dev/null
@@ -0,0 +1,126 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file intermediate_buffer.hpp
+ * @brief Manages intermediate buffer for inter-context or ddr channels.
+ */
+
+#ifndef _HAILO_INTERMEDIATE_BUFFER_HPP_
+#define _HAILO_INTERMEDIATE_BUFFER_HPP_
+
+#include "os/hailort_driver.hpp"
+#include "vdma_buffer.hpp"
+#include "vdma_descriptor_list.hpp"
+#include "hailo/expected.hpp"
+#include "hailo/buffer.hpp"
+
+
+namespace hailort
+{
+
+class IntermediateBuffer {
+public:
+
+    enum class Type {
+        EXTERNAL_DESC
+    };
+
+    static Expected<std::unique_ptr<IntermediateBuffer>> create(Type type, HailoRTDriver &driver,
+        const uint32_t transfer_size, const uint16_t batch_size);
+
+    virtual ~IntermediateBuffer() = default;
+    IntermediateBuffer(const IntermediateBuffer &) = delete;
+    IntermediateBuffer& operator=(const IntermediateBuffer &) = delete;
+    IntermediateBuffer(IntermediateBuffer &&) = default;
+    IntermediateBuffer& operator=(IntermediateBuffer &&) = delete;
+
+
+    virtual hailo_status program_inter_context() = 0;
+
+    // Returns the amount of programed descriptors
+    virtual Expected<uint16_t> program_ddr() = 0;
+    virtual Expected<uint16_t> program_host_managed_ddr(uint16_t row_size, uint32_t buffered_rows,
+        uint16_t initial_desc_offset) = 0;
+
+    virtual uint64_t dma_address() const = 0;
+    virtual uint16_t descriptors_in_frame() const = 0;
+    virtual uint16_t desc_page_size() const = 0;
+    virtual uint16_t descs_count() const = 0;
+    virtual uint8_t depth() const = 0;
+
+    // Should be only used for host managed ddr buffer, in the future this function may return nullptr (on CCB
+    // case where there is no descriptors list)
+    virtual VdmaDescriptorList* get_desc_list() = 0;
+
+    virtual Expected<Buffer> read() = 0;
+
+protected:
+    IntermediateBuffer() = default;
+};
+
+class ExternalDescIntermediateBuffer : public IntermediateBuffer
+{
+public:
+
+    static Expected<std::unique_ptr<IntermediateBuffer>> create(HailoRTDriver &driver, const uint32_t transfer_size,
+        const uint16_t batch_size);
+
+    ExternalDescIntermediateBuffer(VdmaBuffer &&buffer, VdmaDescriptorList &&desc_list,
+        const uint32_t transfer_size, const uint32_t transfers_count) :
+           m_buffer(std::move(buffer)), m_desc_list(std::move(desc_list)),
+           m_transfer_size(transfer_size), m_transfers_count(transfers_count) {};
+
+    hailo_status program_inter_context() override;
+
+    // Returns the amount of programed descriptors
+    Expected<uint16_t> program_ddr() override;
+    Expected<uint16_t> program_host_managed_ddr(uint16_t row_size, uint32_t buffered_rows,
+        uint16_t initial_desc_offset) override;
+
+    uint64_t dma_address() const override
+    {
+        return m_desc_list.dma_address();
+    }
+
+    uint16_t descriptors_in_frame() const override
+    {
+        return static_cast<uint16_t>(m_desc_list.descriptors_in_buffer(m_transfer_size));
+    }
+
+    uint16_t desc_page_size() const override
+    {
+        return m_desc_list.desc_page_size();
+    }
+
+    uint16_t descs_count() const override
+    {
+        return static_cast<uint16_t>(m_desc_list.count());
+    }
+
+    uint8_t depth() const override
+    {
+        return m_desc_list.depth();
+    }
+
+    // Should be only used for host managed ddr buffer, in the future this function may return nullptr (on CCB
+    // case where there is no descriptors list)
+    VdmaDescriptorList* get_desc_list() override
+    {
+        return &m_desc_list;
+    }
+
+    Expected<Buffer> read() override;
+
+private:
+
+    VdmaBuffer m_buffer;
+    VdmaDescriptorList m_desc_list;
+    const uint32_t m_transfer_size;
+    const uint32_t m_transfers_count;
+};
+
+} /* namespace hailort */
+
+#endif /* _HAILO_INTERMEDIATE_BUFFER_HPP_ */
\ No newline at end of file
diff --git a/hailort/libhailort/src/layer_info.hpp b/hailort/libhailort/src/layer_info.hpp
new file mode 100644 (file)
index 0000000..3027463
--- /dev/null
@@ -0,0 +1,152 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file hef.hpp
+ * @brief Hef parsing and configuration functions
+ **/
+
+#ifndef _HAILO_LAYER_INFO_HPP_
+#define _HAILO_LAYER_INFO_HPP_
+
+#include "hailo/hailort.h"
+#include "hailo/hailort_common.hpp"
+#include "hailort_defaults.hpp"
+
+#include <vector>
+#include <memory>
+#include <map>
+
+namespace hailort
+{
+
+struct BufferIndices {
+    uint32_t index;
+    uint32_t cluster_index;
+};
+
+struct LayerInfo {
+    bool is_mux;
+    std::vector<LayerInfo> predecessor;
+    bool is_defused_nms;
+    // TODO HRT-4441 change fused_layer from vector.
+    std::vector<LayerInfo> fused_nms_layer;
+    hailo_3d_image_shape_t shape;
+    hailo_3d_image_shape_t hw_shape;
+    uint32_t hw_data_bytes;
+    hailo_format_t format;
+    hailo_stream_direction_t direction;
+    uint8_t index;
+    std::string name;
+    hailo_quant_info_t quant_info;
+    hailo_nms_info_t nms_info;
+    uint32_t height_gcd;
+    std::vector<uint32_t> height_ratios;
+    // Note - network name as seen by the user is build in the following way - "network_group_name / partial_network name"
+    // In layer info scope - we would use the partial network name only. 
+    std::string partial_network_name;
+
+    // Simulation Info
+    BufferIndices buffer_indices;
+
+    // HW Info
+    uint16_t core_bytes_per_buffer;
+    uint16_t core_buffers_per_frame;
+};
+
+class LayerInfoUtils {
+public:
+    static hailo_stream_info_t get_stream_info_from_layer_info(const LayerInfo &layer_info)
+    {
+        hailo_stream_info_t res = {};
+        res.hw_data_bytes = layer_info.hw_data_bytes;
+        res.format = layer_info.format;
+        if (HAILO_FORMAT_ORDER_HAILO_NMS == res.format.order) {
+            res.nms_info = layer_info.nms_info;
+            res.hw_frame_size =
+                HailoRTCommon::get_nms_hw_frame_size(res.nms_info);
+        } else {
+            res.shape.height = layer_info.shape.height;
+            res.shape.width = layer_info.shape.width;
+            res.shape.features = layer_info.shape.features;
+            res.hw_shape.height = layer_info.hw_shape.height;
+            res.hw_shape.width = layer_info.hw_shape.width;
+            res.hw_shape.features = layer_info.hw_shape.features;
+            res.hw_frame_size =
+                res.hw_shape.height * res.hw_shape.width * res.hw_shape.features * res.hw_data_bytes;
+        }
+        res.direction = layer_info.direction;
+        res.index = layer_info.index;
+        assert(layer_info.name.length() < HAILO_MAX_NAME_SIZE);
+        strncpy(res.name, layer_info.name.c_str(), layer_info.name.length() + 1);
+        res.quant_info = layer_info.quant_info;
+        res.is_mux = layer_info.is_mux;
+
+        return res;
+    }
+
+    static bool vstream_info_already_in_vector(const std::vector<hailo_vstream_info_t> &vec, const std::string &name)
+    {
+        for (const auto &info : vec) {
+            if (name == info.name) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    static std::vector<hailo_vstream_info_t> get_vstream_infos_from_layer_info(const LayerInfo &layer_info, const std::string &network_group_name)
+    {
+        std::vector<hailo_vstream_info_t> res = {};
+        if (layer_info.is_mux) {
+            for (auto &pred : layer_info.predecessor) {
+                auto vstream_infos = get_vstream_infos_from_layer_info(pred, network_group_name);
+                res.insert(res.end(), vstream_infos.begin(), vstream_infos.end());
+            }
+        } else if (layer_info.is_defused_nms) {
+            for (auto &fused_nms : layer_info.fused_nms_layer) {
+                // In case of fused nms layers, several LayerInfos will contain data about the same fused layer
+                if (!vstream_info_already_in_vector(res, fused_nms.name)) {
+                    auto vstream_info = get_vstream_info_from_layer_info_impl(fused_nms, network_group_name);
+                    res.push_back(vstream_info);
+                }
+            }
+        } else {
+            auto vstream_info = get_vstream_info_from_layer_info_impl(layer_info, network_group_name);
+            res.push_back(vstream_info);
+        }
+
+        return res;
+    }
+
+private:
+    static hailo_vstream_info_t get_vstream_info_from_layer_info_impl(const LayerInfo &layer_info, const std::string &network_group_name)
+    {
+        hailo_vstream_info_t res = {};
+        res.format.type = layer_info.format.type;
+        res.format.flags = layer_info.format.flags;
+        res.format.order = HailoRTDefaults::get_default_host_format_order(layer_info.format);
+        if (HAILO_FORMAT_ORDER_HAILO_NMS == res.format.order) {
+            res.nms_shape.max_bboxes_per_class = layer_info.nms_info.max_bboxes_per_class * layer_info.nms_info.chunks_per_frame;
+            res.nms_shape.number_of_classes = layer_info.nms_info.number_of_classes;
+        } else {
+            res.shape.height = layer_info.shape.height;
+            res.shape.width = layer_info.shape.width;
+            res.shape.features = layer_info.shape.features;
+        }
+        res.direction = layer_info.direction;
+        assert(layer_info.name.length() < HAILO_MAX_STREAM_NAME_SIZE);
+        strncpy(res.name, layer_info.name.c_str(), layer_info.name.length() + 1);
+        auto network_name = network_group_name + HAILO_DEFAULT_NETWORK_NAME_QUALIFIER + layer_info.partial_network_name;
+        assert(network_name.length() < HAILO_MAX_NETWORK_NAME_SIZE);
+        strncpy(res.network_name, network_name.c_str(), network_name.length() + 1);
+        res.quant_info = layer_info.quant_info;
+
+        return res;
+    }
+};
+
+} /* namespace hailort */
+
+#endif /* _HAILO_LAYER_INFO_HPP_ */
diff --git a/hailort/libhailort/src/mipi_stream.cpp b/hailort/libhailort/src/mipi_stream.cpp
new file mode 100644 (file)
index 0000000..59cb243
--- /dev/null
@@ -0,0 +1,155 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file mipi_stream.cpp
+ * @brief MIPI stream implementation.
+ *
+ * MIPI is an interface which defines the connection between a host processor and a display
+ * module, which in our case is a camera. Here we define a MipiInputStream which when it is configured
+ * and opened, the camera module should send input data, meaning that the write functions here are not
+ * implemented.
+ **/
+
+#include <hailo/hailort.h>
+#include "common/utils.hpp"
+#include <control.hpp>
+#include "mipi_stream.hpp"
+
+namespace hailort
+{
+
+MipiInputStream::MipiInputStream(Device &device, const CONTROL_PROTOCOL__mipi_input_config_params_t &mipi_params,
+    EventPtr &&network_group_activated_event, const LayerInfo &layer_info, hailo_status &status) :
+    InputStreamBase(layer_info, HAILO_STREAM_INTERFACE_MIPI, std::move(network_group_activated_event), status),
+    m_device(device),
+    m_is_stream_activated(false),
+    m_mipi_input_params(mipi_params)
+{}
+
+MipiInputStream::~MipiInputStream()
+{
+    if (m_is_stream_activated) {
+        auto status = deactivate_stream();
+        if (HAILO_SUCCESS != status) {
+            LOGGER__ERROR("Close stream failed! (status {} stream index {})", status, m_stream_info.index);
+        }
+    }
+}
+
+hailo_status MipiInputStream::deactivate_stream()
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+
+    ASSERT(m_is_stream_activated);
+
+    status = Control::close_stream(m_device, m_dataflow_manager_id, true);
+    CHECK_SUCCESS(status);
+
+    m_is_stream_activated = false;
+    return HAILO_SUCCESS;
+}
+
+std::chrono::milliseconds MipiInputStream::get_timeout() const
+{
+    LOGGER__WARNING("get_timeout() in MipiInputStream is not supported!");
+    assert(false);
+    return std::chrono::milliseconds(0);
+}
+
+hailo_status MipiInputStream::abort()
+{
+    return HAILO_INVALID_OPERATION;
+}
+
+hailo_status MipiInputStream::clear_abort()
+{
+    return HAILO_INVALID_OPERATION;
+}
+
+CONTROL_PROTOCOL__mipi_input_config_params_t MipiInputStream::hailo_mipi_params_to_control_mipi_params(
+    const hailo_mipi_input_stream_params_t &params)
+{
+    CONTROL_PROTOCOL__mipi_input_config_params_t control_mipi_params;
+    control_mipi_params.common_params.data_type = static_cast<uint8_t>(params.data_type);
+    control_mipi_params.common_params.pixels_per_clock = static_cast<uint8_t>(params.mipi_common_params.pixels_per_clock);
+    control_mipi_params.mipi_rx_id = params.mipi_rx_id;
+    control_mipi_params.common_params.number_of_lanes = params.mipi_common_params.number_of_lanes;
+    control_mipi_params.common_params.clock_selection = static_cast<uint8_t>(params.mipi_common_params.clock_selection);
+    control_mipi_params.common_params.data_rate = params.mipi_common_params.data_rate;
+    control_mipi_params.common_params.virtual_channel_index = params.mipi_common_params.virtual_channel_index;
+    control_mipi_params.common_params.img_width_pixels = params.mipi_common_params.img_width_pixels;
+    control_mipi_params.common_params.img_height_pixels = params.mipi_common_params.img_height_pixels;
+    control_mipi_params.isp_params.isp_enable = params.isp_enable;
+    control_mipi_params.isp_params.isp_img_in_order = static_cast<uint8_t>(params.isp_params.isp_img_in_order);
+    control_mipi_params.isp_params.isp_img_out_data_type = static_cast<uint8_t>(params.isp_params.isp_img_out_data_type);
+    control_mipi_params.isp_params.isp_crop_enable = params.isp_params.isp_crop_enable;
+    control_mipi_params.isp_params.isp_crop_output_width_pixels = params.isp_params.isp_crop_output_width_pixels;
+    control_mipi_params.isp_params.isp_crop_output_height_pixels = params.isp_params.isp_crop_output_height_pixels;
+    control_mipi_params.isp_params.isp_crop_output_width_start_offset_pixels = params.isp_params.isp_crop_output_width_start_offset_pixels;
+    control_mipi_params.isp_params.isp_crop_output_height_start_offset_pixels = params.isp_params.isp_crop_output_height_start_offset_pixels;
+    control_mipi_params.isp_params.isp_test_pattern_enable = params.isp_params.isp_test_pattern_enable;
+    control_mipi_params.isp_params.isp_configuration_bypass = params.isp_params.isp_configuration_bypass;
+    control_mipi_params.isp_params.isp_run_time_ae_enable = params.isp_params.isp_run_time_ae_enable;
+    control_mipi_params.isp_params.isp_run_time_awb_enable = params.isp_params.isp_run_time_awb_enable;
+    control_mipi_params.isp_params.isp_run_time_adt_enable = params.isp_params.isp_run_time_adt_enable;
+    control_mipi_params.isp_params.isp_run_time_af_enable = params.isp_params.isp_run_time_af_enable;
+    control_mipi_params.isp_params.isp_run_time_calculations_interval_ms = params.isp_params.isp_run_time_calculations_interval_ms;
+    control_mipi_params.isp_params.isp_light_frequency = static_cast<uint8_t>(params.isp_params.isp_light_frequency);
+    return control_mipi_params;
+}
+
+hailo_status MipiInputStream::activate_stream()
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    CONTROL_PROTOCOL__config_stream_params_t params = {};
+
+    params.nn_stream_config = m_nn_stream_config;
+    params.communication_type = CONTROL_PROTOCOL__COMMUNICATION_TYPE_MIPI;
+    params.is_input = true;
+    params.stream_index = m_stream_info.index;
+    params.skip_nn_stream_config = false;
+    // Currently hardcoded assign as there are no power mode optimizations over mipi
+    params.power_mode = static_cast<uint8_t>(CONTROL_PROTOCOL__MODE_ULTRA_PERFORMANCE);
+    params.communication_params.mipi_input = m_mipi_input_params;
+
+    status = Control::config_stream_mipi_input(m_device, &params, m_dataflow_manager_id);
+    CHECK_SUCCESS(status);
+
+    status = Control::open_stream(m_device, m_dataflow_manager_id, true);
+    CHECK_SUCCESS(status);
+
+    m_is_stream_activated = true;
+    return HAILO_SUCCESS;
+}
+
+Expected<size_t> MipiInputStream::sync_write_raw_buffer(const MemoryView &buffer)
+{
+    (void)buffer;
+    return make_unexpected(HAILO_INVALID_OPERATION);
+}
+
+hailo_status MipiInputStream::sync_write_all_raw_buffer_no_transform_impl(void *buffer, size_t offset, size_t size)
+{
+    (void)buffer;
+    (void)offset;
+    (void)size;
+    return HAILO_INVALID_OPERATION;
+}
+
+Expected<std::unique_ptr<MipiInputStream>> MipiInputStream::create(Device &device,
+    const LayerInfo &edge_layer, const hailo_mipi_input_stream_params_t &params,
+    EventPtr network_group_activated_event)
+{
+    auto mipi_params = MipiInputStream::hailo_mipi_params_to_control_mipi_params(params);
+    auto status = HAILO_UNINITIALIZED;
+    std::unique_ptr<MipiInputStream> stream(new (std::nothrow) MipiInputStream(device, mipi_params,
+        std::move(network_group_activated_event), edge_layer, status));
+    CHECK_AS_EXPECTED(stream != nullptr, HAILO_OUT_OF_HOST_MEMORY);
+    CHECK_SUCCESS_AS_EXPECTED(status);
+
+    return stream;
+}
+
+} /* namespace hailort */
diff --git a/hailort/libhailort/src/mipi_stream.hpp b/hailort/libhailort/src/mipi_stream.hpp
new file mode 100644 (file)
index 0000000..dfc6233
--- /dev/null
@@ -0,0 +1,65 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file mipi_stream.hpp
+ * @brief MIPI stream definition.
+ *
+ * MipiInputStream is defined which will give the option to infer with data from a MIPI interface/sensor.
+ **/
+
+#ifndef HAILO_MIPI_STREAM_H_
+#define HAILO_MIPI_STREAM_H_
+
+#include "stream_internal.hpp"
+#include "hailo/hailort.h"
+#include "hailo/expected.hpp"
+#include "hailo/event.hpp"
+
+namespace hailort
+{
+
+class MipiInputStream : public InputStreamBase {
+private:
+    MipiInputStream(Device &device, const CONTROL_PROTOCOL__mipi_input_config_params_t &mipi_params,
+        EventPtr &&network_group_activated_event, const LayerInfo &layer_info, hailo_status &status);
+
+    static CONTROL_PROTOCOL__mipi_input_config_params_t hailo_mipi_params_to_control_mipi_params(
+        const hailo_mipi_input_stream_params_t &params);
+
+    Device &m_device;
+    bool m_is_stream_activated;
+    CONTROL_PROTOCOL__mipi_input_config_params_t m_mipi_input_params;
+
+protected:
+    virtual Expected<size_t> sync_write_raw_buffer(const MemoryView &buffer) override;
+    virtual hailo_status sync_write_all_raw_buffer_no_transform_impl(void *buffer, size_t offset, size_t size) override;
+    virtual hailo_status set_timeout(std::chrono::milliseconds timeout) { (void)timeout; return HAILO_INVALID_OPERATION; };
+
+public:
+    static Expected<std::unique_ptr<MipiInputStream>> create(Device &device,
+        const LayerInfo &edge_layer, const hailo_mipi_input_stream_params_t &params,
+        EventPtr network_group_activated_event);
+
+    MipiInputStream(MipiInputStream&& other) :
+        InputStreamBase(std::move(other)),
+        m_device(other.m_device),
+        m_is_stream_activated(std::exchange(other.m_is_stream_activated, false)),
+        m_mipi_input_params(std::move(other.m_mipi_input_params))
+    {}
+
+    virtual ~MipiInputStream();
+
+    virtual hailo_status activate_stream() override;
+    virtual hailo_status deactivate_stream() override;
+    virtual hailo_stream_interface_t get_interface() const override { return HAILO_STREAM_INTERFACE_MIPI; }
+    virtual std::chrono::milliseconds get_timeout() const override;
+    virtual hailo_status abort() override;
+    virtual hailo_status clear_abort() override;
+
+};
+
+} /* namespace hailort */
+
+#endif /* HAILO_MIPI_STREAM_H_ */
diff --git a/hailort/libhailort/src/network_rate_calculator.cpp b/hailort/libhailort/src/network_rate_calculator.cpp
new file mode 100644 (file)
index 0000000..2a4677d
--- /dev/null
@@ -0,0 +1,147 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file network_rate_calculator.cpp
+ * @brief: Network rate calculator
+ **/
+
+#include "hailo/network_rate_calculator.hpp"
+#include "hailo/hailort.h"
+
+#include "eth_stream.hpp"
+#include "common/utils.hpp"
+#include <numeric>
+#include <algorithm>
+
+namespace hailort
+{
+
+Expected<StreamInfoVector> NetworkUdpRateCalculator::get_streams_from_hef(Hef* hef, const std::string &network_group_name)
+{
+    assert(nullptr != hef);
+
+    auto all_streams_infos = hef->get_all_stream_infos(network_group_name);
+    CHECK_EXPECTED(all_streams_infos);
+
+    // We expect to have two or more streams (atleast one for input and one for output)
+    if (all_streams_infos->size() < 2) {
+        return make_unexpected(HAILO_INVALID_HEF);
+    }
+
+    return all_streams_infos;
+}
+
+NetworkUdpRateCalculator::NetworkUdpRateCalculator(std::map<std::string, uint32_t> &&input_edge_shapes,
+    std::map<std::string, uint32_t> &&output_edge_shapes) :
+    m_input_edge_shapes(std::move(input_edge_shapes)),
+    m_output_edge_shapes(std::move(output_edge_shapes)) {}
+
+Expected<NetworkUdpRateCalculator> NetworkUdpRateCalculator::create(Hef* hef, const std::string &network_group_name)
+{
+    if (hef == nullptr) {
+        return make_unexpected(HAILO_INVALID_ARGUMENT);
+    }
+    const auto stream_infos = get_streams_from_hef(hef, network_group_name);
+    if (!stream_infos) {
+        return make_unexpected(stream_infos.status());
+    }
+
+    // Working with HEF for rate_calcs assums that all streams are udp streams
+    std::map<std::string, uint32_t> input_udp_edge_shapes;
+    std::map<std::string, uint32_t> output_udp_edge_shapes;
+    for (auto &info : stream_infos.value()) {
+        if (HAILO_H2D_STREAM == info.direction) {
+            input_udp_edge_shapes.insert(std::make_pair(info.name, info.hw_frame_size));
+        } else if (HAILO_D2H_STREAM == info.direction) {
+            output_udp_edge_shapes.insert(std::make_pair(info.name, info.hw_frame_size));
+        } else {
+            LOGGER__ERROR("Invalid stream direction for stream {}.", info.name);
+            return make_unexpected(HAILO_INTERNAL_FAILURE);
+        }
+    }
+
+    return NetworkUdpRateCalculator(std::move(input_udp_edge_shapes), std::move(output_udp_edge_shapes));
+}
+
+Expected<NetworkUdpRateCalculator> NetworkUdpRateCalculator::create(ConfiguredNetworkGroup &net_group)
+{
+    auto udp_input_streams = net_group.get_input_streams_by_interface(HAILO_STREAM_INTERFACE_ETH);
+    CHECK_AS_EXPECTED(!udp_input_streams.empty(), HAILO_INVALID_OPERATION,
+        "There are no udp input streams in this network_group.");
+    auto udp_output_streams = net_group.get_output_streams_by_interface(HAILO_STREAM_INTERFACE_ETH);
+
+    std::map<std::string, uint32_t> input_udp_edge_shapes;
+    for (const auto &stream : udp_input_streams) {
+        input_udp_edge_shapes.insert(std::make_pair(stream.get().name(),
+            stream.get().get_info().hw_frame_size));
+    }
+    std::map<std::string, uint32_t> output_udp_edge_shapes;
+    for (const auto &stream : udp_output_streams) {
+        output_udp_edge_shapes.insert(std::make_pair(stream.get().name(),
+            stream.get().get_info().hw_frame_size));
+    }
+
+    return NetworkUdpRateCalculator(std::move(input_udp_edge_shapes), std::move(output_udp_edge_shapes));
+}
+
+Expected<std::map<std::string, uint32_t>> NetworkUdpRateCalculator::calculate_inputs_bandwith(uint32_t fps,
+    uint32_t max_supported_bandwidth)
+{
+    if (1 > fps) {
+        fps = 1;
+        LOGGER__WARNING("FPS for rate calculations cannot be smaller than 1. calculating rate_limiter with fps=1.");
+    }
+
+    std::map<std::string, uint32_t> input_rates;
+    std::transform(m_input_edge_shapes.begin(), m_input_edge_shapes.end(), std::inserter(input_rates, input_rates.end()),
+        [fps](auto &input_edge_pair) { return std::make_pair(input_edge_pair.first, (fps * input_edge_pair.second)); });
+
+    std::map<std::string, uint32_t> output_rates = {};
+    std::transform(m_output_edge_shapes.begin(), m_output_edge_shapes.end(), std::inserter(output_rates, output_rates.end()),
+        [fps](auto &output_edge_pair) { return std::make_pair(output_edge_pair.first, (fps * output_edge_pair.second)); });
+
+    uint32_t total_input_rate = std::accumulate(input_rates.begin(), input_rates.end(), 0,
+        [](int value, const auto &p) { return value + p.second; });
+    uint32_t total_output_rate = std::accumulate(output_rates.begin(), output_rates.end(), 0,
+        [](int value, const auto &p) { return value + p.second; });
+
+    if ((total_input_rate > max_supported_bandwidth) || (total_output_rate > max_supported_bandwidth)) {
+        LOGGER__WARNING("Requested rate (input: {} Bps, output: {} Bps) is high and might be unstable. Setting rate to {}.",
+            total_input_rate, total_output_rate, max_supported_bandwidth);
+        if (total_output_rate > total_input_rate) {
+            // Output is bigger than max rate. Adjusting input rate accordingly
+            auto input_output_ratio = (total_input_rate / total_output_rate);
+            LOGGER__WARNING("Output Bps ({}) is bigger than input Bps ({}) output (ratio is: {})", total_output_rate,
+                total_input_rate, input_output_ratio);
+            max_supported_bandwidth *= input_output_ratio;
+        }
+        auto total_inputs_rate_to_max_supported_ratio = (static_cast<float64_t>(max_supported_bandwidth) / total_input_rate);
+        for (auto &rate_pair : input_rates) {
+            auto rate = rate_pair.second * total_inputs_rate_to_max_supported_ratio;
+            rate_pair.second = static_cast<uint32_t>(rate);
+        }
+    }
+
+    return input_rates;
+}
+
+Expected<std::map<uint16_t, uint32_t>> NetworkUdpRateCalculator::get_udp_ports_rates_dict(
+    std::vector<std::reference_wrapper<InputStream>> &udp_input_streams, uint32_t fps, uint32_t max_supported_bandwidth)
+{
+    auto rates_per_name = calculate_inputs_bandwith(fps, max_supported_bandwidth);
+    CHECK_EXPECTED(rates_per_name);
+
+    std::map<uint16_t, uint32_t> results = {};
+    for (const auto &input_stream : udp_input_streams) {
+        uint16_t remote_port = 0;
+        remote_port = reinterpret_cast<EthernetInputStream*>(&(input_stream.get()))->get_remote_port();
+        results.insert(std::make_pair(remote_port,
+            rates_per_name->at(input_stream.get().name())));
+    }
+
+    return results;
+}
+
+} /* namespace hailort */
diff --git a/hailort/libhailort/src/os/CMakeLists.txt b/hailort/libhailort/src/os/CMakeLists.txt
new file mode 100644 (file)
index 0000000..5e4ab74
--- /dev/null
@@ -0,0 +1,37 @@
+cmake_minimum_required(VERSION 3.0.0)
+
+if(WIN32)
+    set(HAILO_OS_DIR "${CMAKE_CURRENT_SOURCE_DIR}/windows")
+    set(HAILO_FULL_OS_DIR ${HAILO_OS_DIR})
+elseif(UNIX)
+    set(HAILO_OS_DIR "${CMAKE_CURRENT_SOURCE_DIR}/posix")
+    if (CMAKE_SYSTEM_NAME STREQUAL QNX)
+        set(HAILO_FULL_OS_DIR ${HAILO_OS_DIR}/qnx)
+    else()
+        set(HAILO_FULL_OS_DIR ${HAILO_OS_DIR}/unix)
+    endif()
+else()
+    message(FATAL_ERROR "Unexpeced platform target, stopping build")
+endif()
+# This is needed for hailortcli CMakeLists. PARENT_SCOPE is added in a different set because
+# adding it to the definitions of HAILO_OS_DIR above will make the variable empty in the current scope.
+set(HAILO_OS_DIR ${HAILO_OS_DIR} PARENT_SCOPE)
+set(HAILO_FULL_OS_DIR ${HAILO_FULL_OS_DIR} PARENT_SCOPE)
+
+
+set(files
+    ${HAILO_OS_DIR}/microsec_timer.cpp
+    ${HAILO_OS_DIR}/file_descriptor.cpp
+    ${HAILO_OS_DIR}/mmap_buffer.cpp
+    ${HAILO_OS_DIR}/hailort_driver.cpp
+    ${HAILO_FULL_OS_DIR}/event.cpp
+)
+
+if(UNIX)
+    # Unix only modules
+    set(files ${files}
+        ${HAILO_OS_DIR}/pcie_driver_sysfs.cpp
+    )
+endif()
+
+set(HAILORT_CPP_OS_SOURCES ${files} PARENT_SCOPE)
diff --git a/hailort/libhailort/src/os/file_descriptor.hpp b/hailort/libhailort/src/os/file_descriptor.hpp
new file mode 100644 (file)
index 0000000..a3f805a
--- /dev/null
@@ -0,0 +1,49 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file file_descriptor.hpp
+ * @brief Wrapper around system file descriptors
+ *
+ * 
+ **/
+
+#ifndef _OS_FILE_DESCRIPTOR_H_
+#define _OS_FILE_DESCRIPTOR_H_
+
+#include "common/logger_macros.hpp"
+#include "hailo/expected.hpp"
+
+namespace hailort
+{
+
+class FileDescriptor
+{
+  public:
+    FileDescriptor(underlying_handle_t fd);
+    ~FileDescriptor();
+
+    FileDescriptor(const FileDescriptor &other) = delete;
+    FileDescriptor &operator=(const FileDescriptor &other) = delete;
+    FileDescriptor(FileDescriptor &&other) noexcept;
+    FileDescriptor &operator=(FileDescriptor &&other) noexcept 
+    {
+        std::swap(m_fd, other.m_fd);
+        return *this;
+    };
+
+    operator underlying_handle_t() const
+    {
+        return m_fd;
+    }
+
+    Expected<FileDescriptor> duplicate();
+
+  private:
+    underlying_handle_t m_fd;
+};
+
+} /* namespace hailort */
+
+#endif /* _OS_FILE_DESCRIPTOR_H_ */
\ No newline at end of file
diff --git a/hailort/libhailort/src/os/hailort_driver.hpp b/hailort/libhailort/src/os/hailort_driver.hpp
new file mode 100755 (executable)
index 0000000..a3db6c8
--- /dev/null
@@ -0,0 +1,237 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file hailort_driver.hpp
+ * @brief Low level interface to PCI driver
+ *
+ * 
+ **/
+#ifndef _HAILORT_DRIVER_HPP_
+#define _HAILORT_DRIVER_HPP_
+
+#include "hailo/hailort.h"
+#include "hailo/expected.hpp"
+#include "d2h_event_queue.hpp"
+#include "os/file_descriptor.hpp"
+
+#include <mutex>
+#include <thread>
+#include <chrono>
+#include <utility>
+
+namespace hailort
+{
+
+#define DEVICE_NODE_NAME       "hailo"
+
+#define PENDING_BUFFERS_SIZE (128)
+static_assert((0 == ((PENDING_BUFFERS_SIZE - 1) &  PENDING_BUFFERS_SIZE)), "PENDING_BUFFERS_SIZE must be a power of 2");
+
+#define MIN_ACTIVE_TRANSFERS_SCALE (2)
+#define MAX_ACTIVE_TRANSFERS_SCALE (4)
+
+#define HAILO_MAX_BATCH_SIZE ((PENDING_BUFFERS_SIZE / MIN_ACTIVE_TRANSFERS_SCALE) - 1)
+
+// When measuring latency, each channel is capable of PENDING_BUFFERS_SIZE active transfers, each transfer raises max of 2 timestamps
+#define MAX_IRQ_TIMESTAMPS_SIZE (PENDING_BUFFERS_SIZE * 2)
+
+#define PCIE_EXPECTED_MD5_LENGTH (16)
+
+enum class PciBar {
+    bar0 = 0,
+    bar1,
+    bar2,
+    bar3,
+    bar4,
+    bar5,
+};
+
+// NOTE: don't change members from this struct without updating all code using it (platform specific)
+struct ChannelInterruptTimestamp {
+    std::chrono::nanoseconds timestamp;
+    uint16_t desc_num_processed;
+};
+
+struct ChannelInterruptTimestampList {
+    ChannelInterruptTimestamp timestamp_list[MAX_IRQ_TIMESTAMPS_SIZE];
+    size_t count;
+};
+
+class HailoRTDriver final
+{
+public:
+
+    struct DeviceInfo {
+        std::string dev_path;
+
+        // Board information
+        uint32_t vendor_id;
+        uint32_t device_id;
+
+        // PCIe board location
+        uint32_t domain;
+        uint32_t bus;
+        uint32_t device;
+        uint32_t func;
+    };
+
+    enum class DmaDirection {
+        H2D = 0,
+        D2H,
+        BOTH
+    };
+
+    // TODO: move to general place
+    enum class BoardType {
+        HAILO8 = 0,
+        MERCURY = 1
+    };
+
+    enum class DmaType {
+        PCIE,
+        DRAM
+    };
+
+    using VdmaBufferHandle = size_t;
+    using VdmaChannelHandle = uint64_t;
+
+    static Expected<HailoRTDriver> create(const std::string &dev_path);
+
+    static Expected<std::vector<DeviceInfo>> scan_pci();
+
+    hailo_status read_bar(PciBar bar, off_t offset, size_t size, void *buf);
+    hailo_status write_bar(PciBar bar, off_t offset, size_t size, const void *buf);
+
+    Expected<uint32_t> read_vdma_channel_registers(off_t offset, size_t size);
+    hailo_status write_vdma_channel_registers(off_t offset, size_t size, uint32_t data);
+
+    hailo_status vdma_buffer_sync(VdmaBufferHandle buffer, DmaDirection sync_direction, void *address, size_t buffer_size);
+
+    Expected<VdmaChannelHandle> vdma_channel_enable(uint32_t channel_index, DmaDirection data_direction,
+        uintptr_t desc_list_handle, bool enable_timestamps_measure);
+    hailo_status vdma_channel_disable(uint32_t channel_index, VdmaChannelHandle channel_handle);
+    Expected<ChannelInterruptTimestampList> wait_channel_interrupts(uint32_t channel_index, VdmaChannelHandle channel_handle,
+        const std::chrono::milliseconds &timeout);
+    hailo_status vdma_channel_abort(uint32_t channel_index, VdmaChannelHandle channel_handle);
+    hailo_status vdma_channel_clear_abort(uint32_t channel_index, VdmaChannelHandle channel_handle);
+
+    Expected<D2H_EVENT_MESSAGE_t> read_notification();
+    hailo_status disable_notifications();
+
+    hailo_status fw_control(const void *request, size_t request_len, const uint8_t request_md5[PCIE_EXPECTED_MD5_LENGTH],
+        void *response, size_t *response_len, uint8_t response_md5[PCIE_EXPECTED_MD5_LENGTH],
+        std::chrono::milliseconds timeout, hailo_cpu_id_t cpu_id);
+
+    /**
+     * Read data from the debug log buffer.
+     *
+     * @param[in]     buffer            - A pointer to the buffer that would receive the debug log data.
+     * @param[in]     buffer_size       - The size in bytes of the buffer.
+     * @param[out]    read_bytes        - Upon success, receives the number of bytes that were read; otherwise, untouched.
+     * @param[in]     cpu_id            - The cpu source of the debug log.
+     * @return hailo_status
+     */
+    hailo_status read_log(uint8_t *buffer, size_t buffer_size, size_t *read_bytes, hailo_cpu_id_t cpu_id);
+
+    hailo_status reset_nn_core();
+
+    /**
+     * Pins a page aligned user buffer to physical memory, creates an IOMMU mapping (pci_mag_sg).
+     * The buffer is used for streaming D2H or H2D using DMA.
+     *
+     * @param[in] data_direction - direction is used for optimization.
+     * @param[in] driver_buff_handle - handle to driver allocated buffer - INVALID_DRIVER_BUFFER_HANDLE_VALUE in case
+     *  of user allocated buffer
+     */ 
+    Expected<VdmaBufferHandle> vdma_buffer_map(void *user_address, size_t required_size, DmaDirection data_direction,
+        uintptr_t driver_buff_handle);
+
+    /**
+    * Unmaps user buffer mapped using HailoRTDriver::map_buffer.
+    */
+    hailo_status vdma_buffer_unmap(VdmaBufferHandle handle);
+
+    /**
+     * Allocate vdma descriptors buffer that is accessable via kernel mode, user mode and the given board (using DMA).
+     * 
+     * @param[in] desc_count - number of descriptors to allocate. The descriptor max size is DESC_MAX_SIZE.
+     * @return Upon success, returns Expected of a pair <desc_handle, dma_address>.
+     *         Otherwise, returns Unexpected of ::hailo_status error.
+     */
+    Expected<std::pair<uintptr_t, uint64_t>> descriptors_list_create(size_t desc_count);
+   
+    /**
+     * Frees a vdma descriptors buffer allocated by 'create_descriptors_buffer'.
+     */
+    hailo_status descriptors_list_release(uintptr_t desc_handle);
+    Expected<uintptr_t> vdma_low_memory_buffer_alloc(size_t size);
+    hailo_status vdma_low_memory_buffer_free(uintptr_t buffer_handle);
+
+    /**
+     * Configure vdma channel descriptors to point to the given user address.
+     */
+    hailo_status descriptors_list_bind_vdma_buffer(uintptr_t desc_handle, VdmaBufferHandle buffer_handle,
+        uint16_t desc_page_size, uint8_t channel_index);
+
+    /**
+     * The actual desc page size might be smaller than the once requested, depends on the host capabilities.
+     */
+    uint16_t calc_desc_page_size(uint16_t requested_size) const
+    {
+        if (m_desc_max_page_size < requested_size) {
+            LOGGER__WARNING("Requested desc page size ({}) is bigger than max on this host ({}).",
+                requested_size, m_desc_max_page_size);
+        }
+        return static_cast<uint16_t>(std::min(static_cast<uint32_t>(requested_size), static_cast<uint32_t>(m_desc_max_page_size)));
+    }
+
+    inline BoardType board_type() const
+    {
+        return m_board_type;
+    }
+
+    inline DmaType dma_type() const
+    {
+        return m_dma_type;
+    }
+
+    FileDescriptor& fd() {return m_fd;}
+
+    const std::string &dev_path() const
+    {
+        return m_dev_path;
+    }
+
+    hailo_status mark_as_used();
+
+    inline bool allocate_driver_buffer() const {
+        return m_allocate_driver_buffer;
+    }
+
+    HailoRTDriver(const HailoRTDriver &other) = delete;
+    HailoRTDriver &operator=(const HailoRTDriver &other) = delete;
+    HailoRTDriver(HailoRTDriver &&other) noexcept = default;
+    HailoRTDriver &operator=(HailoRTDriver &&other) = default;
+
+    static const VdmaChannelHandle  INVALID_VDMA_CHANNEL_HANDLE;
+    static const uintptr_t          INVALID_DRIVER_BUFFER_HANDLE_VALUE;
+    static const uint8_t            INVALID_VDMA_CHANNEL_INDEX;
+
+private:
+
+    HailoRTDriver(const std::string &dev_path, FileDescriptor &&fd, hailo_status &status);
+
+    FileDescriptor m_fd;
+    std::string m_dev_path;
+    uint16_t m_desc_max_page_size;
+    BoardType m_board_type;
+    DmaType m_dma_type;
+    bool m_allocate_driver_buffer;
+};
+
+} /* namespace hailort */
+
+#endif  /* _HAILORT_DRIVER_HPP_ */
diff --git a/hailort/libhailort/src/os/microsec_timer.hpp b/hailort/libhailort/src/os/microsec_timer.hpp
new file mode 100644 (file)
index 0000000..87f70bc
--- /dev/null
@@ -0,0 +1,39 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file timer.hpp
+ * @brief High resolution (microsec granularity) timer.
+ **/
+
+#ifndef __OS_MICROSEC_TIMER_HPP__
+#define __OS_MICROSEC_TIMER_HPP__
+
+#include <hailo/platform.h>
+#include <hailo/hailort.h>
+#include "common/utils.hpp"
+
+namespace hailort
+{
+
+class MicrosecTimer final
+{
+public:
+    MicrosecTimer() = delete;
+
+    /**
+     * Sleeps for the desired time
+     * 
+     * @param[in] microsecs    The desired sleep period
+     * @note Passing 0 as a parameter will cause a context switch
+     * @note Some implmentions of sleep may go into a busy-loop if the platform (os and processor)
+     *       doesn't support such short periods.
+     * @note This function is guaranteed to sleep for at least the desired time, though it may sleep for more.
+     */
+    static void sleep(uint64_t microsecs);
+};
+
+} /* namespace hailort */
+
+#endif /* __OS_MICROSEC_TIMER_HPP__ */
diff --git a/hailort/libhailort/src/os/mmap_buffer.hpp b/hailort/libhailort/src/os/mmap_buffer.hpp
new file mode 100644 (file)
index 0000000..1f6031c
--- /dev/null
@@ -0,0 +1,146 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file mmap_buffer.hpp
+ * @brief RAII wrapper around memory mapping (mmap)
+ *
+ * 
+ **/
+
+#ifndef _OS_MMAP_BUFFER_H_
+#define _OS_MMAP_BUFFER_H_
+
+#include "hailo/hailort.h"
+#include "hailo/expected.hpp"
+#include "common/logger_macros.hpp"
+#include "common/utils.hpp"
+#include "os/file_descriptor.hpp"
+
+namespace hailort
+{
+
+class MmapBufferImpl final {
+public:
+
+    static Expected<MmapBufferImpl> create_shared_memory(size_t length);
+    static Expected<MmapBufferImpl> create_file_map(size_t length, FileDescriptor &file, uintptr_t offset);
+
+    MmapBufferImpl() : m_address(INVALID_ADDR), m_length(0), m_unmappable(false) {}
+
+    ~MmapBufferImpl()
+    {
+        // On failure `unmap` already logs the error
+        (void) unmap();
+    }
+
+    MmapBufferImpl(const MmapBufferImpl &other) = delete;
+    MmapBufferImpl &operator=(const MmapBufferImpl &other) = delete;
+    MmapBufferImpl(MmapBufferImpl &&other) noexcept : 
+        m_address(std::exchange(other.m_address, INVALID_ADDR)), 
+        m_length(std::move(other.m_length)) {};
+    MmapBufferImpl &operator=(MmapBufferImpl &&other) noexcept 
+    {
+        std::swap(m_address, other.m_address);
+        std::swap(m_length, other.m_length);
+        std::swap(m_unmappable, other.m_unmappable);
+        return *this;
+    };
+
+    void *get() {
+        return m_address;
+    }
+
+    explicit operator bool() const
+    {
+        return (INVALID_ADDR != m_address);
+    }
+
+    hailo_status unmap();
+
+
+private:
+    MmapBufferImpl(void *address, size_t length, bool unmappable = false) :
+        m_address(address), m_length(length), m_unmappable(unmappable) {}
+
+    static void * const INVALID_ADDR;
+
+    void *m_address;
+    size_t m_length;
+    bool   m_unmappable;
+};
+
+template<typename T>
+class MmapBuffer final
+{
+public:
+
+    static Expected<MmapBuffer<T>> create_shared_memory(size_t length)
+    {
+        auto mmap = MmapBufferImpl::create_shared_memory(length);
+        CHECK_EXPECTED(mmap);
+        return MmapBuffer<T>(std::move(mmap.release()));
+    }
+
+    static Expected<MmapBuffer<T>> create_file_map(size_t length, FileDescriptor &file, uintptr_t offset)
+    {
+        auto mmap = MmapBufferImpl::create_file_map(length, file, offset);
+        CHECK_EXPECTED(mmap);
+        return MmapBuffer<T>(std::move(mmap.release()));
+    }
+
+
+    MmapBuffer() = default;
+    ~MmapBuffer() = default;
+
+    MmapBuffer(const MmapBuffer<T> &other) = delete;
+    MmapBuffer<T> &operator=(const MmapBuffer<T> &other) = delete;
+    MmapBuffer(MmapBuffer<T> &&other) noexcept = default;
+    MmapBuffer<T> &operator=(MmapBuffer<T> &&other) noexcept = default;
+
+    T* operator->()
+    {
+        return get();
+    }
+
+    T* get() {
+        return reinterpret_cast<T*>(m_mmap.get());
+    }
+
+
+    template<typename U=T>
+    std::enable_if_t<!std::is_void<U>::value, U&> operator*()
+    {
+        return get()[0];
+    }
+
+    template<typename U=T>
+    std::enable_if_t<!std::is_void<U>::value, U&> operator[](size_t i)
+    {
+        return get()[i];
+    }
+
+    explicit operator bool() const
+    {
+        return bool(m_mmap);
+    }
+
+    // 'munmap' the current mapped buffer (if currently mapped).
+    // It it safe to call it multiple times
+    hailo_status unmap()
+    {
+        return m_mmap.unmap();
+    }
+
+private:
+
+    MmapBuffer(MmapBufferImpl mmap) :
+        m_mmap(std::move(mmap)) {}
+
+    MmapBufferImpl m_mmap;
+};
+
+} /* namespace hailort */
+
+#endif //_OS_MMAP_BUFFER_H_
\ No newline at end of file
diff --git a/hailort/libhailort/src/os/posix/file_descriptor.cpp b/hailort/libhailort/src/os/posix/file_descriptor.cpp
new file mode 100644 (file)
index 0000000..a70c669
--- /dev/null
@@ -0,0 +1,47 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file file_descriptor.cpp
+ * @brief Wrapper around system file descriptors for Unix
+ **/
+
+
+#include "common/logger_macros.hpp"
+#include "os/file_descriptor.hpp"
+#include <errno.h>
+
+namespace hailort
+{
+
+#define INVALID_FD (-1)
+
+
+FileDescriptor::FileDescriptor(underlying_handle_t fd) : m_fd(fd)
+{}
+
+FileDescriptor::~FileDescriptor()
+{
+    if (m_fd != INVALID_FD) {
+        if (0 != close(m_fd)) {
+            LOGGER__ERROR("Failed to close fd. errno={}", errno);
+        }
+    }
+}
+
+FileDescriptor::FileDescriptor(FileDescriptor &&other) noexcept : m_fd(std::exchange(other.m_fd, INVALID_FD))
+{}
+
+Expected<FileDescriptor> FileDescriptor::duplicate()
+{
+    auto new_fd = FileDescriptor(dup(m_fd));
+    if (INVALID_FD == new_fd) {
+        LOGGER__ERROR("Failed duplicating fd. errno={}", errno);
+        return make_unexpected(HAILO_OPEN_FILE_FAILURE);
+    }
+
+    return new_fd;
+}
+
+} /* namespace hailort */
diff --git a/hailort/libhailort/src/os/posix/hailort_driver.cpp b/hailort/libhailort/src/os/posix/hailort_driver.cpp
new file mode 100755 (executable)
index 0000000..8682813
--- /dev/null
@@ -0,0 +1,605 @@
+#include "os/hailort_driver.hpp"
+#include "os/posix/pcie_driver_sysfs.hpp"
+#include "hailo_ioctl_common.h"
+#include "common/logger_macros.hpp"
+#include "common/utils.hpp"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <sys/ioctl.h>
+#include <sys/time.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <stdint.h>
+#include <errno.h>
+
+namespace hailort
+{
+constexpr hailo_dma_data_direction direction_to_dma_data_direction(HailoRTDriver::DmaDirection direction) {
+    switch (direction){
+    case HailoRTDriver::DmaDirection::H2D:
+        return HAILO_DMA_TO_DEVICE;
+    case HailoRTDriver::DmaDirection::D2H:
+        return HAILO_DMA_FROM_DEVICE;
+    case HailoRTDriver::DmaDirection::BOTH:
+        return HAILO_DMA_BIDIRECTIONAL;
+    default:
+        assert(true);
+        // On release build Return value that will make ioctls to fail.
+        return HAILO_DMA_NONE;
+    }
+}
+
+constexpr enum hailo_cpu_id translate_cpu_id(hailo_cpu_id_t cpu_id)
+{   
+    switch (cpu_id)
+    {
+    case HAILO_CPU_ID_0:
+        return HAILO_CPU_ID_CPU0;
+    case HAILO_CPU_ID_1:
+        return HAILO_CPU_ID_CPU1;
+    default:
+        assert(true);
+        // On release build Return value that will make ioctls to fail.
+        return HAILO_CPU_ID_NONE;
+    }
+}
+
+static Expected<ChannelInterruptTimestampList> create_interrupt_timestamp_list(hailo_vdma_channel_wait_params &inter_data)
+{
+    CHECK_AS_EXPECTED(inter_data.timestamps_count <= MAX_IRQ_TIMESTAMPS_SIZE, HAILO_PCIE_DRIVER_FAIL,
+        "Invalid channel interrupt timestamps count returned {}", inter_data.timestamps_count);
+    ChannelInterruptTimestampList timestamp_list;
+
+    timestamp_list.count = inter_data.timestamps_count;
+    for (size_t i = 0; i < timestamp_list.count; i++) {
+        timestamp_list.timestamp_list[i].timestamp = std::chrono::nanoseconds(inter_data.timestamps[i].timestamp_ns);
+        timestamp_list.timestamp_list[i].desc_num_processed = inter_data.timestamps[i].desc_num_processed;
+    }
+    return timestamp_list;
+}
+
+const HailoRTDriver::VdmaChannelHandle HailoRTDriver::INVALID_VDMA_CHANNEL_HANDLE = INVALID_CHANNEL_HANDLE_VALUE;
+const uintptr_t HailoRTDriver::INVALID_DRIVER_BUFFER_HANDLE_VALUE = INVALID_DRIVER_HANDLE_VALUE;
+const uint8_t HailoRTDriver::INVALID_VDMA_CHANNEL_INDEX = INVALID_VDMA_CHANNEL;
+
+Expected<HailoRTDriver> HailoRTDriver::create(const std::string &dev_path)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+
+    auto fd = FileDescriptor(open(dev_path.c_str(), O_RDWR));
+    if (0 > fd) {
+        LOGGER__ERROR("Failed to open board {}", dev_path);
+        return make_unexpected(HAILO_OPEN_FILE_FAILURE);
+    }
+
+    HailoRTDriver object(dev_path, std::move(fd), status);
+    if (HAILO_SUCCESS != status) {
+        return make_unexpected(status);
+    }
+    return object;
+}
+
+static hailo_status validate_driver_version(const hailo_driver_info &driver_info)
+{
+    hailo_version_t library_version{};
+    auto status = hailo_get_library_version(&library_version);
+    CHECK_SUCCESS(status);
+    CHECK((driver_info.major_version == library_version.major) &&
+        (driver_info.minor_version == library_version.minor) &&
+        (driver_info.revision_version == library_version.revision), HAILO_INVALID_DRIVER_VERSION,
+        "Driver version ({}.{}.{}) is different from library version ({}.{}.{})",
+        driver_info.major_version, driver_info.minor_version, driver_info.revision_version,
+        library_version.major, library_version.minor, library_version.revision);
+    return HAILO_SUCCESS;
+}
+
+HailoRTDriver::HailoRTDriver(const std::string &dev_path, FileDescriptor &&fd, hailo_status &status) :
+    m_fd(std::move(fd)),
+    m_dev_path(dev_path),
+    m_allocate_driver_buffer(false)
+{
+    hailo_driver_info driver_info = {};
+    if (0 > ioctl(m_fd, HAILO_QUERY_DRIVER_INFO, &driver_info)) {
+        LOGGER__ERROR("Failed query driver info, errno {}", errno);
+        status = HAILO_PCIE_DRIVER_FAIL;
+        return;
+    }
+    LOGGER__INFO("Hailo PCIe driver version {}.{}.{}", driver_info.major_version,
+        driver_info.minor_version, driver_info.revision_version);
+
+    status = validate_driver_version(driver_info);
+    if (HAILO_SUCCESS != status) {
+        LOGGER__ERROR("Driver version mismatch, status {}", status);
+        return;
+    }
+
+    hailo_device_properties device_properties = {};
+    if (0 > ioctl(m_fd, HAILO_QUERY_DEVICE_PROPERTIES, &device_properties)) {
+        LOGGER__ERROR("Failed query pcie device properties, errno {}", errno);
+        status = HAILO_PCIE_DRIVER_FAIL;
+        return;
+    }
+
+    m_desc_max_page_size = device_properties.desc_max_page_size;
+    m_allocate_driver_buffer = (HAILO_ALLOCATION_MODE_DRIVER == device_properties.allocation_mode);
+    switch (device_properties.board_type) {
+    case HAILO8:
+        m_board_type = BoardType::HAILO8;
+        break;
+    case HAILO_MERCURY:
+        m_board_type = BoardType::MERCURY;
+        break;
+    default:
+        LOGGER__ERROR("Invalid board type returned from ioctl {}", device_properties.board_type);
+        status = HAILO_PCIE_DRIVER_FAIL;
+        return;
+    }
+
+    switch (device_properties.dma_type) {
+    case HAILO_DMA_TYPE_PCIE:
+        m_dma_type = DmaType::PCIE;
+        break;
+    case HAILO_DMA_TYPE_DRAM:
+        m_dma_type = DmaType::DRAM;
+        break;
+    default:
+        LOGGER__ERROR("Invalid dma type returned from ioctl {}", device_properties.dma_type);
+        status = HAILO_PCIE_DRIVER_FAIL;
+        return;
+    }
+
+    status = HAILO_SUCCESS;
+}
+
+Expected<D2H_EVENT_MESSAGE_t> HailoRTDriver::read_notification()
+{
+    hailo_d2h_notification notification_buffer = {};
+    D2H_EVENT_MESSAGE_t notification;
+
+    auto rc = ioctl(this->m_fd, HAILO_READ_NOTIFICATION, &notification_buffer);
+    if (0 > rc) {
+        return make_unexpected(HAILO_PCIE_DRIVER_FAIL);
+    }
+
+    CHECK_AS_EXPECTED(sizeof(notification) >= notification_buffer.buffer_len, HAILO_GET_D2H_EVENT_MESSAGE_FAIL,
+        "buffer len is not valid = {}", notification_buffer.buffer_len);
+
+    memcpy(&notification, notification_buffer.buffer, notification_buffer.buffer_len);
+    return notification;
+}
+
+hailo_status HailoRTDriver::disable_notifications()
+{
+    auto rc = ioctl(this->m_fd, HAILO_DISABLE_NOTIFICATION, 0);
+    CHECK(0 <= rc, HAILO_PCIE_DRIVER_FAIL, "HAILO_DISABLE_NOTIFICATION failed with errno:{}", errno);
+
+    return HAILO_SUCCESS;
+}
+
+Expected<std::vector<HailoRTDriver::DeviceInfo>> HailoRTDriver::scan_pci()
+{
+    auto device_names = list_sysfs_pcie_devices();
+    CHECK_EXPECTED(device_names, "Failed listing pcie devices");
+
+    std::vector<HailoRTDriver::DeviceInfo> devices_info;
+    for (const auto &device_name : device_names.value()) {
+        auto device_info = query_device_info(device_name);
+        CHECK_EXPECTED(device_info, "failed parsing device info for {}", device_name);
+        devices_info.push_back(device_info.release());
+    }
+    return devices_info;
+}
+
+Expected<uint32_t> HailoRTDriver::read_vdma_channel_registers(off_t offset, size_t size)
+{
+    hailo_channel_registers_params params = {
+        .transfer_direction = TRANSFER_READ,
+        .offset = offset,
+        .size = size,
+        .data = 0
+    };
+
+    if (0 > ioctl(this->m_fd, HAILO_VDMA_CHANNEL_REGISTERS, &params)) {
+        LOGGER__ERROR("HailoRTDriver::read_vdma_channel_registers failed with errno:{}", errno);
+        return make_unexpected(HAILO_PCIE_DRIVER_FAIL);
+    }
+
+    return std::move(params.data);
+}
+
+hailo_status HailoRTDriver::write_vdma_channel_registers(off_t offset, size_t size, uint32_t data)
+{
+    hailo_channel_registers_params params = {
+        .transfer_direction = TRANSFER_WRITE,
+        .offset = offset,
+        .size = size,
+        .data = data
+    };
+
+    if (0 > ioctl(this->m_fd, HAILO_VDMA_CHANNEL_REGISTERS, &params)) {
+        LOGGER__ERROR("HailoRTDriver::write_vdma_channel_registers failed with errno:{}", errno);
+        return HAILO_PCIE_DRIVER_FAIL;
+    }
+
+    return HAILO_SUCCESS;
+}
+
+hailo_status HailoRTDriver::read_bar(PciBar bar, off_t offset, size_t size, void *buf)
+{
+    if (size == 0) {
+        LOGGER__ERROR("Invalid size to read");
+        return HAILO_INVALID_ARGUMENT;
+    }
+
+    if (buf == nullptr) {
+        LOGGER__ERROR("Read buffer pointer is NULL");
+        return HAILO_INVALID_ARGUMENT;
+    }
+
+    hailo_bar_transfer_params transfer = {
+        .transfer_direction = TRANSFER_READ,
+        .bar_index = static_cast<uint32_t>(bar),
+        .offset = offset,
+        .count = size,
+        .buffer = buf
+    };
+
+    if (0 > ioctl(this->m_fd, HAILO_BAR_TRANSFER, &transfer)) {
+        LOGGER__ERROR("HailoRTDriver::read_bar failed with errno:{}", errno);
+        return HAILO_PCIE_DRIVER_FAIL;
+    }
+
+    return HAILO_SUCCESS;
+}
+
+hailo_status HailoRTDriver::write_bar(PciBar bar, off_t offset, size_t size, const void *buf)
+{
+    if (size == 0) {
+        LOGGER__ERROR("Invalid size to read");
+        return HAILO_INVALID_ARGUMENT;
+    }
+
+    if (buf == nullptr) {
+        LOGGER__ERROR("Read buffer pointer is NULL");
+        return HAILO_INVALID_ARGUMENT;
+    }
+
+    hailo_bar_transfer_params transfer = {
+        .transfer_direction = TRANSFER_WRITE,
+        .bar_index = static_cast<uint32_t>(bar),
+        .offset = offset,
+        .count = size,
+        .buffer = (void*)buf
+    };
+
+    if (0 > ioctl(this->m_fd, HAILO_BAR_TRANSFER, &transfer)) {
+        LOGGER__ERROR("HailoRTDriver::write_bar failed with errno:{}", errno);
+        return HAILO_PCIE_DRIVER_FAIL;
+    }
+
+    return HAILO_SUCCESS;
+}
+
+hailo_status HailoRTDriver::vdma_buffer_sync(VdmaBufferHandle handle, DmaDirection sync_direction, void *address,
+    size_t buffer_size)
+{
+    CHECK(sync_direction != DmaDirection::BOTH, HAILO_INVALID_ARGUMENT, "Can't sync vdma data both host and device");
+    hailo_vdma_buffer_sync_params sync_info{
+        .handle = handle,
+        .sync_type = (sync_direction == DmaDirection::H2D) ? HAILO_SYNC_FOR_DEVICE : HAILO_SYNC_FOR_HOST,
+        .buffer_address = address,
+        .buffer_size = buffer_size
+    };
+    if (0 > ioctl(this->m_fd, HAILO_VDMA_BUFFER_SYNC, &sync_info)) {
+        LOGGER__ERROR("HAILO_VDMA_BUFFER_SYNC failed with errno:{}", errno);
+        return HAILO_PCIE_DRIVER_FAIL;
+    }
+    return HAILO_SUCCESS;
+}
+
+
+Expected<HailoRTDriver::VdmaChannelHandle> HailoRTDriver::vdma_channel_enable(uint32_t channel_index,
+    DmaDirection data_direction, uintptr_t desc_list_handle, bool enable_timestamps_measure)
+{
+    CHECK_AS_EXPECTED(data_direction != DmaDirection::BOTH, HAILO_INVALID_ARGUMENT);
+    hailo_vdma_channel_enable_params params {
+        .channel_index = channel_index,
+        .direction = direction_to_dma_data_direction(data_direction),
+        .desc_list_handle = desc_list_handle,
+        .enable_timestamps_measure = enable_timestamps_measure,
+        .channel_handle = INVALID_CHANNEL_HANDLE_VALUE,
+    };
+
+    if (0 > ioctl(this->m_fd, HAILO_VDMA_CHANNEL_ENABLE, &params)) {
+        LOGGER__ERROR("Failed to enable interrupt for channel {} with errno:{}", channel_index, errno);
+        return make_unexpected(HAILO_PCIE_DRIVER_FAIL);
+    }
+
+    return VdmaChannelHandle(params.channel_handle);
+}
+
+hailo_status HailoRTDriver::vdma_channel_disable(uint32_t channel_index, VdmaChannelHandle channel_handle)
+{
+    hailo_vdma_channel_disable_params params {
+        .channel_index = channel_index,
+        .channel_handle = channel_handle
+    };
+
+    if (0 > ioctl(this->m_fd, HAILO_VDMA_CHANNEL_DISABLE, &params)) {
+        LOGGER__ERROR("Failed to disable interrupt for channel {} with errno:{}", channel_index, errno);
+        return HAILO_PCIE_DRIVER_FAIL;
+    }
+
+    return HAILO_SUCCESS;
+}
+
+Expected<ChannelInterruptTimestampList> HailoRTDriver::wait_channel_interrupts(uint32_t channel_index, VdmaChannelHandle channel_handle,
+    const std::chrono::milliseconds &timeout)
+{
+    CHECK_AS_EXPECTED(timeout.count() >= 0, HAILO_INVALID_ARGUMENT);
+
+    const uint32_t timestamps_count = MAX_IRQ_TIMESTAMPS_SIZE;
+    struct hailo_channel_interrupt_timestamp timestamps[timestamps_count];
+
+    hailo_vdma_channel_wait_params data {
+        .channel_index = channel_index,
+        .channel_handle = channel_handle,
+        .timeout_ms = static_cast<uint64_t>(timeout.count()),
+        .timestamps = timestamps,
+        .timestamps_count = timestamps_count
+    };
+
+    if (0 > ioctl(this->m_fd, HAILO_VDMA_CHANNEL_WAIT_INT, &data)) {
+        const auto ioctl_errno = errno;
+        if (ETIMEDOUT == ioctl_errno) {
+            LOGGER__ERROR("Waiting for interrupt for channel {} timed-out (errno=ETIMEDOUT)", channel_index);
+            return make_unexpected(HAILO_TIMEOUT);
+        }
+        if (ECONNABORTED == ioctl_errno) {
+            LOGGER__INFO("Channel (index={}) was aborted!", channel_index);
+            return make_unexpected(HAILO_STREAM_INTERNAL_ABORT);
+        }
+        if (ECONNRESET == ioctl_errno) {
+            LOGGER__INFO("Channel (index={}) was deactivated!", channel_index);
+            return make_unexpected(HAILO_STREAM_NOT_ACTIVATED);
+        }
+        LOGGER__ERROR("Failed to wait interrupt for channel {} with errno:{}", channel_index, ioctl_errno);
+        return make_unexpected(HAILO_PCIE_DRIVER_FAIL);
+    }
+
+
+    return create_interrupt_timestamp_list(data);
+}
+
+hailo_status HailoRTDriver::fw_control(const void *request, size_t request_len, const uint8_t request_md5[PCIE_EXPECTED_MD5_LENGTH],
+    void *response, size_t *response_len, uint8_t response_md5[PCIE_EXPECTED_MD5_LENGTH],
+    std::chrono::milliseconds timeout, hailo_cpu_id_t cpu_id)
+{
+    CHECK_ARG_NOT_NULL(request);
+    CHECK_ARG_NOT_NULL(response);
+    CHECK_ARG_NOT_NULL(response_len);
+    CHECK(timeout.count() >= 0, HAILO_INVALID_ARGUMENT);
+
+    hailo_fw_control command;
+    static_assert(PCIE_EXPECTED_MD5_LENGTH == sizeof(command.expected_md5), "mismatch md5 size");
+    memcpy(&command.expected_md5, request_md5, sizeof(command.expected_md5));
+    command.buffer_len = static_cast<uint32_t>(request_len);
+    CHECK(request_len <= sizeof(command.buffer), HAILO_INVALID_ARGUMENT,
+        "FW control request len can't be larger than {} (size given {})", sizeof(command.buffer), request_len);
+    memcpy(&command.buffer, request, request_len);
+    command.timeout_ms = static_cast<uint32_t>(timeout.count());
+    command.cpu_id = translate_cpu_id(cpu_id);
+    if (0 > ioctl(this->m_fd, HAILO_FW_CONTROL, &command)) {
+        LOGGER__ERROR("HAILO_FW_CONTROL failed with errno:{}", errno);
+        return HAILO_FW_CONTROL_FAILURE;
+    }
+
+    if (*response_len < command.buffer_len) {
+        LOGGER__ERROR("FW control response len needs to be atleast {} (size given {})", command.buffer_len, *response_len);
+        *response_len = command.buffer_len;
+        return HAILO_INSUFFICIENT_BUFFER;
+    }
+    memcpy(response, command.buffer, command.buffer_len);
+    *response_len = command.buffer_len;
+    memcpy(response_md5, command.expected_md5, PCIE_EXPECTED_MD5_LENGTH);
+
+    return HAILO_SUCCESS;
+}
+
+hailo_status HailoRTDriver::read_log(uint8_t *buffer, size_t buffer_size, size_t *read_bytes, hailo_cpu_id_t cpu_id)
+{
+    CHECK_ARG_NOT_NULL(buffer);
+    CHECK_ARG_NOT_NULL(read_bytes);
+
+    hailo_read_log_params params {
+        .cpu_id = translate_cpu_id(cpu_id),
+        .buffer = buffer,
+        .buffer_size = buffer_size,
+        .read_bytes = 0
+    };
+
+    if (0 > ioctl(this->m_fd, HAILO_READ_LOG, &params)) {
+        LOGGER__ERROR("Failed to read log with errno:{}", errno);
+        return HAILO_PCIE_DRIVER_FAIL;
+    }
+
+    *read_bytes = params.read_bytes;
+
+    return HAILO_SUCCESS;
+}
+hailo_status HailoRTDriver::reset_nn_core()
+{
+    if (0 > ioctl(this->m_fd, HAILO_RESET_NN_CORE, nullptr)) {
+        LOGGER__ERROR("Failed to reset nn core with errno:{}", errno);
+        return HAILO_PCIE_DRIVER_FAIL;
+    }
+
+    return HAILO_SUCCESS;
+}
+Expected<HailoRTDriver::VdmaBufferHandle> HailoRTDriver::vdma_buffer_map(void *user_address, size_t required_size,
+    DmaDirection data_direction, uintptr_t driver_buff_handle)
+{
+    hailo_vdma_buffer_map_params map_user_buffer_info {
+        .user_address = user_address,
+        .size = required_size,
+        .data_direction = direction_to_dma_data_direction(data_direction),
+        .allocated_buffer_handle = driver_buff_handle,
+        .mapped_handle = 0
+    };
+
+    if (0 > ioctl(this->m_fd, HAILO_VDMA_BUFFER_MAP, &map_user_buffer_info)) {
+        LOGGER__ERROR("Failed to map user buffer with errno:{}", errno);
+        return make_unexpected(HAILO_PCIE_DRIVER_FAIL);
+    }
+
+    return VdmaBufferHandle(map_user_buffer_info.mapped_handle);
+}
+
+hailo_status HailoRTDriver::vdma_buffer_unmap(VdmaBufferHandle handle)
+{
+    if (0 > ioctl(this->m_fd, HAILO_VDMA_BUFFER_UNMAP, handle)) {
+        LOGGER__ERROR("Failed to unmap user buffer with errno:{}", errno);
+        return HAILO_PCIE_DRIVER_FAIL;
+    }
+
+    return HAILO_SUCCESS;
+}
+
+Expected<std::pair<uintptr_t, uint64_t>> HailoRTDriver::descriptors_list_create(size_t desc_count)
+{
+    hailo_desc_list_create_params create_desc_info {.desc_count = desc_count, .desc_handle = 0, .dma_address = 0 };
+
+    if (0 > ioctl(this->m_fd, HAILO_DESC_LIST_CREATE, &create_desc_info)) {
+        LOGGER__ERROR("Failed to create descriptors list with errno:{}", errno);
+        return make_unexpected(HAILO_PCIE_DRIVER_FAIL);
+    }
+
+    return std::make_pair(create_desc_info.desc_handle, create_desc_info.dma_address);
+}
+
+hailo_status HailoRTDriver::descriptors_list_release(uintptr_t desc_handle)
+{
+    if (0 > ioctl(this->m_fd, HAILO_DESC_LIST_RELEASE, desc_handle)) {
+        LOGGER__ERROR("Failed to release descriptors list with errno: {}", errno);
+        return HAILO_PCIE_DRIVER_FAIL;
+    }
+
+    return HAILO_SUCCESS; 
+}
+
+hailo_status HailoRTDriver::descriptors_list_bind_vdma_buffer(uintptr_t desc_handle, VdmaBufferHandle buffer_handle,
+    uint16_t desc_page_size, uint8_t channel_index)
+{
+    hailo_desc_list_bind_vdma_buffer_params config_info;
+    config_info.buffer_handle = buffer_handle;
+    config_info.desc_handle = desc_handle;
+    config_info.desc_page_size = desc_page_size;
+    config_info.channel_index = channel_index;
+
+    if (0 > ioctl(this->m_fd, HAILO_DESC_LIST_BIND_VDMA_BUFFER, &config_info)) {
+        LOGGER__ERROR("Failed to bind vdma buffer to descriptors list with errno: {}", errno);
+        return HAILO_PCIE_DRIVER_FAIL;
+    }
+
+    return HAILO_SUCCESS; 
+}
+
+hailo_status HailoRTDriver::vdma_channel_abort(uint32_t channel_index, VdmaChannelHandle channel_handle)
+{
+    hailo_vdma_channel_abort_params params = {
+        .channel_index = channel_index,
+        .channel_handle = channel_handle
+    };
+
+    if (0 > ioctl(this->m_fd, HAILO_VDMA_CHANNEL_ABORT, &params)) {
+        const auto ioctl_errno = errno;
+        if (ECONNRESET == ioctl_errno) {
+            LOGGER__DEBUG("Channel (index={}) was deactivated!", channel_index);
+            return HAILO_STREAM_NOT_ACTIVATED;
+        }
+        else {
+            LOGGER__ERROR("Failed to abort vdma channel (index={}) with errno: {}", channel_index, errno);
+            return HAILO_PCIE_DRIVER_FAIL;
+        }
+    }
+
+    return HAILO_SUCCESS;
+}
+
+hailo_status HailoRTDriver::vdma_channel_clear_abort(uint32_t channel_index, VdmaChannelHandle channel_handle)
+{
+    hailo_vdma_channel_clear_abort_params params = {
+        .channel_index = channel_index,
+        .channel_handle = channel_handle
+    };
+
+    if (0 > ioctl(this->m_fd, HAILO_VDMA_CHANNEL_CLEAR_ABORT, &params)) {
+        const auto ioctl_errno = errno;
+        if (ECONNRESET == ioctl_errno) {
+            LOGGER__DEBUG("Channel (index={}) was deactivated!", channel_index);
+            return HAILO_STREAM_NOT_ACTIVATED;
+        }
+        else {
+            LOGGER__ERROR("Failed to clear abort vdma channel (index={}) with errno: {}", channel_index, errno);
+            return HAILO_PCIE_DRIVER_FAIL;
+        }
+    }
+
+    return HAILO_SUCCESS;
+}
+
+Expected<uintptr_t> HailoRTDriver::vdma_low_memory_buffer_alloc(size_t size)
+{
+    CHECK_AS_EXPECTED(m_allocate_driver_buffer, HAILO_INVALID_OPERATION,
+        "Tried to allocate buffer from driver even though operation is not supported");
+
+    hailo_allocate_buffer_params allocate_params = {
+        .buffer_size    = size,
+        .buffer_handle  = 0
+    };
+
+    if (0 > ioctl(this->m_fd, HAILO_VDMA_LOW_MEMORY_BUFFER_ALLOC, &allocate_params)) {
+        LOGGER__ERROR("Failed to allocate buffer with errno: {}", errno);
+        return make_unexpected(HAILO_PCIE_DRIVER_FAIL);
+    }
+
+    return std::move(allocate_params.buffer_handle);
+}
+
+hailo_status HailoRTDriver::vdma_low_memory_buffer_free(uintptr_t buffer_handle)
+{
+    CHECK(m_allocate_driver_buffer, HAILO_INVALID_OPERATION,
+        "Tried to free allocated buffer from driver even though operation is not supported");
+
+    if (0 > ioctl(this->m_fd, HAILO_VDMA_LOW_MEMORY_BUFFER_FREE, buffer_handle)) {
+        LOGGER__ERROR("Failed to free allocated buffer with errno: {}", errno);
+        return HAILO_PCIE_DRIVER_FAIL;
+    }
+
+    return HAILO_SUCCESS; 
+}
+
+hailo_status HailoRTDriver::mark_as_used()
+{
+    hailo_mark_as_in_use_params params = {
+        .in_use = false
+    };
+    if (0 > ioctl(this->m_fd, HAILO_MARK_AS_IN_USE, &params)) {
+        LOGGER__ERROR("Failed to mark device as in use with errno: {}", errno);
+        return HAILO_PCIE_DRIVER_FAIL;
+    }
+    if (params.in_use) {
+        return HAILO_DEVICE_IN_USE;
+    }
+    return HAILO_SUCCESS;
+}
+
+} /* namespace hailort */
diff --git a/hailort/libhailort/src/os/posix/microsec_timer.cpp b/hailort/libhailort/src/os/posix/microsec_timer.cpp
new file mode 100644 (file)
index 0000000..5366971
--- /dev/null
@@ -0,0 +1,22 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file timer.cpp
+ * @brief Timer wrapper for Unix
+ **/
+
+#include "os/microsec_timer.hpp"
+#include <thread>
+#include <chrono>
+
+namespace hailort
+{
+
+void MicrosecTimer::sleep(uint64_t microsecs)
+{
+    std::this_thread::sleep_for(std::chrono::microseconds(microsecs));
+}
+
+} /* namespace hailort */
diff --git a/hailort/libhailort/src/os/posix/mmap_buffer.cpp b/hailort/libhailort/src/os/posix/mmap_buffer.cpp
new file mode 100644 (file)
index 0000000..1fdd3bf
--- /dev/null
@@ -0,0 +1,62 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file mmap_buffer.cpp
+ * @brief Wrapper around unix memory mapping (mmap)
+ **/
+
+#include "os/mmap_buffer.hpp"
+
+#include <sys/mman.h>
+#include <errno.h>
+
+#if defined(__unix__)
+#include <linux/mman.h>
+#endif
+
+// If MAP_UNINITIALIZED isn't defined (MAP_UNINITIALIZED isn't POSIX standard) then it has no impact in mmap function
+#ifndef MAP_UNINITIALIZED
+#define MAP_UNINITIALIZED (0)
+#endif
+
+namespace hailort
+{
+
+#define INVALID_FD (-1)
+
+
+void * const MmapBufferImpl::INVALID_ADDR = MAP_FAILED;
+
+Expected<MmapBufferImpl> MmapBufferImpl::create_shared_memory(size_t length)
+{
+    void *address = mmap(nullptr, length, PROT_WRITE | PROT_READ, 
+        MAP_ANONYMOUS | MAP_SHARED | MAP_UNINITIALIZED,
+        INVALID_FD, /*offset=*/ 0);
+
+    CHECK_AS_EXPECTED(INVALID_ADDR != address, HAILO_OUT_OF_HOST_MEMORY, "Failed to mmap buffer with errno:{}", errno);
+    return MmapBufferImpl(address, length);
+}
+
+Expected<MmapBufferImpl> MmapBufferImpl::create_file_map(size_t length, FileDescriptor &file, uintptr_t offset)
+{
+    void *address = mmap(nullptr, length, PROT_WRITE | PROT_READ, MAP_SHARED, file, (off_t)offset);
+    CHECK_AS_EXPECTED(INVALID_ADDR != address, HAILO_INTERNAL_FAILURE, "Failed to mmap buffer fd with errno:{}", errno);
+    return MmapBufferImpl(address, length);
+}
+
+hailo_status MmapBufferImpl::unmap()
+{
+    if (INVALID_ADDR != m_address) {
+        if (0 != munmap(m_address, m_length)) {
+            LOGGER__ERROR("munmap of address {}, length: {} failed with errno {}", (void*)m_address, m_length, errno);
+            return HAILO_INTERNAL_FAILURE;
+        }
+        m_address = INVALID_ADDR;
+        m_length = 0;
+    }
+    return HAILO_SUCCESS;
+}
+
+} /* namespace hailort */
diff --git a/hailort/libhailort/src/os/posix/pcie_driver_sysfs.cpp b/hailort/libhailort/src/os/posix/pcie_driver_sysfs.cpp
new file mode 100644 (file)
index 0000000..ddfe4c4
--- /dev/null
@@ -0,0 +1,101 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file pcie_driver_sysfs.cpp
+ * @brief Parse pcie driver sysfs
+ **/
+
+#include "os/posix/pcie_driver_sysfs.hpp"
+#include <stdarg.h>
+#include <dirent.h>
+
+namespace hailort
+{
+
+#define HAILO_PCIE_CLASS_PATH ("/sys/class/hailo_chardev")
+#define HAILO_BOARD_LOCATION_FILENAME ("board_location")
+#define HAILO_DEVICE_ID_FILENAME ("device_id")
+
+
+Expected<std::vector<std::string>> list_sysfs_pcie_devices()
+{
+    DIR *dir_iter = opendir(HAILO_PCIE_CLASS_PATH);
+    if (!dir_iter) {
+        if (ENOENT == errno) {
+            LOGGER__ERROR("Can't find hailo pcie class, this may happen if the driver is not installed (this may happen"
+            " if the kernel was updated), or if there is no connected Hailo board");
+            return make_unexpected(HAILO_PCIE_DRIVER_NOT_INSTALLED);
+        }
+        else {
+            LOGGER__ERROR("Failed to open hailo pcie class ({}), errno {}", HAILO_PCIE_CLASS_PATH, errno);
+            return make_unexpected(HAILO_PCIE_DRIVER_FAIL);
+        }
+    }
+
+    std::vector<std::string> devices;
+    struct dirent *dir = nullptr;
+    while ((dir = readdir(dir_iter)) != nullptr) {
+        std::string device_name(dir->d_name);
+        if (device_name == "." || device_name == "..") {
+            continue;
+        }
+        devices.push_back(device_name);
+    }
+
+    closedir(dir_iter);
+    return devices;
+}
+
+/**
+ * Parses hailo driver sysfs entry using scanf format string
+ * @param device_name - name of the specific device (inside hailo class sysfs directory)
+ * @param sysfs_file_name - file name inside the device sysfs directory
+ * @param expected_count - expected amount of scanf variable
+ * @param fscanf_format - scanf format of the file
+ * @param ... - external arguments, filled by the scanf
+ */
+__attribute__((__format__ (__scanf__, 4, 5)))
+static hailo_status parse_device_sysfs_file(const std::string &device_name, const std::string &sysfs_file_name, uint32_t expected_count,
+    const char *fscanf_format, ...)
+{
+    std::string sysfs_file_path = std::string(HAILO_PCIE_CLASS_PATH) + "/" +
+        device_name + "/" + sysfs_file_name;
+    FILE *file_obj = fopen(sysfs_file_path.c_str(), "r");
+    if (!file_obj) {
+        LOGGER__ERROR("Failed opening sysfs file {}, errno {}", sysfs_file_path, errno);
+        return HAILO_FILE_OPERATION_FAILURE;
+    }
+
+    va_list args;
+    va_start(args, fscanf_format);
+    auto items_count = vfscanf(file_obj, fscanf_format, args);
+    va_end(args);
+    fclose(file_obj);
+
+    if (static_cast<uint32_t>(items_count) != expected_count) {
+        LOGGER__ERROR("Invalid sysfs file format {}", sysfs_file_path);
+        return HAILO_PCIE_DRIVER_FAIL;
+    }
+
+    return HAILO_SUCCESS;
+}
+
+Expected<HailoRTDriver::DeviceInfo> query_device_info(const std::string &device_name)
+{
+    HailoRTDriver::DeviceInfo device_info = {};
+    device_info.dev_path = std::string("/dev/") + device_name;
+
+    auto status = parse_device_sysfs_file(device_name, HAILO_BOARD_LOCATION_FILENAME, 4, "%04x:%02x:%02x.%d",
+        &device_info.domain, &device_info.bus, &device_info.device, &device_info.func);
+    CHECK_SUCCESS_AS_EXPECTED(status, "Failed reading {} file", HAILO_BOARD_LOCATION_FILENAME);
+
+    status = parse_device_sysfs_file(device_name, HAILO_DEVICE_ID_FILENAME, 2, "%x:%x",
+        &device_info.vendor_id, &device_info.device_id);
+    CHECK_SUCCESS_AS_EXPECTED(status, "Failed reading {} file", HAILO_DEVICE_ID_FILENAME);
+
+    return device_info;
+}
+
+} /* namespace hailort */
diff --git a/hailort/libhailort/src/os/posix/pcie_driver_sysfs.hpp b/hailort/libhailort/src/os/posix/pcie_driver_sysfs.hpp
new file mode 100644 (file)
index 0000000..32b60af
--- /dev/null
@@ -0,0 +1,18 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file pcie_driver_sysfs.hpp
+ * @brief Parse pcie driver sysfs
+ **/
+
+#include "os/hailort_driver.hpp"
+
+namespace hailort
+{
+
+Expected<std::vector<std::string>> list_sysfs_pcie_devices();
+Expected<HailoRTDriver::DeviceInfo> query_device_info(const std::string &device_name);
+
+} /* namespace hailort */
diff --git a/hailort/libhailort/src/os/posix/qnx/event.cpp b/hailort/libhailort/src/os/posix/qnx/event.cpp
new file mode 100644 (file)
index 0000000..6109676
--- /dev/null
@@ -0,0 +1,165 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file event.cpp
+ * @brief Event & Semaphore wrapper for QNX
+ *
+ * TODO: doc
+ **/
+#include "hailo/event.hpp"
+#include "hailo/hailort.h"
+#include "common/utils.hpp"
+#include "event_internal.hpp"
+
+#include <poll.h>
+#include <utility>
+
+#define INVALID_FD (-1)
+
+namespace hailort
+{
+
+Waitable::Waitable(underlying_handle_t handle) :
+    m_handle(handle)
+{}
+
+Waitable::~Waitable()
+{
+    if (-1 != m_handle) {
+        (void) close(m_handle);
+    }
+}
+
+Waitable::Waitable(Waitable&& other) :
+    m_handle(std::exchange(other.m_handle, INVALID_FD))
+{}
+
+underlying_handle_t Waitable::get_underlying_handle()
+{
+    return m_handle;
+}
+
+hailo_status Waitable::eventfd_poll(underlying_handle_t fd, std::chrono::milliseconds timeout)
+{
+    (void) fd;
+    (void) timeout;
+    return HAILO_NOT_IMPLEMENTED;
+}
+
+hailo_status Waitable::eventfd_read(underlying_handle_t fd)
+{
+    (void) fd;
+    return HAILO_NOT_IMPLEMENTED;
+}
+
+hailo_status Waitable::eventfd_write(underlying_handle_t fd)
+{
+    (void) fd;
+    return HAILO_NOT_IMPLEMENTED;
+}
+
+Expected<Event> Event::create(const State& initial_state)
+{
+    (void) initial_state;
+    return make_unexpected(HAILO_NOT_IMPLEMENTED);
+}
+
+EventPtr Event::create_shared(const State& initial_state)
+{
+    (void) initial_state;
+    return make_shared_nothrow<Event>(INVALID_FD);
+}
+
+hailo_status Event::wait(std::chrono::milliseconds timeout)
+{
+    (void) timeout;
+    return HAILO_NOT_IMPLEMENTED;
+}
+
+hailo_status Event::signal()
+{
+    return HAILO_NOT_IMPLEMENTED;
+}
+
+bool Event::is_auto_reset()
+{
+    return false;
+}
+
+hailo_status Event::reset()
+{
+    return HAILO_NOT_IMPLEMENTED;
+}
+
+underlying_handle_t Event::open_event_handle(const State& initial_state)
+{
+    (void) initial_state;
+    return INVALID_FD;
+}
+
+Expected<Semaphore> Semaphore::create(uint32_t initial_count)
+{
+    (void) initial_count;
+    return make_unexpected(HAILO_NOT_IMPLEMENTED);
+}
+
+SemaphorePtr Semaphore::create_shared(uint32_t initial_count)
+{
+    (void) initial_count;
+    return make_shared_nothrow<Semaphore>(INVALID_FD);
+}
+
+hailo_status Semaphore::wait(std::chrono::milliseconds timeout)
+{
+    (void) timeout;
+    return HAILO_NOT_IMPLEMENTED;
+}
+
+hailo_status Semaphore::signal()
+{
+    return HAILO_NOT_IMPLEMENTED;
+}
+
+bool Semaphore::is_auto_reset()
+{
+    return true;
+}
+
+underlying_handle_t Semaphore::open_semaphore_handle(uint32_t initial_count)
+{
+    (void) initial_count;
+    return INVALID_FD;
+}
+
+WaitOrShutdown::WaitOrShutdown(WaitablePtr waitable, EventPtr shutdown_event)
+{
+    (void) waitable;
+    (void) shutdown_event;
+}
+
+hailo_status WaitOrShutdown::wait(std::chrono::milliseconds timeout)
+{
+    (void) timeout;
+    return HAILO_NOT_IMPLEMENTED;
+}
+
+hailo_status WaitOrShutdown::signal()
+{
+    return HAILO_NOT_IMPLEMENTED;
+}
+
+WaitOrShutdown::WaitHandleArray WaitOrShutdown::create_wait_handle_array(WaitablePtr waitable, EventPtr shutdown_event)
+{
+    (void) waitable;
+    (void) shutdown_event;
+    // Note the order!
+    WaitHandleArray pfds{{
+        {INVALID_FD, POLLIN, 0},
+        {INVALID_FD, POLLIN, 0}
+    }};
+    return pfds;
+}
+
+} /* namespace hailort */
diff --git a/hailort/libhailort/src/os/posix/unix/event.cpp b/hailort/libhailort/src/os/posix/unix/event.cpp
new file mode 100644 (file)
index 0000000..2e7a898
--- /dev/null
@@ -0,0 +1,295 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file event.cpp
+ * @brief Event & Semaphore wrapper for Unix
+ *
+ * TODO: doc
+ **/
+#include "hailo/event.hpp"
+#include "hailo/hailort.h"
+#include "common/utils.hpp"
+#include "event_internal.hpp"
+
+#include <sys/eventfd.h>
+#include <poll.h>
+#include <utility>
+
+namespace hailort
+{
+
+Waitable::Waitable(underlying_handle_t handle) :
+    m_handle(handle)
+{}
+
+Waitable::~Waitable()
+{
+    if (-1 != m_handle) {
+        (void) close(m_handle);
+    }
+}
+
+Waitable::Waitable(Waitable&& other) :
+    m_handle(std::exchange(other.m_handle, -1))
+{}
+
+underlying_handle_t Waitable::get_underlying_handle()
+{
+    return m_handle;
+}
+
+hailo_status Waitable::eventfd_poll(underlying_handle_t fd, std::chrono::milliseconds timeout)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    struct pollfd pfd{};
+    int poll_ret = -1;
+
+    assert(-1 != fd);
+
+    if (UINT32_MAX < timeout.count()) {
+        status = HAILO_INVALID_ARGUMENT;
+        LOGGER__ERROR("Invalid timeout value: {}", timeout.count());
+        goto l_exit;
+    }
+    if (INT_MAX < timeout.count()) {
+        timeout = std::chrono::milliseconds(INT_MAX);
+    }
+
+    pfd.fd = fd;
+    pfd.events = POLLIN;
+    do {
+        poll_ret = poll(&pfd, 1, static_cast<int>(timeout.count()));
+    } while ((0 > poll_ret) && (EINTR == poll_ret));
+
+    if (0 == poll_ret) {
+        LOGGER__TRACE("Timeout");
+        status = HAILO_TIMEOUT;
+        goto l_exit;
+    }
+    if (0 > poll_ret) {
+        LOGGER__ERROR("poll failed with errno={}", errno);
+        status = HAILO_INTERNAL_FAILURE;
+        goto l_exit;
+    }
+    if (0 == (pfd.revents & POLLIN)) {
+        LOGGER__ERROR("pfd not in read state. revents={}", pfd.revents);
+        status = HAILO_INTERNAL_FAILURE;
+        goto l_exit;
+    }
+
+    status = HAILO_SUCCESS;
+l_exit:
+    return status;
+}
+
+hailo_status Waitable::eventfd_read(underlying_handle_t fd)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    ssize_t read_ret = -1;
+    uint64_t dummy;
+
+    assert(-1 != fd);
+
+    read_ret = read(fd, &dummy, sizeof(dummy));
+    if (sizeof(dummy) != read_ret) {
+        LOGGER__ERROR("read failed. bytes_read={}, expected={}, errno={}", read_ret, sizeof(dummy), errno);
+        status = HAILO_INTERNAL_FAILURE;
+        goto l_exit;    
+    }
+
+    status = HAILO_SUCCESS;
+l_exit:
+    return status;
+}
+
+hailo_status Waitable::eventfd_write(underlying_handle_t fd)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    ssize_t write_ret = -1;
+    uint64_t buffer = 1;
+
+    assert(-1 != fd);
+    
+    write_ret = write(fd, &buffer, sizeof(buffer));
+    if (sizeof(buffer) != write_ret) {
+        LOGGER__ERROR("write failed. bytes_written={}, expected={}, errno={}", write_ret, sizeof(buffer), errno);
+        status = HAILO_INTERNAL_FAILURE;
+        goto l_exit;    
+    }
+
+    status = HAILO_SUCCESS;
+l_exit:
+    return status;
+}
+
+Expected<Event> Event::create(const State& initial_state)
+{
+    const auto handle = open_event_handle(initial_state);
+    if (-1 == handle) {
+        return make_unexpected(HAILO_INTERNAL_FAILURE);
+    }
+    return Event(handle);
+}
+
+EventPtr Event::create_shared(const State& initial_state)
+{
+    const auto handle = open_event_handle(initial_state);
+    if (-1 == handle) {
+        return nullptr;
+    }
+
+    return make_shared_nothrow<Event>(handle);
+}
+
+hailo_status Event::wait(std::chrono::milliseconds timeout)
+{
+    return eventfd_poll(m_handle, timeout);
+}
+
+hailo_status Event::signal()
+{
+    return eventfd_write(m_handle);
+}
+
+bool Event::is_auto_reset()
+{
+    return false;
+}
+
+hailo_status Event::reset()
+{
+    if (HAILO_TIMEOUT == wait(std::chrono::seconds(0))) {
+        // Event is not set nothing to do, otherwise `eventfd_read` would block forever
+        return HAILO_SUCCESS;
+    }
+    return eventfd_read(m_handle);
+}
+
+underlying_handle_t Event::open_event_handle(const State& initial_state)
+{
+    static const int NO_FLAGS = 0;
+    const int state = initial_state == State::signalled ? 1 : 0;
+    const auto handle = eventfd(state, NO_FLAGS);
+    if (-1 == handle) {
+        LOGGER__ERROR("Call to eventfd failed with errno={}", errno);
+    }
+    return handle;
+}
+
+Expected<Semaphore> Semaphore::create(uint32_t initial_count)
+{
+    const auto handle = open_semaphore_handle(initial_count);
+    if (-1 == handle) {
+        return make_unexpected(HAILO_INTERNAL_FAILURE);
+    }
+    return Semaphore(handle);
+}
+
+SemaphorePtr Semaphore::create_shared(uint32_t initial_count)
+{
+    const auto handle = open_semaphore_handle(initial_count);
+    if (-1 == handle) {
+        return nullptr;
+    }
+
+    return make_shared_nothrow<Semaphore>(handle);
+}
+
+hailo_status Semaphore::wait(std::chrono::milliseconds timeout)
+{
+    // TODO: See SDK-16568 (might be necessary in the future)
+    hailo_status status = eventfd_poll(m_handle, timeout);
+    if (HAILO_TIMEOUT == status) {
+        LOGGER__INFO("eventfd_poll failed, status = {}", status);
+        return status;
+    }
+    CHECK_SUCCESS(status);
+
+    status = eventfd_read(m_handle);
+    CHECK_SUCCESS(status);
+
+    return HAILO_SUCCESS;
+}
+
+hailo_status Semaphore::signal()
+{
+    return eventfd_write(m_handle);
+}
+
+bool Semaphore::is_auto_reset()
+{
+    return true;
+}
+
+underlying_handle_t Semaphore::open_semaphore_handle(uint32_t initial_count)
+{
+    static const int SEMAPHORE = EFD_SEMAPHORE;
+    const auto handle = eventfd(initial_count, SEMAPHORE);
+    if (-1 == handle) {
+        LOGGER__ERROR("Call to eventfd failed with errno={}", errno);
+    }
+    return handle;
+}
+
+WaitOrShutdown::WaitOrShutdown(WaitablePtr waitable, EventPtr shutdown_event) :
+    m_waitable(waitable),
+    m_shutdown_event(shutdown_event),
+    m_wait_handle_array(create_wait_handle_array(waitable, shutdown_event))
+{}
+
+hailo_status WaitOrShutdown::wait(std::chrono::milliseconds timeout)
+{
+    int poll_ret = -1;
+    do {
+        poll_ret = poll(m_wait_handle_array.data(), m_wait_handle_array.size(), static_cast<int>(timeout.count()));
+    } while ((0 > poll_ret) && (EINTR == poll_ret));
+
+    if (0 == poll_ret) {
+        LOGGER__TRACE("Timeout");
+        return HAILO_TIMEOUT;
+    }
+    if (0 > poll_ret) {
+        LOGGER__ERROR("poll failed with errno={}", errno);
+        return HAILO_INTERNAL_FAILURE;
+    }
+    if ((0 == (m_wait_handle_array[WAITABLE_INDEX].revents & POLLIN)) &&
+        (0 == (m_wait_handle_array[SHUTDOWN_INDEX].revents & POLLIN))) {
+        LOGGER__ERROR("Both pfds not in read state: waitable.revents={}, shutdown.revents={}",
+            m_wait_handle_array[WAITABLE_INDEX].revents, m_wait_handle_array[SHUTDOWN_INDEX].revents);
+        return HAILO_INTERNAL_FAILURE;
+    }
+
+    if (m_wait_handle_array[SHUTDOWN_INDEX].revents & POLLIN) {
+        return HAILO_SHUTDOWN_EVENT_SIGNALED;
+    }
+
+    if (m_waitable->is_auto_reset() && (m_wait_handle_array[WAITABLE_INDEX].revents & POLLIN)) {
+        uint64_t dummy;
+        ssize_t read_ret = read(m_wait_handle_array[WAITABLE_INDEX].fd, &dummy, sizeof(dummy));
+        if (sizeof(dummy) != read_ret) {
+            LOGGER__ERROR("read failed. bytes_read={}, expected={}, errno={}", read_ret, sizeof(dummy), errno);
+            return HAILO_INTERNAL_FAILURE;
+        }
+    }
+
+    return HAILO_SUCCESS;
+}
+
+hailo_status WaitOrShutdown::signal()
+{
+    return m_waitable->signal();
+}
+
+WaitOrShutdown::WaitHandleArray WaitOrShutdown::create_wait_handle_array(WaitablePtr waitable, EventPtr shutdown_event)
+{
+    // Note the order!
+    WaitHandleArray pfds{{
+        {shutdown_event->get_underlying_handle(), POLLIN, 0},
+        {waitable->get_underlying_handle(), POLLIN, 0}
+    }};
+    return pfds;
+}
+
+} /* namespace hailort */
diff --git a/hailort/libhailort/src/os/windows/event.cpp b/hailort/libhailort/src/os/windows/event.cpp
new file mode 100644 (file)
index 0000000..fd019c9
--- /dev/null
@@ -0,0 +1,228 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file event.cpp
+ * @brief Event & Semaphore wrapper for Windows
+ **/
+#include "hailo/event.hpp"
+#include "hailo/hailort.h"
+#include "common/utils.hpp"
+#include "event_internal.hpp"
+
+#include <utility>
+#include <limits>
+
+namespace hailort
+{
+
+Waitable::Waitable(underlying_handle_t handle) :
+    m_handle(handle)
+{}
+
+Waitable::~Waitable()
+{
+    if (nullptr != m_handle) {
+        (void) CloseHandle(m_handle);
+    }
+}
+
+Waitable::Waitable(Waitable&& other) :
+    m_handle(std::exchange(other.m_handle, nullptr))
+{}
+
+underlying_handle_t Waitable::get_underlying_handle()
+{
+    return m_handle;
+}
+
+static DWORD timeout_millies(long long value)
+{
+    DWORD millies = static_cast<DWORD>(value);
+    if (UINT_MAX < value) {
+        millies = INFINITE;
+    }
+    return millies;
+}
+
+hailo_status Waitable::wait_for_single_object(underlying_handle_t handle, std::chrono::milliseconds timeout)
+{
+    DWORD wait_millies = timeout_millies(timeout.count());
+    assert(nullptr != handle);
+
+    const auto wait_result = WaitForSingleObject(handle, wait_millies);
+    switch (wait_result) {
+        case WAIT_OBJECT_0:
+            return HAILO_SUCCESS;
+        case WAIT_TIMEOUT:
+            return HAILO_TIMEOUT;
+        case WAIT_ABANDONED:
+            LOGGER__ERROR("WaitForSingleObject on handle={:X} returned WAIT_ABANDONED", handle);
+            return HAILO_INTERNAL_FAILURE;
+        default:
+            LOGGER__ERROR("WaitForSingleObject on handle={:X} returned WAIT_FAILED, last_error={}", handle, GetLastError());
+            return HAILO_INTERNAL_FAILURE;
+    }
+}
+
+Expected<Event> Event::create(const State& initial_state)
+{
+    const auto handle = open_event_handle(initial_state);
+    if (nullptr == handle) {
+        return make_unexpected(HAILO_INTERNAL_FAILURE);
+    }
+    return std::move(Event(handle));
+}
+
+EventPtr Event::create_shared(const State& initial_state)
+{
+    const auto handle = open_event_handle(initial_state);
+    if (nullptr == handle) {
+        return nullptr;
+    }
+
+    return make_shared_nothrow<Event>(handle);
+}
+
+hailo_status Event::wait(std::chrono::milliseconds timeout)
+{
+    return wait_for_single_object(m_handle, timeout);
+}
+
+hailo_status Event::signal()
+{
+    const auto result = SetEvent(m_handle);
+    if (0 == result) {
+        LOGGER__ERROR("SetEvent on handle={:X} failed with last_error={}", m_handle, GetLastError());
+        return HAILO_INTERNAL_FAILURE;
+    }
+
+    return HAILO_SUCCESS;
+}
+
+bool Event::is_auto_reset()
+{
+    return false;
+}
+
+hailo_status Event::reset()
+{
+    const auto result = ResetEvent(m_handle);
+    if (0 == result) {
+        LOGGER__ERROR("ResetEvent on handle={:X} failed with last_error={}", m_handle, GetLastError());
+        return HAILO_INTERNAL_FAILURE;
+    }
+    
+    return HAILO_SUCCESS;
+}
+
+underlying_handle_t Event::open_event_handle(const State& initial_state)
+{
+    static const LPSECURITY_ATTRIBUTES NO_INHERITANCE = nullptr;
+    static const BOOL                  MANUAL_RESET = true;
+    static const LPCSTR                NO_NAME = nullptr;
+    const BOOL state = initial_state == State::signalled ? true : false;
+    const auto handle = CreateEventA(NO_INHERITANCE, MANUAL_RESET, state, NO_NAME);
+    if (nullptr == handle) {
+        LOGGER__ERROR("Call to CreateEventA failed with last_error={}", GetLastError());
+    }
+    return handle;
+}
+
+Expected<Semaphore> Semaphore::create(uint32_t initial_count)
+{
+    const auto handle = open_semaphore_handle(initial_count);
+    if (nullptr == handle) {
+        return make_unexpected(HAILO_INTERNAL_FAILURE);
+    }
+    return std::move(Semaphore(handle));
+}
+
+SemaphorePtr Semaphore::create_shared(uint32_t initial_count)
+{
+    const auto handle = open_semaphore_handle(initial_count);
+    if (nullptr == handle) {
+        return nullptr;
+    }
+
+    return make_shared_nothrow<Semaphore>(handle);
+}
+
+hailo_status Semaphore::wait(std::chrono::milliseconds timeout)
+{
+    return wait_for_single_object(m_handle, timeout);
+}
+
+hailo_status Semaphore::signal()
+{
+    static const LONG INCREMENT_BY_ONE = 1;
+    static const PLONG IGNORE_PREVIOUS_COUNT = nullptr;
+    const auto result = ReleaseSemaphore(m_handle, INCREMENT_BY_ONE, IGNORE_PREVIOUS_COUNT);
+    if (0 == result) {
+        LOGGER__ERROR("ReleaseSemaphore on handle={:X} failed with last_error={}", m_handle, GetLastError());
+        return HAILO_INTERNAL_FAILURE;
+    }
+    
+    return HAILO_SUCCESS;
+}
+
+bool Semaphore::is_auto_reset()
+{
+    return true;
+}
+
+underlying_handle_t Semaphore::open_semaphore_handle(uint32_t initial_count)
+{
+    static const LPSECURITY_ATTRIBUTES NO_INHERITANCE = nullptr;
+    static const LONG                  MAX_SIZE = std::numeric_limits<long>::max();
+    static const LPCSTR                NO_NAME = nullptr;
+    const auto handle = CreateSemaphoreA(NO_INHERITANCE, initial_count, MAX_SIZE, NO_NAME);
+    if (nullptr == handle) {
+        LOGGER__ERROR("Call to CreateSemaphoreA failed with last_error={}", GetLastError());
+    }
+    return handle;
+}
+
+WaitOrShutdown::WaitOrShutdown(WaitablePtr waitable, EventPtr shutdown_event) :
+    m_waitable(waitable),
+    m_shutdown_event(shutdown_event),
+    m_wait_handle_array(create_wait_handle_array(waitable, shutdown_event))
+{}
+
+hailo_status WaitOrShutdown::wait(std::chrono::milliseconds timeout)
+{
+    DWORD wait_millies = timeout_millies(timeout.count());
+
+    static const BOOL WAIT_FOR_ANY = false;
+    const auto wait_result = WaitForMultipleObjects(static_cast<DWORD>(m_wait_handle_array.size()),
+        m_wait_handle_array.data(), WAIT_FOR_ANY, wait_millies);
+    switch (wait_result) {
+        case WAIT_OBJECT_0 + WAITABLE_INDEX:
+            return HAILO_SUCCESS;
+        case WAIT_OBJECT_0 + SHUTDOWN_INDEX:
+            return HAILO_SHUTDOWN_EVENT_SIGNALED;
+        case WAIT_TIMEOUT:
+            return HAILO_TIMEOUT;
+        default:
+            LOGGER__ERROR("WaitForMultipleObjects returned {}, last_error={}", wait_result, GetLastError());
+            return HAILO_INTERNAL_FAILURE;
+    }
+}
+
+hailo_status WaitOrShutdown::signal()
+{
+    return m_waitable->signal();
+}
+
+WaitOrShutdown::WaitHandleArray WaitOrShutdown::create_wait_handle_array(WaitablePtr waitable, EventPtr shutdown_event)
+{
+    // Note the order!
+    WaitHandleArray handles{
+        shutdown_event->get_underlying_handle(),
+        waitable->get_underlying_handle()
+    };
+    return handles;
+}
+
+} /* namespace hailort */
diff --git a/hailort/libhailort/src/os/windows/file_descriptor.cpp b/hailort/libhailort/src/os/windows/file_descriptor.cpp
new file mode 100644 (file)
index 0000000..f6b62bd
--- /dev/null
@@ -0,0 +1,44 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file file_descriptor.cpp
+ * @brief Wrapper around system file descriptors for Windows
+ **/
+
+
+#include "common/logger_macros.hpp"
+#include "os/file_descriptor.hpp"
+#include "os/windows/osdep.hpp"
+
+namespace hailort
+{
+
+FileDescriptor::FileDescriptor(underlying_handle_t fd) : m_fd(fd)
+{}
+
+FileDescriptor::~FileDescriptor()
+{
+    // NOTE: This code never tested
+    if (INVALID_HANDLE_VALUE != m_fd && m_fd) {
+        auto close_success = CloseHandle(m_fd);
+        if (0 == close_success) {
+            LOGGER__ERROR("Failed to close handle. last_error={}", GetLastError());
+        }
+    }
+}
+
+FileDescriptor::FileDescriptor(FileDescriptor &&other) noexcept : m_fd(std::exchange(other.m_fd, INVALID_HANDLE_VALUE))
+{}
+
+
+Expected<FileDescriptor> FileDescriptor::duplicate()
+{
+    // NOTE: the DuplicateHadndle() can't be just used to share the file handle
+    // between different processes. All the user-mode pointers got as a result
+    // of mmap operations become invalid
+    return make_unexpected(HAILO_NOT_IMPLEMENTED);
+}
+
+} /* namespace hailort */
diff --git a/hailort/libhailort/src/os/windows/hailort_driver.cpp b/hailort/libhailort/src/os/windows/hailort_driver.cpp
new file mode 100644 (file)
index 0000000..a8854d1
--- /dev/null
@@ -0,0 +1,981 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file hailort_driver.cpp
+ * @brief Low level interface to PCI driver
+ **/
+
+#include "os/windows/osdep.hpp"
+#include "os/hailort_driver.hpp"
+#include "common/logger_macros.hpp"
+#include "common/utils.hpp"
+#include "common/os/windows/string_conversion.hpp"
+#include "os/mmap_buffer.hpp"
+#include "../../../../drivers/win/include/Public.h"
+
+#pragma comment(lib, "cfgmgr32.lib")
+
+namespace hailort
+{
+
+//TODO: unify
+constexpr hailo_dma_data_direction direction_to_dma_data_direction(HailoRTDriver::DmaDirection direction) {
+    switch (direction){
+    case HailoRTDriver::DmaDirection::H2D:
+        return HAILO_DMA_TO_DEVICE;
+    case HailoRTDriver::DmaDirection::D2H:
+        return HAILO_DMA_FROM_DEVICE;
+    case HailoRTDriver::DmaDirection::BOTH:
+        return HAILO_DMA_BIDIRECTIONAL;
+    default:
+        assert(true);
+        // On release build Return value that will make ioctls to fail.
+        return HAILO_DMA_NONE;
+    }
+}
+
+constexpr enum hailo_cpu_id translate_cpu_id(hailo_cpu_id_t cpu_id)
+{   
+    switch (cpu_id)
+    {
+    case HAILO_CPU_ID_0:
+        return HAILO_CPU_ID_CPU0;
+    case HAILO_CPU_ID_1:
+        return HAILO_CPU_ID_CPU1;
+    default:
+        assert(true);
+        // On release build Return value that will make ioctls to fail.
+        return HAILO_CPU_ID_NONE;
+    }
+}
+
+class CDeviceProperty
+{
+public:
+    CDeviceProperty(LPCSTR FriendlyName) : m_FriendlyName(FriendlyName) {}
+    ~CDeviceProperty()
+    {
+        Drop();
+    }
+    bool IsValid() const
+    {
+        return m_Buffer != NULL;
+    }
+    void MoveTo(CDeviceProperty& other)
+    {
+        other.Drop();
+        other.m_Size = m_Size;
+        other.m_Buffer = m_Buffer;
+        other.m_String = m_String;
+        other.m_Type = m_Type;
+        other.m_Value = m_Value;
+        m_Buffer = NULL;
+    }
+    bool Number(uint32_t& Value) const {
+        Value = m_Value;
+        return m_Type == DEVPROP_TYPE_UINT32 && IsValid();
+    }
+protected:
+    PBYTE m_Buffer = NULL;
+    ULONG m_Size = 0;
+    DEVPROPTYPE m_Type = DEVPROP_TYPE_EMPTY;
+    std::wstring m_String;
+    LPCSTR m_FriendlyName;
+    uint32_t m_Value = 0;
+protected:
+    void Drop()
+    {
+        if (m_Buffer) free(m_Buffer);
+        m_Buffer = NULL;
+        m_Size = 0;
+        m_Type = DEVPROP_TYPE_EMPTY;
+    }
+    void PostProcess(CONFIGRET cr)
+    {
+        if (cr != CR_SUCCESS) {
+            Drop();
+        }
+        if (m_Type == DEVPROP_TYPE_STRING) {
+            m_String = (wchar_t *)m_Buffer;
+        }
+        if (m_Type == DEVPROP_TYPE_UINT32) {
+            if (m_Size == sizeof(uint32_t)) {
+                m_Value = *(uint32_t *)m_Buffer;
+            } else {
+                Drop();
+            }
+        }
+    }
+};
+
+class CDeviceInterfaceProperty : public CDeviceProperty
+{
+public:
+    CDeviceInterfaceProperty(LPCWSTR DevInterface, const DEVPROPKEY* Key, LPCSTR FriendlyName, bool AllowRecursion = true);
+};
+
+class CDevInstProperty : public CDeviceProperty
+{
+public:
+    CDevInstProperty(LPCWSTR DevInst, const DEVPROPKEY* Key, LPCSTR FriendlyName) :
+        CDeviceProperty(FriendlyName)
+    {
+        DEVINST dn;
+        CONFIGRET cr = CM_Locate_DevNodeW(&dn, (WCHAR *)DevInst, CM_LOCATE_DEVNODE_NORMAL);
+        if (cr != CR_SUCCESS)
+            return;
+        // try to get the size of the property
+        CM_Get_DevNode_PropertyW(dn, Key, &m_Type, NULL, &m_Size, 0);
+        if (!m_Size)
+            return;
+        m_Buffer = (PBYTE)malloc(m_Size);
+        if (!m_Buffer) {
+            return;
+        }
+        cr = CM_Get_DevNode_PropertyW(dn, Key, &m_Type, m_Buffer, &m_Size, 0);
+        PostProcess(cr);
+    }
+};
+
+class CDeviceInstancePropertyOfInterface : public CDeviceInterfaceProperty
+{
+public:
+    CDeviceInstancePropertyOfInterface(LPCWSTR DevInterface) :
+        CDeviceInterfaceProperty(DevInterface, &DEVPKEY_Device_InstanceId, "DevInstance", false)
+    { }
+    const std::wstring& DevInst() const { return m_String; }
+};
+
+bool IsSame(const HailoRTDriver::DeviceInfo& a, const HailoRTDriver::DeviceInfo& b)
+{
+    return a.bus == b.bus && a.device == b.device && a.func == b.func;
+}
+
+bool IsAny(const HailoRTDriver::DeviceInfo& a)
+{
+    return a.bus == MAXUINT;
+}
+
+class CDevicePCILocation
+{
+public:
+    CDevicePCILocation(LPCWSTR DevInterface)
+    {
+        CDeviceInterfaceProperty BusNumber(DevInterface, &DEVPKEY_Device_BusNumber, "BusNumber");
+        CDeviceInterfaceProperty Address(DevInterface, &DEVPKEY_Device_Address, "Address");
+        m_Valid = BusNumber.Number(m_Location.bus) && Address.Number(m_Location.device);
+        if (m_Valid) {
+            m_Location.func = m_Location.device & 0xff;
+            m_Location.device = m_Location.device >> 16;
+        }
+        std::wstring devInterface = DevInterface;
+        m_Location.dev_path = StringConverter::utf16_to_ansi(devInterface).value();
+    }
+    const HailoRTDriver::DeviceInfo& Location() const { return m_Location; }
+    bool IsValid() const { return m_Valid; }
+private:
+    HailoRTDriver::DeviceInfo m_Location = {};
+    bool m_Valid = false;
+};
+
+CDeviceInterfaceProperty::CDeviceInterfaceProperty(
+    LPCWSTR DevInterface,
+    const DEVPROPKEY* Key,
+    LPCSTR FriendlyName,
+    bool AllowRecursion) : CDeviceProperty(FriendlyName)
+{
+    // try to get the property via device interface
+    CM_Get_Device_Interface_PropertyW(DevInterface, Key, &m_Type, NULL, &m_Size, 0);
+    if (!m_Size) {
+        if (AllowRecursion) {
+            // try to get the property via device instance
+            CDeviceInstancePropertyOfInterface diProp(DevInterface);
+            if (diProp.IsValid()) {
+                const std::wstring& di = diProp.DevInst();
+                CDevInstProperty dip(di.c_str(), Key, FriendlyName);
+                if (dip.IsValid()) {
+                    dip.MoveTo(*this);
+                }
+            }
+        }
+        return;
+    }
+    m_Buffer = (PBYTE)malloc(m_Size);
+    if (!m_Buffer)
+        return;
+    CONFIGRET cr = CM_Get_Device_Interface_PropertyW(
+        DevInterface, Key, &m_Type, m_Buffer, &m_Size, 0);
+    PostProcess(cr);
+}
+
+class CWaitable
+{
+public:
+    ULONG Wait(ULONG millies = INFINITE)
+    {
+        return WaitForSingleObject(m_Handle, millies);
+    }
+    ~CWaitable()
+    {
+         if (m_Handle) {
+             CloseHandle(m_Handle);
+         }
+    }
+protected:
+    CWaitable(HANDLE h) : m_Handle(h) { }
+    HANDLE m_Handle;
+};
+
+class CMutex : public CWaitable
+{
+public:
+    CMutex() : CWaitable(CreateMutex(NULL, false, NULL)) { }
+    void Release()
+    {
+        ReleaseMutex(m_Handle);
+    }
+};
+
+class CEvent : public CWaitable
+{
+public:
+    CEvent(bool Manual) : CWaitable(CreateEvent(NULL, Manual, false, NULL)) { }
+};
+
+class COverlapped : public CEvent
+{
+public:
+    COverlapped() : CEvent(true)
+    {
+        RtlZeroMemory(&m_Overlapped, sizeof(m_Overlapped));
+        m_Overlapped.hEvent = m_Handle;
+    }
+    operator LPOVERLAPPED() { return &m_Overlapped; }
+protected:
+    OVERLAPPED m_Overlapped;
+};
+
+template <typename t>
+class CSync
+{
+public:
+    CSync(t& obj) : m_Obj(obj) { m_Obj.Wait(); }
+    ~CSync() { m_Obj.Release(); }
+private:
+    t& m_Obj;
+};
+using CMutexSync = CSync<CMutex>;
+
+class CDeviceFile
+{
+public:
+    CDeviceFile(std::vector<HailoRTDriver::DeviceInfo>& Instances)
+    {
+        EnumerateInstances(Instances);
+    }
+    CDeviceFile(const std::string& path)
+    {
+        std::vector<HailoRTDriver::DeviceInfo> found;
+        EnumerateInstances(found);
+        for (size_t i = 0; i < found.size(); ++i) {
+            if (path == found[i].dev_path) {
+                Create(path.c_str(), true);
+                break;
+            }
+        }
+    }
+    void Close()
+    {
+        CMutexSync sync(m_Mutex);
+        if (m_Handle) {
+            CloseHandle(m_Handle);
+            m_Handle = NULL;
+        }
+    }
+    ~CDeviceFile()
+    {
+        Unregister();
+        Close();
+    }
+    bool Present() const
+    {
+        return m_Handle;
+    }
+    HANDLE Detach() {
+        CMutexSync sync(m_Mutex);
+        HANDLE h = m_Handle;
+        m_Handle = NULL;
+        return h;
+    }
+protected:
+    void EnumerateInstances(std::vector<HailoRTDriver::DeviceInfo>& Instances)
+    {
+        CONFIGRET cr;
+        WCHAR* names = NULL, * currentName;
+        ULONG len = 0;
+        do {
+            cr = CM_Get_Device_Interface_List_SizeW(
+                &len,
+                &m_InterfaceGuid,
+                NULL,
+                CM_GET_DEVICE_INTERFACE_LIST_PRESENT);
+
+            if (cr != CR_SUCCESS || len < 2) {
+                LOGGER__ERROR("Driver interface not found, error {}", cr);
+                break;
+            }
+            if (len <= 1) {
+                LOGGER__ERROR("Driver interface not found");
+                break;
+            }
+            names = (WCHAR*)malloc(len * sizeof(WCHAR));
+            if (!names) {
+                LOGGER__ERROR("Can't allocate buffer of {} chars", len);
+                cr = CR_OUT_OF_MEMORY;
+                break;
+            }
+            cr = CM_Get_Device_Interface_ListW(
+                &m_InterfaceGuid,
+                NULL,
+                names,
+                len,
+                CM_GET_DEVICE_INTERFACE_LIST_PRESENT);
+            if (cr != CR_SUCCESS) {
+                LOGGER__ERROR("Can't retrieve driver interface, error {}", cr);
+                break;
+            }
+            for (currentName = names; names && *currentName; currentName += wcslen(currentName) + 1) {
+                CDevicePCILocation locationData(currentName);
+                if (!locationData.IsValid())
+                    continue;
+                Instances.push_back(locationData.Location());
+            }
+        } while (false);
+
+        if (names)
+        {
+            free(names);
+        }
+    }
+    bool Notify()
+    {
+        if (m_Handle) {
+            LOGGER__ERROR("Closing the file {}", m_InterfaceName);
+        }
+        Close();
+        return true;
+    }
+    void Create(LPCSTR Name, bool Writable)
+    {
+        ULONG access = GENERIC_READ, share = FILE_SHARE_READ;
+        if (Writable) {
+            access |= GENERIC_WRITE;
+        }
+        else {
+            share |= FILE_SHARE_WRITE;
+        }
+        m_Handle = CreateFileA(
+            Name,
+            access,
+            share,
+            NULL,
+            OPEN_EXISTING,
+            FILE_FLAG_OVERLAPPED,
+            NULL);
+        if (m_Handle == INVALID_HANDLE_VALUE) {
+            m_Handle = NULL;
+            LOGGER__ERROR("can't open '{}'", Name);
+            return;
+        }
+
+        if (!m_SetNotify) {
+            return;
+        }
+
+        CM_NOTIFY_FILTER filter;
+        filter.cbSize = sizeof(filter);
+        filter.Flags = 0;
+        filter.FilterType = CM_NOTIFY_FILTER_TYPE_DEVICEHANDLE;
+        filter.u.DeviceHandle.hTarget = m_Handle;
+        Unregister();
+        CM_Register_Notification(&filter, this, [](
+            _In_ HCMNOTIFICATION,
+            _In_opt_ PVOID             Context,
+            _In_ CM_NOTIFY_ACTION      Action,
+            _In_reads_bytes_(EventDataSize) PCM_NOTIFY_EVENT_DATA,
+            _In_ DWORD) -> DWORD
+            {
+                CDeviceFile* f = (CDeviceFile*)Context;
+                if (Action == CM_NOTIFY_ACTION_DEVICEQUERYREMOVE) {
+                    return f->Notify() ? ERROR_SUCCESS : ERROR_CANCELLED;
+                }
+                if (Action == CM_NOTIFY_ACTION_DEVICEREMOVECOMPLETE) {
+                    f->Notify();
+                }
+                return ERROR_SUCCESS;
+            },
+            &m_Notification);
+    }
+    void Unregister()
+    {
+        if (m_Notification) {
+            CM_Unregister_Notification(m_Notification);
+            m_Notification = NULL;
+        }
+    }
+private:
+    GUID m_InterfaceGuid = GUID_DEVINTERFACE_HailoKM;
+    std::string m_InterfaceName;
+    HCMNOTIFICATION m_Notification = NULL;
+    CMutex m_Mutex;
+    bool m_SetNotify = false;
+    HANDLE m_Handle = NULL;
+};
+
+
+static int ioctl(HANDLE h, ULONG val, tCompatibleHailoIoctlData *ioctl_data)
+{
+    ioctl_data->Parameters.u.value = val;
+    ULONG returned;
+    COverlapped overlapped;
+    bool res = DeviceIoControl(h, HAILO_IOCTL_COMPATIBLE, ioctl_data, sizeof(*ioctl_data),
+                               ioctl_data, sizeof(*ioctl_data), &returned, overlapped);
+    if (!res) {
+        ULONG lastError = GetLastError();
+        if (lastError != ERROR_IO_PENDING) {
+            errno = (int)lastError;
+            return -1;
+        }
+        if (!GetOverlappedResult(h, overlapped, &returned, true)) {
+            errno = (int)GetLastError();
+            return -1;
+        }
+    }
+    return 0;
+}
+
+const HailoRTDriver::VdmaChannelHandle HailoRTDriver::INVALID_VDMA_CHANNEL_HANDLE = INVALID_CHANNEL_HANDLE_VALUE;
+const uintptr_t HailoRTDriver::INVALID_DRIVER_BUFFER_HANDLE_VALUE = INVALID_DRIVER_HANDLE_VALUE;
+const uint8_t HailoRTDriver::INVALID_VDMA_CHANNEL_INDEX = INVALID_VDMA_CHANNEL;
+
+static hailo_status validate_driver_version(const hailo_driver_info &driver_info)
+{
+    hailo_version_t library_version{};
+    auto status = hailo_get_library_version(&library_version);
+    CHECK_SUCCESS(status);
+    CHECK((driver_info.major_version == library_version.major) &&
+        (driver_info.minor_version == library_version.minor) &&
+        (driver_info.revision_version == library_version.revision), HAILO_INVALID_DRIVER_VERSION,
+        "Driver version ({}.{}.{}) is different from library version ({}.{}.{})",
+        driver_info.major_version, driver_info.minor_version, driver_info.revision_version,
+        library_version.major, library_version.minor, library_version.revision);
+    return HAILO_SUCCESS;
+}
+
+HailoRTDriver::HailoRTDriver(const std::string &dev_path, FileDescriptor &&fd, hailo_status &status) :
+    m_fd(std::move(fd)),
+    m_dev_path(dev_path),
+    m_allocate_driver_buffer(false)
+{
+    tCompatibleHailoIoctlData data = {};
+    hailo_driver_info& driver_info = data.Buffer.DriverInfo;
+    if (0 > ioctl(m_fd, HAILO_QUERY_DRIVER_INFO, &data)) {
+        LOGGER__ERROR("Failed to query driver info, errno {}", errno);
+        status = HAILO_PCIE_DRIVER_FAIL;
+        return;
+    }
+    status = validate_driver_version(driver_info);
+    if (HAILO_SUCCESS != status) {
+        LOGGER__ERROR("Driver version mismatch, status {}", status);
+        return;
+    }
+
+    hailo_device_properties& device_properties = data.Buffer.DeviceProperties;
+    if (0 > ioctl(m_fd, HAILO_QUERY_DEVICE_PROPERTIES, &data)) {
+        LOGGER__ERROR("Failed query pcie device properties, errno {}", errno);
+        status = HAILO_PCIE_DRIVER_FAIL;
+        return;
+    }
+
+    m_desc_max_page_size = device_properties.desc_max_page_size;
+    switch (device_properties.board_type) {
+    case HAILO8:
+        m_board_type = BoardType::HAILO8;
+        break;
+    case HAILO_MERCURY:
+        m_board_type = BoardType::MERCURY;
+        break;
+    default:
+        LOGGER__ERROR("Invalid board type {} returned from ioctl", device_properties.board_type);
+        status = HAILO_PCIE_DRIVER_FAIL;
+        return;
+    }
+
+    switch (device_properties.dma_type) {
+    case HAILO_DMA_TYPE_PCIE:
+        m_dma_type = DmaType::PCIE;
+        break;
+    case HAILO_DMA_TYPE_DRAM:
+        m_dma_type = DmaType::DRAM;
+        break;
+    default:
+        LOGGER__ERROR("Invalid dma type returned from ioctl {}", device_properties.dma_type);
+        status = HAILO_PCIE_DRIVER_FAIL;
+        return;
+    }
+
+    status = HAILO_SUCCESS;
+}
+
+Expected<std::vector<HailoRTDriver::DeviceInfo>> HailoRTDriver::scan_pci()
+{
+    std::vector<HailoRTDriver::DeviceInfo> all;
+    CDeviceFile f(all);
+    for (size_t i = 0; i < all.size(); ++i) {
+        const HailoRTDriver::DeviceInfo& di = all[i];
+        LOGGER__INFO("Found {}:{}:{} {}", di.bus, di.device, di.func, di.dev_path);
+    }
+    return std::move(all);
+}
+
+Expected<HailoRTDriver> HailoRTDriver::create(const std::string &dev_path)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    CDeviceFile f(dev_path);
+    if (!f.Present()) {
+        LOGGER__ERROR("Failed to open board {}", dev_path);
+        return make_unexpected(HAILO_OPEN_FILE_FAILURE);
+    }
+    FileDescriptor fd(f.Detach());
+
+    HailoRTDriver platform(dev_path, std::move(fd), status);
+    if (HAILO_SUCCESS != status) {
+        return make_unexpected(status);
+    }
+    return platform;
+}
+
+Expected<D2H_EVENT_MESSAGE_t> HailoRTDriver::read_notification()
+{
+    tCompatibleHailoIoctlData data;
+    D2H_EVENT_MESSAGE_t notification;
+    hailo_d2h_notification& notification_buffer = data.Buffer.D2HNotification;
+
+    auto rc = ioctl(this->m_fd, HAILO_READ_NOTIFICATION, &data);
+    if (0 > rc) {
+        return make_unexpected(HAILO_PCIE_DRIVER_FAIL);
+    }
+
+    CHECK_AS_EXPECTED(sizeof(notification) >= notification_buffer.buffer_len, HAILO_GET_D2H_EVENT_MESSAGE_FAIL,
+        "buffer len is not valid = {}", notification_buffer.buffer_len);
+
+    memcpy(&notification, notification_buffer.buffer, notification_buffer.buffer_len);
+    return std::move(notification);
+}
+
+hailo_status HailoRTDriver::disable_notifications()
+{
+    tCompatibleHailoIoctlData data = {};
+
+    int res = ioctl(m_fd, HAILO_DISABLE_NOTIFICATION, &data);
+    CHECK(0 <= res, HAILO_PCIE_DRIVER_FAIL, "HAILO_DISABLE_NOTIFICATION failed with errno: {}", errno);
+
+    return HAILO_SUCCESS;
+}
+
+hailo_status HailoRTDriver::read_bar(PciBar bar, off_t offset, size_t size, void *buf)
+{
+    if (size == 0) {
+        LOGGER__ERROR("Invalid size to read");
+        return HAILO_INVALID_ARGUMENT;
+    }
+
+    if (buf == nullptr) {
+        LOGGER__ERROR("Read buffer pointer is NULL");
+        return HAILO_INVALID_ARGUMENT;
+    }
+
+    tCompatibleHailoIoctlData data = {};
+    hailo_bar_transfer_params& transfer = data.Buffer.BarTransfer;
+    transfer.transfer_direction = TRANSFER_READ;
+    transfer.bar_index = static_cast<uint32_t>(bar);
+    transfer.offset = offset;
+    transfer.count = size;
+    transfer.buffer = buf;
+
+    if (0 > ioctl(m_fd, HAILO_BAR_TRANSFER, &data)) {
+        LOGGER__ERROR("HailoRTDriver::read_bar failed with errno:{}", errno);
+        return HAILO_PCIE_DRIVER_FAIL;
+    }
+
+    return HAILO_SUCCESS;
+}
+
+hailo_status HailoRTDriver::write_bar(PciBar bar, off_t offset, size_t size, const void *buf)
+{
+    if (size == 0) {
+        LOGGER__ERROR("Invalid size to write");
+        return HAILO_INVALID_ARGUMENT;
+    }
+
+    if (buf == nullptr) {
+        LOGGER__ERROR("Write buffer pointer is NULL");
+        return HAILO_INVALID_ARGUMENT;
+    }
+
+    tCompatibleHailoIoctlData data = {};
+    hailo_bar_transfer_params& transfer = data.Buffer.BarTransfer;
+    transfer.transfer_direction = TRANSFER_WRITE;
+    transfer.bar_index = static_cast<uint32_t>(bar);
+    transfer.offset = offset;
+    transfer.count = size;
+    transfer.buffer = (void*)buf;
+
+    if (0 > ioctl(this->m_fd, HAILO_BAR_TRANSFER, &data)) {
+        LOGGER__ERROR("HailoRTDriver::write_bar failed with errno: {}", errno);
+        return HAILO_PCIE_DRIVER_FAIL;
+    }
+
+    return HAILO_SUCCESS;
+}
+
+Expected<uint32_t> HailoRTDriver::read_vdma_channel_registers(off_t offset, size_t size)
+{
+    tCompatibleHailoIoctlData data = {};
+    hailo_channel_registers_params& params = data.Buffer.ChannelRegisters;
+    params.transfer_direction = TRANSFER_READ;
+    params.offset = offset;
+    params.size = size;
+    params.data = 0;
+
+    if (0 > ioctl(this->m_fd, HAILO_VDMA_CHANNEL_REGISTERS, &data)) {
+        LOGGER__ERROR("HailoRTDriver::read_vdma_channel_registers failed with errno: {}", errno);
+        return make_unexpected(HAILO_PCIE_DRIVER_FAIL);
+    }
+
+    return std::move(params.data);
+}
+
+hailo_status HailoRTDriver::write_vdma_channel_registers(off_t offset, size_t size, uint32_t value)
+{
+    tCompatibleHailoIoctlData data = {};
+    hailo_channel_registers_params& params = data.Buffer.ChannelRegisters;
+    params.transfer_direction = TRANSFER_WRITE;
+    params.offset = offset;
+    params.size = size;
+    params.data = value;
+
+    if (0 > ioctl(this->m_fd, HAILO_VDMA_CHANNEL_REGISTERS, &data)) {
+        LOGGER__ERROR("HailoRTDriver::write_vdma_channel_registers failed with errno: {}", errno);
+        return HAILO_PCIE_DRIVER_FAIL;
+    }
+
+    return HAILO_SUCCESS;
+}
+
+hailo_status HailoRTDriver::vdma_buffer_sync(VdmaBufferHandle handle, DmaDirection sync_direction, void *address,
+    size_t buffer_size)
+{
+    CHECK(sync_direction != DmaDirection::BOTH, HAILO_INVALID_ARGUMENT, "Can't sync vdma data both host and device");
+    tCompatibleHailoIoctlData data = {};
+    hailo_vdma_buffer_sync_params& sync_info = data.Buffer.VdmaBufferSync;
+    sync_info.handle = handle;
+    sync_info.sync_type = (sync_direction == DmaDirection::H2D) ? HAILO_SYNC_FOR_DEVICE : HAILO_SYNC_FOR_HOST;
+    sync_info.buffer_address = address;
+    sync_info.buffer_size = buffer_size;
+    if (0 > ioctl(this->m_fd, HAILO_VDMA_BUFFER_SYNC, &data)) {
+        LOGGER__ERROR("HAILO_VDMA_BUFFER_SYNC failed with errno: {}", errno);
+        return HAILO_PCIE_DRIVER_FAIL;
+    }
+    return HAILO_SUCCESS;
+}
+
+Expected<HailoRTDriver::VdmaChannelHandle> HailoRTDriver::vdma_channel_enable(uint32_t channel_index,
+    DmaDirection data_direction, uintptr_t desc_list_handle, bool enable_timestamps_measure)
+{
+    CHECK_AS_EXPECTED(data_direction != DmaDirection::BOTH, HAILO_INVALID_ARGUMENT);
+    tCompatibleHailoIoctlData data = {};
+    hailo_vdma_channel_enable_params& params = data.Buffer.ChannelEnable;
+    params.channel_index = channel_index;
+    params.direction = direction_to_dma_data_direction(data_direction);
+    params.desc_list_handle = desc_list_handle,
+    params.enable_timestamps_measure = enable_timestamps_measure;
+
+    if (0 > ioctl(this->m_fd, HAILO_VDMA_CHANNEL_ENABLE, &data)) {
+        LOGGER__ERROR("Failed to enable interrupt for channel {} with errno: {}", channel_index, errno);
+        return make_unexpected(HAILO_PCIE_DRIVER_FAIL);
+    }
+
+    return std::move(params.channel_handle);
+}
+
+hailo_status HailoRTDriver::vdma_channel_disable(uint32_t channel_index, VdmaChannelHandle channel_handle)
+{
+    tCompatibleHailoIoctlData data = {};
+    hailo_vdma_channel_disable_params& params = data.Buffer.ChannelDisable;
+    params.channel_index = channel_index;
+    params.channel_handle = channel_handle;
+
+    if (0 > ioctl(this->m_fd, HAILO_VDMA_CHANNEL_DISABLE, &data)) {
+        LOGGER__ERROR("Failed to disable interrupt for channel {} with errno: {}", channel_index, errno);
+        return HAILO_PCIE_DRIVER_FAIL;
+    }
+
+    return HAILO_SUCCESS;
+}
+
+//TODO: unify
+static Expected<ChannelInterruptTimestampList> create_interrupt_timestamp_list(hailo_vdma_channel_wait_params &inter_data)
+{
+    CHECK_AS_EXPECTED(inter_data.timestamps_count <= MAX_IRQ_TIMESTAMPS_SIZE, HAILO_PCIE_DRIVER_FAIL,
+        "Invalid channel interrupt timestamps count returned {}", inter_data.timestamps_count);
+    ChannelInterruptTimestampList timestamp_list;
+    timestamp_list.count = inter_data.timestamps_count;
+    for (size_t i = 0; i < timestamp_list.count; i++) {
+        timestamp_list.timestamp_list[i].timestamp = std::chrono::nanoseconds(inter_data.timestamps[i].timestamp_ns);
+        timestamp_list.timestamp_list[i].desc_num_processed = inter_data.timestamps[i].desc_num_processed;
+    }
+    return std::move(timestamp_list);
+}
+
+Expected<ChannelInterruptTimestampList> HailoRTDriver::wait_channel_interrupts(uint32_t channel_index, VdmaChannelHandle channel_handle,
+    const std::chrono::milliseconds &timeout)
+{
+    CHECK_AS_EXPECTED(timeout.count() >= 0, HAILO_INVALID_ARGUMENT);
+
+    const uint32_t timestamps_count = MAX_IRQ_TIMESTAMPS_SIZE;
+    struct hailo_channel_interrupt_timestamp timestamps[timestamps_count];
+    tCompatibleHailoIoctlData data = {};
+    hailo_vdma_channel_wait_params& wait = data.Buffer.ChannelWait;
+    wait.channel_index = channel_index;
+    wait.channel_handle = channel_handle;
+    wait.timeout_ms = static_cast<uint64_t>(timeout.count());
+    wait.timestamps = timestamps;
+    wait.timestamps_count = timestamps_count;
+
+    if (0 > ioctl(this->m_fd, HAILO_VDMA_CHANNEL_WAIT_INT, &data)) {
+        const auto ioctl_errno = errno;
+        if (ERROR_SEM_TIMEOUT == ioctl_errno) {
+            LOGGER__ERROR("Waiting for interrupt for channel {} timed-out", channel_index);
+            return make_unexpected(HAILO_TIMEOUT);
+        }
+        if (ERROR_OPERATION_ABORTED == ioctl_errno) {
+            LOGGER__INFO("Stream (index={}) was aborted!", channel_index);
+            return make_unexpected(HAILO_STREAM_INTERNAL_ABORT);
+        }
+        if (ERROR_NOT_READY == ioctl_errno) {
+            LOGGER__INFO("Channel (index={}) was deactivated!", channel_index);
+            return make_unexpected(HAILO_STREAM_NOT_ACTIVATED);
+        }
+        LOGGER__ERROR("Failed to wait interrupt for channel {} with errno: {}", channel_index, ioctl_errno);
+        return make_unexpected(HAILO_PCIE_DRIVER_FAIL);
+    }
+
+    return create_interrupt_timestamp_list(wait);
+}
+
+hailo_status HailoRTDriver::fw_control(const void *request, size_t request_len, const uint8_t request_md5[PCIE_EXPECTED_MD5_LENGTH],
+    void *response, size_t *response_len, uint8_t response_md5[PCIE_EXPECTED_MD5_LENGTH],
+    std::chrono::milliseconds timeout, hailo_cpu_id_t cpu_id)
+{
+    CHECK_ARG_NOT_NULL(request);
+    CHECK_ARG_NOT_NULL(response);
+    CHECK_ARG_NOT_NULL(response_len);
+    CHECK(timeout.count() >= 0, HAILO_INVALID_ARGUMENT);
+
+    tCompatibleHailoIoctlData data = {};
+    hailo_fw_control& command = data.Buffer.FirmwareControl;
+    static_assert(PCIE_EXPECTED_MD5_LENGTH == sizeof(command.expected_md5), "mismatch md5 size");
+    memcpy(&command.expected_md5, request_md5, sizeof(command.expected_md5));
+    command.buffer_len = static_cast<uint32_t>(request_len);
+    CHECK(request_len <= sizeof(command.buffer), HAILO_INVALID_ARGUMENT,
+        "FW control request len can't be larger than {} (size given {})", sizeof(command.buffer), request_len);
+    memcpy(&command.buffer, request, request_len);
+    command.timeout_ms = static_cast<uint32_t>(timeout.count());
+    command.cpu_id = translate_cpu_id(cpu_id);
+
+    if (0 > ioctl(this->m_fd, HAILO_FW_CONTROL, &data)) {
+        LOGGER__ERROR("HAILO_FW_CONTROL failed with errno: {}", errno);
+        return HAILO_FW_CONTROL_FAILURE;
+    }
+
+    if (*response_len < command.buffer_len) {
+        LOGGER__ERROR("FW control response len needs to be at least {} (size given {})", command.buffer_len, *response_len);
+        *response_len = command.buffer_len;
+        return HAILO_INSUFFICIENT_BUFFER;
+    }
+    memcpy(response, command.buffer, command.buffer_len);
+    *response_len = command.buffer_len;
+    memcpy(response_md5, command.expected_md5, PCIE_EXPECTED_MD5_LENGTH);
+
+    return HAILO_SUCCESS;
+}
+
+hailo_status read_log(uint8_t *buffer, size_t buffer_size, size_t *read_bytes, hailo_cpu_id_t cpu_id)
+{
+    (void)buffer;
+    (void)buffer_size;
+    (void)read_bytes;
+    (void)cpu_id;
+    return HAILO_PCIE_NOT_SUPPORTED_ON_PLATFORM;
+}
+
+Expected<size_t> HailoRTDriver::vdma_buffer_map(void *user_address, size_t required_size, DmaDirection data_direction, uintptr_t driver_buff_handle)
+{
+    tCompatibleHailoIoctlData data = {};
+    hailo_vdma_buffer_map_params& map_user_buffer_info = data.Buffer.VdmaBufferMap;
+    map_user_buffer_info.user_address = user_address;
+    map_user_buffer_info.size = required_size;
+    map_user_buffer_info.data_direction = direction_to_dma_data_direction(data_direction);
+    map_user_buffer_info.allocated_buffer_handle = driver_buff_handle;
+    map_user_buffer_info.mapped_handle = 0;
+
+    if (0 > ioctl(this->m_fd, HAILO_VDMA_BUFFER_MAP, &data)) {
+        LOGGER__ERROR("Failed to map user buffer with errno: {}", errno);
+        return make_unexpected(HAILO_PCIE_DRIVER_FAIL);
+    }
+
+    return std::move(map_user_buffer_info.mapped_handle);
+}
+
+hailo_status HailoRTDriver::vdma_buffer_unmap(size_t handle)
+{
+    tCompatibleHailoIoctlData data = {};
+    data.Value = handle;
+    if (0 > ioctl(this->m_fd, HAILO_VDMA_BUFFER_UNMAP, &data)) {
+        LOGGER__ERROR("Failed to unmap user buffer with errno: {}", errno);
+        return HAILO_PCIE_DRIVER_FAIL;
+    }
+
+    return HAILO_SUCCESS;
+}
+
+Expected<std::pair<uintptr_t, uint64_t>> HailoRTDriver::descriptors_list_create(size_t desc_count)
+{
+    tCompatibleHailoIoctlData data = {};
+    hailo_desc_list_create_params& create_desc_info = data.Buffer.DescListCreate;
+    create_desc_info.desc_count = desc_count;
+    create_desc_info.desc_handle = 0;
+    create_desc_info.dma_address = 0;
+
+    if (0 > ioctl(this->m_fd, HAILO_DESC_LIST_CREATE, &data)) {
+        LOGGER__ERROR("Failed to create descriptors list with errno: {}", errno);
+        return make_unexpected(HAILO_PCIE_DRIVER_FAIL);
+    }
+
+    return std::move(std::make_pair(create_desc_info.desc_handle, create_desc_info.dma_address));
+}
+
+hailo_status HailoRTDriver::descriptors_list_release(uintptr_t desc_handle)
+{
+    tCompatibleHailoIoctlData data = {};
+    data.Value = desc_handle;
+    if (0 > ioctl(this->m_fd, HAILO_DESC_LIST_RELEASE, &data)) {
+        LOGGER__ERROR("Failed to release descriptors list with errno: {}", errno);
+        return HAILO_PCIE_DRIVER_FAIL;
+    }
+
+    return HAILO_SUCCESS;
+}
+
+hailo_status HailoRTDriver::descriptors_list_bind_vdma_buffer(uintptr_t desc_handle, VdmaBufferHandle buffer_handle,
+    uint16_t desc_page_size,  uint8_t channel_index)
+{
+    tCompatibleHailoIoctlData data = {};
+    hailo_desc_list_bind_vdma_buffer_params& config_info = data.Buffer.DescListBind;
+    config_info.buffer_handle = buffer_handle;
+    config_info.desc_handle = desc_handle;
+    config_info.desc_page_size = desc_page_size;
+    config_info.channel_index = channel_index;
+
+    if (0 > ioctl(this->m_fd, HAILO_DESC_LIST_BIND_VDMA_BUFFER, &data)) {
+        LOGGER__ERROR("Failed to bind vdma buffer to descriptors list with errno: {}", errno);
+        return HAILO_PCIE_DRIVER_FAIL;
+    }
+
+    return HAILO_SUCCESS;
+}
+
+hailo_status HailoRTDriver::vdma_channel_abort(uint32_t channel_index, VdmaChannelHandle channel_handle)
+{
+    tCompatibleHailoIoctlData data = {};
+    hailo_vdma_channel_abort_params& abort_params = data.Buffer.ChannelAbort;
+    abort_params.channel_index = channel_index;
+    abort_params.channel_handle = channel_handle;
+    if (0 > ioctl(this->m_fd, HAILO_VDMA_CHANNEL_ABORT, &data)) {
+        LOGGER__ERROR("Failed to abort vdma channel (index={}) with errno: {}", channel_index, errno);
+        return HAILO_PCIE_DRIVER_FAIL;
+    }
+
+    return HAILO_SUCCESS;
+}
+
+hailo_status HailoRTDriver::vdma_channel_clear_abort(uint32_t channel_index, VdmaChannelHandle channel_handle)
+{
+    tCompatibleHailoIoctlData data = {};
+    hailo_vdma_channel_clear_abort_params& clear_params = data.Buffer.ChannelClearAbort;
+    clear_params.channel_index = channel_index;
+    clear_params.channel_handle = channel_handle;
+    if (0 > ioctl(this->m_fd, HAILO_VDMA_CHANNEL_CLEAR_ABORT, &data)) {
+        LOGGER__ERROR("Failed to clear abort vdma channel (index={}) with errno: {}", channel_index, errno);
+        return HAILO_PCIE_DRIVER_FAIL;
+    }
+
+    return HAILO_SUCCESS;
+}
+
+hailo_status HailoRTDriver::read_log(uint8_t *buffer, size_t buffer_size, size_t *read_bytes, hailo_cpu_id_t cpu_id)
+{
+    (void)buffer;
+    (void)buffer_size;
+    (void)read_bytes;
+    (void)cpu_id;
+    return HAILO_PCIE_NOT_SUPPORTED_ON_PLATFORM;
+}
+
+hailo_status HailoRTDriver::reset_nn_core()
+{
+    LOGGER__ERROR("Reset nn core is not supported over the windows driver");
+    return HAILO_NOT_IMPLEMENTED;
+}
+
+Expected<MmapBufferImpl> MmapBufferImpl::create_file_map(size_t length, FileDescriptor &file, uintptr_t offset)
+{
+    tCompatibleHailoIoctlData data = {};
+    data.Buffer.DescListMmap.desc_handle = offset;
+    data.Buffer.DescListMmap.size = length;
+    if (0 > ioctl(file, HAILO_WINDOWS_DESC_LIST_MMAP, &data)) {
+        LOGGER__ERROR("Failed to map physical memory with errno: {}", errno);
+        return make_unexpected(HAILO_PCIE_DRIVER_FAIL);
+    }
+    // this mapping will be deleted automatically with the physical allocation
+    return MmapBufferImpl(data.Buffer.DescListMmap.user_address, length, false);
+}
+
+Expected<uintptr_t> HailoRTDriver::vdma_low_memory_buffer_alloc(size_t size) {
+    (void) size;
+    return make_unexpected(HAILO_INVALID_OPERATION);
+}
+
+
+hailo_status HailoRTDriver::vdma_low_memory_buffer_free(uintptr_t buffer_handle) {
+    (void) buffer_handle;
+    return HAILO_INVALID_OPERATION;
+}
+
+hailo_status HailoRTDriver::mark_as_used()
+{
+    tCompatibleHailoIoctlData data = {};
+    if (0 > ioctl(this->m_fd, HAILO_MARK_AS_IN_USE, &data)) {
+        LOGGER__ERROR("Failed to mark device as in use with errno: {}", errno);
+        return HAILO_PCIE_DRIVER_FAIL;
+    }
+    if (data.Buffer.MarkAsInUse.in_use) {
+        return HAILO_DEVICE_IN_USE;
+    }
+    return HAILO_SUCCESS;
+}
+
+} /* namespace hailort */
diff --git a/hailort/libhailort/src/os/windows/microsec_timer.cpp b/hailort/libhailort/src/os/windows/microsec_timer.cpp
new file mode 100644 (file)
index 0000000..bc954bf
--- /dev/null
@@ -0,0 +1,71 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file timer.cpp
+ * @brief Timer wrapper for Windows
+ **/
+
+#include "os/microsec_timer.hpp"
+#include <Windows.h>
+#include <thread>
+#include <chrono>
+
+namespace hailort
+{
+
+// TODO: QueryPerformanceFrequency and QueryPerformanceCounter can't fail on systems
+//       running Windows XP or later, hence we ignore the return value. Do we want to support pre xp?
+
+static uint64_t query_frequency()
+{
+    // From MSDN QueryPerformanceFrequency:
+    // The frequency of the performance counter is fixed at system boot and is consistent across all processors.
+    // Therefore, the frequency need only be queried upon application initialization, and the result can be cached.
+    static LARGE_INTEGER frequency{};
+    if (frequency.QuadPart != 0) {
+        // The function has already been called
+        return frequency.QuadPart;
+    }
+    (void)QueryPerformanceFrequency(&frequency);
+    return frequency.QuadPart;
+}
+
+// Note: This function will cause a busy-loop for a parameter of microsecs <= 1000.
+//       This is because Windows' Sleep only works in millisec granulrity.
+//       An iteresting alternative is to use SetWaitableTimer, which is supposed to work
+//       in 100 nanosec (=0.1 microsec) granulrity. However, our testing showed that the
+//       precision of SetWaitableTimer wasn't good enougth for the sleeps required by eth_stream's
+//       token_bucket (less than 10 microsecs for certian bandwidths).
+void MicrosecTimer::sleep(uint64_t microsecs)
+{
+    static const uint64_t MINIMUM_SLEEP_TIME_USEC = 1000;
+    static const uint64_t frequency = query_frequency();
+
+    if (microsecs > MINIMUM_SLEEP_TIME_USEC || microsecs == 0) {
+        std::this_thread::sleep_for(std::chrono::microseconds(microsecs));
+    } else {
+        LARGE_INTEGER starting_time{};
+        LARGE_INTEGER ending_time{};
+        LARGE_INTEGER elapsed_microseconds{};
+
+        (void)QueryPerformanceCounter(&starting_time);
+        // Note: elapsed_microseconds.QuadPart will always be non-negative, hence the cast is ok.
+        // It will be non-negative because it's either zero (the initial value) or ending_time - starting_time.
+        // Since ending_time is set from a call to QueryPerformanceCounter the occours after the first call to
+        // QueryPerformanceCounter in which starting_time is set, ending_time will be greater than starting time.
+        while (static_cast<uint64_t>(elapsed_microseconds.QuadPart) < microsecs) {
+            (void)QueryPerformanceCounter(&ending_time);
+            elapsed_microseconds.QuadPart = ending_time.QuadPart - starting_time.QuadPart;
+            elapsed_microseconds.QuadPart *= 1000000;
+            elapsed_microseconds.QuadPart /= frequency;
+            // Note: This will relinquish the remainder of the thread's time slice. 
+            //       Testing proved that this sleep didn't harm the performance of the token_bucket.
+            //       This sleep is here for consistency with the documention. 
+            std::this_thread::sleep_for(std::chrono::microseconds(0));
+        }
+    }
+}
+
+} /* namespace hailort */
diff --git a/hailort/libhailort/src/os/windows/mmap_buffer.cpp b/hailort/libhailort/src/os/windows/mmap_buffer.cpp
new file mode 100644 (file)
index 0000000..3107cc1
--- /dev/null
@@ -0,0 +1,32 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file mmap_buffer.cpp
+ * @brief Wrapper around windows memory mapping (mmap). Not implemented yet
+ **/
+
+#include "os/mmap_buffer.hpp"
+
+namespace hailort
+{
+
+void * const MmapBufferImpl::INVALID_ADDR = NULL;
+
+Expected<MmapBufferImpl> MmapBufferImpl::create_shared_memory(size_t length)
+{
+    void *address = VirtualAlloc(NULL, length, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
+    CHECK_AS_EXPECTED(INVALID_ADDR != address, HAILO_OUT_OF_HOST_MEMORY, "Failed to mmap buffer with error:{}", GetLastError());
+    return MmapBufferImpl(address, length, true);
+}
+
+hailo_status MmapBufferImpl::unmap()
+{
+    if (m_unmappable) {
+        VirtualFree(m_address, m_length, MEM_RELEASE);
+    }
+    return HAILO_SUCCESS;
+}
+
+} /* namespace hailort */
diff --git a/hailort/libhailort/src/os/windows/osdep.hpp b/hailort/libhailort/src/os/windows/osdep.hpp
new file mode 100644 (file)
index 0000000..d30cc6a
--- /dev/null
@@ -0,0 +1,9 @@
+#pragma once
+
+#include <initguid.h>
+#include <Windows.h>
+#include <io.h>
+#include <cfgmgr32.h>
+#include <winioctl.h>
+#include <cguid.h>
+#include <devpkey.h>
diff --git a/hailort/libhailort/src/pcie_device.cpp b/hailort/libhailort/src/pcie_device.cpp
new file mode 100644 (file)
index 0000000..d71a5c3
--- /dev/null
@@ -0,0 +1,316 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file pcie_device.cpp
+ * @brief TODO: brief
+ *
+ * TODO: doc
+ **/
+
+#include "pcie_device.hpp"
+#include "hailo/hailort.h"
+#include "common/utils.hpp"
+#include "hailo/device.hpp"
+#include "hailo/hef.hpp"
+#include "control.hpp"
+#include "hlpcie.hpp"
+#include "common/compiler_extensions_compat.hpp"
+#include "os/hailort_driver.hpp"
+#include "context_switch/multi_context/resource_manager.hpp"
+
+#include <new>
+#include <algorithm>
+
+namespace hailort
+{
+
+#define ISTATUS_HOST_FW_IRQ_EVENT        0x2000000    /* IN BCS_ISTATUS_HOST_FW_IRQ_MASK  IN ISTATUS_HOST */
+#define ISTATUS_HOST_FW_IRQ_CONTROL      0x4000000    /* IN BCS_ISTATUS_HOST_FW_IRQ_MASK  IN ISTATUS_HOST */
+#define ISTATUS_HOST_FW_IRQ_FW_LOAD      0x6000000    /* IN BCS_ISTATUS_HOST_FW_IRQ_MASK  IN ISTATUS_HOST */
+
+
+#ifndef HAILO_EMULATOR
+static const uint32_t PCIE_DEFAULT_TIMEOUT_MS = 1000;
+#else /* ifndef HAILO_EMULATOR */
+static const uint32_t PCIE_DEFAULT_TIMEOUT_MS = 50000;
+#endif /* ifndef HAILO_EMULATOR */
+
+
+Expected<std::vector<hailo_pcie_device_info_t>> PcieDevice::scan()
+{
+    auto scan_results = HailoRTDriver::scan_pci();
+    if (!scan_results) {
+        LOGGER__ERROR("scan pci failed");
+        return make_unexpected(scan_results.status());
+    }
+
+    std::vector<hailo_pcie_device_info_t> out_results(scan_results->size());
+    std::transform(scan_results->begin(), scan_results->end(), std::begin(out_results), [](const auto &scan_result) {
+        hailo_pcie_device_info_t device_info = {};
+        device_info.domain = scan_result.domain;
+        device_info.bus = scan_result.bus;
+        device_info.device = scan_result.device;
+        device_info.func = scan_result.func;
+        return device_info;
+    });
+    return out_results;
+}
+
+Expected<std::unique_ptr<PcieDevice>> PcieDevice::create()
+{
+    // Take the first device
+    auto scan_result = scan();
+    CHECK_EXPECTED(scan_result, "Failed scanning pcie devices");
+    CHECK_AS_EXPECTED(scan_result->size() == 1, HAILO_INVALID_OPERATION,
+        "Expected only 1 PCIe device. Pass `hailo_pcie_device_info_t` to create a specific PCIe device");
+    return create(scan_result->at(0));
+}
+
+Expected<std::unique_ptr<PcieDevice>> PcieDevice::create(const hailo_pcie_device_info_t &device_info)
+{
+    auto scan_results = HailoRTDriver::scan_pci();
+    if (!scan_results) {
+        LOGGER__ERROR("scan pci failed");
+        return make_unexpected(scan_results.status());
+    }
+
+    // Find device index based on the information from "device_info"
+    auto device_found = std::find_if(scan_results->cbegin(), scan_results->cend(),
+         [device_info](const auto &compared_board) {
+            return (device_info.bus == compared_board.bus) && 
+                   (device_info.device == compared_board.device) &&
+                   (device_info.func == compared_board.func) &&
+                   ((HAILO_PCIE_ANY_DOMAIN == device_info.domain) || (device_info.domain == compared_board.domain));
+        });
+
+    if (device_found == std::end(scan_results.value())) {
+        LOGGER__ERROR("Requested device not found");
+        return make_unexpected(HAILO_INVALID_ARGUMENT);
+    }
+
+    auto driver = HailoRTDriver::create(device_found->dev_path);
+    if (!driver) {
+        LOGGER__ERROR("Failed to initialize HailoRTDriver");
+        return make_unexpected(driver.status());
+    }
+
+    hailo_status status = HAILO_UNINITIALIZED;
+    auto device = std::unique_ptr<PcieDevice>(new (std::nothrow) PcieDevice(driver.release(), device_info, status));
+    CHECK_AS_EXPECTED((nullptr != device), HAILO_OUT_OF_HOST_MEMORY);
+
+    if (HAILO_SUCCESS != status) {
+        LOGGER__ERROR("Failed creating PcieDevice");
+        return make_unexpected(status);
+    }
+
+    return device;
+}
+
+// same format as in lspci - [<domain>].<bus>.<device>.<func> 
+// domain (0 to ffff) bus (0 to ff), device (0 to 1f) and function (0 to 7).
+static const char *DEVICE_ID_STRING_FMT_SHORT = "%02x:%02x.%d";
+static constexpr int DEVICE_ID_ELEMENTS_COUNT_SHORT = 3;
+static constexpr int DEVICE_ID_STRING_LENGTH_SHORT = 7; // Length without null terminator
+
+static const char *DEVICE_ID_STRING_FMT_LONG = "%04x:%02x:%02x.%d";
+static constexpr int DEVICE_ID_ELEMENTS_COUNT_LONG = 4;
+static constexpr int DEVICE_ID_STRING_LENGTH_LONG = 12; // Length without null terminator
+
+static constexpr int DEVICE_ID_MAX_STRING_LENGTH = std::max(DEVICE_ID_STRING_LENGTH_SHORT, DEVICE_ID_STRING_LENGTH_LONG);
+
+Expected<hailo_pcie_device_info_t> PcieDevice::parse_pcie_device_info(const std::string &device_info_str)
+{
+    hailo_pcie_device_info_t device_info{};
+    int scanf_res = sscanf(device_info_str.c_str(), DEVICE_ID_STRING_FMT_LONG,
+        &device_info.domain, &device_info.bus, &device_info.device, &device_info.func);
+    if (DEVICE_ID_ELEMENTS_COUNT_LONG != scanf_res) {
+        // Domain not included, trying short
+        device_info.domain = HAILO_PCIE_ANY_DOMAIN;
+        scanf_res = sscanf(device_info_str.c_str(), DEVICE_ID_STRING_FMT_SHORT,
+            &device_info.bus, &device_info.device, &device_info.func);
+        CHECK_AS_EXPECTED(DEVICE_ID_ELEMENTS_COUNT_SHORT == scanf_res,
+            HAILO_INVALID_ARGUMENT,
+            "Invalid device info string (format is [<domain>].<bus>.<device>.<func>) {}");
+    }
+
+    return device_info;
+}
+
+Expected<std::string> PcieDevice::pcie_device_info_to_string(const hailo_pcie_device_info_t &device_info)
+{
+    char device_string[DEVICE_ID_MAX_STRING_LENGTH + 1] = { 0 };
+
+    if (HAILO_PCIE_ANY_DOMAIN != device_info.domain) {
+        int res = snprintf(device_string, DEVICE_ID_STRING_LENGTH_LONG + 1, DEVICE_ID_STRING_FMT_LONG, 
+            device_info.domain, device_info.bus, device_info.device, device_info.func);
+        // If the users give invalid device_info on release, they will get an invalid string.
+        CHECK_AS_EXPECTED((DEVICE_ID_STRING_LENGTH_LONG) == res, HAILO_INVALID_ARGUMENT, "Invalid device info");
+    }
+    else {
+        int res = snprintf(device_string, DEVICE_ID_STRING_LENGTH_SHORT + 1, DEVICE_ID_STRING_FMT_SHORT, 
+            device_info.bus, device_info.device, device_info.func);
+        // If the users gives invalid device_info on release, they will get an invalid string.
+        CHECK_AS_EXPECTED((DEVICE_ID_STRING_LENGTH_SHORT) == res, HAILO_INVALID_ARGUMENT, "Invalid device info");
+    }
+
+    return std::string(device_string);
+}
+
+PcieDevice::PcieDevice(HailoRTDriver &&driver, const hailo_pcie_device_info_t &device_info, hailo_status &status) :
+    VdmaDevice::VdmaDevice(std::move(driver), Device::Type::PCIE),
+    m_fw_up(false),
+    m_device_info(device_info)
+{
+    // Send identify if FW is loaded
+    status = HAILO_PCIE__read_atr_to_validate_fw_is_up(m_driver, &m_fw_up);
+    if (HAILO_SUCCESS != status) {
+        LOGGER__ERROR("HAILO_PCIE__read_atr_to_validate_fw_is_up failed with status {}", status);
+        return;
+    }
+
+    // Note: m_fw_up needs to be called after each reset/fw_update if we want the device's
+    //       state to remain valid after these ops (see HRT-3116)
+    if (m_fw_up) {
+        status = update_fw_state();
+        if (HAILO_SUCCESS != status) {
+            LOGGER__ERROR("update_fw_state() failed with status {}", status);
+            return;
+        }
+    } else {
+        LOGGER__WARNING("FW is not loaded to the device. Please load FW before using the device.");
+        m_is_control_version_supported = false;
+    }
+
+    auto message = pcie_device_info_to_string(device_info);
+    if (HAILO_SUCCESS != message.status()) {
+        status = message.status();
+        LOGGER__ERROR("pcie_device_info_to_string() failed with status {}", status);
+        return;
+    }
+    m_device_id = message.release();
+
+    this->activate_notifications(m_device_id);
+
+    status = HAILO_SUCCESS;
+}
+
+PcieDevice::~PcieDevice()
+{
+    auto status = stop_notification_fetch_thread();
+    if (HAILO_SUCCESS != status) {
+        LOGGER__WARNING("Stopping notification thread ungracefully");
+    }
+}
+
+hailo_status PcieDevice::fw_interact_impl(uint8_t *request_buffer, size_t request_size, uint8_t *response_buffer, 
+                                          size_t *response_size, hailo_cpu_id_t cpu_id)
+{
+    return HAILO_PCIE__fw_interact(m_driver, request_buffer, (uint32_t)request_size,  response_buffer, 
+                                    response_size, PCIE_DEFAULT_TIMEOUT_MS, cpu_id);
+}
+
+void PcieDevice::set_is_control_version_supported(bool value)
+{
+    m_is_control_version_supported = value;
+}
+
+Expected<hailo_device_architecture_t> PcieDevice::get_architecture() const
+{
+    if (!m_fw_up) {
+        LOGGER__WARNING("FW is not loaded to the device. Please load FW before using the device.");
+        return make_unexpected(HAILO_INVALID_OPERATION);
+    }
+
+    return Expected<hailo_device_architecture_t>(m_device_architecture);
+}
+
+hailo_status PcieDevice::direct_write_memory(uint32_t address, const void *buffer, uint32_t size)
+{
+    return HAILO_PCIE__write_memory(m_driver, address, buffer, size);
+}
+
+hailo_status PcieDevice::direct_read_memory(uint32_t address, void *buffer, uint32_t size)
+{
+    return HAILO_PCIE__read_memory(m_driver, address, buffer, size);
+}
+
+const char *PcieDevice::get_dev_id() const
+{
+    return m_device_id.c_str();
+}
+
+hailo_status PcieDevice::close_all_vdma_channels()
+{
+    auto status = HAILO_UNINITIALIZED;
+
+    // TODO: Add one icotl to stop all channels at once (HRT-6097)
+    for (int channel_index = 0; channel_index <= MAX_H2D_CHANNEL_INDEX; channel_index++) {
+        auto host_registers = VdmaChannelRegs(m_driver, channel_index, HailoRTDriver::DmaDirection::H2D);
+        status = host_registers.stop_channel();
+        CHECK_SUCCESS(status);
+
+        auto device_registers = VdmaChannelRegs(m_driver, channel_index, HailoRTDriver::DmaDirection::D2H);
+        status = device_registers.stop_channel();
+        CHECK_SUCCESS(status);
+    }
+
+    for (int channel_index = MIN_D2H_CHANNEL_INDEX; channel_index <= MAX_D2H_CHANNEL_INDEX; channel_index++) {
+        auto host_registers = VdmaChannelRegs(m_driver, channel_index, HailoRTDriver::DmaDirection::D2H);
+        status = host_registers.stop_channel();
+        CHECK_SUCCESS(status);
+
+        auto device_registers = VdmaChannelRegs(m_driver, channel_index, HailoRTDriver::DmaDirection::H2D);
+        status = device_registers.stop_channel();
+        CHECK_SUCCESS(status);
+    }
+
+    return HAILO_SUCCESS;
+}
+
+hailo_status PcieDevice::reset_impl(CONTROL_PROTOCOL__reset_type_t reset_type)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    HAILO_COMMON_STATUS_t common_status = HAILO_COMMON_STATUS__UNINITIALIZED;
+    CONTROL_PROTOCOL__request_t request = {};
+    size_t request_size = 0;
+    uint8_t response_buffer[RESPONSE_MAX_BUFFER_SIZE] = {};
+    size_t response_size = RESPONSE_MAX_BUFFER_SIZE;
+    CONTROL_PROTOCOL__response_header_t *header = NULL;
+    CONTROL_PROTOCOL__payload_t *payload = NULL;
+    bool is_expecting_response = true;
+
+    CHECK(CONTROL_PROTOCOL__RESET_TYPE__CHIP != reset_type, HAILO_INVALID_OPERATION,
+        "Chip reset is not supported for PCIe device.");
+
+    if ((CONTROL_PROTOCOL__RESET_TYPE__FORCED_SOFT == reset_type) || (CONTROL_PROTOCOL__RESET_TYPE__SOFT == reset_type)) {
+        is_expecting_response = false; // TODO: Check boot source, set is_expecting_response = (boot_source != pcie)
+        status = close_all_vdma_channels();
+        CHECK_SUCCESS(status);
+    }
+
+    common_status = CONTROL_PROTOCOL__pack_reset_request(&request, &request_size, m_control_sequence, reset_type);
+    status = (HAILO_COMMON_STATUS__SUCCESS == common_status) ? HAILO_SUCCESS : HAILO_INTERNAL_FAILURE;
+    CHECK_SUCCESS(status);
+
+    LOGGER__DEBUG("Sending reset request");
+    status = this->fw_interact((uint8_t*)(&request), request_size, (uint8_t*)&response_buffer, &response_size);
+    // fw_interact should return failure if response is not expected
+    // TODO: fix logic with respect to is_expecting_response, implement wait_for_wakeup();
+    if (HAILO_SUCCESS == status) {
+        status = Control::parse_and_validate_response(response_buffer, (uint32_t)(response_size), &header,
+            &payload, &request);
+        CHECK_SUCCESS(status);
+        CHECK(is_expecting_response, HAILO_INTERNAL_FAILURE, "Recived valid response from FW for control who is not expecting one.");
+    } else if ((HAILO_FW_CONTROL_FAILURE == status) && (!is_expecting_response)){
+        status = HAILO_SUCCESS;
+    } else {
+        return status;
+    }
+
+    LOGGER__DEBUG("Board has been reset successfully");
+    return HAILO_SUCCESS;
+}
+
+} /* namespace hailort */
diff --git a/hailort/libhailort/src/pcie_device.hpp b/hailort/libhailort/src/pcie_device.hpp
new file mode 100644 (file)
index 0000000..1cf280d
--- /dev/null
@@ -0,0 +1,78 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file pcie_device.hpp
+ * @brief TODO: brief
+ *
+ * TODO: doc
+ **/
+
+#ifndef HAILO_PCIE_DEVICE_H_
+#define HAILO_PCIE_DEVICE_H_
+
+#include "hailo/hailort.h"
+#include "hailo/expected.hpp"
+#include "hlpcie.hpp"
+#include "vdma_channel.hpp"
+#include "vdma_device.hpp"
+
+namespace hailort
+{
+
+class PcieDevice : public VdmaDevice {
+public:
+
+    static Expected<std::vector<hailo_pcie_device_info_t>> scan();
+    static Expected<std::unique_ptr<PcieDevice>> create();
+    static Expected<std::unique_ptr<PcieDevice>> create(const hailo_pcie_device_info_t &device_info);
+    static Expected<hailo_pcie_device_info_t> parse_pcie_device_info(const std::string &device_info_str);
+    static Expected<std::string> pcie_device_info_to_string(const hailo_pcie_device_info_t &device_info);
+
+    virtual ~PcieDevice();
+
+    virtual hailo_status fw_interact_impl(uint8_t *request_buffer, size_t request_size,
+        uint8_t *response_buffer, size_t *response_size, hailo_cpu_id_t cpu_id) override;
+
+    virtual hailo_status reset_impl(CONTROL_PROTOCOL__reset_type_t reset_type) override;
+    virtual hailo_status direct_write_memory(uint32_t address, const void *buffer, uint32_t size) override;
+    virtual hailo_status direct_read_memory(uint32_t address, void *buffer, uint32_t size) override;
+    virtual bool is_stream_interface_supported(const hailo_stream_interface_t& stream_interface) const override
+    {
+        switch (stream_interface) {
+        case HAILO_STREAM_INTERFACE_ETH:
+        case HAILO_STREAM_INTERFACE_CORE:
+            return false;
+        case HAILO_STREAM_INTERFACE_PCIE:
+        case HAILO_STREAM_INTERFACE_MIPI:
+            return true;
+        default:
+            LOGGER__ERROR("Invalid stream interface");
+            return false;
+        }
+    }
+
+    // TODO: used for tests
+    void set_is_control_version_supported(bool value);
+    virtual Expected<hailo_device_architecture_t> get_architecture() const override;
+
+    const hailo_pcie_device_info_t get_device_info() const
+    {
+        return m_device_info;
+    }
+    virtual const char* get_dev_id() const override;
+
+private:
+    PcieDevice(HailoRTDriver &&driver, const hailo_pcie_device_info_t &device_info, hailo_status &status);
+
+    hailo_status close_all_vdma_channels();
+
+    bool m_fw_up;
+    const hailo_pcie_device_info_t m_device_info;
+    std::string m_device_id;
+};
+
+} /* namespace hailort */
+
+#endif /* HAILO_PCIE_DEVICE_H_ */
diff --git a/hailort/libhailort/src/pcie_stream.cpp b/hailort/libhailort/src/pcie_stream.cpp
new file mode 100644 (file)
index 0000000..9e51d25
--- /dev/null
@@ -0,0 +1,72 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file pcie_stream.cpp
+ **/
+
+#include "pcie_stream.hpp"
+
+namespace hailort
+{
+
+PcieInputStream::PcieInputStream(
+    PcieDevice &device,
+    uint8_t channel_index,
+    const LayerInfo &edge_layer,
+    EventPtr network_group_activated_event,
+    uint16_t batch_size,
+    LatencyMeterPtr latency_meter,
+    const std::chrono::milliseconds &transfer_timeout,
+    hailo_status &status) :
+        VdmaInputStream(device, channel_index, edge_layer, network_group_activated_event,
+            batch_size, latency_meter, transfer_timeout, HAILO_STREAM_INTERFACE_PCIE, status)
+    {}
+
+Expected<std::unique_ptr<PcieInputStream>> PcieInputStream::create(Device &device,
+    uint8_t channel_index, const LayerInfo &edge_layer, uint16_t batch_size,
+    EventPtr network_group_activated_event, LatencyMeterPtr latency_meter)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+
+    PcieDevice *pcie_device = reinterpret_cast<PcieDevice*>(&device);
+    std::unique_ptr<PcieInputStream> local_stream(new (std::nothrow) PcieInputStream(*pcie_device,
+        channel_index, edge_layer, std::move(network_group_activated_event), batch_size,
+        latency_meter, DEFAULT_TRANSFER_TIMEOUT, status));
+    CHECK((nullptr != local_stream), make_unexpected(HAILO_OUT_OF_HOST_MEMORY));
+    CHECK_SUCCESS_AS_EXPECTED(status);
+
+    return local_stream;
+}
+
+Expected<std::unique_ptr<PcieOutputStream>> PcieOutputStream::create(Device &device,
+    uint8_t channel_index, const LayerInfo &edge_layer, uint16_t batch_size,
+    EventPtr network_group_activated_event, LatencyMeterPtr latency_meter)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    PcieDevice *pcie_device = reinterpret_cast<PcieDevice*>(&device);
+
+    std::unique_ptr<PcieOutputStream> local_stream(new (std::nothrow) PcieOutputStream(*pcie_device,
+        channel_index, edge_layer, std::move(network_group_activated_event),
+        batch_size, latency_meter, DEFAULT_TRANSFER_TIMEOUT, status));
+    CHECK((nullptr != local_stream), make_unexpected(HAILO_OUT_OF_HOST_MEMORY));
+    CHECK_SUCCESS_AS_EXPECTED(status);
+
+    return local_stream;
+}
+
+PcieOutputStream::PcieOutputStream(
+    PcieDevice &device,
+    uint8_t channel_index,
+    const LayerInfo &edge_layer,
+    EventPtr network_group_activated_event,
+    uint16_t batch_size,
+    LatencyMeterPtr latency_meter,
+    const std::chrono::milliseconds &transfer_timeout,
+    hailo_status &status) :
+        VdmaOutputStream(device, channel_index, edge_layer,
+            network_group_activated_event, batch_size, latency_meter, transfer_timeout, status)
+    {}
+
+} /* namespace hailort */
diff --git a/hailort/libhailort/src/pcie_stream.hpp b/hailort/libhailort/src/pcie_stream.hpp
new file mode 100644 (file)
index 0000000..314537c
--- /dev/null
@@ -0,0 +1,71 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file pcie_stream.hpp
+ * @brief Stream object for PCIe device
+ **/
+
+#ifndef _HAILO_PCIE_STREAM_H_
+#define _HAILO_PCIE_STREAM_H_
+
+#include "vdma_stream.hpp"
+#include "pcie_device.hpp"
+
+namespace hailort
+{
+
+class PcieInputStream : public VdmaInputStream {
+public:
+    PcieInputStream(PcieInputStream &&other) = default;
+    virtual ~PcieInputStream() = default;
+
+    static Expected<std::unique_ptr<PcieInputStream>> create(Device &device, uint8_t channel_index,
+        const LayerInfo &edge_layer, uint16_t batch_size, EventPtr network_group_activated_event,
+        LatencyMeterPtr latency_meter = nullptr);
+
+    virtual hailo_stream_interface_t get_interface() const override { return HAILO_STREAM_INTERFACE_PCIE; }
+
+private:
+    PcieInputStream(
+        PcieDevice &device,
+        uint8_t channel_index,
+        const LayerInfo &edge_layer,
+        EventPtr network_group_activated_event,
+        uint16_t batch_size,
+        LatencyMeterPtr latency_meter,
+        const std::chrono::milliseconds &transfer_timeout,
+        hailo_status &status);
+
+    friend class VDeviceInputStream;
+};
+
+class PcieOutputStream : public VdmaOutputStream {
+public:
+    PcieOutputStream(PcieOutputStream &&other) = default;
+    virtual ~PcieOutputStream() = default;
+
+    static Expected<std::unique_ptr<PcieOutputStream>> create(Device &device, uint8_t channel_index,
+        const LayerInfo &edge_layer, uint16_t batch_size, EventPtr network_group_activated_event, 
+        LatencyMeterPtr latency_meter);
+
+    virtual hailo_stream_interface_t get_interface() const override { return HAILO_STREAM_INTERFACE_PCIE; }
+
+    friend class VDeviceOutputStream;
+
+private:
+    explicit PcieOutputStream(
+        PcieDevice &device,
+        uint8_t channel_index,
+        const LayerInfo &edge_layer,
+        EventPtr network_group_activated_event,
+        uint16_t batch_size,
+        LatencyMeterPtr latency_meter,
+        const std::chrono::milliseconds &transfer_timeout,
+        hailo_status &status);
+};
+
+} /* namespace hailort */
+
+#endif /* _HAILO_PCIE_STREAM_H_ */
diff --git a/hailort/libhailort/src/pipeline.cpp b/hailort/libhailort/src/pipeline.cpp
new file mode 100644 (file)
index 0000000..c65122e
--- /dev/null
@@ -0,0 +1,1397 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file pipeline.cpp
+ * @brief Implemention of the pipeline
+ **/
+
+#include "pipeline.hpp"
+#include "common/utils.hpp"
+#include "common/runtime_statistics_internal.hpp"
+
+namespace hailort
+{
+
+PipelineBuffer::Metadata::Metadata(PipelineTimePoint start_time) :
+    m_start_time(start_time)
+{}
+
+PipelineBuffer::Metadata::Metadata() :
+    Metadata(PipelineTimePoint{})
+{}
+
+PipelineTimePoint PipelineBuffer::Metadata::get_start_time() const
+{
+    return m_start_time;
+}
+
+void PipelineBuffer::Metadata::set_start_time(PipelineTimePoint val)
+{
+    m_start_time = val;
+}
+
+PipelineBuffer::PipelineBuffer() :
+    PipelineBuffer(Type::DATA)
+{}
+
+PipelineBuffer::PipelineBuffer(Type type) :
+    m_type(type),
+    m_buffer(),
+    m_should_release_buffer(false),
+    m_pool(nullptr),
+    m_view(),
+    m_metadata()
+{}
+
+PipelineBuffer::PipelineBuffer(MemoryView view, bool should_measure) :
+    m_type(Type::DATA),
+    m_buffer(),
+    m_should_release_buffer(false),
+    m_pool(nullptr),
+    m_view(view),
+    m_metadata(Metadata(add_timestamp(should_measure)))
+{}
+
+PipelineBuffer::PipelineBuffer(Buffer &&buffer, BufferPoolPtr pool, bool should_measure) :
+    m_type(Type::DATA),
+    m_buffer(std::move(buffer)),
+    m_should_release_buffer(true),
+    m_pool(pool),
+    m_view(m_buffer),
+    m_metadata(Metadata(add_timestamp(should_measure)))
+{}
+
+PipelineBuffer::PipelineBuffer(PipelineBuffer &&other) :
+    m_type(other.m_type),
+    m_buffer(std::move(other.m_buffer)),
+    m_should_release_buffer(std::exchange(other.m_should_release_buffer, false)),
+    m_pool(std::move(other.m_pool)),
+    m_view(std::move(other.m_view)),
+    m_metadata(std::move(other.m_metadata))
+{}
+
+PipelineBuffer &PipelineBuffer::operator=(PipelineBuffer &&other)
+{
+    m_type = other.m_type,
+    m_buffer = std::move(other.m_buffer);
+    m_should_release_buffer = std::exchange(other.m_should_release_buffer, false);
+    m_pool = std::move(other.m_pool);
+    m_view = std::move(other.m_view);
+    m_metadata = std::move(other.m_metadata);
+    return *this;
+}
+
+PipelineBuffer::~PipelineBuffer()
+{
+    if (!m_should_release_buffer) {
+        return;
+    }
+
+    hailo_status status = m_pool->release_buffer(std::move(m_buffer));
+    if (HAILO_SUCCESS != status) {
+        LOGGER__ERROR("Releasing buffer in buffer pool failed! status = {}", status);
+    }
+}
+
+PipelineBuffer::operator bool() const
+{
+    return !m_view.empty();
+}
+
+uint8_t* PipelineBuffer::data()
+{
+    return m_view.data();
+}
+
+size_t PipelineBuffer::size() const
+{
+    return m_view.size();
+}
+
+MemoryView PipelineBuffer::as_view()
+{
+    return m_view;
+}
+
+PipelineBuffer::Type PipelineBuffer::get_type() const
+{
+    return m_type;
+}
+
+PipelineBuffer::Metadata PipelineBuffer::get_metadata() const
+{
+    return m_metadata;
+}
+
+void PipelineBuffer::set_metadata(Metadata &&val) 
+{
+    m_metadata = std::move(val);
+}
+
+PipelineTimePoint PipelineBuffer::add_timestamp(bool should_measure)
+{
+    return should_measure ? std::chrono::steady_clock::now() : PipelineTimePoint{};
+}
+
+Expected<BufferPoolPtr> BufferPool::create(size_t buffer_size, size_t buffer_count, EventPtr shutdown_event,
+                                           hailo_pipeline_elem_stats_flags_t elem_flags, hailo_vstream_stats_flags_t vstream_flags)
+{
+    AccumulatorPtr queue_size_accumulator = nullptr;
+    if ((elem_flags & HAILO_PIPELINE_ELEM_STATS_MEASURE_QUEUE_SIZE) != 0) {
+        queue_size_accumulator = make_shared_nothrow<FullAccumulator<double>>("queue_size");
+        CHECK_AS_EXPECTED(nullptr != queue_size_accumulator, HAILO_OUT_OF_HOST_MEMORY);
+    }
+    const bool measure_vstream_latency = (vstream_flags & HAILO_VSTREAM_STATS_MEASURE_LATENCY) != 0;
+
+    auto free_buffers = SpscQueue<Buffer>::create(buffer_count, shutdown_event, BUFFER_POOL_DEFAULT_QUEUE_TIMEOUT);
+    CHECK_EXPECTED(free_buffers);
+
+    for (size_t i = 0; i < buffer_count; i++) {
+        auto buffer = Buffer::create(buffer_size);
+        CHECK_EXPECTED(buffer);
+
+        hailo_status status = free_buffers->enqueue(buffer.release());
+        CHECK_SUCCESS_AS_EXPECTED(status);
+    }
+
+    auto buffer_pool_ptr = make_shared_nothrow<BufferPool>(buffer_size, measure_vstream_latency,
+        free_buffers.release(), std::move(queue_size_accumulator));
+    CHECK_AS_EXPECTED(nullptr != buffer_pool_ptr, HAILO_OUT_OF_HOST_MEMORY);
+
+    return buffer_pool_ptr;
+}
+
+BufferPool::BufferPool(size_t buffer_size, bool measure_vstream_latency, SpscQueue<Buffer> &&free_buffers, AccumulatorPtr &&queue_size_accumulator) :
+    m_buffer_size(buffer_size),
+    m_measure_vstream_latency(measure_vstream_latency),
+    m_free_buffers(std::move(free_buffers)),
+    m_queue_size_accumulator(std::move(queue_size_accumulator))
+{}
+
+size_t BufferPool::buffer_size()
+{
+    return m_buffer_size;
+}
+
+Expected<PipelineBuffer> BufferPool::acquire_buffer(std::chrono::milliseconds timeout)
+{
+    if (nullptr != m_queue_size_accumulator) {
+        m_queue_size_accumulator->add_data_point(static_cast<double>(m_free_buffers.size_approx()));
+    }
+    auto buffer = m_free_buffers.dequeue(timeout);
+    if (HAILO_SHUTDOWN_EVENT_SIGNALED == buffer.status()) {
+        return make_unexpected(buffer.status());
+    }
+    else if (HAILO_TIMEOUT == buffer.status()) {
+        LOGGER__WARNING("Failed to acquire buffer because the buffer pool is empty. This could be caused by uneven reading and writing speeds, with a short user-defined timeout.");
+        return make_unexpected(buffer.status());
+    }
+    CHECK_EXPECTED(buffer);
+    return PipelineBuffer(buffer.release(), shared_from_this(), m_measure_vstream_latency);
+}
+
+AccumulatorPtr BufferPool::get_queue_size_accumulator()
+{
+    return m_queue_size_accumulator;
+}
+
+Expected<PipelineBuffer> BufferPool::get_available_buffer(PipelineBuffer &&optional, std::chrono::milliseconds timeout)
+{
+    if (optional) {
+        CHECK_AS_EXPECTED(optional.size() == buffer_size(), HAILO_INVALID_OPERATION,
+            "Optional buffer size must be equal to pool buffer size. Optional buffer size = {}, buffer pool size = {}",
+            optional.size(), buffer_size());
+        return std::move(optional);
+    }
+    
+    auto acquired_buffer = acquire_buffer(timeout);
+    if (HAILO_SHUTDOWN_EVENT_SIGNALED == acquired_buffer.status()) {
+        return make_unexpected(acquired_buffer.status());
+    }
+    CHECK_EXPECTED(acquired_buffer, "Failed to acquire buffer");
+    return acquired_buffer.release();
+}
+
+hailo_status BufferPool::release_buffer(Buffer &&buffer)
+{
+    // This can be called after the shutdown event was signaled so we ignore it here
+    return m_free_buffers.enqueue(std::move(buffer), true);
+}
+
+Expected<DurationCollector> DurationCollector::create(hailo_pipeline_elem_stats_flags_t flags,
+    uint32_t num_frames_before_collection_start)
+{
+    AccumulatorPtr latency_accumulator = nullptr;
+    const auto measure_latency = should_measure_latency(flags);
+    if (measure_latency) {
+        latency_accumulator = make_shared_nothrow<FullAccumulator<double>>("latency");
+        CHECK_AS_EXPECTED(nullptr != latency_accumulator, HAILO_OUT_OF_HOST_MEMORY);
+    }
+
+    AccumulatorPtr average_fps_accumulator = nullptr;
+    const auto measure_average_fps = should_measure_average_fps(flags);
+    if (measure_average_fps) {
+        average_fps_accumulator = make_shared_nothrow<AverageFPSAccumulator<double>>("fps");
+        CHECK_AS_EXPECTED(nullptr != average_fps_accumulator, HAILO_OUT_OF_HOST_MEMORY);
+    }
+
+    return DurationCollector(measure_latency, measure_average_fps, std::move(latency_accumulator),
+        std::move(average_fps_accumulator), num_frames_before_collection_start);
+}
+
+DurationCollector::DurationCollector(bool measure_latency, bool measure_average_fps,
+                                      AccumulatorPtr &&latency_accumulator, AccumulatorPtr &&average_fps_accumulator,
+                                      uint32_t num_frames_before_collection_start) :
+    m_measure_latency(measure_latency),
+    m_measure_average_fps(measure_average_fps),
+    m_measure(m_measure_latency || m_measure_average_fps),
+    m_latency_accumulator(std::move(latency_accumulator)),
+    m_average_fps_accumulator(std::move(average_fps_accumulator)),
+    m_start(),
+    m_count(0),
+    m_num_frames_before_collection_start(num_frames_before_collection_start)
+{}
+
+void DurationCollector::start_measurement()
+{
+    if (!m_measure) {
+        return;
+    }
+
+    m_count++;
+    if (m_count < m_num_frames_before_collection_start) {
+        return;
+    }
+
+    m_start = std::chrono::steady_clock::now();
+}
+
+void DurationCollector::complete_measurement()
+{
+    if ((!m_measure) || (m_count < m_num_frames_before_collection_start)) {
+        return;
+    }
+
+    const auto duration_sec = std::chrono::duration_cast<std::chrono::duration<double>>(
+        std::chrono::steady_clock::now() - m_start).count();
+    if (m_measure_latency) {
+        m_latency_accumulator->add_data_point(duration_sec);
+    }
+
+    if (m_measure_average_fps) {
+        m_average_fps_accumulator->add_data_point(duration_sec);
+    }
+}
+
+AccumulatorPtr DurationCollector::get_latency_accumulator()
+{
+    return m_latency_accumulator;
+}
+
+AccumulatorPtr DurationCollector::get_average_fps_accumulator()
+{
+    return m_average_fps_accumulator;
+}
+
+bool DurationCollector::should_measure_latency(hailo_pipeline_elem_stats_flags_t flags)
+{
+    return (flags & HAILO_PIPELINE_ELEM_STATS_MEASURE_LATENCY) != 0;
+}
+
+bool DurationCollector::should_measure_average_fps(hailo_pipeline_elem_stats_flags_t flags)
+{
+    return (flags & HAILO_PIPELINE_ELEM_STATS_MEASURE_FPS) != 0;
+}
+
+PipelineObject::PipelineObject(const std::string &name) : m_name(name)
+{}
+
+const std::string &PipelineObject::name() const
+{
+    return m_name;
+}
+
+std::string PipelineObject::create_element_name(const std::string &element_name, const std::string &stream_name, uint8_t stream_index)
+{
+    std::stringstream name;
+    name << element_name << static_cast<uint32_t>(stream_index) << "_" << stream_name;
+    return name.str();
+}
+
+hailo_status PipelinePad::link_pads(std::shared_ptr<PipelineElement> left, std::shared_ptr<PipelineElement> right,
+    uint32_t left_source_index, uint32_t right_sink_index)
+{
+    CHECK_ARG_NOT_NULL(left);
+    CHECK_ARG_NOT_NULL(right);
+    return link_pads(*left, *right, left_source_index, right_sink_index);
+}
+
+hailo_status PipelinePad::link_pads(PipelineElement &left, PipelineElement &right, uint32_t left_source_index,
+    uint32_t right_sink_index)
+{
+    CHECK(left_source_index < left.sources().size(), HAILO_INVALID_ARGUMENT,
+        "Cannot link source pad #{} for PipelineElement '{}', it has only {} source pads.",
+        left_source_index, left.name(), left.sources().size());
+    CHECK(right_sink_index < right.sinks().size(), HAILO_INVALID_ARGUMENT,
+        "Cannot link sink pad #{} for PipelineElement '{}', it has only {} sink pads.",
+        right_sink_index, right.name(), right.sinks().size());
+    auto &left_source_pad = left.sources()[left_source_index];
+    auto &right_sink_pad = right.sinks()[right_sink_index];
+
+    left_source_pad.set_next(&right_sink_pad);
+    right_sink_pad.set_prev(&left_source_pad);
+
+    return HAILO_SUCCESS;
+}
+
+// Initial value of the counter
+uint32_t PipelinePad::index = 0;
+std::string PipelinePad::create_pad_name(const std::string &element_name, Type pad_type)
+{
+    std::stringstream string_stream;
+    const auto pad_type_name = (pad_type == Type::SINK) ? "sink" : "source";
+    string_stream << element_name << "(" << pad_type_name << index++ << ")";
+    return string_stream.str();
+}
+
+PipelinePad::PipelinePad(PipelineElement &element, const std::string &element_name, Type pad_type) :
+    PipelineObject(create_pad_name(element_name, pad_type)),
+    m_element(element),
+    m_next(nullptr),
+    m_prev(nullptr),
+    m_push_complete_callback(nullptr),
+    m_pull_complete_callback(nullptr)
+{}
+
+hailo_status PipelinePad::activate()
+{
+    return m_element.activate();
+}
+
+hailo_status PipelinePad::deactivate()
+{
+    return m_element.deactivate();
+}
+
+hailo_status PipelinePad::post_deactivate()
+{
+    return m_element.post_deactivate();
+}
+
+hailo_status PipelinePad::clear()
+{
+    return m_element.clear();
+}
+
+hailo_status PipelinePad::flush()
+{
+    return m_element.flush();
+}
+
+hailo_status PipelinePad::run_push(PipelineBuffer &&buffer)
+{
+    if (m_push_complete_callback) {
+        auto metadata = buffer.get_metadata();
+        const auto status = m_element.run_push(std::move(buffer));
+        m_push_complete_callback(metadata);
+        return status;
+    }
+
+    return m_element.run_push(std::move(buffer));
+}
+
+Expected<PipelineBuffer> PipelinePad::run_pull(PipelineBuffer &&optional)
+{
+    auto result = m_element.run_pull(std::move(optional), *this);
+    if (m_pull_complete_callback && result) {
+        m_pull_complete_callback(result->get_metadata());
+    }
+
+    return result;
+}
+
+void PipelinePad::set_push_complete_callback(PushCompleteCallback push_complete_callback)
+{
+    m_push_complete_callback = push_complete_callback;
+}
+
+void PipelinePad::set_pull_complete_callback(PullCompleteCallback pull_complete_callback)
+{
+    m_pull_complete_callback = pull_complete_callback;
+}
+
+void PipelinePad::set_next(PipelinePad *next)
+{
+    m_next = next;
+}
+
+void PipelinePad::set_prev(PipelinePad *prev)
+{
+    m_prev = prev;
+}
+
+PipelinePad *PipelinePad::next()
+{
+    return m_next;
+}
+
+PipelinePad *PipelinePad::prev()
+{
+    return m_prev;
+}
+
+PipelineElement &PipelinePad::element()
+{
+    return m_element;
+}
+
+const PipelinePad *PipelinePad::next() const
+{
+    return m_next;
+}
+
+const PipelinePad *PipelinePad::prev() const
+{
+    return m_prev;
+}
+
+const PipelineElement &PipelinePad::element() const
+{
+    return m_element;
+}
+
+SourceElement::SourceElement(const std::string &name, DurationCollector &&duration_collector,
+                             std::shared_ptr<std::atomic<hailo_status>> &&pipeline_status) :
+    PipelineElement(name, std::move(duration_collector), std::move(pipeline_status))
+{
+    m_sources.emplace_back(*this, name, PipelinePad::Type::SOURCE);
+}
+
+PipelinePad &SourceElement::source()
+{
+    return m_sources[0];
+}
+
+SinkElement::SinkElement(const std::string &name, DurationCollector &&duration_collector,
+                         std::shared_ptr<std::atomic<hailo_status>> &&pipeline_status) :
+    PipelineElement(name, std::move(duration_collector), std::move(pipeline_status))
+{
+    m_sinks.emplace_back(*this, name, PipelinePad::Type::SINK);
+}
+
+PipelinePad &SinkElement::sink()
+{
+    return m_sinks[0];
+}
+
+IntermediateElement::IntermediateElement(const std::string &name, DurationCollector &&duration_collector,
+                                         std::shared_ptr<std::atomic<hailo_status>> &&pipeline_status) :
+    PipelineElement(name, std::move(duration_collector), std::move(pipeline_status))
+{
+    m_sinks.emplace_back(*this, name, PipelinePad::Type::SINK);
+    m_sources.emplace_back(*this, name, PipelinePad::Type::SOURCE);
+}
+
+hailo_status IntermediateElement::flush()
+{
+    return next_pad().flush();
+}
+
+PipelineElement::PipelineElement(const std::string &name, DurationCollector &&duration_collector,
+                                 std::shared_ptr<std::atomic<hailo_status>> &&pipeline_status) :
+    PipelineObject(name),
+    m_duration_collector(std::move(duration_collector)),
+    m_pipeline_status(std::move(pipeline_status)),
+    m_sinks(),
+    m_sources()
+{}
+
+AccumulatorPtr PipelineElement::get_fps_accumulator()
+{
+    return m_duration_collector.get_average_fps_accumulator();
+}
+
+AccumulatorPtr PipelineElement::get_latency_accumulator()
+{
+    return m_duration_collector.get_latency_accumulator();
+}
+
+std::vector<AccumulatorPtr> PipelineElement::get_queue_size_accumulators()
+{
+    return std::vector<AccumulatorPtr>();
+}
+
+std::vector<PipelinePad> &PipelineElement::sinks()
+{
+    return m_sinks;
+}
+
+std::vector<PipelinePad> &PipelineElement::sources()
+{
+    return m_sources;
+}
+
+const std::vector<PipelinePad> &PipelineElement::sinks() const
+{
+    return m_sinks;
+}
+
+const std::vector<PipelinePad> &PipelineElement::sources() const
+{
+    return m_sources;
+}
+
+std::string PipelineElement::description() const
+{
+    std::stringstream element_description;
+    element_description << "(" << this->name() << ")";
+    return element_description.str();
+}
+
+FilterElement::FilterElement(const std::string &name, DurationCollector &&duration_collector,
+                             std::shared_ptr<std::atomic<hailo_status>> &&pipeline_status) :
+    IntermediateElement(name, std::move(duration_collector), std::move(pipeline_status))
+{}
+
+hailo_status FilterElement::activate()
+{
+    return next_pad().activate();
+}
+
+hailo_status FilterElement::deactivate()
+{
+    return next_pad().deactivate();
+}
+
+hailo_status FilterElement::post_deactivate()
+{
+    return next_pad().post_deactivate();
+}
+
+hailo_status FilterElement::clear()
+{
+    return next_pad().clear();
+}
+
+hailo_status FilterElement::run_push(PipelineBuffer &&buffer)
+{
+    auto output = action(std::move(buffer), PipelineBuffer());
+    if (HAILO_SHUTDOWN_EVENT_SIGNALED == output.status()) {
+        return output.status();
+    }
+    CHECK_EXPECTED_AS_STATUS(output);
+
+    hailo_status status = next_pad().run_push(output.release());
+    if (HAILO_SHUTDOWN_EVENT_SIGNALED == status) {
+        LOGGER__INFO("run_push of FilterElement was shutdown!");
+        return status;
+    }
+    CHECK_SUCCESS(status);
+
+    return HAILO_SUCCESS;
+}
+
+Expected<PipelineBuffer> FilterElement::run_pull(PipelineBuffer &&optional, const PipelinePad &/*source*/)
+{
+    auto buffer = next_pad().run_pull();
+    if (HAILO_SHUTDOWN_EVENT_SIGNALED == buffer.status()) {
+        LOGGER__INFO("run_pull in FilterElement was shutdown!");
+        return make_unexpected(buffer.status());
+    }
+    CHECK_EXPECTED(buffer);
+    return action(buffer.release(), std::move(optional));
+}
+
+Expected<SpscQueue<PipelineBuffer>> BaseQueueElement::create_queue(size_t queue_size, EventPtr shutdown_event)
+{
+    auto queue = SpscQueue<PipelineBuffer>::create(queue_size, shutdown_event);
+    CHECK_EXPECTED(queue);
+
+    return queue.release();
+}
+
+BaseQueueElement::BaseQueueElement(SpscQueue<PipelineBuffer> &&queue, EventPtr shutdown_event, const std::string &name,
+                                   std::chrono::milliseconds timeout, DurationCollector &&duration_collector,
+                                   AccumulatorPtr &&queue_size_accumulator, std::shared_ptr<std::atomic<hailo_status>> &&pipeline_status,
+                                   Event &&activation_event, Event &&deactivation_event) :
+    IntermediateElement(name, std::move(duration_collector), std::move(pipeline_status)),
+    m_queue(std::move(queue)),
+    m_shutdown_event(shutdown_event),
+    m_timeout(timeout),
+    m_is_thread_running(true),
+    m_activation_event(std::move(activation_event)),
+    m_deactivation_event(std::move(deactivation_event)),
+    m_queue_size_accumulator(std::move(queue_size_accumulator))
+{}
+
+void BaseQueueElement::start_thread()
+{
+    m_thread = std::thread([this] () {
+        while (m_is_thread_running.load()) {
+            auto status = m_activation_event.wait(INIFINITE_TIMEOUT());
+
+            if (!m_is_thread_running) {
+                LOGGER__INFO("Thread in element {} is not running anymore, exiting..", this->name());
+                break;
+            }
+            if (HAILO_SUCCESS == status) {
+                status = run_in_thread();
+            }
+
+            if (HAILO_SUCCESS != status) {
+                if (HAILO_SHUTDOWN_EVENT_SIGNALED != status) {
+                    // We do not want to log error for HAILO_STREAM_INTERNAL_ABORT
+                    if (HAILO_STREAM_INTERNAL_ABORT != status) {
+                        LOGGER__ERROR("Queue element {} run in thread function failed! status = {}", this->name(), status);
+                    }
+
+                    // Store the real error in pipeline_status
+                    m_pipeline_status->store(status);
+
+                    // Signal other threads to stop
+                    hailo_status shutdown_status = m_shutdown_event->signal();
+                    if (HAILO_SUCCESS != shutdown_status) {
+                        LOGGER__CRITICAL("Failed shutting down queue with status {}", shutdown_status);
+                    }
+                }
+                //Thread has done its execution. Mark to the thread to wait for activation again
+                hailo_status event_status = m_activation_event.reset();
+                if (HAILO_SUCCESS != event_status) {
+                    LOGGER__CRITICAL("Failed reset activation event of element {}, with status {}", this->name(), event_status);
+                }
+
+                // Mark to deactivation function that the thread is done
+                event_status = m_deactivation_event.signal();
+                if (HAILO_SUCCESS != event_status) {
+                    LOGGER__CRITICAL("Failed signaling deactivation event of element {}, with status {}", this->name(), event_status);
+                }
+            }
+        }
+    });
+}
+
+void BaseQueueElement::stop_thread()
+{
+    m_shutdown_event->signal();
+
+    // Mark thread as not running, then wake it in case it is waiting on m_activation_event
+    m_is_thread_running = false;
+    m_activation_event.signal();
+
+    if (m_thread.joinable()) {
+        m_thread.join();
+    }
+}
+
+std::vector<AccumulatorPtr> BaseQueueElement::get_queue_size_accumulators()
+{
+    if (nullptr == m_queue_size_accumulator) {
+        return std::vector<AccumulatorPtr>();
+    }
+    return {m_queue_size_accumulator};
+}
+
+hailo_status BaseQueueElement::activate()
+{
+    hailo_status status = next_pad().activate();
+    CHECK_SUCCESS(status);
+
+    status = m_activation_event.signal();
+    CHECK_SUCCESS(status);
+
+    return HAILO_SUCCESS;
+}
+
+hailo_status BaseQueueElement::post_deactivate()
+{
+    hailo_status status = m_deactivation_event.wait(INIFINITE_TIMEOUT());
+    if (HAILO_SUCCESS != status) {
+        LOGGER__ERROR("Failed to post_deactivate() in {} with status {}", name(), status);
+    }
+
+    status = m_deactivation_event.reset();
+    if (HAILO_SUCCESS != status) {
+        LOGGER__ERROR("Failed to reset of deactivation event in {} with status {}", name(), status);
+    }
+
+    return next_pad().post_deactivate();
+}
+
+hailo_status BaseQueueElement::clear()
+{
+    auto status = next_pad().clear();
+    if (HAILO_SUCCESS != status) {
+        LOGGER__ERROR("Failed to clear() in {} with status {}", name(), status);
+    }
+
+    auto queue_status = m_queue.clear();
+    CHECK_SUCCESS(queue_status, "Failed to clear() queue in {} with status {}", name(), status);
+
+    return status;
+}
+
+hailo_status BaseQueueElement::set_timeout(std::chrono::milliseconds timeout)
+{
+    m_timeout = timeout;
+    return HAILO_SUCCESS;
+}
+
+std::string BaseQueueElement::description() const
+{
+    std::stringstream element_description;
+
+    element_description << "(" << this->name();
+    if (HAILO_INFINITE != this->m_timeout.count()) {
+        element_description << " | timeout: "  << std::chrono::duration_cast<std::chrono::seconds>(this->m_timeout).count() << "s";
+    }
+    element_description << ")";
+
+    return element_description.str();
+}
+
+hailo_status BaseQueueElement::pipeline_status()
+{
+    auto status = m_pipeline_status->load();
+
+    // We treat HAILO_STREAM_INTERNAL_ABORT as success because it is caused by user action (aborting streams)
+    if (HAILO_STREAM_INTERNAL_ABORT == status) {
+        return HAILO_SUCCESS;
+    }
+    return status;
+}
+
+Expected<std::shared_ptr<PushQueueElement>> PushQueueElement::create(const std::string &name, std::chrono::milliseconds timeout,
+        size_t queue_size, hailo_pipeline_elem_stats_flags_t flags, EventPtr shutdown_event,
+        std::shared_ptr<std::atomic<hailo_status>> pipeline_status)
+{
+    auto queue = BaseQueueElement::create_queue(queue_size, shutdown_event);
+    CHECK_EXPECTED(queue);
+
+    auto activation_event = Event::create(Event::State::not_signalled);
+    CHECK_EXPECTED(activation_event);
+
+    auto deactivation_event = Event::create(Event::State::not_signalled);
+    CHECK_EXPECTED(deactivation_event);
+
+    auto duration_collector = DurationCollector::create(flags);
+    CHECK_EXPECTED(duration_collector);
+
+    AccumulatorPtr queue_size_accumulator = nullptr;
+    if ((flags & HAILO_PIPELINE_ELEM_STATS_MEASURE_QUEUE_SIZE) != 0) {
+        queue_size_accumulator = make_shared_nothrow<FullAccumulator<double>>("queue_size");
+        CHECK_AS_EXPECTED(nullptr != queue_size_accumulator, HAILO_OUT_OF_HOST_MEMORY);
+    }
+
+    auto queue_ptr = make_shared_nothrow<PushQueueElement>(queue.release(), shutdown_event, name, timeout,
+        duration_collector.release(), std::move(queue_size_accumulator), std::move(pipeline_status),
+        activation_event.release(), deactivation_event.release());
+    CHECK_AS_EXPECTED(nullptr != queue_ptr, HAILO_OUT_OF_HOST_MEMORY, "Creating PushQueueElement {} failed!", name);
+
+    LOGGER__INFO("Created {}", queue_ptr->name());
+
+    return queue_ptr;
+}
+
+Expected<std::shared_ptr<PushQueueElement>> PushQueueElement::create(const std::string &name, const hailo_vstream_params_t &vstream_params,
+        EventPtr shutdown_event, std::shared_ptr<std::atomic<hailo_status>> pipeline_status)
+{
+    return PushQueueElement::create(name, std::chrono::milliseconds(vstream_params.timeout_ms),
+        vstream_params.queue_size, vstream_params.pipeline_elements_stats_flags, shutdown_event, pipeline_status);
+}
+
+PushQueueElement::PushQueueElement(SpscQueue<PipelineBuffer> &&queue, EventPtr shutdown_event, const std::string &name,
+                                   std::chrono::milliseconds timeout, DurationCollector &&duration_collector, 
+                                   AccumulatorPtr &&queue_size_accumulator, std::shared_ptr<std::atomic<hailo_status>> &&pipeline_status,
+                                   Event &&activation_event, Event &&deactivation_event) :
+    BaseQueueElement(std::move(queue), shutdown_event, name, timeout, std::move(duration_collector), std::move(queue_size_accumulator),
+                     std::move(pipeline_status), std::move(activation_event), std::move(deactivation_event))
+{
+    start_thread();
+}
+
+PushQueueElement::~PushQueueElement()
+{
+    stop_thread();
+}
+
+hailo_status PushQueueElement::run_push(PipelineBuffer &&buffer)
+{
+    if (nullptr != m_queue_size_accumulator) {
+        m_queue_size_accumulator->add_data_point(static_cast<double>(m_queue.size_approx()));
+    }
+    hailo_status status = m_queue.enqueue(std::move(buffer), m_timeout);
+    if (HAILO_SHUTDOWN_EVENT_SIGNALED == status) {
+        auto queue_thread_status = pipeline_status();
+        CHECK_SUCCESS(queue_thread_status,
+            "Shutdown event was signaled in enqueue of queue element {} because thread has failed with status={}!", name(),
+            queue_thread_status);
+        LOGGER__INFO("Shutdown event was signaled in enqueue of queue element {}!", name());
+        return HAILO_SHUTDOWN_EVENT_SIGNALED;
+    }
+    CHECK_SUCCESS(status);
+    return HAILO_SUCCESS;
+}
+
+Expected<PipelineBuffer> PushQueueElement::run_pull(PipelineBuffer &&/*optional*/, const PipelinePad &/*source*/)
+{
+    return make_unexpected(HAILO_INVALID_OPERATION);
+}
+
+hailo_status PushQueueElement::deactivate()
+{
+    // Mark to the threads that deactivate() was called.
+    hailo_status status = m_queue.enqueue(PipelineBuffer(PipelineBuffer::Type::DEACTIVATE));
+    if (HAILO_SUCCESS != status) {
+        // We want to deactivate source even if enqueue failed
+        auto deactivation_status = next_pad().deactivate();
+        if (HAILO_SUCCESS != deactivation_status) {
+            LOGGER__ERROR("Deactivate of source in {} has failed with status {}", name(), status);
+            // TODO (HRT-4156): return error status?
+        }
+        if ((HAILO_STREAM_INTERNAL_ABORT == status) || (HAILO_SHUTDOWN_EVENT_SIGNALED == status)) {
+            LOGGER__INFO("enqueue() in element {} was aborted, got status = {}", name(), status);
+        } else {
+            LOGGER__ERROR("enqueue() in element {} failed, got status = {}", name(), status);
+        }
+    }
+
+    return HAILO_SUCCESS;
+}
+
+PipelinePad &PushQueueElement::next_pad()
+{
+    // Note: The next elem to be run is downstream from this elem (i.e. buffers are pushed)
+    return *m_sources[0].next();
+}
+
+hailo_status PushQueueElement::run_in_thread()
+{
+    auto buffer = m_queue.dequeue(INIFINITE_TIMEOUT());
+    if (HAILO_SHUTDOWN_EVENT_SIGNALED == buffer.status()) {
+        LOGGER__INFO("Shutdown event was signaled in dequeue of queue element {}!", name());
+        return HAILO_SHUTDOWN_EVENT_SIGNALED;
+    }
+    CHECK_EXPECTED_AS_STATUS(buffer);
+
+    // Return if deactivated
+    if (PipelineBuffer::Type::DEACTIVATE == buffer->get_type()) {
+        hailo_status status = m_shutdown_event->signal();
+        CHECK_SUCCESS(status);
+
+        status = next_pad().deactivate();
+        if (HAILO_SUCCESS != status) {
+            LOGGER__ERROR("Deactivate of source in {} has failed with status {}", name(), status);
+        }
+
+        return HAILO_SHUTDOWN_EVENT_SIGNALED;
+    }
+
+    hailo_status status = next_pad().run_push(buffer.release());
+    if (HAILO_STREAM_INTERNAL_ABORT == status) {
+        LOGGER__INFO("run_push of {} was aborted!", name());
+        return status;
+    }
+    CHECK_SUCCESS(status);
+
+    return HAILO_SUCCESS;
+}
+
+Expected<std::shared_ptr<PullQueueElement>> PullQueueElement::create(const std::string &name, std::chrono::milliseconds timeout,
+        size_t queue_size, hailo_pipeline_elem_stats_flags_t flags, EventPtr shutdown_event,
+        std::shared_ptr<std::atomic<hailo_status>> pipeline_status)
+{
+    auto queue = BaseQueueElement::create_queue(queue_size, shutdown_event);
+    CHECK_EXPECTED(queue);
+
+    auto activation_event = Event::create(Event::State::not_signalled);
+    CHECK_EXPECTED(activation_event);
+
+    auto deactivation_event = Event::create(Event::State::not_signalled);
+    CHECK_EXPECTED(deactivation_event);
+
+    auto duration_collector = DurationCollector::create(flags);
+    CHECK_EXPECTED(duration_collector);
+
+    AccumulatorPtr queue_size_accumulator = nullptr;
+    if ((flags & HAILO_PIPELINE_ELEM_STATS_MEASURE_QUEUE_SIZE) != 0) {
+        queue_size_accumulator = make_shared_nothrow<FullAccumulator<double>>("queue_size");
+        CHECK_AS_EXPECTED(nullptr != queue_size_accumulator, HAILO_OUT_OF_HOST_MEMORY);
+    }
+
+    auto queue_ptr = make_shared_nothrow<PullQueueElement>(queue.release(), shutdown_event, name, timeout,
+        duration_collector.release(), std::move(queue_size_accumulator), std::move(pipeline_status),
+        activation_event.release(), deactivation_event.release());
+    CHECK_AS_EXPECTED(nullptr != queue_ptr, HAILO_OUT_OF_HOST_MEMORY, "Creating PullQueueElement {} failed!", name);
+
+    LOGGER__INFO("Created {}", queue_ptr->name());
+
+    return queue_ptr;
+}
+Expected<std::shared_ptr<PullQueueElement>> PullQueueElement::create(const std::string &name, const hailo_vstream_params_t &vstream_params,
+        EventPtr shutdown_event, std::shared_ptr<std::atomic<hailo_status>> pipeline_status)
+{
+    return PullQueueElement::create(name, std::chrono::milliseconds(vstream_params.timeout_ms),
+        vstream_params.queue_size, vstream_params.pipeline_elements_stats_flags, shutdown_event, pipeline_status);
+}
+
+PullQueueElement::PullQueueElement(SpscQueue<PipelineBuffer> &&queue, EventPtr shutdown_event, const std::string &name,
+                                   std::chrono::milliseconds timeout, DurationCollector &&duration_collector,
+                                   AccumulatorPtr &&queue_size_accumulator, std::shared_ptr<std::atomic<hailo_status>> &&pipeline_status,
+                                   Event &&activation_event, Event &&deactivation_event) :
+    BaseQueueElement(std::move(queue), shutdown_event, name, timeout, std::move(duration_collector), std::move(queue_size_accumulator),
+                     std::move(pipeline_status), std::move(activation_event), std::move(deactivation_event))
+{
+    start_thread();
+}
+
+PullQueueElement::~PullQueueElement()
+{
+    stop_thread();
+}
+
+hailo_status PullQueueElement::run_push(PipelineBuffer &&/*buffer*/)
+{
+    return HAILO_INVALID_OPERATION;
+}
+
+Expected<PipelineBuffer> PullQueueElement::run_pull(PipelineBuffer &&optional, const PipelinePad &/*sink*/)
+{
+    CHECK_AS_EXPECTED(!optional, HAILO_INVALID_ARGUMENT, "Optional buffer is not allowed in queue element!");
+
+    if (nullptr != m_queue_size_accumulator) {
+        m_queue_size_accumulator->add_data_point(static_cast<double>(m_queue.size_approx()));
+    }
+    auto output = m_queue.dequeue(m_timeout);
+    if (HAILO_SHUTDOWN_EVENT_SIGNALED == output.status()) {
+        auto queue_thread_status = pipeline_status();
+        CHECK_SUCCESS_AS_EXPECTED(queue_thread_status,
+            "Shutdown event was signaled in dequeue of queue element {} because thread has failed with status={}!", name(),
+            queue_thread_status);
+        LOGGER__INFO("Shutdown event was signaled in dequeue of queue element {}!", name());
+        return make_unexpected(HAILO_SHUTDOWN_EVENT_SIGNALED);
+    }
+    CHECK_EXPECTED(output);
+
+    return output;
+}
+
+hailo_status PullQueueElement::deactivate()
+{
+    hailo_status status = next_pad().deactivate();
+    if (HAILO_SUCCESS != status) {
+        LOGGER__ERROR("deactivate of source in {} has failed with status {}", name(), status);
+    }
+
+    status = m_shutdown_event->signal();
+    if (HAILO_SUCCESS != status) {
+        LOGGER__CRITICAL("Signaling shutdown event has failed with status = {}", status);
+    }
+
+    return HAILO_SUCCESS;
+}
+
+PipelinePad &PullQueueElement::next_pad()
+{
+    // Note: The next elem to be run is upstream from this elem (i.e. buffers are pulled)
+    return *m_sinks[0].prev();
+}
+
+hailo_status PullQueueElement::run_in_thread()
+{
+    auto buffer = next_pad().run_pull();
+    if (HAILO_SHUTDOWN_EVENT_SIGNALED == buffer.status()) {
+        LOGGER__INFO("Shutdown event was signaled in run_pull of queue element {}!", name());
+        return HAILO_SHUTDOWN_EVENT_SIGNALED;
+    }
+    if (HAILO_STREAM_INTERNAL_ABORT == buffer.status()) {
+        LOGGER__INFO("run_pull of queue element {} was aborted!", name());
+        return HAILO_STREAM_INTERNAL_ABORT;
+    }
+    if (HAILO_NETWORK_GROUP_NOT_ACTIVATED == buffer.status()) {
+        LOGGER__INFO("run_pull of queue element {} was called before network_group is activated!", name());
+        return HAILO_NETWORK_GROUP_NOT_ACTIVATED;
+    }
+    CHECK_EXPECTED_AS_STATUS(buffer);
+    
+    hailo_status status = m_queue.enqueue(buffer.release(), INIFINITE_TIMEOUT());
+    if (HAILO_SHUTDOWN_EVENT_SIGNALED == status) {
+        LOGGER__INFO("Shutdown event was signaled in enqueue of queue element {}!", name());
+        return HAILO_SHUTDOWN_EVENT_SIGNALED;
+    }
+    CHECK_SUCCESS(status);
+
+    return HAILO_SUCCESS;
+}
+
+Expected<std::shared_ptr<UserBufferQueueElement>> UserBufferQueueElement::create(const std::string &name, std::chrono::milliseconds timeout,
+    hailo_pipeline_elem_stats_flags_t flags, EventPtr shutdown_event, std::shared_ptr<std::atomic<hailo_status>> pipeline_status)
+{
+    auto pending_buffer_queue = BaseQueueElement::create_queue(1, shutdown_event);
+    CHECK_EXPECTED(pending_buffer_queue);
+
+    auto full_buffer_queue = BaseQueueElement::create_queue(1, shutdown_event);
+    CHECK_EXPECTED(full_buffer_queue);
+
+    auto activation_event = Event::create(Event::State::not_signalled);
+    CHECK_EXPECTED(activation_event);
+
+    auto deactivation_event = Event::create(Event::State::not_signalled);
+    CHECK_EXPECTED(deactivation_event);
+
+    auto duration_collector = DurationCollector::create(flags);
+    CHECK_EXPECTED(duration_collector);
+
+    AccumulatorPtr queue_size_accumulator = nullptr;
+    if ((flags & HAILO_PIPELINE_ELEM_STATS_MEASURE_QUEUE_SIZE) != 0) {
+        queue_size_accumulator = make_shared_nothrow<FullAccumulator<double>>("queue_size");
+        CHECK_AS_EXPECTED(nullptr != queue_size_accumulator, HAILO_OUT_OF_HOST_MEMORY);
+    }
+
+    auto queue_ptr = make_shared_nothrow<UserBufferQueueElement>(pending_buffer_queue.release(),
+        full_buffer_queue.release(), shutdown_event, name, timeout, duration_collector.release(),
+        std::move(queue_size_accumulator), std::move(pipeline_status), activation_event.release(),
+        deactivation_event.release());
+    CHECK_AS_EXPECTED(nullptr != queue_ptr, HAILO_OUT_OF_HOST_MEMORY, "Creating UserBufferQueueElement {} failed!", name);
+
+    LOGGER__INFO("Created {}", queue_ptr->name());
+
+    return queue_ptr;
+}
+
+Expected<std::shared_ptr<UserBufferQueueElement>> UserBufferQueueElement::create(const std::string &name, const hailo_vstream_params_t &vstream_params,
+        EventPtr shutdown_event, std::shared_ptr<std::atomic<hailo_status>> pipeline_status)
+{
+    return UserBufferQueueElement::create(name, std::chrono::milliseconds(vstream_params.timeout_ms),
+        vstream_params.pipeline_elements_stats_flags, shutdown_event, pipeline_status);
+}
+
+UserBufferQueueElement::UserBufferQueueElement(SpscQueue<PipelineBuffer> &&queue, SpscQueue<PipelineBuffer> &&full_buffer_queue,
+                                               EventPtr shutdown_event, const std::string &name, std::chrono::milliseconds timeout,
+                                               DurationCollector &&duration_collector, AccumulatorPtr &&queue_size_accumulator,
+                                               std::shared_ptr<std::atomic<hailo_status>> &&pipeline_status,
+                                               Event &&activation_event, Event &&deactivation_event) :
+    PullQueueElement(std::move(queue), shutdown_event, name, timeout, std::move(duration_collector),
+                     std::move(queue_size_accumulator), std::move(pipeline_status), std::move(activation_event),
+                     std::move(deactivation_event)),
+    m_full_buffer_queue(std::move(full_buffer_queue))
+{}
+
+Expected<PipelineBuffer> UserBufferQueueElement::run_pull(PipelineBuffer &&optional, const PipelinePad &/*source*/)
+{
+    CHECK_AS_EXPECTED(optional, HAILO_INVALID_ARGUMENT, "Optional buffer must be valid in {}!", name());
+
+    hailo_status status = m_queue.enqueue(std::move(optional), m_timeout);
+    if (HAILO_SHUTDOWN_EVENT_SIGNALED == status) {
+        LOGGER__INFO("Shutdown event was signaled in enqueue of queue element {}!", name());
+        return make_unexpected(HAILO_SHUTDOWN_EVENT_SIGNALED);
+    }
+    CHECK_SUCCESS_AS_EXPECTED(status);
+
+    if (nullptr != m_queue_size_accumulator) {
+        m_queue_size_accumulator->add_data_point(static_cast<double>(m_full_buffer_queue.size_approx()));
+    }
+    auto output = m_full_buffer_queue.dequeue(m_timeout);
+    if (HAILO_SHUTDOWN_EVENT_SIGNALED == output.status()) {
+        LOGGER__INFO("Shutdown event was signaled in dequeue of queue element {}!", name());
+        return make_unexpected(HAILO_SHUTDOWN_EVENT_SIGNALED);
+    }
+    CHECK_EXPECTED(output);
+
+    CHECK_AS_EXPECTED(output->data() == optional.data(), HAILO_INTERNAL_FAILURE, "The buffer received in {} was not the same as the user buffer!", name());
+    return output;
+}
+
+hailo_status UserBufferQueueElement::clear()
+{
+    auto status = next_pad().clear();
+    if (HAILO_SUCCESS != status) {
+        LOGGER__ERROR("Failed to clear() in {} with status {}", name(), status);
+    }
+
+    auto queue_clear_status = m_full_buffer_queue.clear();
+    if (HAILO_SUCCESS != queue_clear_status) {
+        LOGGER__ERROR("Failed to clear() in {} with status {}", name(), queue_clear_status);
+        status = queue_clear_status;
+    }
+
+    queue_clear_status = m_queue.clear();
+    if (HAILO_SUCCESS != queue_clear_status) {
+        LOGGER__ERROR("Failed to clear() in {} with status {}", name(), queue_clear_status);
+        status = queue_clear_status;
+    }
+
+    return status;
+}
+
+hailo_status UserBufferQueueElement::run_in_thread()
+{
+    auto optional = m_queue.dequeue(INIFINITE_TIMEOUT());
+    if (HAILO_SHUTDOWN_EVENT_SIGNALED == optional.status()) {
+        LOGGER__INFO("Shutdown event was signaled in dequeue of {}!", name());
+        return HAILO_SHUTDOWN_EVENT_SIGNALED;
+    }
+    CHECK_EXPECTED_AS_STATUS(optional);
+
+    auto buffer = next_pad().run_pull(optional.release());
+    if (HAILO_SHUTDOWN_EVENT_SIGNALED == buffer.status()) {
+        LOGGER__INFO("Shutdown event was signaled in run_pull of {}!", name());
+        return HAILO_SHUTDOWN_EVENT_SIGNALED;
+    }
+    if (HAILO_STREAM_INTERNAL_ABORT == buffer.status()) {
+        LOGGER__INFO("run_pull of {} was aborted!", name());
+        return HAILO_STREAM_INTERNAL_ABORT;
+    }
+    CHECK_EXPECTED_AS_STATUS(buffer);
+    
+    hailo_status status = m_full_buffer_queue.enqueue(buffer.release(), INIFINITE_TIMEOUT());
+    if (HAILO_SHUTDOWN_EVENT_SIGNALED == status) {
+        LOGGER__INFO("Shutdown event was signaled in enqueue of {}!", name());
+        return HAILO_SHUTDOWN_EVENT_SIGNALED;
+    }
+    CHECK_SUCCESS(status);
+
+    return HAILO_SUCCESS;
+}
+
+BaseMuxElement::BaseMuxElement(size_t sink_count, const std::string &name, std::chrono::milliseconds timeout,
+                               DurationCollector &&duration_collector, std::shared_ptr<std::atomic<hailo_status>> &&pipeline_status) :
+    PipelineElement(name, std::move(duration_collector), std::move(pipeline_status)),
+    m_timeout(timeout)
+{
+    m_sources.emplace_back(*this, name, PipelinePad::Type::SOURCE);
+    m_sinks.reserve(sink_count);
+    for (uint32_t i = 0; i < sink_count; ++i) {
+        m_sinks.emplace_back(*this, name, PipelinePad::Type::SINK);
+    }
+}
+
+hailo_status BaseMuxElement::run_push(PipelineBuffer &&/*buffer*/)
+{
+    return HAILO_NOT_IMPLEMENTED;
+}
+
+Expected<PipelineBuffer> BaseMuxElement::run_pull(PipelineBuffer &&optional, const PipelinePad &/*source*/)
+{
+    std::vector<PipelineBuffer> inputs;
+    inputs.reserve(m_sinks.size());
+    for (auto &sink : m_sinks) {
+        auto buffer = sink.prev()->run_pull();
+        if (HAILO_SHUTDOWN_EVENT_SIGNALED == buffer.status()) {
+            return make_unexpected(buffer.status());
+        }
+        CHECK_EXPECTED(buffer);
+
+        inputs.push_back(buffer.release());
+    }
+
+    auto output = action(std::move(inputs), std::move(optional));
+    CHECK_EXPECTED(output);
+
+    return output;
+}
+
+hailo_status BaseMuxElement::activate()
+{
+    for (auto &sink : m_sinks) {
+        hailo_status status = sink.prev()->activate();
+        CHECK_SUCCESS(status);
+    }
+    return HAILO_SUCCESS;
+}
+
+hailo_status BaseMuxElement::deactivate()
+{
+    for (auto &sink : m_sinks) {
+        hailo_status status = sink.prev()->deactivate();
+        CHECK_SUCCESS(status);
+    }
+    return HAILO_SUCCESS;
+}
+
+hailo_status BaseMuxElement::post_deactivate()
+{
+    for (auto &sink : m_sinks) {
+        hailo_status status = sink.prev()->post_deactivate();
+        CHECK_SUCCESS(status);
+    }
+    return HAILO_SUCCESS;
+}
+
+hailo_status BaseMuxElement::clear()
+{
+    hailo_status status = HAILO_SUCCESS;
+    for (auto &sink : m_sinks) {
+        hailo_status clear_status = sink.prev()->clear();
+        if (HAILO_SUCCESS != clear_status) {
+            status = clear_status;
+        }
+    }
+    return status;
+}
+
+hailo_status BaseMuxElement::flush()
+{
+    hailo_status status = HAILO_SUCCESS;
+    for (auto &sink : m_sinks) {
+        hailo_status clear_status = sink.prev()->flush();
+        if (HAILO_SUCCESS != clear_status) {
+            status = clear_status;
+        }
+    }
+    return status;
+}
+
+BaseDemuxElement::BaseDemuxElement(size_t source_count, const std::string &name, std::chrono::milliseconds timeout,
+                                   DurationCollector &&duration_collector, std::shared_ptr<std::atomic<hailo_status>> &&pipeline_status) :
+    PipelineElement(name, std::move(duration_collector), std::move(pipeline_status)),
+    m_timeout(timeout),
+    m_is_activated(false),
+    m_was_stream_aborted(false),
+    m_index_of_source(),
+    m_was_source_called(source_count, false),
+    m_buffers_for_action()
+{
+    m_sinks.emplace_back(*this, name, PipelinePad::Type::SINK);
+    m_sources.reserve(source_count);
+    for (uint32_t i = 0; i < source_count; i++) {
+        m_sources.emplace_back(*this, name, PipelinePad::Type::SOURCE);
+        m_index_of_source[&m_sources[i]] = i;
+    }
+}
+
+hailo_status BaseDemuxElement::run_push(PipelineBuffer &&/*buffer*/)
+{
+    return HAILO_NOT_IMPLEMENTED;
+}
+
+Expected<PipelineBuffer> BaseDemuxElement::run_pull(PipelineBuffer &&optional, const PipelinePad &source)
+{
+    CHECK_AS_EXPECTED(!optional, HAILO_INVALID_ARGUMENT, "Optional buffer is not allowed in demux element!");
+
+    // TODO: should we lock here? or only right before wait_for?
+    std::unique_lock<std::mutex> lock(m_mutex);
+    if (!m_is_activated) {
+        return make_unexpected(HAILO_SHUTDOWN_EVENT_SIGNALED);
+    }
+
+    m_was_source_called[m_index_of_source[&source]] = true;
+    if (were_all_sinks_called()) {
+        auto input = next_pad().run_pull();
+        if (HAILO_STREAM_INTERNAL_ABORT == input.status()) {
+            LOGGER__INFO("run_pull of demux element was aborted!");
+            m_was_stream_aborted = true;
+            lock.unlock();
+            m_cv.notify_all();
+            return make_unexpected(input.status());
+        }
+        if (HAILO_SHUTDOWN_EVENT_SIGNALED == input.status()) {
+            return make_unexpected(input.status());
+        }
+        CHECK_EXPECTED(input);
+
+        auto outputs = action(input.release());
+        if (HAILO_SHUTDOWN_EVENT_SIGNALED == outputs.status()) {
+            return make_unexpected(outputs.status());
+        }
+        CHECK_EXPECTED(outputs);
+
+        m_buffers_for_action = outputs.release();
+
+        for (uint32_t i = 0; i < m_was_source_called.size(); i++) {
+            m_was_source_called[i] = false;
+        }
+
+        // Manual unlocking is done before notifying, to avoid waking up the waiting thread only to block again
+        lock.unlock();
+        m_cv.notify_all();
+    } else {
+        auto cv_status = m_cv.wait_for(lock, m_timeout);
+        CHECK_AS_EXPECTED(std::cv_status::timeout != cv_status, HAILO_TIMEOUT, "Waiting for other threads in demux {} has reached a timeout!", name());
+
+        if (m_was_stream_aborted) {
+            return make_unexpected(HAILO_STREAM_INTERNAL_ABORT);
+        }
+
+        // We check if the element is not activated in case notify_all() was called from deactivate()
+        if (!m_is_activated) {
+            return make_unexpected(HAILO_SHUTDOWN_EVENT_SIGNALED);
+        }
+    }
+
+    assert(m_index_of_source[&source] < m_buffers_for_action.size());
+    return std::move(m_buffers_for_action[m_index_of_source[&source]]);
+}
+
+bool BaseDemuxElement::were_all_sinks_called()
+{
+    return std::all_of(m_was_source_called.begin(), m_was_source_called.end(), [](bool v) { return v; });
+}
+
+hailo_status BaseDemuxElement::activate()
+{
+    if (m_is_activated) {
+        return HAILO_SUCCESS;
+    }
+    m_is_activated = true;// TODO Should this always be true, no matter the status of source().activate()?
+    m_was_stream_aborted = false;
+    return next_pad().activate();
+}
+
+hailo_status BaseDemuxElement::deactivate()
+{
+    if (!m_is_activated) {
+        return HAILO_SUCCESS;
+    }
+    m_is_activated = false;
+
+    // deactivate should be called before mutex acquire and notify_all because it is possible that all queues are waiting on
+    // the run_pull of the source (HwRead) and the mutex is already acquired so this would prevent a timeout error
+    hailo_status status = next_pad().deactivate();
+    if (HAILO_SUCCESS != status) {
+        LOGGER__ERROR("deactivate of source in {} has failed with status {}", name(), status);
+    }
+
+    {
+        // There is a case where the other thread is halted (via context switch) before the wait_for() function,
+        // then we call notify_all() here, and then the wait_for() is called - resulting in a timeout.
+        // notify_all() only works on threads which are already waiting, so that's why we acquire the lock here.
+        std::unique_lock<std::mutex> lock(m_mutex);
+    }
+    m_cv.notify_all();
+
+    return HAILO_SUCCESS;
+}
+
+hailo_status BaseDemuxElement::post_deactivate()
+{
+    for (uint32_t i = 0; i < m_was_source_called.size(); i++) {
+        m_was_source_called[i] = false;
+    }
+    return next_pad().post_deactivate();
+}
+
+hailo_status BaseDemuxElement::clear()
+{
+    return next_pad().clear();
+}
+
+hailo_status BaseDemuxElement::flush()
+{
+    return next_pad().flush();
+}
+
+PipelinePad &BaseDemuxElement::next_pad()
+{
+    // Note: The next elem to be run is upstream from this elem (i.e. buffers are pulled)
+    return *m_sinks[0].prev();
+}
+
+hailo_status BaseDemuxElement::set_timeout(std::chrono::milliseconds timeout)
+{
+    m_timeout = timeout;
+    return HAILO_SUCCESS;
+}
+
+} /* namespace hailort */
diff --git a/hailort/libhailort/src/pipeline.hpp b/hailort/libhailort/src/pipeline.hpp
new file mode 100644 (file)
index 0000000..585dd3c
--- /dev/null
@@ -0,0 +1,490 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file pipeline.hpp
+ * @brief Hailo Infer Pipeline
+ **/
+
+#ifndef _HAILO_PIPELINE_HPP_
+#define _HAILO_PIPELINE_HPP_
+
+#include "hailo/buffer.hpp"
+#include "hailo/runtime_statistics.hpp"
+#include "thread_safe_queue.hpp"
+
+#include <memory>
+#include <thread>
+#include <sstream>
+#include <functional>
+
+namespace hailort
+{
+
+using PipelineTimePoint = std::chrono::steady_clock::time_point;
+#define BUFFER_POOL_DEFAULT_QUEUE_TIMEOUT (std::chrono::milliseconds(10000))
+#define DEFAULT_NUM_FRAMES_BEFORE_COLLECTION_START (100)
+
+class BufferPool;
+using BufferPoolPtr = std::shared_ptr<BufferPool>;
+
+class PipelineBuffer final
+{
+public:
+    class Metadata final
+    {
+    public:
+        explicit Metadata(PipelineTimePoint start_time);
+        // Creates an empty metadata object
+        Metadata();
+        
+        ~Metadata() = default;
+        Metadata(const Metadata &) = default;
+        Metadata &operator=(const Metadata &) = delete;
+        Metadata(Metadata &&other) = default;
+        Metadata &operator=(Metadata &&other) = default;
+
+        PipelineTimePoint get_start_time() const;
+        void set_start_time(PipelineTimePoint val);
+
+    private:
+        PipelineTimePoint m_start_time;
+    };
+
+    enum class Type {
+        DATA = 0,
+        FLUSH,
+        DEACTIVATE
+    };
+    
+    // Creates an empty PipelineBuffer (with no buffer/memory view)
+    PipelineBuffer();
+    PipelineBuffer(Type type);
+    PipelineBuffer(MemoryView view, bool should_measure = false);
+    PipelineBuffer(Buffer &&buffer, BufferPoolPtr pool, bool should_measure = false);
+    ~PipelineBuffer();
+    
+    PipelineBuffer(const PipelineBuffer &) = delete;
+    PipelineBuffer &operator=(const PipelineBuffer &) = delete;
+    PipelineBuffer(PipelineBuffer &&other);
+    PipelineBuffer &operator=(PipelineBuffer &&other);
+    explicit operator bool() const;
+
+    uint8_t* data();
+    size_t size() const;
+    MemoryView as_view();
+    Type get_type() const;
+    Metadata get_metadata() const;
+    void set_metadata(Metadata &&val);
+
+private:
+    Type m_type;
+    Buffer m_buffer;
+    bool m_should_release_buffer;
+    BufferPoolPtr m_pool;
+    MemoryView m_view;
+    Metadata m_metadata;
+
+    static PipelineTimePoint add_timestamp(bool should_measure);
+};
+
+// The buffer pool has to be created as a shared pointer (via the create function) because we use shared_from_this(),
+// which is only allowed if there is already a shared pointer pointing to "this"!
+class BufferPool : public std::enable_shared_from_this<BufferPool>
+{
+public:
+    static Expected<BufferPoolPtr> create(size_t buffer_size, size_t buffer_count, EventPtr shutdown_event,
+        hailo_pipeline_elem_stats_flags_t elem_flags, hailo_vstream_stats_flags_t vstream_flags);
+    BufferPool(size_t buffer_size, bool measure_vstream_latency, SpscQueue<Buffer> &&free_buffers, AccumulatorPtr &&queue_size_accumulator);
+    virtual ~BufferPool() = default;
+
+    size_t buffer_size();
+    Expected<PipelineBuffer> acquire_buffer(std::chrono::milliseconds timeout);
+    AccumulatorPtr get_queue_size_accumulator();
+    Expected<PipelineBuffer> get_available_buffer(PipelineBuffer &&optional, std::chrono::milliseconds timeout);
+
+private:
+    hailo_status release_buffer(Buffer &&buffer);
+
+    const size_t m_buffer_size;
+    const bool m_measure_vstream_latency;
+    SpscQueue<Buffer> m_free_buffers;
+    AccumulatorPtr m_queue_size_accumulator;
+
+    friend class PipelineBuffer;
+};
+
+class DurationCollector final
+{
+public:
+    // TODO: HRT-4258
+    // Note: We start measuring the FPS/latency after num_frames_before_collection_start calls to start_measurement + 
+    //       complete_measurement. This is to allow the vstream pipeline to stabilize. Thus we ignore invalid
+    //       measurements that are due to buffering that occours when the pipeline starts.
+    static Expected<DurationCollector> create(hailo_pipeline_elem_stats_flags_t flags,
+        uint32_t num_frames_before_collection_start = DEFAULT_NUM_FRAMES_BEFORE_COLLECTION_START);
+    DurationCollector(const DurationCollector &) = delete;
+    DurationCollector(DurationCollector &&other) = default;
+    DurationCollector &operator=(const DurationCollector &) = delete;
+    DurationCollector &operator=(DurationCollector &&other) = delete;
+    ~DurationCollector() = default;
+    
+    void start_measurement();
+    void complete_measurement();
+
+    // latency_accumulator will measure latency in seconds
+    AccumulatorPtr get_latency_accumulator();
+    // average_fps_accumulator will measure fps in seconds^-1
+    AccumulatorPtr get_average_fps_accumulator();
+
+private:
+    DurationCollector(bool measure_latency, bool measure_average_fps,
+                      AccumulatorPtr &&latency_accumulator, AccumulatorPtr &&average_fps_accumulator,
+                      uint32_t num_frames_before_collection_start);
+    static bool should_measure_latency(hailo_pipeline_elem_stats_flags_t flags);
+    static bool should_measure_average_fps(hailo_pipeline_elem_stats_flags_t flags);
+
+    const bool m_measure_latency;
+    const bool m_measure_average_fps;
+    const bool m_measure;
+    AccumulatorPtr m_latency_accumulator;
+    AccumulatorPtr m_average_fps_accumulator;
+    PipelineTimePoint m_start;
+    size_t m_count;
+    const size_t m_num_frames_before_collection_start;
+};
+
+class PipelineObject
+{
+public:
+    PipelineObject(const std::string &name);
+    virtual ~PipelineObject() = default;
+    PipelineObject(PipelineObject &&) noexcept = default;
+    PipelineObject& operator=(PipelineObject &&) noexcept = default;
+
+    const std::string &name() const;
+
+    static std::string create_element_name(const std::string &element_name, const std::string &stream_name, uint8_t stream_index);
+
+private:
+    std::string m_name;
+};
+
+class PipelineElement;
+using PushCompleteCallback = std::function<void(const PipelineBuffer::Metadata&)>;
+using PullCompleteCallback = std::function<void(const PipelineBuffer::Metadata&)>;
+
+class PipelinePad final : public PipelineObject
+{
+public:
+    enum class Type
+    {
+        SOURCE,
+        SINK
+    };
+
+    // Link left's source pad (left->sources()[left_source_index]) with right's sink pad (right->right()[right_sink_index])
+    static hailo_status link_pads(std::shared_ptr<PipelineElement> left, std::shared_ptr<PipelineElement> right,
+        uint32_t left_source_index = 0, uint32_t right_sink_index = 0);
+    // Link left's source pad (left.sources()[left_source_index]) with right's sink pad (right.right()[right_sink_index])
+    static hailo_status link_pads(PipelineElement &left, PipelineElement &right, uint32_t left_source_index = 0,
+        uint32_t right_sink_index = 0);
+    static std::string create_pad_name(const std::string &element_name, Type pad_type);
+
+    PipelinePad(PipelineElement &element, const std::string &element_name, Type pad_type);
+    PipelinePad(const PipelinePad &) = delete;
+    PipelinePad(PipelinePad &&other) = default;
+    PipelinePad &operator=(const PipelinePad &) = delete;
+    PipelinePad &operator=(PipelinePad &&other) = delete;
+    ~PipelinePad() = default;
+
+    virtual hailo_status activate();
+    virtual hailo_status deactivate();
+    virtual hailo_status post_deactivate();
+    virtual hailo_status clear();
+    virtual hailo_status flush();
+    virtual hailo_status run_push(PipelineBuffer &&buffer);
+    virtual Expected<PipelineBuffer> run_pull(PipelineBuffer &&optional = PipelineBuffer());
+    void set_push_complete_callback(PushCompleteCallback push_complete_callback);
+    void set_pull_complete_callback(PullCompleteCallback pull_complete_callback);
+    void set_next(PipelinePad *next);
+    void set_prev(PipelinePad *prev);
+    PipelinePad *next();
+    PipelinePad *prev();
+    PipelineElement &element();
+    const PipelinePad *next() const;
+    const PipelinePad *prev() const;
+    const PipelineElement &element() const;
+
+protected:
+    PipelineElement &m_element;
+    PipelinePad *m_next;
+    PipelinePad *m_prev;
+    PushCompleteCallback m_push_complete_callback;
+    PullCompleteCallback m_pull_complete_callback;
+
+private:
+    // Automatic naming isn't thread safe
+    static uint32_t index;
+};
+
+// Note: PipelinePads accept 'PipelineElement &' in their ctor. PipelineElements can pass "*this" to their
+//       PipelinePads (sources/sinks) in the PipelineElement ctor. This is OK because the ctor of PipelinePad
+//       does nothing with the element reference other than setting it as class member.
+class PipelineElement : public PipelineObject
+{
+public:
+    PipelineElement(const std::string &name, DurationCollector &&duration_collector,
+                    std::shared_ptr<std::atomic<hailo_status>> &&pipeline_status);
+    virtual ~PipelineElement() = default;
+
+    PipelineElement(PipelineElement &&other) = delete;
+    PipelineElement(const PipelineElement &) = delete;
+    PipelineElement &operator=(const PipelineElement &) = delete;
+    PipelineElement &operator=(PipelineElement &&other) = delete;
+
+    virtual hailo_status activate() = 0;
+    virtual hailo_status deactivate() = 0;
+    virtual hailo_status post_deactivate() = 0;
+    virtual hailo_status clear() = 0;
+    virtual hailo_status flush() = 0;
+    virtual hailo_status run_push(PipelineBuffer &&buffer) = 0;
+    virtual Expected<PipelineBuffer> run_pull(PipelineBuffer &&optional, const PipelinePad &source) = 0;
+    AccumulatorPtr get_fps_accumulator();
+    AccumulatorPtr get_latency_accumulator();
+    virtual std::vector<AccumulatorPtr> get_queue_size_accumulators();
+    std::vector<PipelinePad> &sinks();
+    std::vector<PipelinePad> &sources();
+    const std::vector<PipelinePad> &sinks() const;
+    const std::vector<PipelinePad> &sources() const;
+    virtual std::string description() const;
+
+protected:
+    DurationCollector m_duration_collector;
+    std::shared_ptr<std::atomic<hailo_status>> m_pipeline_status;
+    std::vector<PipelinePad> m_sinks;
+    std::vector<PipelinePad> m_sources;
+};
+
+// An element with one source pad only (generates data)
+class SourceElement : public PipelineElement
+{
+public:
+    SourceElement(const std::string &name, DurationCollector &&duration_collector,
+                  std::shared_ptr<std::atomic<hailo_status>> &&pipeline_status);
+    PipelinePad &source();
+};
+
+// An element with one sink pad only (consumes data)
+class SinkElement : public PipelineElement
+{
+public:
+    SinkElement(const std::string &name, DurationCollector &&duration_collector,
+                std::shared_ptr<std::atomic<hailo_status>> &&pipeline_status);
+    PipelinePad &sink();
+};
+
+// Transfers data from one pad to another pad. Has one sink pad and one source pad.
+class IntermediateElement : public PipelineElement
+{
+public:
+    IntermediateElement(const std::string &name, DurationCollector &&duration_collector,
+                        std::shared_ptr<std::atomic<hailo_status>> &&pipeline_status);
+    virtual PipelinePad &next_pad() = 0;
+    virtual hailo_status flush() override;
+};
+
+class FilterElement : public IntermediateElement
+{
+public:
+    FilterElement(const std::string &name, DurationCollector &&duration_collector,
+                  std::shared_ptr<std::atomic<hailo_status>> &&pipeline_status);
+    virtual ~FilterElement() = default;
+
+    virtual hailo_status activate() override;
+    virtual hailo_status deactivate() override;
+    virtual hailo_status post_deactivate() override;
+    virtual hailo_status clear() override;
+    virtual hailo_status run_push(PipelineBuffer &&buffer) override;
+    virtual Expected<PipelineBuffer> run_pull(PipelineBuffer &&optional, const PipelinePad &source) override;
+
+protected:
+    // The optional buffer functions as an output buffer that the user can write to instead of acquiring a new buffer
+    virtual Expected<PipelineBuffer> action(PipelineBuffer &&input, PipelineBuffer &&optional) = 0;
+};
+
+class BaseQueueElement : public IntermediateElement
+{
+public:
+    virtual ~BaseQueueElement() = default;
+
+    virtual hailo_status activate() override;
+    virtual hailo_status deactivate() = 0;
+    virtual hailo_status post_deactivate() override;
+    virtual hailo_status clear() override;
+    hailo_status set_timeout(std::chrono::milliseconds timeout);
+    virtual std::string description() const override;
+
+    static constexpr auto INIFINITE_TIMEOUT() { return std::chrono::milliseconds(HAILO_INFINITE); }
+
+protected:
+    static Expected<SpscQueue<PipelineBuffer>> create_queue(size_t queue_size, EventPtr shutdown_event);
+    BaseQueueElement(SpscQueue<PipelineBuffer> &&queue, EventPtr shutdown_event, const std::string &name,
+        std::chrono::milliseconds timeout, DurationCollector &&duration_collector,
+        AccumulatorPtr &&queue_size_accumulator, std::shared_ptr<std::atomic<hailo_status>> &&pipeline_status,
+        Event &&activation_event, Event &&deactivation_event);
+
+    hailo_status pipeline_status();
+
+    /// Starts/stops the queue thread. This functions needs to be called on subclasses ctor and dtor
+    /// accordingly because otherwise, if we will start/stop thread in this class we will face pure-call
+    /// to `run_in_thread`.
+    /// This functions don't return status because they are meant to be called on ctor and dtor 
+    void start_thread();
+    void stop_thread();
+
+    virtual std::vector<AccumulatorPtr> get_queue_size_accumulators() override;
+
+    virtual hailo_status run_in_thread() = 0;
+
+    SpscQueue<PipelineBuffer> m_queue;
+    EventPtr m_shutdown_event;
+    std::chrono::milliseconds m_timeout;
+    std::thread m_thread;
+    std::atomic_bool m_is_thread_running;
+    Event m_activation_event;
+    Event m_deactivation_event;
+    AccumulatorPtr m_queue_size_accumulator;
+};
+
+class PushQueueElement : public BaseQueueElement
+{
+public:
+    static Expected<std::shared_ptr<PushQueueElement>> create(const std::string &name, std::chrono::milliseconds timeout,
+        size_t queue_size, hailo_pipeline_elem_stats_flags_t flags, EventPtr shutdown_event,
+        std::shared_ptr<std::atomic<hailo_status>> pipeline_status);
+    static Expected<std::shared_ptr<PushQueueElement>> create(const std::string &name, const hailo_vstream_params_t &vstream_params,
+        EventPtr shutdown_event, std::shared_ptr<std::atomic<hailo_status>> pipeline_status);
+    PushQueueElement(SpscQueue<PipelineBuffer> &&queue, EventPtr shutdown_event, const std::string &name,
+        std::chrono::milliseconds timeout, DurationCollector &&duration_collector, AccumulatorPtr &&queue_size_accumulator,
+        std::shared_ptr<std::atomic<hailo_status>> &&pipeline_status, Event &&activation_event, Event &&deactivation_event);
+    virtual ~PushQueueElement();
+
+    virtual hailo_status run_push(PipelineBuffer &&buffer) override;
+    virtual Expected<PipelineBuffer> run_pull(PipelineBuffer &&optional, const PipelinePad &source) override;
+    virtual hailo_status deactivate() override;
+    virtual PipelinePad &next_pad() override;
+
+protected:
+    virtual hailo_status run_in_thread() override;
+};
+
+class PullQueueElement : public BaseQueueElement
+{
+public:
+    static Expected<std::shared_ptr<PullQueueElement>> create(const std::string &name, std::chrono::milliseconds timeout,
+        size_t queue_size, hailo_pipeline_elem_stats_flags_t flags, EventPtr shutdown_event,
+        std::shared_ptr<std::atomic<hailo_status>> pipeline_status);
+    static Expected<std::shared_ptr<PullQueueElement>> create(const std::string &name, const hailo_vstream_params_t &vstream_params,
+        EventPtr shutdown_event, std::shared_ptr<std::atomic<hailo_status>> pipeline_status);
+    PullQueueElement(SpscQueue<PipelineBuffer> &&queue, EventPtr shutdown_event, const std::string &name,
+        std::chrono::milliseconds timeout, DurationCollector &&duration_collector, AccumulatorPtr &&queue_size_accumulator,
+        std::shared_ptr<std::atomic<hailo_status>> &&pipeline_status, Event &&activation_event, Event &&deactivation_event);
+    virtual ~PullQueueElement();
+
+    virtual hailo_status run_push(PipelineBuffer &&buffer) override;
+    virtual Expected<PipelineBuffer> run_pull(PipelineBuffer &&optional, const PipelinePad &source) override;
+    virtual hailo_status deactivate() override;
+    virtual PipelinePad &next_pad() override;
+
+protected:
+    virtual hailo_status run_in_thread() override;
+};
+
+class UserBufferQueueElement : public PullQueueElement
+{
+public:
+    static Expected<std::shared_ptr<UserBufferQueueElement>> create(const std::string &name, std::chrono::milliseconds timeout,
+        hailo_pipeline_elem_stats_flags_t flags, EventPtr shutdown_event, std::shared_ptr<std::atomic<hailo_status>> pipeline_status);
+    static Expected<std::shared_ptr<UserBufferQueueElement>> create(const std::string &name, const hailo_vstream_params_t &vstream_params,
+        EventPtr shutdown_event, std::shared_ptr<std::atomic<hailo_status>> pipeline_status);
+    UserBufferQueueElement(SpscQueue<PipelineBuffer> &&queue, SpscQueue<PipelineBuffer> &&full_buffer_queue, EventPtr shutdown_event,
+        const std::string &name, std::chrono::milliseconds timeout, DurationCollector &&duration_collector, AccumulatorPtr &&queue_size_accumulator,
+        std::shared_ptr<std::atomic<hailo_status>> &&pipeline_status, Event &&activation_event, Event &&deactivation_event);
+
+    virtual Expected<PipelineBuffer> run_pull(PipelineBuffer &&optional, const PipelinePad &source) override;
+    virtual hailo_status clear() override;
+
+protected:
+    virtual hailo_status run_in_thread() override;
+
+private:
+    SpscQueue<PipelineBuffer> m_full_buffer_queue;
+};
+
+class BaseMuxElement : public PipelineElement
+{
+public:
+    BaseMuxElement(size_t sink_count, const std::string &name, std::chrono::milliseconds timeout,
+        DurationCollector &&duration_collector, std::shared_ptr<std::atomic<hailo_status>> &&pipeline_status);
+    virtual ~BaseMuxElement() = default;
+
+    virtual hailo_status run_push(PipelineBuffer &&buffer) override;
+    virtual Expected<PipelineBuffer> run_pull(PipelineBuffer &&optional, const PipelinePad &source) override;
+    virtual hailo_status activate() override;
+    virtual hailo_status deactivate() override;
+    virtual hailo_status post_deactivate() override;
+    virtual hailo_status clear() override;
+    virtual hailo_status flush() override;
+
+protected:
+    virtual Expected<PipelineBuffer> action(std::vector<PipelineBuffer> &&inputs, PipelineBuffer &&optional) = 0;
+
+    std::chrono::milliseconds m_timeout;
+};
+
+class BaseDemuxElement : public PipelineElement
+{
+public:
+    BaseDemuxElement(size_t source_count, const std::string &name, std::chrono::milliseconds timeout,
+        DurationCollector &&duration_collector, std::shared_ptr<std::atomic<hailo_status>> &&pipeline_status);
+    virtual ~BaseDemuxElement() = default;
+
+    virtual hailo_status run_push(PipelineBuffer &&buffer) override;
+    virtual Expected<PipelineBuffer> run_pull(PipelineBuffer &&optional, const PipelinePad &source) override;
+    virtual hailo_status activate() override;
+    virtual hailo_status deactivate() override;
+    virtual hailo_status post_deactivate() override;
+    virtual hailo_status clear() override;
+    virtual hailo_status flush() override;
+    hailo_status set_timeout(std::chrono::milliseconds timeout);
+
+protected:
+    virtual Expected<std::vector<PipelineBuffer>> action(PipelineBuffer &&input) = 0;
+
+    std::chrono::milliseconds m_timeout;
+
+private:
+    bool were_all_sinks_called();
+    PipelinePad &next_pad();
+
+    std::atomic_bool m_is_activated;
+    std::atomic_bool m_was_stream_aborted;
+    std::unordered_map<const PipelinePad*, uint32_t> m_index_of_source;
+    std::vector<bool> m_was_source_called;
+    std::vector<PipelineBuffer> m_buffers_for_action;
+    std::mutex m_mutex;
+    std::condition_variable m_cv;
+};
+
+enum class AccumulatorType
+{
+    FPS,
+    LATENCY,
+    QUEUE_SIZE
+};
+
+} /* namespace hailort */
+
+#endif /* _HAILO_PIPELINE_HPP_ */
diff --git a/hailort/libhailort/src/sensor_config_utils.cpp b/hailort/libhailort/src/sensor_config_utils.cpp
new file mode 100644 (file)
index 0000000..3f67079
--- /dev/null
@@ -0,0 +1,248 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file sensor_config_utils.cpp
+ * @brief Utilities for sensor_config operations
+ **/
+
+#include "sensor_config_utils.hpp"
+#include "common/string_utils.hpp"
+#include "common/utils.hpp"
+
+#include <fstream>
+#include <sstream>
+#include <iomanip>
+
+namespace hailort
+{
+
+Expected<SENSOR_CONFIG_OPCODES_t> SensorConfigUtils::get_sensor_opcode_by_name(const std::string &name)
+{
+    if (name == "SENSOR_CONFIG_OPCODES_WR") {
+        return SENSOR_CONFIG_OPCODES_WR;
+    }
+    else if (name == "SENSOR_CONFIG_OPCODES_RD") {
+        return SENSOR_CONFIG_OPCODES_RD;
+    }
+    else if (name == "SENSOR_CONFIG_OPCODES_RMW") {
+        return SENSOR_CONFIG_OPCODES_RMW;
+    }
+    else if (name == "SENSOR_CONFIG_OPCODES_DELAY") {
+        return SENSOR_CONFIG_OPCODES_DELAY;
+    } 
+    else { 
+        LOGGER__ERROR("Failed getting opcode value by name: {}", name);
+        return make_unexpected(HAILO_NOT_FOUND);
+    }
+}
+
+Expected<std::string> SensorConfigUtils::convert_opcode_to_string(uint8_t opcode)
+{
+    switch (opcode) {
+    case SENSOR_CONFIG_OPCODES_WR:
+        return std::string("SENSOR_CONFIG_OPCODES_WR");
+
+    case SENSOR_CONFIG_OPCODES_RD:
+        return std::string("SENSOR_CONFIG_OPCODES_RD");
+
+    case SENSOR_CONFIG_OPCODES_RMW:
+        return std::string("SENSOR_CONFIG_OPCODES_RMW");
+
+    case SENSOR_CONFIG_OPCODES_DELAY:
+        return std::string("SENSOR_CONFIG_OPCODES_DELAY");
+
+    default:
+        LOGGER__ERROR("Failed converting opcode to string");
+        return make_unexpected(HAILO_NOT_FOUND);
+    }
+}
+
+Expected<std::vector<SENSOR_CONFIG__operation_cfg_t>> SensorConfigUtils::read_config_file(const std::string &config_file_path)
+{
+    std::ifstream config_file;
+    config_file.open(config_file_path, std::ios::in);
+    CHECK_AS_EXPECTED(config_file.is_open(), HAILO_OPEN_FILE_FAILURE, "Failed opening sensor config file with errno: {}", errno);
+
+    std::vector<SENSOR_CONFIG__operation_cfg_t> control_buffers;
+    std::string line;
+    std::string col; 
+
+    while(std::getline(config_file, line)) {
+        std::stringstream s(line); 
+        CHECK_AS_EXPECTED(s.good(), HAILO_FILE_OPERATION_FAILURE, "Failed reading line in sensor config file with errno: {}", errno);
+
+        SENSOR_CONFIG__operation_cfg_t config_entry = {};
+    
+        // opcode
+        std::getline(s, col, ',' );
+        CHECK_AS_EXPECTED(s.good(), HAILO_FILE_OPERATION_FAILURE, "Failed reading sensor config file opcode with errno: {}", errno);
+        auto opcode = get_sensor_opcode_by_name(col);
+        CHECK_EXPECTED(opcode, "Failed getting opcode value");
+        config_entry.operation = static_cast<uint8_t>(opcode.value());
+
+        // length
+        std::getline(s, col, ',' );
+        CHECK_AS_EXPECTED(s.good(), HAILO_FILE_OPERATION_FAILURE, "Failed reading sensor config file length with errno: {}", errno);
+        auto length = StringUtils::to_uint8(col, 10);
+        CHECK_EXPECTED(length);
+        config_entry.length = length.value();
+
+        // page
+        std::getline(s, col, ',' );
+        CHECK_AS_EXPECTED(s.good(), HAILO_FILE_OPERATION_FAILURE, "Failed reading sensor config file page with errno: {}", errno);
+        auto page = StringUtils::to_int32(col, 16);
+        CHECK_EXPECTED(page);
+        if (0 > page.value()) {
+            config_entry.page = 0xff;
+        } else {
+            auto page_uint8 = StringUtils::to_uint8(col, 16);
+            CHECK_EXPECTED(page_uint8);
+            config_entry.page = page_uint8.value();
+        }
+        
+        // address
+        std::getline(s, col, ',' );
+        CHECK_AS_EXPECTED(s.good(), HAILO_FILE_OPERATION_FAILURE, "Failed reading sensor config file address with errno: {}", errno);
+        auto address = StringUtils::to_uint32(col, 16);
+        CHECK_EXPECTED(address);
+        config_entry.address = address.value();
+
+        // bitmask
+        std::getline(s, col, ',' );
+        CHECK_AS_EXPECTED(s.good(), HAILO_FILE_OPERATION_FAILURE, "Failed reading sensor config file bitmask with errno: {}", errno);
+        auto bitmask = StringUtils::to_uint32(col, 16);
+        CHECK_EXPECTED(bitmask);
+        config_entry.bitmask = bitmask.value();
+        
+        // value
+        std::getline(s, col, ',' );
+        CHECK_AS_EXPECTED(!s.bad(), HAILO_FILE_OPERATION_FAILURE, "Failed reading sensor config file value with errno: {}", errno);
+        auto value = StringUtils::to_uint32(col, 16);
+        CHECK_EXPECTED(value);
+        config_entry.value = value.value();
+
+        control_buffers.emplace_back(config_entry);
+    }
+    CHECK_AS_EXPECTED(!config_file.bad(), HAILO_FILE_OPERATION_FAILURE, "Failed reading line in sensor config file with errno: {}", errno);
+
+    return control_buffers;
+}
+
+Expected<SENSOR_CONFIG__operation_cfg_t> SensorConfigUtils::create_config_entry(uint8_t page, uint32_t address, uint8_t length, const std::string &hex_value)
+{
+    auto config_entry_value = StringUtils::to_uint32(hex_value, 16);
+    CHECK_EXPECTED(config_entry_value);
+
+    SENSOR_CONFIG__operation_cfg_t config_entry = {};
+    config_entry.value = config_entry_value.value();
+    config_entry.operation = SENSOR_CONFIG_OPCODES_WR;
+    config_entry.length = length;
+    config_entry.page = page;
+    config_entry.address = address;
+    config_entry.bitmask = 0xFFFF;
+
+    return config_entry;
+}
+
+Expected<std::vector<SENSOR_CONFIG__operation_cfg_t>> SensorConfigUtils::read_isp_config_file(const std::string &isp_static_config_file_path, const std::string &isp_runtime_config_file_path)
+{
+    std::vector<std::string> config_files = {isp_static_config_file_path, isp_runtime_config_file_path};
+    std::vector<SENSOR_CONFIG__operation_cfg_t> control_buffers;
+
+    for (const auto &config_file_path : config_files) {
+        std::ifstream config_file;
+        config_file.open(config_file_path, std::ios::in);
+        CHECK_AS_EXPECTED(config_file.is_open(), HAILO_OPEN_FILE_FAILURE, "Failed opening sensor ISP config file with errno: {}", errno);
+
+        std::string line;
+        uint8_t page = 0;
+        uint32_t address = 0;
+
+        while (std::getline(config_file, line)) {
+            size_t comment_index = line.find("//"); 
+            if (((std::string::npos != comment_index) && (0 == comment_index)) || ("\n" == line) ||
+                ("\r\n" == line) || ("\r" == line) || ("" == line)) {
+                continue;
+            }
+
+            std::string::iterator it = line.begin();
+            CHECK_AS_EXPECTED(line.size() >= CONFIG_HEX_VALUE_LAST_CHAR_OFFSET, HAILO_INVALID_ARGUMENT, "Failed processing line {}. The line is not in the expected format. ", line);
+            std::string prefix(it, it + CONFIG_PREFIX_LENGTH);
+            std::string hex_value(it + CONFIG_PREFIX_LENGTH, it + CONFIG_HEX_VALUE_LAST_CHAR_OFFSET);
+
+            // page 
+            if ("btp" == prefix) {
+                auto page_expected = StringUtils::to_uint8(hex_value, 16);
+                CHECK_EXPECTED(page_expected);
+                page = page_expected.value();            
+            }
+
+            // address
+            else if ("bta" == prefix) {
+                auto address_expected = StringUtils::to_uint32(hex_value, 16);
+                CHECK_EXPECTED(address_expected);
+                address = address_expected.value();           
+            }
+
+            else if ("btb" == prefix) {
+                auto config_entry = create_config_entry(page, address, 8, hex_value);
+                CHECK_EXPECTED(config_entry);
+
+                control_buffers.emplace_back(config_entry.release());
+                address = address + 1;
+            }
+
+            else if ("bth" == prefix) {
+                auto config_entry = create_config_entry(page, address, 16, hex_value);
+                CHECK_EXPECTED(config_entry);
+
+                control_buffers.emplace_back(config_entry.release());
+                address = address + 2;
+            } 
+
+            else if ("btw" == prefix) {
+                auto config_entry = create_config_entry(page, address, 32, hex_value);
+                CHECK_EXPECTED(config_entry);
+
+                control_buffers.emplace_back(config_entry.release());
+                address = address + 4;
+            }
+            
+            else {
+                LOGGER__ERROR("Invalid configuration prefix: {}", prefix);
+                return make_unexpected(HAILO_NOT_FOUND);
+            }
+        }
+        CHECK_AS_EXPECTED(!config_file.bad(), HAILO_FILE_OPERATION_FAILURE, "Failed reading line in sensor ISP config file with errno: {}", errno);
+    }
+    
+    return control_buffers;
+}
+
+hailo_status SensorConfigUtils::dump_config_to_csv(SENSOR_CONFIG__operation_cfg_t *operation_cfg, const std::string &config_file_path, uint32_t entries_count)
+{
+    std::ofstream config_file;
+    config_file.open(config_file_path, std::ios::out);
+    CHECK(config_file.is_open(), HAILO_OPEN_FILE_FAILURE, "Failed opening sensor config file with errno: {}", errno);
+
+    for (size_t i = 0; i < entries_count; i++) {
+        SENSOR_CONFIG__operation_cfg_t *config_entry = &operation_cfg[i];
+
+        int page = (config_entry->page == 0xff) ? -1 : config_entry->page;
+        int hex_width_filler = (config_entry->length == 8) ? 2 : 4;
+        auto opcode_string = convert_opcode_to_string(config_entry->operation);
+        CHECK_EXPECTED_AS_STATUS(opcode_string);   
+
+        // There is no need to restore flags since they only affect the fstream "config_file" and doens't affect std::cout or other files.
+        config_file << std::dec << opcode_string.value() << "," << static_cast<uint32_t>(config_entry->length) << "," << page <<
+            ",0x" << std::uppercase << std::hex << std::setfill('0') << std::setw(4) << config_entry->address <<
+            ",0x" << std::setfill('0') << std::setw(hex_width_filler) << config_entry->bitmask <<
+            ",0x" << std::setfill('0') << std::setw(hex_width_filler) << config_entry->value << std::endl;
+    }
+
+    return HAILO_SUCCESS;
+}
+
+} /* namespace hailort */
diff --git a/hailort/libhailort/src/sensor_config_utils.hpp b/hailort/libhailort/src/sensor_config_utils.hpp
new file mode 100644 (file)
index 0000000..3894cf5
--- /dev/null
@@ -0,0 +1,43 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file sensor_config_utils.hpp
+ * @brief Utilities for sensor_config operations
+ **/
+
+#ifndef _HAILO_SENSOR_CONFIG_UTILS_HPP_
+#define _HAILO_SENSOR_CONFIG_UTILS_HPP_
+
+#include "hailo/hailort.h"
+#include "hailo/expected.hpp"
+#include "control_protocol.h"
+
+#include <vector>
+#include <string>
+
+namespace hailort
+{
+
+#define MAX_CONFIG_INFO_ENTRIES (CONTROL_PROTOCOL__MAX_REQUEST_PAYLOAD_SIZE / sizeof(SENSOR_CONFIG__operation_cfg_t))
+#define MAX_CONFIG_ENTRIES_DATA_SIZE (MAX_CONFIG_INFO_ENTRIES * sizeof(SENSOR_CONFIG__operation_cfg_t))
+#define MAX_NON_ISP_SECTIONS (6)
+#define CONFIG_PREFIX_LENGTH (3)
+#define CONFIG_HEX_VALUE_LAST_CHAR_OFFSET (9)
+
+static_assert((MAX_CONFIG_INFO_ENTRIES > 0) ,"MAX_CONFIG_INFO_ENTRIES must be larger than 0");
+
+class SensorConfigUtils {
+public:
+    static Expected<SENSOR_CONFIG_OPCODES_t> get_sensor_opcode_by_name(const std::string &name);
+    static Expected<std::vector<SENSOR_CONFIG__operation_cfg_t>> read_config_file(const std::string &config_file_path);
+    static Expected<SENSOR_CONFIG__operation_cfg_t> create_config_entry(uint8_t page, uint32_t address, uint8_t length, const std::string &hex_value);
+    static Expected<std::vector<SENSOR_CONFIG__operation_cfg_t>> read_isp_config_file(const std::string &isp_static_config_file_path, const std::string &isp_runtime_config_file_path);
+    static Expected<std::string> convert_opcode_to_string(uint8_t opcode);
+    static hailo_status dump_config_to_csv(SENSOR_CONFIG__operation_cfg_t *operation_cfg, const std::string &config_file_path, uint32_t entries_count);
+};
+
+} /* namespace hailort */
+
+#endif /* _HAILO_SENSOR_CONFIG_UTILS_HPP_ */
diff --git a/hailort/libhailort/src/stream.cpp b/hailort/libhailort/src/stream.cpp
new file mode 100644 (file)
index 0000000..07ee85b
--- /dev/null
@@ -0,0 +1,128 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file stream.cpp
+ * @brief Implementation of stream abstraction
+ **/
+
+#include "hailo/stream.hpp"
+#include "hailo/hailort.h"
+#include "hailo/hailort_common.hpp"
+#include "hailo/transform.hpp"
+#include "common/utils.hpp"
+#include "hef_internal.hpp"
+
+#include <sstream>
+
+namespace hailort
+{
+
+hailo_status InputStream::flush()
+{
+    return HAILO_SUCCESS;
+}
+
+hailo_status InputStream::write(const MemoryView &buffer)
+{
+    CHECK((buffer.size() % m_stream_info.hw_frame_size) == 0, HAILO_INVALID_ARGUMENT,
+        "write size {} must be a multiple of hw size {}", buffer.size(), m_stream_info.hw_frame_size);
+
+    CHECK(((buffer.size() % HailoRTCommon::HW_DATA_ALIGNMENT) == 0), HAILO_INVALID_ARGUMENT,
+        "Input must be aligned to {} (got {})", HailoRTCommon::HW_DATA_ALIGNMENT, buffer.size());
+    
+    return sync_write_all_raw_buffer_no_transform_impl(const_cast<uint8_t*>(buffer.data()), 0, buffer.size());
+}
+
+std::string InputStream::to_string() const
+{
+    std::stringstream string_stream;
+    string_stream << "InputStream(index=" << static_cast<uint32_t>(m_stream_info.index)
+                    << ", name=" << m_stream_info.name << ")";
+    return string_stream.str();
+}
+
+OutputStream::OutputStream(OutputStream &&other) : m_stream_info(std::move(other.m_stream_info)),
+    m_dataflow_manager_id(std::move(other.m_dataflow_manager_id)),
+    m_invalid_frames_count(static_cast<uint32_t>(other.m_invalid_frames_count))
+{}
+
+hailo_status OutputStream::read_nms(void *buffer, size_t offset, size_t size)
+{
+    uint32_t num_of_classes = m_stream_info.nms_info.number_of_classes;
+    uint32_t max_bboxes_per_class = m_stream_info.nms_info.max_bboxes_per_class;
+    uint32_t chunks_per_frame = m_stream_info.nms_info.chunks_per_frame;
+    size_t bbox_size = m_stream_info.nms_info.bbox_size;
+    size_t transfer_size = bbox_size;
+
+    CHECK(size == m_stream_info.hw_frame_size, HAILO_INSUFFICIENT_BUFFER,
+        "On nms stream buffer size should be {} (given size {})", m_stream_info.hw_frame_size, size);
+
+    for (uint32_t chunk_index = 0; chunk_index < chunks_per_frame; chunk_index++) {
+        for (uint32_t class_index = 0; class_index < num_of_classes; class_index++) {
+            nms_bbox_counter_t class_bboxes_count = 0;
+            nms_bbox_counter_t* class_bboxes_count_ptr = (nms_bbox_counter_t*)(reinterpret_cast<uint8_t*>(buffer) + offset);
+            offset += sizeof(*class_bboxes_count_ptr);
+
+            // Read bboxes until reaching delimiter
+            for (;;) {
+                MemoryView buffer_view(static_cast<uint8_t*>(buffer) + offset, transfer_size);
+                auto expected_bytes_read = sync_read_raw_buffer(buffer_view);
+                if ((HAILO_STREAM_INTERNAL_ABORT == expected_bytes_read.status()) ||
+                    ((HAILO_STREAM_NOT_ACTIVATED == expected_bytes_read.status()))) {
+                    return expected_bytes_read.status();
+                }
+                CHECK_EXPECTED_AS_STATUS(expected_bytes_read, "Failed reading nms bbox");
+                transfer_size = expected_bytes_read.release();
+                CHECK(transfer_size == bbox_size, HAILO_INTERNAL_FAILURE,
+                    "Data read from the device was size {}, should be bbox size {}", transfer_size, bbox_size);
+
+                if (HailoRTCommon::NMS_DELIMITER == *(uint64_t*)((uint8_t*)buffer + offset)) {
+                    break;
+                }
+
+                class_bboxes_count++;
+                CHECK(class_bboxes_count <= max_bboxes_per_class, HAILO_INTERNAL_FAILURE,
+                    "Data read from the device for the current class was size {}, max size is {}", class_bboxes_count, max_bboxes_per_class);
+                offset += bbox_size;
+            }
+
+            *class_bboxes_count_ptr = class_bboxes_count;
+        }
+    }
+    return HAILO_SUCCESS;
+}
+
+hailo_status OutputStream::read(MemoryView buffer)
+{
+    CHECK((buffer.size() % m_stream_info.hw_frame_size) == 0, HAILO_INVALID_ARGUMENT,
+        "When read size {} must be a multiple of hw size {}", buffer.size(), m_stream_info.hw_frame_size);
+
+    if (m_stream_info.format.order == HAILO_FORMAT_ORDER_HAILO_NMS){
+        return read_nms(buffer.data(), 0, buffer.size());
+    } else {
+        return this->read_all(buffer);
+    }
+}
+
+std::string OutputStream::to_string() const
+{
+    std::stringstream string_stream;
+    string_stream << "OutputStream(index=" << static_cast<uint32_t>(m_stream_info.index)
+                    << ", name=" << m_stream_info.name << ")";
+    return string_stream.str();
+}
+
+uint32_t OutputStream::get_invalid_frames_count() const
+{
+    return m_invalid_frames_count.load();
+}
+
+void OutputStream::increase_invalid_frames_count(uint32_t value)
+{
+    m_invalid_frames_count = m_invalid_frames_count + value;
+}
+
+
+} /* namespace hailort */
diff --git a/hailort/libhailort/src/stream_internal.cpp b/hailort/libhailort/src/stream_internal.cpp
new file mode 100644 (file)
index 0000000..0b80c01
--- /dev/null
@@ -0,0 +1,30 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file stream_internal.cpp
+ * @brief Implementation of InputStreamBase and OutputStreamBase
+ **/
+
+#include "stream_internal.hpp"
+#include "hailo/hailort.h"
+#include "hailo/expected.hpp"
+#include "common/logger_macros.hpp"
+#include "hailo/transform.hpp"
+#include "common/utils.hpp"
+
+namespace hailort
+{
+
+EventPtr &InputStreamBase::get_network_group_activated_event()
+{
+    return m_network_group_activated_event;
+}
+
+EventPtr &OutputStreamBase::get_network_group_activated_event()
+{
+    return m_network_group_activated_event;
+}
+
+} /* namespace hailort */
diff --git a/hailort/libhailort/src/stream_internal.hpp b/hailort/libhailort/src/stream_internal.hpp
new file mode 100644 (file)
index 0000000..4ba4467
--- /dev/null
@@ -0,0 +1,136 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file stream_internal.hpp
+ * @brief Class declaration for InputStreamBase/OutputStreamBase that implement the basic InputStream/OutputStream
+ *        "interface" (not technically an interface, but good enough). All internal input/output streams
+ *        should inherit from the InputStreamBase/OutputStreamBase classes.
+ *        Hence, the hierarchy is as follows:
+ * 
+ * InputStream                      (External "interface")
+ * └── InputStreamBase              (Base class)
+ *     ├── VdmaInputStream          (Base class for vdma streams)
+ *     │   ├── PcieInputStream
+ *     │   └── CoreInputStream
+ *     ├── EthernetInputStream
+ *     └── MipiInputStream
+ * 
+ *
+ * OutputStream                      (External "interface")
+ * └── OutputStreamBase              (Base class)
+ *     ├── VdmaOutputStream          (Base class for vdma streams)
+ *     │   ├── PcieOutputStream
+ *     │   └── CoreOutputStream
+ *     └── EthernetOutputStream
+ * 
+ **/
+
+#ifndef _STREAM_INTERNAL_HPP_
+#define _STREAM_INTERNAL_HPP_
+
+#include "hailo/stream.hpp"
+#include "hailo/event.hpp"
+#include "hailo/hailort_common.hpp"
+#include "hef_internal.hpp"
+#include "control_protocol.hpp"
+#include "layer_info.hpp"
+
+namespace hailort
+{
+
+typedef struct hailo_mux_info_t{
+    hailo_stream_info_t info;
+    uint32_t row_size;
+    uint32_t row_counter;
+    uint32_t rows_gcd;
+    uint32_t offset;
+    uint32_t current_offset; // Current demuxing offset
+    uint32_t successors_count;
+    struct hailo_mux_info_t *successors[HailoRTCommon::MAX_MUX_PREDECESSORS];
+    void* buffer;
+} hailo_mux_info_t;
+
+class InputStreamBase : public InputStream
+{
+public:
+    virtual ~InputStreamBase() = default;
+
+    InputStreamBase(const InputStreamBase&) = delete;
+    InputStreamBase& operator=(const InputStreamBase&) = delete;
+    InputStreamBase(InputStreamBase&&) = default;
+
+    CONTROL_PROTOCOL__nn_stream_config_t m_nn_stream_config;
+
+protected:
+    explicit InputStreamBase(const LayerInfo &layer_info, hailo_stream_interface_t stream_interface,
+        EventPtr &&network_group_activated_event, hailo_status &status) :
+        m_network_group_activated_event(std::move(network_group_activated_event))
+    {
+        m_stream_info = LayerInfoUtils::get_stream_info_from_layer_info(layer_info);
+
+        const bool hw_padding_supported = HefConfigurator::is_hw_padding_supported(layer_info);
+        auto nn_stream_config = HefConfigurator::parse_nn_stream_config(layer_info,
+            hw_padding_supported && (HAILO_STREAM_INTERFACE_MIPI != stream_interface)); // On MIPI networks, we don't want to use hw padding nn stream config.
+        if(!nn_stream_config) {
+            LOGGER__ERROR("Failed parse nn stream config");
+            status = nn_stream_config.status();
+            return;
+        }
+        m_nn_stream_config = nn_stream_config.release();
+        status = HAILO_SUCCESS;
+    }
+
+    virtual EventPtr &get_network_group_activated_event() override;
+
+private:
+    EventPtr m_network_group_activated_event;
+};
+
+
+class OutputStreamBase : public OutputStream
+{
+public:
+    virtual ~OutputStreamBase() = default;
+
+    OutputStreamBase(const OutputStreamBase&) = delete;
+    OutputStreamBase& operator=(const OutputStreamBase&) = delete;
+    OutputStreamBase(OutputStreamBase&&) = default;
+
+    virtual const LayerInfo& get_layer_info() override
+    {
+        return m_layer_info;
+    };
+
+    CONTROL_PROTOCOL__nn_stream_config_t m_nn_stream_config;
+
+protected:
+    explicit OutputStreamBase(const LayerInfo &layer_info,
+        EventPtr &&network_group_activated_event, hailo_status &status) :
+        m_layer_info(layer_info), m_network_group_activated_event(std::move(network_group_activated_event))
+    {
+        m_stream_info = LayerInfoUtils::get_stream_info_from_layer_info(m_layer_info);
+
+        const bool hw_padding_supported = HefConfigurator::is_hw_padding_supported(m_layer_info);
+        auto nn_stream_config = HefConfigurator::parse_nn_stream_config(m_layer_info, hw_padding_supported);
+        if(!nn_stream_config) {
+            LOGGER__ERROR("Failed parse nn stream config");
+            status = nn_stream_config.status();
+            return;
+        }
+        m_nn_stream_config = nn_stream_config.release();
+        status = HAILO_SUCCESS;
+    }
+
+    virtual EventPtr &get_network_group_activated_event() override;
+
+    LayerInfo m_layer_info;
+
+private:
+    EventPtr m_network_group_activated_event;
+};
+
+} /* namespace hailort */
+
+#endif /* _STREAM_INTERNAL_HPP_ */
diff --git a/hailort/libhailort/src/thread_safe_map.hpp b/hailort/libhailort/src/thread_safe_map.hpp
new file mode 100644 (file)
index 0000000..c2c341c
--- /dev/null
@@ -0,0 +1,78 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file thread_safe_map.hpp
+ * @brief Thread safe map
+ **/
+
+#ifndef HAILO_THREAD_SAFE_MAP_HPP_
+#define HAILO_THREAD_SAFE_MAP_HPP_
+
+#include <map>
+#include <mutex>
+
+namespace hailort
+{
+
+template<class K, class V>
+class SafeMap {
+public:
+    SafeMap() : m_map(), m_mutex() {}
+    virtual ~SafeMap() = default;
+    SafeMap(SafeMap &&map) : m_map(std::move(map.m_map)), m_mutex() {};
+
+    V& operator[](const K& k) {
+        std::lock_guard<std::mutex> lock(m_mutex);
+        return m_map[k];
+    }
+
+    V& operator[](K&& k) {
+        std::lock_guard<std::mutex> lock(m_mutex);
+        return m_map[k];
+    }
+
+    V& at(K& k) {
+        std::unique_lock<std::mutex> lock(m_mutex);
+        return m_map.at(k);
+    }
+
+    V& at(const K& k) {
+        std::unique_lock<std::mutex> lock(m_mutex);
+        return m_map.at(k);
+    }
+
+    typename std::map<K, V>::iterator find(K& k) {
+        std::unique_lock<std::mutex> lock(m_mutex);
+        return m_map.find(k);
+    }
+
+    typename std::map<K, V>::iterator find(const K& k) {
+        std::unique_lock<std::mutex> lock(m_mutex);
+        return m_map.find(k);
+    }
+
+    void clear() {
+        std::unique_lock<std::mutex> lock(m_mutex);
+        m_map.clear();
+    }
+
+    typename std::map<K, V>::iterator begin() {
+        std::unique_lock<std::mutex> lock(m_mutex);
+        return m_map.begin();
+    }
+
+    typename std::map<K, V>::iterator end() {
+        std::unique_lock<std::mutex> lock(m_mutex);
+        return m_map.end();
+    }
+
+protected:
+    std::map<K, V> m_map;
+    mutable std::mutex m_mutex;
+};
+
+} /* namespace hailort */
+
+#endif // HAILO_THREAD_SAFE_MAP_HPP_
diff --git a/hailort/libhailort/src/thread_safe_queue.hpp b/hailort/libhailort/src/thread_safe_queue.hpp
new file mode 100644 (file)
index 0000000..1c45b56
--- /dev/null
@@ -0,0 +1,328 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file thread_safe_queue.hpp
+ * @brief Thread safe queue taken from https://stackoverflow.com/a/16075550
+ **/
+
+#ifndef HAILO_THREAD_SAFE_QUEUE_HPP_
+#define HAILO_THREAD_SAFE_QUEUE_HPP_
+
+#include "hailo/expected.hpp"
+#include "common/utils.hpp"
+#include "hailo/event.hpp"
+#include "common/logger_macros.hpp"
+#include "event_internal.hpp"
+
+// Define __unix__ for inclusion of readerwriterqueue.h because readerwriterqueue is implemented over POSIX standards 
+// but checks __unix__ - otherwise QNX returns unsupported platform (need HAILO_UNDEF_UNIX_FLAG in order to undefine
+// __unix__ only in case of defining it here)
+#if defined(__QNX__) && !defined(__unix__)
+#define __unix__
+#define HAILO_UNDEF_UNIX_FLAG 
+#endif
+
+#include "readerwriterqueue.h"
+
+#if defined(HAILO_UNDEF_UNIX_FLAG)
+#undef __unix__
+#undef HAILO_UNDEF_UNIX_FLAG
+#endif
+
+#include <queue>
+#include <mutex>
+#include <memory>
+#include <condition_variable>
+#include <chrono>
+
+namespace hailort
+{
+
+#define DEFAULT_TIMEOUT_MS (1000)
+
+// A threadsafe-queue. - https://stackoverflow.com/a/16075550
+template <class T>
+class SafeQueue {
+public:
+    SafeQueue() : m_queue(), m_mutex(), m_queue_not_empty(), m_timeout(DEFAULT_TIMEOUT_MS) {}
+    virtual ~SafeQueue() = default;
+
+    // Add an element to the queue.
+    virtual void push(T t) {
+        std::lock_guard<std::mutex> lock(m_mutex);
+        m_queue.push(t);
+        m_queue_not_empty.notify_one();
+    }
+
+    // Get the "front"-element.
+    // If the queue is empty, wait till a element is available.
+    virtual T pop() {
+        std::unique_lock<std::mutex> lock(m_mutex);
+        while (m_queue.empty()) {
+            // release lock as long as the wait and require it afterwards.
+            m_queue_not_empty.wait_for(lock, m_timeout);
+        }
+        T val = m_queue.front();
+        m_queue.pop();
+        return val;
+    }
+
+protected:
+    std::queue<T> m_queue;
+    mutable std::mutex m_mutex;
+    std::condition_variable m_queue_not_empty;
+    const std::chrono::milliseconds m_timeout;
+};
+
+ template <class T>
+ class SafeQueueMaxSize : public SafeQueue<T> {
+ public:
+    SafeQueueMaxSize(uint32_t max_size) :
+        SafeQueue<T>::SafeQueue(),
+        m_max_size(max_size),
+        m_queue_not_full()
+    {}
+    virtual ~SafeQueueMaxSize() = default;
+
+    virtual void push(T t) override {
+        std::unique_lock<std::mutex> lock(this->m_mutex);
+        m_queue_not_full.wait(lock, [&]{return this->m_queue.size() < m_max_size;});
+
+        this->m_queue.push(t);
+        this->m_queue_not_empty.notify_one();
+    }
+
+    virtual T pop() override {
+        std::unique_lock<std::mutex> lock(this->m_mutex);
+        this->m_queue_not_empty.wait(lock, [&]{return !this->m_queue.empty();});
+        
+        T val = this->m_queue.front();
+        this->m_queue.pop();
+        
+        if (this->m_queue.size() < m_max_size) {
+            m_queue_not_full.notify_one();
+        }
+        return val;
+    }
+protected:
+    const uint32_t m_max_size;
+    std::condition_variable m_queue_not_full;
+};
+
+// Single-Producer Single-Consumer Queue
+// The queue's size is limited
+template<typename T, size_t MAX_BLOCK_SIZE = 512>
+class SpscQueue
+{
+private:
+    typedef moodycamel::ReaderWriterQueue<T, MAX_BLOCK_SIZE> ReaderWriterQueue;
+
+public:
+    static constexpr auto INIFINITE_TIMEOUT() { return std::chrono::milliseconds(HAILO_INFINITE); }
+
+    SpscQueue(size_t max_size, SemaphorePtr items_enqueued_sema, SemaphorePtr items_dequeued_sema,
+              EventPtr shutdown_event, std::chrono::milliseconds default_timeout) :
+        m_inner(max_size),
+        m_items_enqueued_sema_or_shutdown(items_enqueued_sema, shutdown_event),
+        m_items_enqueued_sema(items_enqueued_sema),
+        m_items_dequeued_sema_or_shutdown(items_dequeued_sema, shutdown_event),
+        m_items_dequeued_sema(items_dequeued_sema),
+        m_default_timeout(default_timeout)
+    {}
+
+    virtual ~SpscQueue() = default;
+    SpscQueue(SpscQueue&&) = default;
+
+    static Expected<SpscQueue> create(size_t max_size, const EventPtr& shutdown_event,
+        std::chrono::milliseconds default_timeout = std::chrono::milliseconds(1000))
+    {
+        if (0 == max_size) {
+            LOGGER__ERROR("Invalid queue max_size (must be greater than zero)");
+            return make_unexpected(HAILO_INVALID_ARGUMENT);
+        }
+
+        // * items_enqueued_sema:
+        //   +1 for each enqueued item
+        //   -1 for each dequeued item
+        //   Blocks when there are no items in the queue (hence when the queue is built it starts at zero)
+        // * items_dequeued_sema:
+        //   +1 for each dequeued item
+        //   -1 for each enqueued item
+        //   Blocks when the queue is full (which happens when it's value reaches zero, hence it starts at queue size)
+        const auto items_enqueued_sema = Semaphore::create_shared(0);
+        if (nullptr == items_enqueued_sema) {
+            LOGGER__ERROR("Failed creating items_enqueued_sema semaphore");
+            return make_unexpected(HAILO_INTERNAL_FAILURE);
+        }
+        const auto items_dequeued_sema = Semaphore::create_shared(static_cast<uint32_t>(max_size));
+        if (nullptr == items_dequeued_sema) {
+            LOGGER__ERROR("Failed creating items_dequeued_sema semaphore");
+            return make_unexpected(HAILO_INTERNAL_FAILURE);
+        }
+
+        return SpscQueue(max_size, items_enqueued_sema, items_dequeued_sema, shutdown_event, default_timeout);
+    }
+
+    static std::shared_ptr<SpscQueue> create_shared(size_t max_size, const EventPtr& shutdown_event,
+        std::chrono::milliseconds default_timeout = std::chrono::milliseconds(1000))
+    {
+        auto queue = create(max_size, shutdown_event, default_timeout);
+        if (!queue) {
+            LOGGER__ERROR("Failed creating queue. status={}", queue.status());
+            return nullptr;
+        }
+
+        return make_shared_nothrow<SpscQueue>(queue.release());
+    }
+
+    static std::unique_ptr<SpscQueue> create_unique(size_t max_size, const EventPtr& shutdown_event,
+        std::chrono::milliseconds default_timeout = std::chrono::milliseconds(1000))
+    {
+        auto queue = create(max_size, shutdown_event, default_timeout);
+        if (!queue) {
+            LOGGER__ERROR("Failed creating queue. status={}", queue.status());
+            return nullptr;
+        }
+
+        return make_unique_nothrow<SpscQueue>(queue.release());
+    }
+    
+    Expected<T> dequeue(std::chrono::milliseconds timeout, bool ignore_shutdown_event = false) AE_NO_TSAN
+    {
+        hailo_status wait_result = HAILO_UNINITIALIZED;
+        if (ignore_shutdown_event) {
+            wait_result = m_items_enqueued_sema->wait(timeout);
+        } else {
+            wait_result = m_items_enqueued_sema_or_shutdown.wait(timeout);
+        }
+
+        if (HAILO_SHUTDOWN_EVENT_SIGNALED == wait_result) {
+            LOGGER__TRACE("Shutdown event has been signaled");
+            return make_unexpected(wait_result);
+        }
+        if (HAILO_TIMEOUT == wait_result) {
+            LOGGER__TRACE("Timeout, the queue is empty");
+            return make_unexpected(wait_result);
+        }
+        if (HAILO_SUCCESS != wait_result) {
+            LOGGER__WARNING("m_items_enqueued_sema received an unexpected failure");
+            return make_unexpected(wait_result);
+        }
+        
+        // The queue isn't empty
+        T result{};
+        const bool success = m_inner.try_dequeue(result);
+        assert(success);
+        AE_UNUSED(success);
+
+        const auto signal_result = m_items_dequeued_sema_or_shutdown.signal();
+        if (HAILO_SUCCESS != signal_result) {
+            return make_unexpected(signal_result);
+        }
+        return result;
+    }
+
+    Expected<T> dequeue() AE_NO_TSAN
+    {
+        return dequeue(m_default_timeout);
+    }
+
+    hailo_status enqueue(const T& result, std::chrono::milliseconds timeout) AE_NO_TSAN
+    {
+        const auto wait_result = m_items_dequeued_sema_or_shutdown.wait(timeout);
+        if (HAILO_SHUTDOWN_EVENT_SIGNALED == wait_result) {
+            LOGGER__TRACE("Shutdown event has been signaled");
+            return wait_result;
+        }
+        if (HAILO_TIMEOUT == wait_result) {
+            LOGGER__TRACE("Timeout, the queue is full");
+            return wait_result;
+        }
+        if (HAILO_SUCCESS != wait_result) {
+            LOGGER__WARNING("m_items_dequeued_sema received an unexpected failure");
+            return wait_result;
+        }
+
+        // The queue isn't full
+        const bool success = m_inner.try_enqueue(result);
+        assert(success);
+        AE_UNUSED(success);
+
+        return m_items_enqueued_sema_or_shutdown.signal();
+    }
+
+    inline hailo_status enqueue(const T& result) AE_NO_TSAN
+    {
+        return enqueue(result, m_default_timeout);
+    }
+
+    // TODO: Do away with two copies of this function? (SDK-16481)
+    hailo_status enqueue(T&& result, std::chrono::milliseconds timeout, bool ignore_shutdown_event = false) AE_NO_TSAN
+    {
+        hailo_status wait_result = HAILO_UNINITIALIZED;
+        if (ignore_shutdown_event) {
+            wait_result = m_items_dequeued_sema->wait(timeout);
+        } else {
+            wait_result = m_items_dequeued_sema_or_shutdown.wait(timeout);
+        }
+
+        if (HAILO_SHUTDOWN_EVENT_SIGNALED == wait_result) {
+            LOGGER__TRACE("Shutdown event has been signaled");
+            return wait_result;
+        }
+        if (HAILO_TIMEOUT == wait_result) {
+            LOGGER__TRACE("Timeout, the queue is full");
+            return wait_result;
+        }
+        if (HAILO_SUCCESS != wait_result) {
+            LOGGER__WARNING("m_items_dequeued_sema received an unexpected failure");
+            return wait_result;
+        }
+
+        // The queue isn't full
+        const bool success = m_inner.try_enqueue(std::move(result));
+        assert(success);
+        AE_UNUSED(success);
+
+        return m_items_enqueued_sema_or_shutdown.signal();
+    }
+
+    // TODO: HRT-3810, remove hacky argument ignore_shutdown_event
+    inline hailo_status enqueue(T&& result, bool ignore_shutdown_event = false) AE_NO_TSAN
+    {
+        return enqueue(std::move(result), m_default_timeout, ignore_shutdown_event);
+    }
+
+    size_t size_approx()
+    {
+        return m_inner.size_approx();
+    }
+
+    hailo_status clear() AE_NO_TSAN
+    {
+        auto status = HAILO_SUCCESS;
+        while (HAILO_SUCCESS == status) {
+            auto output = dequeue(std::chrono::milliseconds(0), true);
+            status = output.status();
+        }
+
+        if (HAILO_TIMEOUT == status) {
+            return HAILO_SUCCESS;
+        }
+        return status;
+    }
+
+private:
+    ReaderWriterQueue m_inner;
+    WaitOrShutdown m_items_enqueued_sema_or_shutdown;
+    SemaphorePtr m_items_enqueued_sema;
+    WaitOrShutdown m_items_dequeued_sema_or_shutdown;
+    SemaphorePtr m_items_dequeued_sema;
+    std::chrono::milliseconds m_default_timeout;
+};
+
+} /* namespace hailort */
+
+#endif // HAILO_THREAD_SAFE_QUEUE_HPP_
diff --git a/hailort/libhailort/src/token_bucket.hpp b/hailort/libhailort/src/token_bucket.hpp
new file mode 100644 (file)
index 0000000..59b2d37
--- /dev/null
@@ -0,0 +1,309 @@
+// Note:
+// * This module is taken from Facebook's open source Folly library: https://github.com/facebook/folly (v2020.08.17.00)
+// * Changes:
+//   * Changes made to the module are delimited with "BEGIN/END HAILO CHANGES"
+//   * The file has been renamed from "TokenBucket.h" to "token_bucket.hpp"
+//   * Removed:
+//     * folly namespace
+//     * BasicTokenBucket
+//     * From BasicDynamicTokenBucket:
+//       * Copy ctor and assignment operator
+//       * available()
+//       * reset()
+//   * Original file: https://github.com/facebook/folly/blob/v2020.08.17.00/folly/TokenBucket.h
+// * Copyright notices follow.
+
+/*
+ * Copyright (c) Facebook, Inc. and its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef TOKEN_BUCKET_HPP_
+#define TOKEN_BUCKET_HPP_
+
+#include <algorithm>
+#include <atomic>
+#include <chrono>
+#include <thread>
+
+// BEGIN HAILO CHANGES
+#include <hailo/hailort.h>
+#include "hailo/expected.hpp"
+#include "os/microsec_timer.hpp"
+
+namespace hailort
+{
+// END HAILO CHANGES
+
+/**
+ * Thread-safe (atomic) token bucket implementation.
+ *
+ * A token bucket (http://en.wikipedia.org/wiki/Token_bucket) models a stream
+ * of events with an average rate and some amount of burstiness. The canonical
+ * example is a packet switched network: the network can accept some number of
+ * bytes per second and the bytes come in finite packets (bursts). A token
+ * bucket stores up to a fixed number of tokens (the burst size). Some number
+ * of tokens are removed when an event occurs. The tokens are replenished at a
+ * fixed rate. Failure to allocate tokens implies resource is unavailable and
+ * caller needs to implement its own retry mechanism. For simple cases where
+ * caller is okay with a FIFO starvation-free scheduling behavior, there are
+ * also APIs to 'borrow' from the future effectively assigning a start time to
+ * the caller when it should proceed with using the resource. It is also
+ * possible to 'return' previously allocated tokens to make them available to
+ * other users. Returns in excess of burstSize are considered expired and
+ * will not be available to later callers.
+ *
+ * This implementation records the last time it was updated. This allows the
+ * token bucket to add tokens "just in time" when tokens are requested.
+ *
+ * The "dynamic" base variant allows the token generation rate and maximum
+ * burst size to change with every token consumption.
+ *
+ * @tparam Clock Clock type, must be steady i.e. monotonic.
+ */
+template <typename Clock = std::chrono::steady_clock>
+class BasicDynamicTokenBucket {
+  static_assert(Clock::is_steady, "clock must be steady");
+
+ public:
+  /**
+   * Constructor.
+   *
+   * @param zeroTime Initial time at which to consider the token bucket
+   *                 starting to fill. Defaults to 0, so by default token
+   *                 buckets are "full" after construction.
+   */
+  explicit BasicDynamicTokenBucket(double zeroTime = 0) noexcept
+      : zeroTime_(zeroTime) {}
+
+  BasicDynamicTokenBucket(const BasicDynamicTokenBucket&) = delete;
+  BasicDynamicTokenBucket& operator=(const BasicDynamicTokenBucket&) = delete;
+
+  // BEGIN HAILO CHANGES
+  BasicDynamicTokenBucket(BasicDynamicTokenBucket&& other) :
+      zeroTime_(other.zeroTime_.load())
+  {}
+  // END HAILO CHANGES
+
+
+  /**
+   * Returns the current time in seconds since Epoch.
+   */
+  static double defaultClockNow() noexcept {
+    auto const now = Clock::now().time_since_epoch();
+    return std::chrono::duration<double>(now).count();
+  }
+
+  /**
+   * Attempts to consume some number of tokens. Tokens are first added to the
+   * bucket based on the time elapsed since the last attempt to consume tokens.
+   * Note: Attempts to consume more tokens than the burst size will always
+   * fail.
+   *
+   * Thread-safe.
+   *
+   * @param toConsume The number of tokens to consume.
+   * @param rate Number of tokens to generate per second.
+   * @param burstSize Maximum burst size. Must be greater than 0.
+   * @param nowInSeconds Current time in seconds. Should be monotonically
+   *                     increasing from the nowInSeconds specified in
+   *                     this token bucket's constructor.
+   * @return True if the rate limit check passed, false otherwise.
+   */
+  bool consume(
+      double toConsume,
+      double rate,
+      double burstSize,
+      double nowInSeconds = defaultClockNow()) {
+    assert(rate > 0);
+    assert(burstSize > 0);
+
+    if (nowInSeconds <= zeroTime_.load()) {
+      return 0;
+    }
+
+    return consumeImpl(
+        rate, burstSize, nowInSeconds, [toConsume](double& tokens) {
+          if (tokens < toConsume) {
+            return false;
+          }
+          tokens -= toConsume;
+          return true;
+        });
+  }
+
+  /**
+   * Similar to consume, but always consumes some number of tokens.  If the
+   * bucket contains enough tokens - consumes toConsume tokens.  Otherwise the
+   * bucket is drained.
+   *
+   * Thread-safe.
+   *
+   * @param toConsume The number of tokens to consume.
+   * @param rate Number of tokens to generate per second.
+   * @param burstSize Maximum burst size. Must be greater than 0.
+   * @param nowInSeconds Current time in seconds. Should be monotonically
+   *                     increasing from the nowInSeconds specified in
+   *                     this token bucket's constructor.
+   * @return number of tokens that were consumed.
+   */
+  double consumeOrDrain(
+      double toConsume,
+      double rate,
+      double burstSize,
+      double nowInSeconds = defaultClockNow()) {
+    assert(rate > 0);
+    assert(burstSize > 0);
+
+    if (nowInSeconds <= zeroTime_.load()) {
+      return 0;
+    }
+
+    double consumed;
+    consumeImpl(
+        rate, burstSize, nowInSeconds, [&consumed, toConsume](double& tokens) {
+          if (tokens < toConsume) {
+            consumed = tokens;
+            tokens = 0.0;
+          } else {
+            consumed = toConsume;
+            tokens -= toConsume;
+          }
+          return true;
+        });
+    return consumed;
+  }
+
+  /**
+   * Return extra tokens back to the bucket. This will move the zeroTime_
+   * value back based on the rate.
+   *
+   * Thread-safe.
+   */
+  void returnTokens(double tokensToReturn, double rate) {
+    assert(rate > 0);
+    assert(tokensToReturn > 0);
+
+    returnTokensImpl(tokensToReturn, rate);
+  }
+
+  // BEGIN HAILO CHANGES
+  /**
+   * Like consumeOrDrain but the call will always satisfy the asked for count.
+   * It does so by borrowing tokens from the future (zeroTime_ will move
+   * forward) if the currently available count isn't sufficient.
+   *
+   * Returns a Expected<double>. The Expected wont be set if the request
+   * cannot be satisfied: only case is when it is larger than burstSize. The
+   * value of the Expected is a double indicating the time in seconds that the
+   * caller needs to wait at which the reservation becomes valid. The caller
+   * could simply sleep for the returned duration to smooth out the allocation
+   * to match the rate limiter or do some other computation in the meantime. In
+   * any case, any regular consume or consumeOrDrain calls will fail to allocate
+   * any tokens until the future time is reached.
+   *
+   * Note: It is assumed the caller will not ask for a very large count nor use
+   * it immediately (if not waiting inline) as that would break the burst
+   * prevention the limiter is meant to be used for.
+   *
+   * Thread-safe.
+   */
+  Expected<double> consumeWithBorrowNonBlocking(
+      double toConsume,
+      double rate,
+      double burstSize,
+      double nowInSeconds = defaultClockNow()) {
+    assert(rate > 0);
+    assert(burstSize > 0);
+
+    if (burstSize < toConsume) {
+      return make_unexpected(HAILO_INVALID_ARGUMENT);
+    }
+
+    while (toConsume > 0) {
+      double consumed =
+          consumeOrDrain(toConsume, rate, burstSize, nowInSeconds);
+      if (consumed > 0) {
+        toConsume -= consumed;
+      } else {
+        double zeroTimeNew = returnTokensImpl(-toConsume, rate);
+        double napTime = std::max(0.0, zeroTimeNew - nowInSeconds);
+        return napTime;
+      }
+    }
+    return 0;
+  }
+
+  /**
+   * Convenience wrapper around non-blocking borrow to sleep inline until
+   * reservation is valid.
+   */
+  bool consumeWithBorrowAndWait(
+      double toConsume,
+      double rate,
+      double burstSize,
+      double nowInSeconds = defaultClockNow()) {
+    auto res = consumeWithBorrowNonBlocking(toConsume, rate, burstSize, nowInSeconds);
+    if (!res.has_value()) {
+      return false;
+    }
+    if (res.value() > 0) {
+      MicrosecTimer::sleep(static_cast<uint64_t>(res.value() * 1000000));
+    }
+    return true;
+  }
+  // END HAILO CHANGES
+
+ private:
+  template <typename TCallback>
+  bool consumeImpl(
+      double rate,
+      double burstSize,
+      double nowInSeconds,
+      const TCallback& callback) {
+    auto zeroTimeOld = zeroTime_.load();
+    double zeroTimeNew;
+    do {
+      auto tokens = std::min((nowInSeconds - zeroTimeOld) * rate, burstSize);
+      if (!callback(tokens)) {
+        return false;
+      }
+      zeroTimeNew = nowInSeconds - tokens / rate;
+    } while (!zeroTime_.compare_exchange_weak(zeroTimeOld, zeroTimeNew));
+
+    return true;
+  }
+
+  /**
+   * Adjust zeroTime based on rate and tokenCount and return the new value of
+   * zeroTime_. Note: Token count can be negative to move the zeroTime_ value
+   * into the future.
+   */
+  double returnTokensImpl(double tokenCount, double rate) {
+    auto zeroTimeOld = zeroTime_.load();
+    double zeroTimeNew;
+    do {
+      zeroTimeNew = zeroTimeOld - tokenCount / rate;
+    } while (!zeroTime_.compare_exchange_weak(zeroTimeOld, zeroTimeNew));
+    return zeroTimeNew;
+  }
+
+  std::atomic<double> zeroTime_;
+};
+
+using DynamicTokenBucket = BasicDynamicTokenBucket<>;
+
+} /* namespace hailort */
+
+#endif /* TOKEN_BUCKET_HPP_ */
diff --git a/hailort/libhailort/src/transform.cpp b/hailort/libhailort/src/transform.cpp
new file mode 100644 (file)
index 0000000..65ffe22
--- /dev/null
@@ -0,0 +1,1958 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file transform.cpp
+ * @brief Implements transform module
+ **/
+#include "hailo/transform.hpp"
+#include "hailo/hailort.h"
+#include "hailo/stream.hpp"
+#include "hailo/expected.hpp"
+#include "hailo/hailort_common.hpp"
+#include "hailo/quantization.hpp"
+#include "hailort_defaults.hpp"
+#include "common/compiler_extensions_compat.hpp"
+#include "common/logger_macros.hpp"
+#include "common/utils.hpp"
+#include "transform_internal.hpp"
+
+#include <type_traits>
+#include <sstream>
+
+namespace hailort
+{
+
+#define HW_DATA_ALIGNMENT (8)
+#define RGB_FEATURES (3)
+
+
+bool TransformContextUtils::should_quantize(const hailo_stream_direction_t stream_direction, 
+    const hailo_format_t &src_format, const hailo_format_t &dst_format, const hailo_quant_info_t &quant_info)
+{
+    if (HAILO_H2D_STREAM == stream_direction) {
+        return (!(HAILO_FORMAT_FLAGS_QUANTIZED & src_format.flags) &&
+            (HAILO_FORMAT_FLAGS_QUANTIZED & dst_format.flags) &&
+            !((Quantization::is_identity_qp(quant_info)) && (src_format.type == dst_format.type)));
+    } else {
+        return (HAILO_FORMAT_FLAGS_QUANTIZED & src_format.flags) && 
+            !(HAILO_FORMAT_FLAGS_QUANTIZED & dst_format.flags);
+    }
+}
+
+bool TransformContextUtils::should_transpose(const hailo_format_flags_t &src_flags, const hailo_format_flags_t &dst_flags)
+{
+    return ((HAILO_FORMAT_FLAGS_TRANSPOSED & src_flags) != (HAILO_FORMAT_FLAGS_TRANSPOSED & dst_flags));
+}
+
+bool TransformContextUtils::should_reorder(const hailo_3d_image_shape_t &src_image_shape, const hailo_format_t &src_format,
+    const hailo_3d_image_shape_t &dst_image_shape, const hailo_format_t &dst_format)
+{
+
+    /* If shapes and format are different - need to use transform_context */
+    if  (!((src_image_shape.features    == dst_image_shape.features)     && 
+            (src_image_shape.height         == dst_image_shape.height)   && 
+            (src_image_shape.width          == dst_image_shape.width)    &&
+            (src_format.order               == dst_format.order)         &&
+            (src_format.type                == dst_format.type))) {
+        return true;
+    }
+
+    /* Some orders has to be reordered, even if shapes and types are the same 
+    Note: In order to add new order to the list - add test to test_transform with all shapes and types same 
+    pre and post transform */
+    switch (src_format.order) {
+        case HAILO_FORMAT_ORDER_NHWC:
+        case HAILO_FORMAT_ORDER_NHCW:
+        case HAILO_FORMAT_ORDER_NC:
+        case HAILO_FORMAT_ORDER_NHW:
+        case HAILO_FORMAT_ORDER_FCR:
+        case HAILO_FORMAT_ORDER_BAYER_RGB:
+        case HAILO_FORMAT_ORDER_12_BIT_BAYER_RGB:
+        case HAILO_FORMAT_ORDER_YUY2:
+            return false;
+        case HAILO_FORMAT_ORDER_F8CR:
+        case HAILO_FORMAT_ORDER_HAILO_NMS:
+        case HAILO_FORMAT_ORDER_RGB888:
+        case HAILO_FORMAT_ORDER_NCHW:
+            return true;
+        default:
+            LOGGER__WARN("Hailo Internal warning - Unrecognised order. Transformation optimization would not be activated");
+            /* In case user asks to add new order - please add this order to one of the true or false lists */
+            assert(false);
+            return true;
+    }
+}
+
+bool TransformContextUtils::is_transformation_required(const hailo_stream_direction_t stream_direction,
+    const hailo_3d_image_shape_t &src_image_shape, const hailo_format_t &src_format,
+    const hailo_3d_image_shape_t &dst_image_shape, const hailo_format_t &dst_format, const hailo_quant_info_t &quant_info)
+{
+    /* This function should be called after auto expend function */
+    assert((HAILO_FORMAT_ORDER_AUTO != src_format.order) && (HAILO_FORMAT_ORDER_AUTO != dst_format.order));
+    assert((HAILO_FORMAT_TYPE_AUTO != src_format.type) && (HAILO_FORMAT_TYPE_AUTO != dst_format.type));
+
+    return (should_quantize(stream_direction, src_format, dst_format, quant_info) ||
+        should_transpose(src_format.flags, dst_format.flags) ||
+        should_reorder(src_image_shape, src_format, dst_image_shape, dst_format));
+}
+
+std::string TransformContextUtils::make_quantization_description(hailo_format_type_t src_type,
+    hailo_format_type_t dst_type, hailo_quant_info_t quant_info)
+{
+    std::stringstream quant_description;
+    quant_description << "Quantization - src_type: " << HailoRTCommon::get_format_type_str(src_type) <<
+        ", dst_type " << HailoRTCommon::get_format_type_str(dst_type) <<
+        ", qp_scale: " << quant_info.qp_scale <<
+        ", qp_zp: " << quant_info.qp_zp <<
+        ", limvals_min: " << quant_info.limvals_min <<
+        ", limvals_max: " << quant_info.limvals_max;
+
+    return quant_description.str();
+}
+
+std::string TransformContextUtils::make_reorder_description(hailo_format_order_t src_order, hailo_3d_image_shape_t src_shape,
+    hailo_format_order_t dst_order, hailo_3d_image_shape_t dst_shape)
+{
+    std::stringstream reorder_description;
+    reorder_description << "Reorder - src_order: " << HailoRTCommon::get_format_order_str(src_order) << ", src_shape: (" <<
+        src_shape.height << ", " << src_shape.width << ", " << src_shape.features << ")" <<
+        ", dst_order: " << HailoRTCommon::get_format_order_str(dst_order) << ", dst_shape: (" <<
+        dst_shape.height << ", " << dst_shape.width << ", " << dst_shape.features << ")";
+
+    return reorder_description.str();
+}
+
+std::string TransformContextUtils::make_transpose_description(hailo_3d_image_shape_t src_shape, hailo_3d_image_shape_t transposed_shape)
+{
+    std::stringstream transpose_description;
+    transpose_description << "Transpose - src_shape: (" <<
+        src_shape.height << ", " << src_shape.width << ", " << src_shape.features << ")" <<
+        ", dst_shape: (" << transposed_shape.height << ", " << transposed_shape.width << ", " << transposed_shape.features << ")";
+
+    return transpose_description.str();
+}
+
+void copy_output_buffer_float32(float32_t *dst_ptr, uint32_t frame_size)
+{
+    for (int32_t i = (int32_t)frame_size - 1; i >= 0; i--) {
+        dst_ptr[i] = (float32_t)(*((uint8_t*)dst_ptr + i));
+    }
+}
+
+/* Transpose funcs */
+static hailo_3d_image_shape_t transposed_shape(const hailo_3d_image_shape_t &shape)
+{
+    hailo_3d_image_shape_t transposed_shape = shape;
+    std::swap(transposed_shape.height, transposed_shape.width);
+    return transposed_shape;
+}
+
+static hailo_status transform__transpose_NHWC(const void *src_ptr, const hailo_3d_image_shape_t &shape,
+    size_t feature_bytes_size, void *dst_ptr)
+{
+    // Flatten the features, look at the data as HW matrix
+    const size_t element_size = shape.features * feature_bytes_size;
+    const uint8_t *src_matrix = reinterpret_cast<const uint8_t*>(src_ptr);
+    uint8_t *dst_matrix = reinterpret_cast<uint8_t*>(dst_ptr);
+    for (size_t r = 0; r < shape.height; r++) {
+        for (size_t c = 0; c < shape.width; c++) {
+            // dest[c][r] = src[r][c]
+            size_t src_offset = element_size * ((r * shape.width) + c);
+            const uint8_t *src_pos = src_matrix + src_offset;
+
+            size_t dst_offset = element_size * ((c * shape.height) + r);
+            uint8_t *dst_pos = dst_matrix + dst_offset;
+
+            memcpy(dst_pos, src_pos, element_size);
+        }
+    }
+
+    return HAILO_SUCCESS;
+}
+
+hailo_status transform__transpose_buffer(const void *src_ptr, const hailo_3d_image_shape_t &shape,
+    const hailo_format_t &format, void *dst_ptr)
+{
+    switch (format.order)
+    {
+    case HAILO_FORMAT_ORDER_NHWC:
+    case HAILO_FORMAT_ORDER_NHW:
+    case HAILO_FORMAT_ORDER_BAYER_RGB:
+    case HAILO_FORMAT_ORDER_12_BIT_BAYER_RGB:
+    case HAILO_FORMAT_ORDER_FCR:
+    case HAILO_FORMAT_ORDER_F8CR:
+        return transform__transpose_NHWC(src_ptr, shape, HailoRTCommon::get_format_data_bytes(format), dst_ptr);
+    default:
+        LOGGER__ERROR("Transpose is not supported for order {}", format.order);
+        return HAILO_INVALID_OPERATION;
+    }
+}
+
+hailo_status transpose_buffer(const MemoryView src, const hailo_3d_image_shape_t &shape,
+    const hailo_format_t &format, MemoryView dst)
+{
+    if ((src.size() != dst.size()) || (src.size() != HailoRTCommon::get_frame_size(shape, format))) {
+        LOGGER__ERROR("transpose NHWC invalid buffers size");
+        return HAILO_INVALID_ARGUMENT;
+    }
+
+    return transform__transpose_buffer(src.data(), shape, format, dst.data());
+}
+
+
+/* Re-Ordering funcs */
+template <typename T>
+void transform__h2d_NHWC_to_NHWC(const T *src_ptr, hailo_3d_image_shape_t *src_image_shape,
+    T *dst_ptr, hailo_3d_image_shape_t *dst_image_shape)
+{
+    /* Validate arguments */
+    ASSERT(NULL != src_ptr);
+    ASSERT(NULL != dst_ptr);
+
+    size_t src_offset = 0;
+    size_t dst_offset = 0;
+    uint32_t src_row_size = src_image_shape->width * src_image_shape->features;
+    uint32_t pad_size = (dst_image_shape->width - src_image_shape->width) * dst_image_shape->features;
+
+    /* copy src to dst, and pad width to 8 elements */
+    for (uint32_t r = 0; r < src_image_shape->height ; r++) {
+        src_offset = r * src_image_shape->width * src_image_shape->features;
+        dst_offset = r * dst_image_shape->width * dst_image_shape->features;
+        memcpy(dst_ptr + dst_offset, src_ptr + src_offset, src_row_size * sizeof(T));
+        memset(dst_ptr + dst_offset + src_row_size, 0, pad_size * sizeof(T));
+    }
+}
+
+template <typename T>
+void transform__d2h_NHWC_to_NHWC(const T *src_ptr, hailo_3d_image_shape_t *src_image_shape,
+    T *dst_ptr, hailo_3d_image_shape_t *dst_image_shape)
+{
+    /* Validate arguments */
+    ASSERT(NULL != src_ptr);
+    ASSERT(NULL != dst_ptr);
+
+    size_t src_offset = 0;
+    size_t dst_offset = 0;
+
+    // copy and removed padded features
+    for (uint32_t r = 0; r < dst_image_shape->height ; r++) {
+        for (uint32_t c = 0; c < dst_image_shape->width ; c++) {
+            src_offset = r * src_image_shape->width * src_image_shape->features + c * src_image_shape->features;
+            dst_offset = r * dst_image_shape->width * dst_image_shape->features + c * dst_image_shape->features;
+            memcpy(dst_ptr + dst_offset, src_ptr + src_offset, dst_image_shape->features * sizeof(T));
+        }
+    }
+}
+
+template <typename T>
+void transform__h2d_NHWC_to_NHCW(const T *src_ptr, hailo_3d_image_shape_t *src_image_shape,
+    T *dst_ptr, hailo_3d_image_shape_t *dst_image_shape)
+{
+    /* Validate arguments */
+    ASSERT(NULL != src_ptr);
+    ASSERT(NULL != dst_ptr);
+
+    uint32_t src_row_size = src_image_shape->width * src_image_shape->features;
+    uint32_t dst_row_size = dst_image_shape->width * dst_image_shape->features;
+
+    size_t src_offset = 0;
+    size_t dst_offset = 0;
+    uint32_t pad_size = dst_image_shape->width - src_image_shape->width;
+
+    /* transpose - switch width and channels */
+    for (uint32_t r = 0; r < src_image_shape->height ; r++) {
+        for (uint32_t f = 0; f < src_image_shape->features; f++) {
+            for (uint32_t c = 0; c < src_image_shape->width; c++) {
+                src_offset = r * src_row_size + c * src_image_shape->features + f;
+                dst_offset = r * dst_row_size + f * dst_image_shape->width + c;
+                dst_ptr[dst_offset] = src_ptr[src_offset];
+            }
+            /* pad width to 8 elemnts */
+            if (pad_size != 0) {
+                dst_offset = r * dst_row_size + f * dst_image_shape->width + src_image_shape->width;
+                memset(dst_ptr + dst_offset, 0, pad_size);
+            }
+        }
+    }
+}
+
+template <typename T>
+void transform__d2h_NHCW_to_NHWC(const T *src_ptr, hailo_3d_image_shape_t *src_image_shape,
+    T *dst_ptr, hailo_3d_image_shape_t *dst_image_shape)
+{
+    /* Validate arguments */
+    ASSERT(NULL != src_ptr);
+    ASSERT(NULL != dst_ptr);
+
+    /* transpose - switch channels and width, ignore padded elements */
+    const auto row_size_src = src_image_shape->width * src_image_shape->features;
+    const auto row_size_dest = dst_image_shape->width * dst_image_shape->features;
+    for (uint32_t r = 0; r < dst_image_shape->height ; r++) {
+        const auto row_offset_src = r * row_size_src;
+        const auto row_offset_dest = r * row_size_dest;
+        for (uint32_t c = 0; c < dst_image_shape->width; c++) {
+            const auto src_offset = row_offset_src + c;
+            const auto dest_offset = row_offset_dest + c * dst_image_shape->features;
+            for (uint32_t f = 0; f < dst_image_shape->features; f++) {
+                dst_ptr[dest_offset + f] = src_ptr[src_offset + f * src_image_shape->width];
+            }
+        }
+    }
+}
+
+template <typename T>
+void transform__d2h_NHW_to_NHW(const T *src_ptr, hailo_3d_image_shape_t *src_image_shape, T *dst_ptr,
+    hailo_3d_image_shape_t *dst_image_shape)
+{
+    /* Validate arguments */
+    ASSERT(NULL != src_ptr);
+    ASSERT(NULL != dst_ptr);
+
+    for (uint32_t row = 0; row < dst_image_shape->height; row++) {
+        const T *src = src_ptr + (row * src_image_shape->width);
+        T* dst = dst_ptr + row * dst_image_shape->width;
+        std::copy_n(src, dst_image_shape->width, dst);
+    }
+}
+
+template <typename T>
+void transform__h2d_NC_to_NC(const T *src_ptr, hailo_3d_image_shape_t *src_image_shape,
+    T *dst_ptr, hailo_3d_image_shape_t *dst_image_shape)
+{
+    /* Validate arguments */
+    ASSERT(NULL != src_ptr);
+    ASSERT(NULL != dst_ptr);
+
+    /* copy src to dst, and pad channels to 8 elements */
+    memcpy(dst_ptr, src_ptr, src_image_shape->features * sizeof(T));
+    memset(dst_ptr + src_image_shape->features, 0, (dst_image_shape->features - src_image_shape->features) * sizeof(T));
+}
+
+template <typename T>
+void transform__d2h_NC_to_NC(const T *src_ptr, T *dst_ptr, hailo_3d_image_shape_t *dst_image_shape)
+{
+    /* Validate arguments */
+    ASSERT(NULL != src_ptr);
+    ASSERT(NULL != dst_ptr);
+
+    memcpy(dst_ptr, src_ptr, dst_image_shape->features * sizeof(T));
+}
+
+static inline void transform__parse_and_copy_bbox (hailo_bbox_t *dst, uint64_t* proposal)
+{
+    dst->y_min = (uint16_t)((*((uint64_t*)proposal) & 0xfff000000000) >> 36);
+    dst->x_min = (uint16_t)((*((uint64_t*)proposal) & 0xfff000000) >> 24);
+    dst->y_max = (uint16_t)((*((uint64_t*)proposal) & 0xfff000) >> 12);
+    dst->x_max = (uint16_t)((*((uint64_t*)proposal) & 0xfff));
+    dst->score = (uint16_t)((*((uint64_t*)proposal) & 0xffff000000000000) >> 48);
+}
+
+void transform__d2h_NMS(const uint8_t *src_ptr, uint8_t *dst_ptr, const hailo_nms_info_t &nms_info, std::vector<size_t> &chunk_offsets)
+{
+    /* Validate arguments */
+    ASSERT(NULL != src_ptr);
+    ASSERT(NULL != dst_ptr);
+
+    uint32_t num_of_classes = nms_info.number_of_classes;
+    uint32_t bbox_size = nms_info.bbox_size;
+
+    size_t bbox_index = 0;
+    size_t src_offset = 0;
+    size_t dst_offset = 0;
+
+    nms_bbox_counter_t class_bboxes_count = 0;
+
+    // For each class, we need to merge bboxes from all nms chunks. Therefore we use chunk_offsets - for
+    // each nms chunk we store its offset, any time we finish parsing some class bboxes, we update the
+    // offset
+
+    // First, init the chunk_offset vector
+    assert(chunk_offsets.size() == nms_info.chunks_per_frame);
+    size_t current_offset = 0;
+    chunk_offsets[0] = current_offset;
+    for (size_t chunk_index = 1; chunk_index < nms_info.chunks_per_frame; chunk_index++) {
+        // Skip all classes. Can be optimized if we store the size of each chunk in the begining of the buffer
+        for (size_t class_index = 0; class_index < num_of_classes; class_index++) {
+            class_bboxes_count = *(reinterpret_cast<const nms_bbox_counter_t*>(src_ptr + current_offset));
+            current_offset += sizeof(nms_bbox_counter_t) + (class_bboxes_count * bbox_size); 
+        }
+        chunk_offsets[chunk_index] = current_offset;
+    }
+
+    // Now, the merge itself
+    for (size_t class_index = 0; class_index < num_of_classes; class_index++) {
+        nms_bbox_counter_t *dst_bbox_counter = reinterpret_cast<nms_bbox_counter_t*>(dst_ptr + dst_offset);
+        *dst_bbox_counter = 0;
+
+        dst_offset += sizeof(nms_bbox_counter_t);
+
+        for (size_t chunk_index = 0; chunk_index < nms_info.chunks_per_frame; chunk_index++) {
+            // Add bbox from all chunks of current class
+            src_offset = chunk_offsets[chunk_index];
+            class_bboxes_count = *((nms_bbox_counter_t*)((uint8_t*)src_ptr + src_offset));
+            *dst_bbox_counter = static_cast<nms_bbox_counter_t>(*dst_bbox_counter + class_bboxes_count);
+
+            src_offset += sizeof(nms_bbox_counter_t);
+
+            for (bbox_index = 0; bbox_index < class_bboxes_count; bbox_index++) {
+                transform__parse_and_copy_bbox((hailo_bbox_t *)(dst_ptr + dst_offset), (uint64_t*)(src_ptr + src_offset));
+                src_offset += bbox_size;
+                dst_offset += sizeof(hailo_bbox_t);
+            }
+
+            chunk_offsets[chunk_index] = src_offset;
+        }
+    }
+}
+
+template <typename T>
+void transform__h2d_FCR(const T *src_ptr, hailo_3d_image_shape_t *src_image_shape,
+    T *dst_ptr, hailo_3d_image_shape_t *dst_image_shape)
+{
+    /* Validate arguments */
+    ASSERT(NULL != src_ptr);
+    ASSERT(NULL != dst_ptr);
+
+    size_t src_offset = 0;
+    size_t dst_offset = 0;
+    uint32_t src_row_size = src_image_shape->width * src_image_shape->features;
+    uint32_t dst_row_size = dst_image_shape->width * dst_image_shape->features;
+    uint32_t pad_size = dst_image_shape->features - src_image_shape->features;
+
+    for (uint32_t r = 0; r < src_image_shape->height ; r++) {
+        for (uint32_t c = 0; c < src_image_shape->width; c++) {
+            src_offset = r * src_row_size + c * src_image_shape->features;
+            dst_offset = r * dst_row_size + c * dst_image_shape->features;
+
+            memcpy(dst_ptr + dst_offset, src_ptr + src_offset, src_image_shape->features * sizeof(T));
+            dst_offset += src_image_shape->features;
+            memset(dst_ptr + dst_offset, 0, pad_size * sizeof(T));
+        }
+    }
+}
+
+template <typename T>
+void transform__h2d_F8CR(const T *src_ptr, hailo_3d_image_shape_t *src_image_shape,
+    T *dst_ptr, hailo_3d_image_shape_t *dst_image_shape)
+{
+    /* Validate arguments */
+    ASSERT(NULL != src_ptr);
+    ASSERT(NULL != dst_ptr);
+
+    uint32_t src_row_size = src_image_shape->width * src_image_shape->features;
+    uint32_t dst_row_size = dst_image_shape->width * dst_image_shape->features;
+    uint32_t src_features = src_image_shape->features;
+    size_t src_offset = 0;
+    size_t dst_offset = 0;
+
+    /* copy src data to dst, 8channels * width at a time, pad features to 8 elemnts */
+    for (uint32_t r = 0; r < src_image_shape->height ; r++) {
+        for (uint32_t c = 0; c < src_image_shape->width; c++) {
+            for (uint32_t f = 0; f < src_image_shape->features; f+=8) {
+                src_offset = r * src_row_size + c * src_image_shape->features + f;
+                dst_offset = r * dst_row_size + c * HW_DATA_ALIGNMENT + f * dst_image_shape->width;
+                if (f + HW_DATA_ALIGNMENT <= src_image_shape->features) {
+                    /* take 8 full features for each column and write them */
+                    memcpy(dst_ptr + dst_offset, src_ptr + src_offset, HW_DATA_ALIGNMENT * sizeof(T));
+                }
+                else {
+                    /* take the last 8 or less features, pad features to 8 and write */
+                    auto last_features = (src_features % HW_DATA_ALIGNMENT);
+                    auto remainder = (HW_DATA_ALIGNMENT - last_features);
+                    memcpy(dst_ptr + dst_offset, src_ptr + src_offset, last_features * sizeof(T));
+                    dst_offset += last_features;
+                    memset(dst_ptr + dst_offset, 0, remainder * sizeof(T));
+                }
+            }
+        }
+    }
+}
+
+template <typename T>
+void transform__d2h_F8CR(const T *src_ptr, hailo_3d_image_shape_t *src_image_shape,
+    T *dst_ptr, hailo_3d_image_shape_t *dst_image_shape)
+{
+    /* Validate arguments */
+    ASSERT(NULL != src_ptr);
+    ASSERT(NULL != dst_ptr);
+
+    uint32_t src_row_size = src_image_shape->width * src_image_shape->features;
+    uint32_t dst_row_size = dst_image_shape->width * dst_image_shape->features;
+    uint32_t dst_features = dst_image_shape->features;
+    uint32_t src_offset = 0;
+    uint32_t dst_offset = 0;
+
+    for (uint32_t r = 0; r < dst_image_shape->height ; r++) {
+        for (uint32_t c = 0; c < dst_image_shape->width; c++) {
+            for (uint32_t f = 0; f < dst_image_shape->features; f+=8) {
+                src_offset = r * src_row_size + c * HW_DATA_ALIGNMENT + f * src_image_shape->width;
+                dst_offset = r * dst_row_size + c * dst_image_shape->features + f;
+                if (f + HW_DATA_ALIGNMENT <= dst_image_shape->features) {
+                    /* copy the first dst_image_features (which are aligned to 8)! */
+                    memcpy(dst_ptr + dst_offset, src_ptr + src_offset, HW_DATA_ALIGNMENT * sizeof(T));
+                    }
+                else {
+                    /* copy the last 8 or less features, remove pad */
+                    memcpy(dst_ptr + dst_offset, src_ptr + src_offset, (dst_features % HW_DATA_ALIGNMENT) * sizeof(T));
+                }
+            }
+        }
+    }
+}
+
+template <typename T>
+void transform__d2h_BAYER_RGB(const T *src_ptr, hailo_3d_image_shape_t *src_image_shape,
+    T *dst_ptr, hailo_3d_image_shape_t *dst_image_shape)
+{
+    /* Validate arguments */
+    ASSERT(NULL != src_ptr);
+    ASSERT(NULL != dst_ptr);
+
+    uint32_t src_offset = 0;
+    uint32_t dst_offset = 0;
+
+    for (uint32_t r = 0; r < dst_image_shape->height ; r++) {
+        src_offset = r * src_image_shape->width;
+        dst_offset = r * dst_image_shape->width;
+        memcpy(dst_ptr + dst_offset, src_ptr + src_offset, dst_image_shape->width * sizeof(T));
+    }
+}
+
+template <typename T>
+hailo_status transform__h2d_NHWC_to_RGB888(const T *src_ptr, hailo_3d_image_shape_t *src_image_shape,
+    T *dst_ptr, hailo_3d_image_shape_t *dst_image_shape)
+{
+    size_t src_offset = 0;
+    size_t dst_offset = 0;
+    uint32_t pad_size = (dst_image_shape->width - src_image_shape->width) * dst_image_shape->features;
+
+    /* Validate arguments */
+    ASSERT(NULL != src_ptr);
+    ASSERT(NULL != dst_ptr);
+
+    CHECK(((RGB_FEATURES == src_image_shape->features) && ((RGB_FEATURES + 1) == dst_image_shape->features)),
+        HAILO_INVALID_ARGUMENT,
+        "User features must be {}, received {}. HW features must be {}, received {}",
+        RGB_FEATURES, src_image_shape->features, RGB_FEATURES + 1, dst_image_shape->features);
+
+    for (uint32_t r = 0; r < src_image_shape->height ; r++) {
+        for (uint32_t c = 0; c < src_image_shape->width; c++) {
+            src_offset = r * src_image_shape->width * src_image_shape->features + c * src_image_shape->features;
+            dst_offset = r * dst_image_shape->width * dst_image_shape->features + c * dst_image_shape->features;
+
+            /* Copy while flipping the data feature-wise */
+            for (uint32_t f = 0; f < src_image_shape->features; f++) {
+                dst_ptr[dst_offset + f] = src_ptr[src_offset + src_image_shape->features - f - 1];
+            }
+            /* add another zero byte */
+            dst_ptr[dst_offset + RGB_FEATURES] = 0;
+        }
+        /* move dst_offset 4 features (RGB + 1 zero byte) and pad width if needed */
+        memset(dst_ptr + dst_offset + RGB_FEATURES + 1, 0, pad_size * sizeof(T));
+    }
+
+    return HAILO_SUCCESS;
+}
+
+template<typename T>
+hailo_status transform__h2d_NCHW_to_NHCW(
+    const T *src_ptr, hailo_3d_image_shape_t *src_image_shape,
+    T *dst_ptr, hailo_3d_image_shape_t *dst_image_shape)
+{
+    /* Validate arguments */
+    ASSERT(NULL != src_ptr);
+    ASSERT(NULL != dst_ptr);
+    CHECK(src_image_shape->features == dst_image_shape->features, HAILO_INVALID_ARGUMENT,
+          "NCHW_to_NHCW Transform features src/dst should be the same");
+    CHECK(src_image_shape->height == dst_image_shape->height, HAILO_INVALID_ARGUMENT,
+          "NCHW_to_NHCW Transform height src/dst should be the same");
+    CHECK(src_image_shape->width <= dst_image_shape->width, HAILO_INVALID_ARGUMENT,
+          "NCHW_to_NHCW Transform src width should be smaller/equal than dst width");
+    CHECK((dst_image_shape->width % HW_DATA_ALIGNMENT) == 0, HAILO_INVALID_ARGUMENT,
+          "NCHW_to_NHCW Transform dst width must be aligned to {}", HW_DATA_ALIGNMENT);
+
+    size_t width_size = src_image_shape->width;
+    size_t pad_size = (dst_image_shape->width - src_image_shape->width);
+    for (uint32_t c = 0; c < src_image_shape->features; c++) {
+        for (uint32_t r = 0; r < src_image_shape->height; r++) {
+            // Copy width
+            const T *src = src_ptr +
+                src_image_shape->width * src_image_shape->height * c +
+                src_image_shape->width * r;
+            T *dst = dst_ptr +
+                dst_image_shape->features * dst_image_shape->width * r +
+                dst_image_shape->width * c;
+
+            std::copy_n(src, width_size, dst);
+            if (pad_size != 0) {
+                std::fill_n(dst + width_size, pad_size, static_cast<T>(0));
+            }
+        }
+    }
+
+    return HAILO_SUCCESS;
+}
+
+template<typename T>
+hailo_status transform__d2h_NHCW_to_NCHW(
+    const T *src_ptr, hailo_3d_image_shape_t *src_image_shape,
+    T *dst_ptr, hailo_3d_image_shape_t *dst_image_shape)
+{
+    /* Validate arguments */
+    ASSERT(NULL != src_ptr);
+    ASSERT(NULL != dst_ptr);
+    CHECK(src_image_shape->features == dst_image_shape->features, HAILO_INVALID_ARGUMENT,
+          "NCHW_to_NHCW Transform features src/dst should be the same");
+    CHECK(src_image_shape->height == dst_image_shape->height, HAILO_INVALID_ARGUMENT,
+          "NCHW_to_NHCW Transform height src/dst should be the same");
+    CHECK(dst_image_shape->width <= src_image_shape->width, HAILO_INVALID_ARGUMENT,
+          "NCHW_to_NHCW Transform dst width should be smaller/equal than src width");
+    CHECK((src_image_shape->width % HW_DATA_ALIGNMENT) == 0, HAILO_INVALID_ARGUMENT,
+          "NCHW_to_NHCW Transform src width must be aligned to {}", HW_DATA_ALIGNMENT);
+
+    size_t width_size = dst_image_shape->width;
+    for (uint32_t r = 0; r < src_image_shape->height; r++) {
+        for (uint32_t c = 0; c < src_image_shape->features; c++) {
+            // Copy width
+            T *dst = dst_ptr +
+                dst_image_shape->width * dst_image_shape->height * c +
+                dst_image_shape->width * r;
+            const T *src = src_ptr +
+                src_image_shape->features * src_image_shape->width * r +
+                src_image_shape->width * c;
+
+            std::copy_n(src, width_size, dst);
+        }
+    }
+
+    return HAILO_SUCCESS;
+}
+
+template<typename T>
+hailo_status transform__d2h_argmax_NHCW_to_NHW(const T *src_ptr, const hailo_3d_image_shape_t &src_image_shape,
+    T *dst_ptr, const hailo_3d_image_shape_t &dst_image_shape)
+{
+    assert(nullptr != src_ptr);
+    assert(nullptr != dst_ptr);
+
+    CHECK(src_image_shape.height == dst_image_shape.height, HAILO_INVALID_OPERATION,
+        "NHCW_to_NHW argmax Transform is supported only when src height ({}) is equal to dst height ({})",
+        src_image_shape.height, dst_image_shape.height);
+    CHECK(src_image_shape.width >= dst_image_shape.width, HAILO_INVALID_OPERATION,
+        "NHCW_to_NHW argmax Transform is supported only when src width ({}) is equal/larger than dst width ({})",
+        src_image_shape.width, dst_image_shape.width);
+    CHECK(dst_image_shape.features == 1, HAILO_INVALID_OPERATION,
+        "NHCW_to_NHW argmax Transform is supported only when dst features ({}) is 1",
+        dst_image_shape.features);
+    CHECK(src_image_shape.features < std::numeric_limits<T>::max(), HAILO_INVALID_OPERATION,
+        "NHCW_to_NHW argmax Transform is supported only when src features ({}) is smaller than {}",
+        src_image_shape.features, std::numeric_limits<T>::max());
+
+    const auto src_row_size = src_image_shape.width * src_image_shape.features;
+    const auto dst_row_size = dst_image_shape.width;
+    for (uint32_t r = 0; r < src_image_shape.height; r++) {
+        // For each row, we iterate on all columns, and find the max feature. It can be implemented better by iteratre
+        // over all features, and on each iteration save the max value for each column.
+        const T *src_row = src_ptr + (r * src_row_size);
+        T *dst_row = dst_ptr + (r * dst_row_size);
+        for (uint32_t w = 0; w < dst_image_shape.width; w++) {
+            const T *offset_in_row = src_row + w;
+            T max_index = 0;
+            T max_value = *offset_in_row;
+
+            for (uint32_t c = 1; c < src_image_shape.features; c++) {
+                offset_in_row += src_image_shape.width;
+                const auto &current_value = *offset_in_row;
+                if (current_value > max_value) {
+                    max_index = static_cast<T>(c);
+                    max_value = current_value;
+                }
+            }
+
+            dst_row[w] = max_index;
+        }
+    }
+
+    return HAILO_SUCCESS;
+}
+
+
+template <typename T>
+hailo_status transform__h2d_YUY2_to_YUY2(const T *src_ptr, T *dst_ptr, uint32_t shape_size)
+{
+    /* Validate arguments */
+    ASSERT(NULL != src_ptr);
+    ASSERT(NULL != dst_ptr);
+
+    CHECK((shape_size % HW_DATA_ALIGNMENT) == 0, HAILO_INVALID_ARGUMENT,
+          "YUY2_to_YUY2 Transform shape_size must be aligned to {}", HW_DATA_ALIGNMENT);
+
+    std::copy_n(src_ptr, shape_size, dst_ptr);
+
+    return HAILO_SUCCESS;
+}
+
+hailo_status InputTransformContext::quantize_stream(const void *src_ptr, void *quant_buffer)
+{
+    auto shape_size = HailoRTCommon::get_shape_size(m_src_image_shape);
+
+    switch (m_src_format.type) {
+        case HAILO_FORMAT_TYPE_UINT8:
+            if (m_dst_format.type == HAILO_FORMAT_TYPE_UINT8) {
+                Quantization::quantize_input_buffer<uint8_t, uint8_t>((uint8_t*)src_ptr, (uint8_t*)quant_buffer, shape_size, m_dst_quant_info);
+            }
+            else {
+                return HAILO_INVALID_OPERATION;
+            }
+            break;
+        case HAILO_FORMAT_TYPE_UINT16:
+            if (m_dst_format.type == HAILO_FORMAT_TYPE_UINT16) {
+                Quantization::quantize_input_buffer<uint16_t, uint16_t>((uint16_t*)src_ptr, (uint16_t *)quant_buffer, shape_size, m_dst_quant_info);
+            }
+            else {
+                return HAILO_INVALID_OPERATION;
+            }
+            break;
+        case HAILO_FORMAT_TYPE_FLOAT32:
+            if (m_dst_format.type == HAILO_FORMAT_TYPE_UINT8) {
+                Quantization::quantize_input_buffer<float32_t, uint8_t>((float32_t*)src_ptr, (uint8_t*)quant_buffer, shape_size, m_dst_quant_info);
+            }
+            else if (m_dst_format.type == HAILO_FORMAT_TYPE_UINT16) {
+                Quantization::quantize_input_buffer<float32_t, uint16_t>((float32_t*)src_ptr, (uint16_t*)quant_buffer, shape_size, m_dst_quant_info);
+            }
+            else {
+                return HAILO_INVALID_OPERATION;
+            }
+            break;
+        default:
+            LOGGER__ERROR("Invalid src-buffer's type format");
+            return HAILO_INVALID_ARGUMENT;
+    }
+    return HAILO_SUCCESS;
+}
+
+hailo_status FrameOutputTransformContext::quantize_stream(const void *dst_ptr)
+{
+    auto shape_size = HailoRTCommon::get_shape_size(m_dst_image_shape);
+
+    switch (m_dst_format.type) {
+        case HAILO_FORMAT_TYPE_UINT8:
+            if (HAILO_FORMAT_TYPE_UINT8 == m_src_format.type) {
+                Quantization::dequantize_output_buffer_in_place<uint8_t, uint8_t>((uint8_t*)dst_ptr, shape_size, m_dst_quant_info);
+            }
+            else {
+                return HAILO_INVALID_OPERATION;
+            }
+            break;
+        case HAILO_FORMAT_TYPE_UINT16:
+            if (HAILO_FORMAT_TYPE_UINT16 == m_src_format.type) {
+                Quantization::dequantize_output_buffer_in_place<uint16_t, uint16_t>((uint16_t*)dst_ptr, shape_size, m_dst_quant_info);
+            }
+            else {
+                return HAILO_INVALID_OPERATION;
+            }
+            break;
+        case HAILO_FORMAT_TYPE_FLOAT32:
+            /* if output layer is argmax - do not rescale */
+            if (HAILO_FORMAT_ORDER_NHW != m_dst_format.order) {
+                if (m_src_format.type == HAILO_FORMAT_TYPE_UINT8) {
+                    Quantization::dequantize_output_buffer_in_place<float32_t, uint8_t>((float32_t*)dst_ptr, shape_size, m_dst_quant_info);
+                }
+                else if (m_src_format.type == HAILO_FORMAT_TYPE_UINT16) {
+                    Quantization::dequantize_output_buffer_in_place<float32_t, uint16_t>((float32_t*)dst_ptr, shape_size, m_dst_quant_info);
+                }
+                else {
+                    return HAILO_INVALID_OPERATION;
+                }
+            } else {
+                copy_output_buffer_float32((float32_t*)dst_ptr, shape_size);
+            }
+            break;
+        default:
+            LOGGER__ERROR("Invalid dst-buffer's type format");
+            return HAILO_INVALID_ARGUMENT;
+    }
+
+    return HAILO_SUCCESS;
+}
+
+hailo_status reorder_input_stream(const void *src_ptr, hailo_3d_image_shape_t src_image_shape, hailo_format_t src_format, 
+    void *dst_ptr, hailo_3d_image_shape_t dst_image_shape, hailo_format_t dst_format)
+{
+    if ((HAILO_FORMAT_ORDER_NHWC == src_format.order) &&
+        (HAILO_FORMAT_ORDER_NHCW == dst_format.order)) {
+        switch (dst_format.type) {
+            case HAILO_FORMAT_TYPE_UINT8:
+                transform__h2d_NHWC_to_NHCW<uint8_t>((uint8_t*)src_ptr, &src_image_shape, (uint8_t*)dst_ptr, &dst_image_shape);
+                break;
+            case HAILO_FORMAT_TYPE_UINT16:
+                transform__h2d_NHWC_to_NHCW<uint16_t>((uint16_t*)src_ptr, &src_image_shape, (uint16_t*)dst_ptr, &dst_image_shape);
+                break;
+            default:
+                LOGGER__ERROR("Invalid src-buffer's type format");
+                return HAILO_INVALID_ARGUMENT;
+        }
+        return HAILO_SUCCESS;
+    }
+
+    if ((HAILO_FORMAT_ORDER_NHWC == src_format.order) &&
+        (HAILO_FORMAT_ORDER_NHWC == dst_format.order)) {
+        switch (dst_format.type) {
+            case HAILO_FORMAT_TYPE_UINT8:
+                transform__h2d_NHWC_to_NHWC<uint8_t>((uint8_t*)src_ptr, &src_image_shape, (uint8_t*)dst_ptr, &dst_image_shape);
+                break;
+            case HAILO_FORMAT_TYPE_UINT16:
+                transform__h2d_NHWC_to_NHWC<uint16_t>((uint16_t*)src_ptr, &src_image_shape, (uint16_t*)dst_ptr, &dst_image_shape);
+                break;
+            default:
+                LOGGER__ERROR("Invalid src-buffer's type format");
+                return HAILO_INVALID_ARGUMENT;
+        }
+        return HAILO_SUCCESS;
+    }
+
+    if ((HAILO_FORMAT_ORDER_NC == src_format.order) &&
+        (HAILO_FORMAT_ORDER_NC == dst_format.order)) {
+        switch (dst_format.type) {
+            case HAILO_FORMAT_TYPE_UINT8:
+                transform__h2d_NC_to_NC<uint8_t>((uint8_t*)src_ptr, &src_image_shape, (uint8_t*)dst_ptr, &dst_image_shape);
+                break;
+            case HAILO_FORMAT_TYPE_UINT16:
+                transform__h2d_NC_to_NC<uint16_t>((uint16_t*)src_ptr, &src_image_shape, (uint16_t*)dst_ptr, &dst_image_shape);
+                break;
+            default:
+                LOGGER__ERROR("Invalid src-buffer's type format");
+                return HAILO_INVALID_ARGUMENT;
+        }
+        return HAILO_SUCCESS;
+    }
+
+    if ((HAILO_FORMAT_ORDER_FCR == src_format.order) &&
+        (HAILO_FORMAT_ORDER_FCR == dst_format.order)) {
+        assert(0 == (dst_image_shape.features % 8));
+        switch (dst_format.type) {
+            case HAILO_FORMAT_TYPE_UINT8:
+                transform__h2d_FCR<uint8_t>((uint8_t*)src_ptr, &src_image_shape, (uint8_t*)dst_ptr, &dst_image_shape);
+                break;
+            case HAILO_FORMAT_TYPE_UINT16:
+                transform__h2d_FCR<uint16_t>((uint16_t*)src_ptr, &src_image_shape, (uint16_t*)dst_ptr, &dst_image_shape);
+                break;
+            default:
+                LOGGER__ERROR("Invalid src-buffer's type format");
+                return HAILO_INVALID_ARGUMENT;
+        }
+        return HAILO_SUCCESS;
+    }
+
+    if ((HAILO_FORMAT_ORDER_F8CR == src_format.order) &&
+        (HAILO_FORMAT_ORDER_F8CR == dst_format.order)) {
+        switch (dst_format.type) {
+            case HAILO_FORMAT_TYPE_UINT8:
+                transform__h2d_F8CR<uint8_t>((uint8_t*)src_ptr, &src_image_shape, (uint8_t*)dst_ptr, &dst_image_shape);
+                break;
+            case HAILO_FORMAT_TYPE_UINT16:
+                transform__h2d_F8CR<uint16_t>((uint16_t*)src_ptr, &src_image_shape, (uint16_t*)dst_ptr, &dst_image_shape);
+                break;
+            default:
+                LOGGER__ERROR("Invalid src-buffer's type format");
+                return HAILO_INVALID_ARGUMENT;
+        }
+        return HAILO_SUCCESS;
+    }
+
+    if ((HAILO_FORMAT_ORDER_BAYER_RGB == src_format.order) &&
+        (HAILO_FORMAT_ORDER_BAYER_RGB == dst_format.order)) {
+        assert(1 == src_image_shape.features);
+        switch (dst_format.type) {
+            case HAILO_FORMAT_TYPE_UINT8:
+                transform__h2d_NHWC_to_NHWC<uint8_t>((uint8_t*)src_ptr, &src_image_shape, (uint8_t*)dst_ptr, &dst_image_shape);
+                break;
+            case HAILO_FORMAT_TYPE_UINT16:
+                transform__h2d_NHWC_to_NHWC<uint16_t>((uint16_t*)src_ptr, &src_image_shape, (uint16_t*)dst_ptr, &dst_image_shape);
+                break;
+            default:
+                LOGGER__ERROR("Invalid src-buffer's type format");
+                return HAILO_INVALID_ARGUMENT;
+        }
+        return HAILO_SUCCESS;
+    }
+
+    if ((HAILO_FORMAT_ORDER_12_BIT_BAYER_RGB == src_format.order) &&
+        (HAILO_FORMAT_ORDER_12_BIT_BAYER_RGB == dst_format.order)) {
+        assert(1 == src_image_shape.features);
+        switch (dst_format.type) {
+            case HAILO_FORMAT_TYPE_UINT8:
+                transform__h2d_NHWC_to_NHWC<uint8_t>((uint8_t*)src_ptr, &src_image_shape, (uint8_t*)dst_ptr, &dst_image_shape);
+                break;
+            case HAILO_FORMAT_TYPE_UINT16:
+                transform__h2d_NHWC_to_NHWC<uint16_t>((uint16_t*)src_ptr, &src_image_shape, (uint16_t*)dst_ptr, &dst_image_shape);
+                break;
+            default:
+                LOGGER__ERROR("Invalid src-buffer's type format");
+                return HAILO_INVALID_ARGUMENT;
+        }
+        return HAILO_SUCCESS;
+    }
+
+    if ((HAILO_FORMAT_ORDER_NHWC == src_format.order) &&
+        (HAILO_FORMAT_ORDER_RGB888 == dst_format.order)) {
+        switch (dst_format.type) {
+            case HAILO_FORMAT_TYPE_UINT8:
+                return transform__h2d_NHWC_to_RGB888<uint8_t>((uint8_t*)src_ptr, &src_image_shape, (uint8_t*)dst_ptr, &dst_image_shape);
+                break;
+            case HAILO_FORMAT_TYPE_UINT16:
+                return transform__h2d_NHWC_to_RGB888<uint16_t>((uint16_t*)src_ptr, &src_image_shape, (uint16_t*)dst_ptr, &dst_image_shape);
+                break;
+            default:
+                LOGGER__ERROR("Invalid src-buffer's type format");
+                return HAILO_INVALID_ARGUMENT;
+        }
+    }
+
+    if ((HAILO_FORMAT_ORDER_NCHW == src_format.order) &&
+        (HAILO_FORMAT_ORDER_NHCW == dst_format.order)) {
+        switch (dst_format.type) {
+            case HAILO_FORMAT_TYPE_UINT8:
+                return transform__h2d_NCHW_to_NHCW<uint8_t>((uint8_t*)src_ptr, &src_image_shape, (uint8_t*)dst_ptr, &dst_image_shape);
+                break;
+            case HAILO_FORMAT_TYPE_UINT16:
+                return transform__h2d_NCHW_to_NHCW<uint16_t>((uint16_t*)src_ptr, &src_image_shape, (uint16_t*)dst_ptr, &dst_image_shape);
+                break;
+            default:
+                LOGGER__ERROR("Invalid src-buffer's type format");
+                return HAILO_INVALID_ARGUMENT;
+        }
+    }
+
+    if ((HAILO_FORMAT_ORDER_YUY2 == src_format.order) &&
+        (HAILO_FORMAT_ORDER_YUY2 == dst_format.order)) {
+        auto shape_size = HailoRTCommon::get_shape_size(src_image_shape);
+        switch (dst_format.type) {
+            case HAILO_FORMAT_TYPE_UINT8:
+                return transform__h2d_YUY2_to_YUY2<uint8_t>((uint8_t*)src_ptr, (uint8_t*)dst_ptr, shape_size);
+                break;
+            case HAILO_FORMAT_TYPE_UINT16:
+                return transform__h2d_YUY2_to_YUY2<uint16_t>((uint16_t*)src_ptr, (uint16_t*)dst_ptr, shape_size);
+                break;
+            default:
+                LOGGER__ERROR("Invalid src-buffer's type format");
+                return HAILO_INVALID_ARGUMENT;
+        }
+    }
+
+    LOGGER__ERROR("Unsupported input stream transformation from hailo_format_order_t "
+                "{} to hailo_format_order_t {}", src_format.order, dst_format.order);
+    return HAILO_INVALID_OPERATION;
+}
+
+hailo_status reorder_output_stream(const void *src_ptr, hailo_3d_image_shape_t src_image_shape, hailo_format_t src_format, 
+    void *dst_ptr, hailo_3d_image_shape_t dst_image_shape, hailo_format_t dst_format)
+{
+    if ((HAILO_FORMAT_ORDER_NHCW == src_format.order) &&
+        (HAILO_FORMAT_ORDER_NHWC == dst_format.order)) {
+        switch (src_format.type) {
+            case HAILO_FORMAT_TYPE_UINT8:
+                transform__d2h_NHCW_to_NHWC<uint8_t>((uint8_t*)src_ptr, &src_image_shape, (uint8_t*)dst_ptr, &dst_image_shape);
+                break;
+            case HAILO_FORMAT_TYPE_UINT16:
+                transform__d2h_NHCW_to_NHWC<uint16_t>((uint16_t*)src_ptr, &src_image_shape, (uint16_t*)dst_ptr, &dst_image_shape);
+                break;
+            default:
+                LOGGER__ERROR("Invalid src-buffer's type format");
+                return HAILO_INVALID_ARGUMENT;
+        }
+    }
+    else if ((HAILO_FORMAT_ORDER_NC == src_format.order) &&
+            (HAILO_FORMAT_ORDER_NC == dst_format.order)) {
+            switch (src_format.type) {
+                case HAILO_FORMAT_TYPE_UINT8:
+                    transform__d2h_NC_to_NC<uint8_t>((uint8_t*)src_ptr, (uint8_t*)dst_ptr, &dst_image_shape);
+                    break;
+                case HAILO_FORMAT_TYPE_UINT16:
+                    transform__d2h_NC_to_NC<uint16_t>((uint16_t*)src_ptr, (uint16_t*)dst_ptr, &dst_image_shape);
+                    break;
+                default:
+                    LOGGER__ERROR("Invalid src-buffer's type format");
+                    return HAILO_INVALID_ARGUMENT;
+            }
+    }
+    else if ((HAILO_FORMAT_ORDER_NHW == src_format.order) &&
+            (HAILO_FORMAT_ORDER_NHW == dst_format.order)) {
+            switch (src_format.type) {
+                case HAILO_FORMAT_TYPE_UINT8:
+                    transform__d2h_NHW_to_NHW<uint8_t>((uint8_t*)src_ptr, &src_image_shape, (uint8_t*)dst_ptr, &dst_image_shape);
+                    break;
+                case HAILO_FORMAT_TYPE_UINT16:
+                    transform__d2h_NHW_to_NHW<uint16_t>((uint16_t*)src_ptr, &src_image_shape, (uint16_t*)dst_ptr, &dst_image_shape);
+                    break;
+                default:
+                    LOGGER__ERROR("Invalid src-buffer's type format");
+                    return HAILO_INVALID_ARGUMENT;
+            }
+    }
+    else if ((HAILO_FORMAT_ORDER_FCR == src_format.order) &&
+        (HAILO_FORMAT_ORDER_FCR == dst_format.order)) {
+            switch (src_format.type) {
+                case HAILO_FORMAT_TYPE_UINT8:
+                    transform__d2h_NHWC_to_NHWC<uint8_t>((uint8_t*)src_ptr, &src_image_shape, (uint8_t*)dst_ptr, &dst_image_shape);
+                    break;
+                case HAILO_FORMAT_TYPE_UINT16:
+                    transform__d2h_NHWC_to_NHWC<uint16_t>((uint16_t*)src_ptr, &src_image_shape, (uint16_t*)dst_ptr, &dst_image_shape);
+                    break;
+                default:
+                    LOGGER__ERROR("Invalid src-buffer's type format");
+                    return HAILO_INVALID_ARGUMENT;
+            }
+    }
+    else if ((HAILO_FORMAT_ORDER_F8CR == src_format.order) &&
+        (HAILO_FORMAT_ORDER_F8CR == dst_format.order)) {
+            switch (src_format.type) {
+                case HAILO_FORMAT_TYPE_UINT8:
+                    transform__d2h_F8CR<uint8_t>((uint8_t*)src_ptr, &src_image_shape, (uint8_t*)dst_ptr, &dst_image_shape);
+                    break;
+                case HAILO_FORMAT_TYPE_UINT16:
+                    transform__d2h_F8CR<uint16_t>((uint16_t*)src_ptr, &src_image_shape, (uint16_t*)dst_ptr, &dst_image_shape);
+                    break;
+                default:
+                    LOGGER__ERROR("Invalid src-buffer's type format");
+                    return HAILO_INVALID_ARGUMENT;
+            }
+    }
+    else if ((HAILO_FORMAT_ORDER_BAYER_RGB == src_format.order) &&
+        (HAILO_FORMAT_ORDER_BAYER_RGB == dst_format.order)) {
+            assert((1 == src_image_shape.features) && (1 == dst_image_shape.features));
+            switch (src_format.type) {
+                case HAILO_FORMAT_TYPE_UINT8:
+                    transform__d2h_BAYER_RGB<uint8_t>((uint8_t*)src_ptr, &src_image_shape, (uint8_t*)dst_ptr, &dst_image_shape);
+                    break;
+                case HAILO_FORMAT_TYPE_UINT16:
+                    transform__d2h_BAYER_RGB<uint16_t>((uint16_t*)src_ptr, &src_image_shape, (uint16_t*)dst_ptr, &dst_image_shape);
+                    break;
+                default:
+                    LOGGER__ERROR("Invalid src-buffer's type format");
+                    return HAILO_INVALID_ARGUMENT;
+            }
+    } else if ((HAILO_FORMAT_ORDER_NHCW == src_format.order) &&
+               (HAILO_FORMAT_ORDER_NCHW) == dst_format.order) {
+            switch (src_format.type) {
+                case HAILO_FORMAT_TYPE_UINT8:
+                    transform__d2h_NHCW_to_NCHW<uint8_t>((uint8_t*)src_ptr, &src_image_shape, (uint8_t*)dst_ptr, &dst_image_shape);
+                    break;
+                case HAILO_FORMAT_TYPE_UINT16:
+                    transform__d2h_NHCW_to_NCHW<uint16_t>((uint16_t*)src_ptr, &src_image_shape, (uint16_t*)dst_ptr, &dst_image_shape);
+                    break;
+                default:
+                    LOGGER__ERROR("Invalid src-buffer's type format");
+                    return HAILO_INVALID_ARGUMENT;
+            }
+    } else if ((HAILO_FORMAT_ORDER_NHW == src_format.order) &&
+               (HAILO_FORMAT_ORDER_NCHW) == dst_format.order) {
+
+            CHECK((src_image_shape.features == 1) && (dst_image_shape.features == 1), HAILO_INVALID_ARGUMENT,
+                "Invalid number of features. Expected 1, received hw: {}, user: {}",
+                    src_image_shape.features, dst_image_shape.features);
+            switch (src_format.type) {
+                // We call for transform__d2h_NHW_to_NHW function since NCHW is the same as NHW when the the image's features = 1.
+                case HAILO_FORMAT_TYPE_UINT8:
+                    transform__d2h_NHW_to_NHW<uint8_t>((uint8_t*)src_ptr, &src_image_shape, (uint8_t*)dst_ptr, &dst_image_shape);
+                    break;
+                case HAILO_FORMAT_TYPE_UINT16:
+                    transform__d2h_NHW_to_NHW<uint16_t>((uint16_t*)src_ptr, &src_image_shape, (uint16_t*)dst_ptr, &dst_image_shape);
+                    break;
+                default:
+                    LOGGER__ERROR("Invalid src-buffer's type format");
+                    return HAILO_INVALID_ARGUMENT;
+            }
+    } else if ((HAILO_FORMAT_ORDER_NHCW == src_format.order) &&
+               (HAILO_FORMAT_ORDER_NHW == dst_format.order)  &&
+               (0 != (HAILO_FORMAT_FLAGS_HOST_ARGMAX & src_format.flags)))  {
+            switch (src_format.type) {
+            case HAILO_FORMAT_TYPE_UINT8:
+                return transform__d2h_argmax_NHCW_to_NHW<uint8_t>((uint8_t*)src_ptr, src_image_shape, (uint8_t*)dst_ptr, dst_image_shape);
+            case HAILO_FORMAT_TYPE_UINT16:
+                return transform__d2h_argmax_NHCW_to_NHW<uint16_t>((uint16_t*)src_ptr, src_image_shape, (uint16_t*)dst_ptr, dst_image_shape);
+            default:
+                LOGGER__ERROR("Invalid src-buffer's type format");
+                return HAILO_INVALID_ARGUMENT;
+            }
+    } else if ((HAILO_FORMAT_ORDER_NHWC == src_format.order) &&
+               (HAILO_FORMAT_ORDER_NHWC) == dst_format.order) {
+            switch (src_format.type) {
+                case HAILO_FORMAT_TYPE_UINT8:
+                    transform__d2h_NHWC_to_NHWC<uint8_t>((uint8_t*)src_ptr, &src_image_shape, (uint8_t*)dst_ptr, &dst_image_shape);
+                    break;
+                case HAILO_FORMAT_TYPE_UINT16:
+                    transform__d2h_NHWC_to_NHWC<uint16_t>((uint16_t*)src_ptr, &src_image_shape, (uint16_t*)dst_ptr, &dst_image_shape);
+                    break;
+                default:
+                    LOGGER__ERROR("Invalid src-buffer's type format {}", src_format.type);
+                    return HAILO_INVALID_ARGUMENT;
+            }
+    } else {
+        LOGGER__INFO("Unsupported output stream transformation from hailo_format_order_t "
+                    "{} to hailo_format_order_t {}", src_format.order, dst_format.order);
+        return HAILO_INVALID_OPERATION;
+    }
+
+    return HAILO_SUCCESS;
+}
+
+/* Public funcs */
+hailo_status InputTransformContext::transform_inner(const void *src_ptr, void *quant_buffer, void *dst_ptr, 
+    MemoryView transpose_buffer)
+{
+    void *orig_dst_ptr = nullptr;
+    hailo_3d_image_shape_t transposed_image_shape = m_src_image_shape;
+    hailo_format_t quantized_src_format = m_src_format;
+
+    if (!(m_should_quantize || m_should_transpose || m_should_reorder)) {
+        /* If transform was created without any actual use - just copy src_ptr to dst_ptr */
+        LOGGER__WARN("Transformer was created, but not needed and can be removed. copies src buffer to dst buffer");
+        auto frame_size = HailoRTCommon::get_frame_size(m_dst_image_shape, m_dst_format);
+        memcpy(dst_ptr, src_ptr, frame_size);
+        return HAILO_SUCCESS;
+    }
+
+    if (m_should_quantize) {
+        /* If final step - output of this quant func is the dst_ptr */
+        orig_dst_ptr = (m_should_transpose || m_should_reorder) ? quant_buffer : dst_ptr;
+        auto status = quantize_stream(src_ptr, orig_dst_ptr);
+        CHECK_SUCCESS(status);
+        src_ptr = orig_dst_ptr;
+        quantized_src_format.type = m_dst_format.type;
+    }
+
+    if (!(m_should_transpose || m_should_reorder)) {
+        /* If quantize is the only step - need to copy src buffer to dst buffer */
+        auto frame_size = HailoRTCommon::get_frame_size(m_dst_image_shape, m_dst_format);
+        memcpy(dst_ptr, src_ptr, frame_size);
+        return HAILO_SUCCESS;
+    }
+
+    if (m_should_transpose) {
+        if (transpose_buffer.empty()) {
+            LOGGER__ERROR("Transpose buffer not given");
+            return HAILO_INVALID_ARGUMENT;
+        }
+
+        if (transpose_buffer.size() != HailoRTCommon::get_frame_size(m_src_image_shape, quantized_src_format)) {
+            LOGGER__ERROR("Transpose buffer size mismatch (expected {}, actual {})",
+                HailoRTCommon::get_frame_size(m_src_image_shape, quantized_src_format), transpose_buffer.size());
+            return HAILO_INVALID_ARGUMENT;
+        }
+
+        /* If final step - output of this quant func is the dst_ptr */
+        orig_dst_ptr = (m_should_reorder) ? transpose_buffer.data() : dst_ptr;
+        auto status = transform__transpose_buffer(src_ptr, m_src_image_shape, quantized_src_format, orig_dst_ptr);
+        CHECK_SUCCESS(status);
+
+        src_ptr = transpose_buffer.data();
+        transposed_image_shape = transposed_shape(m_src_image_shape);
+    }
+
+    if (m_should_reorder){
+        auto status = reorder_input_stream(src_ptr, transposed_image_shape, quantized_src_format, dst_ptr, 
+            m_dst_image_shape, m_dst_format);
+        CHECK_SUCCESS(status);
+    }
+
+    return HAILO_SUCCESS;
+}
+
+hailo_status FrameOutputTransformContext::transform_inner(const void *src_ptr, void *dst_ptr, MemoryView transpose_buffer)
+{
+    hailo_format_t transposed_format = m_dst_format;
+    hailo_3d_image_shape_t transposed_image_shape = m_dst_image_shape;
+    transposed_format.type = m_src_format.type;
+
+    void *orig_dst_ptr = nullptr;
+    void *orig_src_ptr = nullptr;
+
+    if (!(m_should_quantize || m_should_transpose || m_should_reorder)) {
+        /* If transform context was created without any actual use - just copy src_ptr to dst_ptr */
+        LOGGER__WARN("Transform context was created, but not needed and can be removed. copies src buffer to dst buffer");
+        auto frame_size = HailoRTCommon::get_frame_size(m_dst_image_shape, m_dst_format);
+        memcpy(dst_ptr, src_ptr, frame_size);
+        return HAILO_SUCCESS;
+    }
+
+    if (m_should_reorder) {
+        if (m_should_transpose) {
+            /* If user needs to reorder and transform - the output of the reorder is the transform buffer*/
+            if (transpose_buffer.empty()) {
+                LOGGER__ERROR("Transpose buffer not given");
+                return HAILO_INVALID_ARGUMENT;
+            }
+
+            if (transpose_buffer.size() != HailoRTCommon::get_frame_size(m_dst_image_shape, transposed_format)) {
+                LOGGER__ERROR("Transpose buffer size mismatch (expected {}, actual {})",
+                    HailoRTCommon::get_frame_size(m_dst_image_shape, transposed_format), transpose_buffer.size());
+                return HAILO_INVALID_ARGUMENT;
+            }
+
+            // Prepare transpose - the order transformation will be applied to the transpose buffer, later we will transpose
+            // from dst_ptr (transpose_buffer) to orig_dst_ptr (user buffer)
+            orig_dst_ptr = transpose_buffer.data();
+            transposed_image_shape = transposed_shape(m_dst_image_shape);
+        } else {
+            orig_dst_ptr = dst_ptr;
+        }
+        auto status = reorder_output_stream(src_ptr, m_src_image_shape, m_src_format, orig_dst_ptr, transposed_image_shape, 
+            m_dst_format);
+        CHECK_SUCCESS(status);
+    }
+
+    if (m_should_transpose) {
+        orig_src_ptr = (m_should_reorder) ? orig_dst_ptr : const_cast<void *>(src_ptr);
+        auto status = transform__transpose_buffer(orig_src_ptr, transposed_image_shape, transposed_format, dst_ptr);
+        CHECK_SUCCESS(status);
+
+        transposed_image_shape = transposed_shape(transposed_image_shape);
+    }
+
+    if (m_should_quantize) {
+        auto status = quantize_stream(dst_ptr);
+        CHECK_SUCCESS(status);
+    }
+    
+    if (!(m_should_transpose || m_should_reorder)) {
+        /* If quantize is the only step - need to copy src buffer to dst buffer */
+        auto frame_size = HailoRTCommon::get_frame_size(m_dst_image_shape, m_dst_format);
+        memcpy(dst_ptr, src_ptr, frame_size);
+    }
+
+    return HAILO_SUCCESS;
+}
+
+
+hailo_status transform_demux_raw_frame(const void *src, uint32_t offset,
+    hailo_mux_info_t *mux_info, uint32_t mux_row_count)
+{
+    // This is a recursive function with a maximum depth of HailoRTCommon::MUX_INFO_COUNT.
+    hailo_status status = HAILO_UNINITIALIZED;
+    struct hailo_mux_info_t *predecessor = NULL;
+    uint32_t row_size = 0;
+
+    CHECK_ARG_NOT_NULL(src);
+
+    for (uint32_t i = 0; i < mux_row_count; i++) {
+        for (uint32_t j = 0; j < mux_info->successors_count; j++) {
+            predecessor = mux_info->successors[j];
+            row_size = predecessor->row_size;
+
+            if ((predecessor->info.is_mux) && (i < predecessor->rows_gcd)) {
+                status = transform_demux_raw_frame(src, offset, predecessor, predecessor->info.hw_shape.height / mux_info->rows_gcd);
+                CHECK_SUCCESS(status);                
+            }
+
+            if (!(predecessor->info.is_mux)) {
+                if (predecessor->row_counter < predecessor->info.shape.height) {
+                    memcpy((uint8_t*)predecessor->buffer + predecessor->current_offset, (uint8_t*)src + offset, row_size);
+                    predecessor->current_offset += row_size;
+                }
+                
+                predecessor->row_counter++;
+                if (predecessor->row_counter == (predecessor->info.hw_shape.height + 1)) {
+                    predecessor->row_counter = 0;
+                }
+            }
+            
+            offset += row_size;
+        }
+    }
+
+    return HAILO_SUCCESS;
+}
+
+hailo_status validate_input_transform_params(hailo_3d_image_shape_t src_image_shape, hailo_format_t src_format,
+    hailo_3d_image_shape_t dst_image_shape, hailo_format_t dst_format)
+{
+    /* Check quantize flags - where quantize is no needed */
+    if ((HAILO_FORMAT_FLAGS_QUANTIZED & src_format.flags) && !(HAILO_FORMAT_FLAGS_QUANTIZED & dst_format.flags)) {
+        LOGGER__ERROR("Cannot dequantize input data");
+        return HAILO_INVALID_ARGUMENT;
+    }
+
+    if ((HAILO_FORMAT_FLAGS_QUANTIZED & src_format.flags) && (HAILO_FORMAT_TYPE_FLOAT32 == src_format.type)) {
+        LOGGER__ERROR("float32 data isn't quantized");
+        return HAILO_INVALID_ARGUMENT;
+    }
+
+    /* Check for overscale transformation*/
+    CHECK((hailo_format_type_t::HAILO_FORMAT_TYPE_AUTO == src_format.type) || (src_format.type >= dst_format.type),
+        HAILO_INVALID_ARGUMENT, "Overscale transformation is not supported");
+
+    /* Check device type */
+    if (!((HAILO_FORMAT_TYPE_UINT16 == dst_format.type) || (HAILO_FORMAT_TYPE_UINT8 == dst_format.type))) {
+        LOGGER__ERROR("unsupported device type {}", dst_format.type);
+        return HAILO_INVALID_ARGUMENT;
+    }
+
+    /* Check reorder flags - where no reorder is needed */
+    if ((HAILO_FORMAT_ORDER_FCR == src_format.order) &&
+        (HAILO_FORMAT_ORDER_FCR == dst_format.order)) {
+        if (0 != (dst_image_shape.features % 8)) {
+            LOGGER__ERROR("HW features must be aligned to {}. passed hw features - {}",
+                HW_DATA_ALIGNMENT, dst_image_shape.features);
+            return HAILO_INVALID_ARGUMENT;
+        }
+    } else if ((HAILO_FORMAT_ORDER_BAYER_RGB == src_format.order) &&
+        (HAILO_FORMAT_ORDER_BAYER_RGB == dst_format.order)) {
+        if (src_image_shape.features != 1) {
+            LOGGER__ERROR("Invalid Bayer user features. Expected 1, received {}", src_image_shape.features);
+            return HAILO_INVALID_ARGUMENT;
+        }
+    } else if ((HAILO_FORMAT_ORDER_12_BIT_BAYER_RGB == src_format.order) &&
+        (HAILO_FORMAT_ORDER_12_BIT_BAYER_RGB == dst_format.order)) {
+        if (src_image_shape.features != 1) {
+            LOGGER__ERROR("Invalid Bayer user features. Expected 1, received {}", src_image_shape.features);
+            return HAILO_INVALID_ARGUMENT;
+        }
+    } else if ((HAILO_FORMAT_ORDER_YUY2 == src_format.order) &&
+        (HAILO_FORMAT_ORDER_YUY2 == dst_format.order)) {
+        auto shape_size = HailoRTCommon::get_shape_size(src_image_shape);
+        CHECK((shape_size % HW_DATA_ALIGNMENT) == 0, HAILO_INVALID_ARGUMENT,
+          "YUY2_to_YUY2 Transform shape_size must be aligned to {}", HW_DATA_ALIGNMENT);
+    }
+
+    return HAILO_SUCCESS;
+}
+
+hailo_status validate_output_transform_params(hailo_3d_image_shape_t src_image_shape, hailo_format_t src_format,
+    hailo_3d_image_shape_t dst_image_shape, hailo_format_t dst_format)
+{
+    /* Check quantize flags - where quantize is no needed */
+    if (!(HAILO_FORMAT_FLAGS_QUANTIZED & src_format.flags) && (HAILO_FORMAT_FLAGS_QUANTIZED & dst_format.flags)) {
+        LOGGER__ERROR("Cannot quantize output data");
+        return HAILO_INVALID_ARGUMENT;
+    }
+
+    /* Check device type */
+    if (!((HAILO_FORMAT_TYPE_UINT16 == src_format.type) || (HAILO_FORMAT_TYPE_UINT8 == src_format.type))) {
+        LOGGER__ERROR("unsupported device type {}", dst_format.type);
+        return HAILO_INVALID_ARGUMENT;
+    }
+
+    /* Check for underscale transformation*/
+    CHECK((hailo_format_type_t::HAILO_FORMAT_TYPE_AUTO == dst_format.type) || (src_format.type <= dst_format.type),
+        HAILO_INVALID_ARGUMENT, "Underscale transformation is not supported");
+
+    /* Check reorder flags - where no reorder is needed */
+    if ((HAILO_FORMAT_ORDER_BAYER_RGB == src_format.order) &&
+        (HAILO_FORMAT_ORDER_BAYER_RGB == dst_format.order)) {
+        if ((src_image_shape.features != 1) || (dst_image_shape.features != 1)) {
+            LOGGER__ERROR("Invalid Bayer user or hw features. Expected 1, received user: {}, hw: {}",
+                src_image_shape.features, dst_image_shape.features);
+            return HAILO_INVALID_ARGUMENT;
+        }
+    }
+
+    return HAILO_SUCCESS;
+}
+
+bool InputTransformContext::is_transformation_required(
+    const hailo_3d_image_shape_t &src_image_shape, const hailo_format_t &src_format, 
+    const hailo_3d_image_shape_t &dst_image_shape, const hailo_format_t &dst_format, 
+    const hailo_quant_info_t &quant_info)
+{
+    auto host_format = HailoRTDefaults::expand_auto_format(src_format, dst_format);
+    return TransformContextUtils::is_transformation_required(HAILO_H2D_STREAM, src_image_shape, host_format,
+        dst_image_shape, dst_format, quant_info);
+}
+
+std::string InputTransformContext::description() const
+{
+    std::stringstream transform_description;
+    bool first = true;
+
+    if (m_should_quantize) {
+        if (!first) {
+            transform_description << " | ";
+        } else {
+            first = false;
+        }
+        transform_description << TransformContextUtils::make_quantization_description(m_src_format.type, m_dst_format.type, m_dst_quant_info);
+    }
+
+    if (m_should_transpose) {
+        if (!first) {
+            transform_description << " | ";
+        } else {
+            first = false;
+        }
+        transform_description << TransformContextUtils::make_transpose_description(m_src_image_shape, transposed_shape(m_src_image_shape));
+    }
+
+    if (m_should_reorder) {
+        if (!first) {
+            transform_description << " | ";
+        } else {
+            first = false;
+        }
+        transform_description << TransformContextUtils::make_reorder_description(m_src_format.order, m_src_image_shape, m_dst_format.order, m_dst_image_shape);
+    }
+
+    return transform_description.str();
+}
+
+Expected<std::unique_ptr<InputTransformContext>> InputTransformContext::create(const hailo_3d_image_shape_t &src_image_shape,
+    const hailo_format_t &src_format, const hailo_3d_image_shape_t &dst_image_shape,
+    const hailo_format_t &dst_format, const hailo_quant_info_t &dst_quant_info)
+{
+    auto status = validate_input_transform_params(src_image_shape, src_format, dst_image_shape, dst_format);
+    CHECK_SUCCESS_AS_EXPECTED(status);
+
+    const auto internal_src_format = HailoRTDefaults::expand_auto_format(src_format, dst_format);
+
+    const auto src_frame_size = HailoRTCommon::get_frame_size(src_image_shape, internal_src_format);
+    const auto dst_frame_size = HailoRTCommon::get_frame_size(dst_image_shape, dst_format);
+
+    Buffer quant_buffer;
+    bool should_quantize = TransformContextUtils::should_quantize(HAILO_H2D_STREAM, src_format, dst_format, 
+        dst_quant_info);
+    if (should_quantize) {
+        auto expected_quant_buffer = Buffer::create(src_frame_size, 0);
+        CHECK_EXPECTED(expected_quant_buffer);
+        quant_buffer = expected_quant_buffer.release();
+    }
+
+    Buffer transpose_buffer;
+    bool should_transpose = TransformContextUtils::should_transpose(src_format.flags, dst_format.flags);
+    if (should_transpose) {
+        auto expected_transpose_buffer = Buffer::create(get_transpose_buffer_size(src_image_shape,
+            dst_format.type));
+        CHECK_EXPECTED(expected_transpose_buffer);
+        transpose_buffer = expected_transpose_buffer.release();
+    }
+
+    auto should_reorder = TransformContextUtils::should_reorder(src_image_shape, src_format, dst_image_shape, dst_format);
+
+    std::unique_ptr<InputTransformContext> transform_context(new (std::nothrow) InputTransformContext(src_frame_size, src_image_shape,
+        internal_src_format, dst_frame_size, dst_image_shape, dst_format, dst_quant_info, std::move(quant_buffer),
+        std::move(transpose_buffer), should_quantize, should_transpose, should_reorder));
+    CHECK_AS_EXPECTED(nullptr != transform_context, HAILO_OUT_OF_HOST_MEMORY);
+
+    return transform_context;
+}
+
+Expected<std::unique_ptr<InputTransformContext>> InputTransformContext::create(const hailo_stream_info_t &stream_info,
+    const hailo_transform_params_t &transform_params)
+{
+    return create(stream_info.shape, transform_params.user_buffer_format, stream_info.hw_shape, stream_info.format,
+        stream_info.quant_info);
+}
+
+Expected<std::unique_ptr<InputTransformContext>> InputTransformContext::create(const hailo_stream_info_t &stream_info, bool quantized,
+    hailo_format_type_t format_type)
+{
+    return create(stream_info, HailoRTDefaults::get_transform_params(quantized, format_type));
+}
+
+InputTransformContext::InputTransformContext(size_t src_frame_size, const hailo_3d_image_shape_t &src_image_shape,
+    const hailo_format_t &src_format, size_t dst_frame_size, const hailo_3d_image_shape_t &dst_image_shape,
+    const hailo_format_t &dst_format, const hailo_quant_info_t &dst_quant_info, Buffer &&quant_buffer,
+    Buffer &&transpose_buffer,const bool should_quantize, const bool should_transpose, const bool should_reorder) :
+        m_src_frame_size(src_frame_size),
+        m_src_image_shape(src_image_shape),
+        m_src_format(src_format),
+        m_dst_frame_size(dst_frame_size),
+        m_dst_image_shape(dst_image_shape),
+        m_dst_format(dst_format),
+        m_dst_quant_info(dst_quant_info),
+        m_should_quantize(should_quantize),
+        m_should_transpose(should_transpose),
+        m_should_reorder(should_reorder),
+        m_quant_buffer(std::move(quant_buffer)),
+        m_transpose_buffer(std::move(transpose_buffer))
+{}
+
+hailo_status InputTransformContext::transform(const MemoryView src, MemoryView dst)
+{
+    /* Check sizes */
+    CHECK(src.size() == m_src_frame_size, HAILO_INVALID_ARGUMENT,
+        "src size must be {}. passed size - {}", m_src_frame_size, src.size());
+    CHECK(dst.size() == m_dst_frame_size, HAILO_INVALID_ARGUMENT,
+        "dst_size must be {}. passed size - {}", m_dst_frame_size, dst.size());
+
+    hailo_status status = transform_inner(src.data(),
+        quant_buffer().data(), dst.data(), transpose_buffer());
+    CHECK_SUCCESS(status);
+    return HAILO_SUCCESS;
+}
+
+size_t InputTransformContext::get_src_frame_size() const
+{
+    return m_src_frame_size;
+}
+
+size_t InputTransformContext::get_dst_frame_size() const
+{
+    return m_dst_frame_size;
+}
+
+bool OutputTransformContext::is_transformation_required(
+    const hailo_3d_image_shape_t &src_image_shape, const hailo_format_t &src_format, 
+    const hailo_3d_image_shape_t &dst_image_shape, const hailo_format_t &dst_format, 
+    const hailo_quant_info_t &quant_info)
+{
+    auto host_format = HailoRTDefaults::expand_auto_format(dst_format, src_format);
+    return TransformContextUtils::is_transformation_required(HAILO_D2H_STREAM, src_image_shape, src_format, 
+        dst_image_shape, host_format, quant_info);
+}
+
+Expected<std::unique_ptr<OutputTransformContext>> OutputTransformContext::create(const hailo_3d_image_shape_t &src_image_shape,
+        const hailo_format_t &src_format, const hailo_3d_image_shape_t &dst_image_shape,
+        const hailo_format_t &dst_format, const hailo_quant_info_t &dst_quant_info, const hailo_nms_info_t &nms_info)
+{
+    auto status = validate_output_transform_params(src_image_shape, src_format, dst_image_shape, dst_format);
+    CHECK_SUCCESS_AS_EXPECTED(status);
+    
+    if (HAILO_FORMAT_ORDER_HAILO_NMS == src_format.order) {
+        return NMSOutputTransformContext::create(src_format, dst_format, dst_quant_info, nms_info);
+    }
+
+    return FrameOutputTransformContext::create(src_image_shape, src_format, dst_image_shape, dst_format, dst_quant_info);
+}
+
+Expected<std::unique_ptr<OutputTransformContext>> OutputTransformContext::create(const hailo_stream_info_t &stream_info,
+    const hailo_transform_params_t &transform_params)
+{
+    return create(stream_info.hw_shape, stream_info.format, stream_info.shape,
+        transform_params.user_buffer_format, stream_info.quant_info, stream_info.nms_info);
+}
+
+Expected<std::unique_ptr<OutputTransformContext>> OutputTransformContext::create(const hailo_stream_info_t &stream_info, bool quantized,
+    hailo_format_type_t format_type)
+{
+    return create(stream_info, HailoRTDefaults::get_transform_params(quantized, format_type));
+}
+
+OutputTransformContext::OutputTransformContext(size_t src_frame_size, const hailo_format_t &src_format, size_t dst_frame_size,
+    const hailo_format_t &dst_format, const hailo_quant_info_t &dst_quant_info, const bool should_quantize, 
+    const bool should_transpose, const bool should_reorder) :
+        m_src_frame_size(src_frame_size),
+        m_src_format(src_format),
+        m_dst_frame_size(dst_frame_size),
+        m_dst_format(dst_format),
+        m_dst_quant_info(dst_quant_info),
+        m_should_quantize(should_quantize),
+        m_should_transpose(should_transpose),
+        m_should_reorder(should_reorder)
+{}
+
+FrameOutputTransformContext::FrameOutputTransformContext(size_t src_frame_size, const hailo_3d_image_shape_t &src_image_shape,
+    const hailo_format_t &src_format, size_t dst_frame_size, const hailo_3d_image_shape_t &dst_image_shape,
+    const hailo_format_t &dst_format, const hailo_quant_info_t &dst_quant_info, Buffer&& transpose_buffer,
+    const bool should_quantize, const bool should_transpose, const bool should_reorder) :
+        OutputTransformContext(src_frame_size, src_format, dst_frame_size, dst_format, dst_quant_info, should_quantize, 
+            should_transpose, should_reorder), m_src_image_shape(src_image_shape), m_dst_image_shape(dst_image_shape), 
+            m_transpose_buffer(std::move(transpose_buffer))
+{}
+
+Expected<std::unique_ptr<OutputTransformContext>> FrameOutputTransformContext::create(const hailo_3d_image_shape_t &src_image_shape,
+    const hailo_format_t &src_format, const hailo_3d_image_shape_t &dst_image_shape,
+    const hailo_format_t &dst_format, const hailo_quant_info_t &dst_quant_info)
+{
+    const auto internal_dst_format = HailoRTDefaults::expand_auto_format(dst_format, src_format);
+
+    const auto src_frame_size = HailoRTCommon::get_frame_size(src_image_shape, src_format);
+    const auto dst_frame_size = HailoRTCommon::get_frame_size(dst_image_shape, internal_dst_format);
+
+    auto should_quantize = TransformContextUtils::should_quantize(HAILO_D2H_STREAM, src_format, dst_format, 
+        dst_quant_info);
+
+    Buffer transpose_buffer;
+    auto should_transpose = TransformContextUtils::should_transpose(src_format.flags, dst_format.flags);
+    if (should_transpose) {
+        auto expected_transpose_buffer = Buffer::create(get_transpose_buffer_size(dst_image_shape, src_format.type));
+        CHECK_EXPECTED(expected_transpose_buffer);
+        transpose_buffer = expected_transpose_buffer.release();
+    }
+
+    auto should_reorder = TransformContextUtils::should_reorder(src_image_shape, src_format, dst_image_shape, dst_format);
+
+    std::unique_ptr<OutputTransformContext> frame_transform_context = std::make_unique<FrameOutputTransformContext>(src_frame_size,
+        src_image_shape, src_format, dst_frame_size, dst_image_shape, internal_dst_format, dst_quant_info, std::move(transpose_buffer),
+        should_quantize, should_transpose, should_reorder);
+
+    CHECK_AS_EXPECTED(nullptr != frame_transform_context, HAILO_OUT_OF_HOST_MEMORY);
+
+    return frame_transform_context;
+}
+
+NMSOutputTransformContext::NMSOutputTransformContext(size_t src_frame_size, const hailo_format_t &src_format, 
+    size_t dst_frame_size, const hailo_format_t &dst_format, const hailo_quant_info_t &dst_quant_info,
+    const hailo_nms_info_t &nms_info, Buffer &&quant_buffer, const bool should_quantize, const bool should_transpose) :
+        OutputTransformContext(src_frame_size, src_format, dst_frame_size, dst_format, dst_quant_info, should_quantize ,should_transpose, 
+        true), m_nms_info(nms_info), m_chunk_offsets(nms_info.chunks_per_frame, 0), m_quant_buffer(std::move(quant_buffer))
+{}
+
+Expected<std::unique_ptr<OutputTransformContext>> NMSOutputTransformContext::create(const hailo_format_t &src_format,
+    const hailo_format_t &dst_format, const hailo_quant_info_t &dst_quant_info, const hailo_nms_info_t &nms_info)
+{
+    // Validate params
+    CHECK_AS_EXPECTED(HAILO_FORMAT_ORDER_HAILO_NMS == src_format.order, HAILO_INVALID_ARGUMENT,
+        "Format order should be HAILO_FORMAT_ORDER_HAILO_NMS");
+
+    const auto internal_dst_format = HailoRTDefaults::expand_auto_format(dst_format, src_format);
+
+    CHECK_AS_EXPECTED(HAILO_FORMAT_ORDER_HAILO_NMS == internal_dst_format.order, HAILO_INVALID_ARGUMENT,
+        "Format order should be HAILO_FORMAT_ORDER_HAILO_NMS");
+
+    if (internal_dst_format.flags & HAILO_FORMAT_FLAGS_QUANTIZED) {
+        CHECK_AS_EXPECTED(HAILO_FORMAT_TYPE_UINT16 == internal_dst_format.type, HAILO_INVALID_ARGUMENT,
+            "Format order HAILO_FORMAT_ORDER_HAILO_NMS without quantization is allowed only with type HAILO_FORMAT_TYPE_UINT16");
+    }
+    else {
+        CHECK_AS_EXPECTED((HAILO_FORMAT_TYPE_UINT16 == internal_dst_format.type) || (HAILO_FORMAT_TYPE_FLOAT32 == internal_dst_format.type),
+            HAILO_INVALID_ARGUMENT,
+            "Format order HAILO_FORMAT_ORDER_HAILO_NMS with quantization is allowed only with type HAILO_FORMAT_TYPE_UINT16 or HAILO_FORMAT_TYPE_FLOAT32");
+    }
+
+    const auto src_frame_size = HailoRTCommon::get_nms_hw_frame_size(nms_info);
+    auto dst_frame_size = HailoRTCommon::get_nms_host_frame_size(nms_info, internal_dst_format);
+
+    Buffer quant_buffer;
+    const bool should_quantize = (src_format.flags & HAILO_FORMAT_FLAGS_QUANTIZED) &&
+        !(internal_dst_format.flags & HAILO_FORMAT_FLAGS_QUANTIZED);
+    if (should_quantize) {
+        dst_frame_size = HailoRTCommon::get_nms_host_frame_size(nms_info, internal_dst_format);
+        auto expected_nms_quant_buffer = Buffer::create(dst_frame_size, 0);
+        CHECK_EXPECTED(expected_nms_quant_buffer);
+        quant_buffer = expected_nms_quant_buffer.release();
+    }
+
+    auto should_transpose = TransformContextUtils::should_transpose(src_format.flags, dst_format.flags);
+
+    std::unique_ptr<OutputTransformContext> nms_transform_context = std::make_unique<NMSOutputTransformContext>(src_frame_size,
+        src_format, dst_frame_size, internal_dst_format, dst_quant_info, nms_info, std::move(quant_buffer),
+        should_quantize, should_transpose);
+    CHECK_AS_EXPECTED(nullptr != nms_transform_context, HAILO_OUT_OF_HOST_MEMORY);
+
+    return nms_transform_context;
+}
+
+hailo_status FrameOutputTransformContext::transform(const MemoryView src, MemoryView dst)
+{
+    /* Check sizes */
+    CHECK(src.size() == m_src_frame_size, HAILO_INVALID_ARGUMENT,
+        "src size must be {}. passed size - {}", m_src_frame_size, src.size());
+    CHECK(dst.size() == m_dst_frame_size, HAILO_INVALID_ARGUMENT,
+        "dst_size must be {}. passed size - {}", m_dst_frame_size, dst.size());
+
+    auto status = transform_inner(src.data(), dst.data(), MemoryView(m_transpose_buffer));
+    CHECK_SUCCESS(status);
+
+    return HAILO_SUCCESS;
+}
+
+hailo_status NMSOutputTransformContext::transform(const MemoryView src, MemoryView dst)
+{
+    /* Check sizes */
+    CHECK(src.size() == m_src_frame_size, HAILO_INVALID_ARGUMENT,
+        "src size must be {}. passed size - {}", m_src_frame_size, src.size());
+    CHECK(dst.size() == m_dst_frame_size, HAILO_INVALID_ARGUMENT,
+        "dst_size must be {}. passed size - {}", m_dst_frame_size, dst.size());
+
+    assert((HAILO_FORMAT_ORDER_HAILO_NMS == m_src_format.order) && (HAILO_FORMAT_ORDER_HAILO_NMS == m_dst_format.order));
+
+    auto shape_size = HailoRTCommon::get_nms_host_shape_size(m_nms_info);
+
+    if (!(HAILO_FORMAT_FLAGS_QUANTIZED & m_src_format.flags) && (HAILO_FORMAT_FLAGS_QUANTIZED & m_dst_format.flags)) {
+        LOGGER__ERROR("Cannot quantize output data");
+        return HAILO_INVALID_OPERATION;
+    }
+
+    if ((HAILO_FORMAT_FLAGS_TRANSPOSED & m_src_format.flags) || (HAILO_FORMAT_FLAGS_TRANSPOSED & m_dst_format.flags)) {
+        LOGGER__ERROR("NMS doesn't support transposed format currently");
+        return HAILO_INVALID_OPERATION;
+    }
+
+    if (!((HAILO_FORMAT_FLAGS_QUANTIZED & m_src_format.flags) &&
+        !(HAILO_FORMAT_FLAGS_QUANTIZED & m_dst_format.flags))) {
+            transform__d2h_NMS((uint8_t*)src.data(), (uint8_t*)dst.data(), m_nms_info, m_chunk_offsets);
+    } 
+    else {
+        transform__d2h_NMS((uint8_t*)src.data(), m_quant_buffer.data(), m_nms_info, m_chunk_offsets);
+    }
+
+    if ((HAILO_FORMAT_FLAGS_QUANTIZED & m_src_format.flags) && !(HAILO_FORMAT_FLAGS_QUANTIZED & m_dst_format.flags)) {
+        // NMS has to be uint16 or float32
+        switch (m_dst_format.type) {
+            case HAILO_FORMAT_TYPE_UINT16:
+                if (m_src_format.type == HAILO_FORMAT_TYPE_UINT16) {
+                    Quantization::dequantize_output_buffer_nms<uint16_t, uint16_t>((uint16_t*)m_quant_buffer.data(),
+                        (uint16_t*)dst.data(), shape_size, m_dst_quant_info, m_nms_info.number_of_classes);
+                } 
+                else {
+                    return HAILO_INVALID_OPERATION;
+                }
+                break;
+            case HAILO_FORMAT_TYPE_FLOAT32:
+                if (m_src_format.type == HAILO_FORMAT_TYPE_UINT16) {
+                    Quantization::dequantize_output_buffer_nms<float32_t, uint16_t>((uint16_t*)m_quant_buffer.data(),
+                        (float32_t*)dst.data(), shape_size, m_dst_quant_info, m_nms_info.number_of_classes);
+                }
+                else {
+                    return HAILO_INVALID_OPERATION;
+                }
+                break;
+            default:
+                LOGGER__ERROR("Invalid dst-buffer's type format");
+                return HAILO_INVALID_ARGUMENT;
+        }
+    }
+
+    return HAILO_SUCCESS;
+}
+
+std::string FrameOutputTransformContext::description() const
+{
+    std::stringstream transform_description;
+    bool first = true;
+
+    if (m_should_quantize) {
+        if (!first) {
+            transform_description << " | ";
+        } else {
+            first = false;
+        }
+        transform_description << TransformContextUtils::make_quantization_description(m_src_format.type, m_dst_format.type, m_dst_quant_info);
+    }
+
+    if (m_should_transpose) {
+        if (!first) {
+            transform_description << " | ";
+        } else {
+            first = false;
+        }
+        transform_description << TransformContextUtils::make_transpose_description(m_src_image_shape, transposed_shape(m_src_image_shape));
+    }
+
+    if (m_should_reorder) {
+        if (!first) {
+            transform_description << " | ";
+        } else {
+            first = false;
+        }
+        transform_description << TransformContextUtils::make_reorder_description(m_src_format.order, m_src_image_shape, m_dst_format.order, m_dst_image_shape);
+    }
+
+    return transform_description.str();
+}
+
+std::string NMSOutputTransformContext::description() const
+{
+    std::stringstream transform_description;
+
+    transform_description << "number_of_classes: " << m_nms_info.number_of_classes <<
+        ", max_bboxes_per_class: " << m_nms_info.max_bboxes_per_class;
+
+    if (m_should_quantize) {
+        transform_description << " | " <<
+            TransformContextUtils::make_quantization_description(m_src_format.type, m_dst_format.type, m_dst_quant_info);
+    }
+
+    return transform_description.str();
+}
+
+size_t OutputTransformContext::get_src_frame_size() const
+{
+    return m_src_frame_size;
+}
+
+size_t OutputTransformContext::get_dst_frame_size() const
+{
+    return m_dst_frame_size;
+}
+
+Expected<std::unique_ptr<OutputDemuxer>> OutputDemuxer::create(OutputStream &output_stream)
+{
+    auto obj = OutputDemuxerBase::create(output_stream.get_frame_size(), output_stream.get_layer_info());
+    CHECK_EXPECTED(obj);
+
+    auto obj_ptr = make_unique_nothrow<OutputDemuxerBase>(obj.release());
+    CHECK_AS_EXPECTED(nullptr != obj_ptr, HAILO_OUT_OF_HOST_MEMORY);
+
+    return Expected<std::unique_ptr<OutputDemuxer>>(std::move(obj_ptr));
+}
+
+Expected<OutputDemuxerBase> OutputDemuxerBase::create(size_t src_frame_size, const LayerInfo &layer_info)
+{
+    // Validate params
+    CHECK_AS_EXPECTED((HAILO_FORMAT_ORDER_HAILO_NMS != layer_info.format.order), HAILO_INVALID_OPERATION,
+        "NMS layer does not support mux.");
+
+    auto mux_infos = get_mux_infos_from_layer_info(layer_info);
+    CHECK_EXPECTED(mux_infos);
+
+    return OutputDemuxerBase(src_frame_size, mux_infos.release());
+}
+
+hailo_status OutputDemuxerBase::get_mux_info_from_layer_info_impl(hailo_mux_info_t &mux_info, const LayerInfo &layer_info,
+    uint32_t &offset, uint32_t height_ratio, std::vector<hailo_mux_info_t> &res, size_t &number_of_mux_infos)
+{
+    // This is a recursive function with a maximum depth of HailoRTCommon::MUX_INFO_COUNT. 
+    mux_info.info = LayerInfoUtils::get_stream_info_from_layer_info(layer_info);
+
+    mux_info.row_size = height_ratio * layer_info.hw_shape.width * layer_info.hw_shape.features;
+    mux_info.row_counter = 0;
+
+    if (mux_info.info.is_mux) {
+        int i = 0;
+        CHECK(layer_info.predecessor.size() <= HailoRTCommon::MUX_INFO_COUNT, HAILO_INTERNAL_FAILURE, "Too many mux edges");
+        for (auto &pred : layer_info.predecessor) {
+            hailo_mux_info_t successor = {};
+            auto status = get_mux_info_from_layer_info_impl(successor,
+                pred, offset, layer_info.height_ratios[i], res, number_of_mux_infos);
+            CHECK_SUCCESS(status);
+            res.push_back(successor);
+            mux_info.successors[i] = &(res.back());
+            i++;
+            number_of_mux_infos++;
+        }
+        mux_info.successors_count = static_cast<uint32_t>(layer_info.predecessor.size());
+        mux_info.rows_gcd = layer_info.height_gcd;
+    } else {
+        mux_info.offset = offset;
+        offset += mux_info.info.hw_frame_size;
+    }
+
+    return HAILO_SUCCESS;
+}
+
+hailo_status fuse_buffers(const std::vector<MemoryView> &buffers,
+    const std::vector<hailo_nms_info_t> &infos_of_buffers, MemoryView dst)
+{
+    CHECK_ARG_NOT_NULL(dst.data());
+    CHECK(buffers.size() == infos_of_buffers.size(), HAILO_INVALID_ARGUMENT,
+        "Vectors of buffers and NMS infos does not match!");
+    CHECK(HailoRTCommon::MAX_DEFUSED_LAYER_COUNT >= buffers.size(), HAILO_INVALID_ARGUMENT,
+        "Buffers count is bigger than allowed! ({} > {})", buffers.size(), HailoRTCommon::MAX_DEFUSED_LAYER_COUNT);
+
+    // Order the buffers by their class group index, which specifies in what order they should me fused.
+    auto frames = std::vector<std::pair<const hailo_nms_info_t*, const MemoryView*>>(buffers.size());
+    for (uint32_t i = 0; i < infos_of_buffers.size(); ++i) {
+        frames[infos_of_buffers[i].defuse_info.class_group_index].first = &infos_of_buffers[i];
+        frames[infos_of_buffers[i].defuse_info.class_group_index].second = &buffers[i];
+    }
+
+    uint32_t total_num_of_classes = 0;
+    size_t total_size_of_buffers = 0;
+    for (const auto &frame_pair : frames) {
+        auto &info = *frame_pair.first;
+        auto &buffer = *frame_pair.second;
+        total_num_of_classes += info.number_of_classes * info.chunks_per_frame;
+        total_size_of_buffers += buffer.size();
+        CHECK(buffer.size() == HailoRTCommon::get_nms_hw_frame_size(info), HAILO_INVALID_ARGUMENT,
+            "Source buffer size is not same as NMS HW frame size! ({} != {})", buffer.size(),
+            HailoRTCommon::get_nms_hw_frame_size(info));
+    }
+
+    // Each frame contributes 1 extra bbox_size at the end of it which acts as a delimiter, but we don't copy those to the fused buffer.
+    // We keep the size of the dst buffer 1 bbox_size too big to stay in the format of not defused nms frames.
+    total_size_of_buffers -= (frames.size() - 1) * frames[0].first->bbox_size;
+
+    CHECK(dst.size() == total_size_of_buffers, HAILO_INVALID_ARGUMENT,
+        "Size of destination buffer is not same as the expected size of the fused frame! (size: {}, expected: {})",
+        dst.size(), total_size_of_buffers);
+
+    uint32_t offsets[HailoRTCommon::MAX_DEFUSED_LAYER_COUNT] = {0};
+    uint32_t dst_offset = 0;
+    for (uint32_t i = 0; i < total_num_of_classes; i++) {
+        size_t buff_index = (i % frames.size());
+        auto &info = *frames[buff_index].first;
+        auto &buffer = *frames[buff_index].second;
+
+        const uint8_t *src_ptr = buffer.data();
+        // TODO: Maybe change asserts to checks
+        assert(offsets[buff_index] + sizeof(nms_bbox_counter_t) <= buffer.size());
+        nms_bbox_counter_t bbox_count = *reinterpret_cast<const nms_bbox_counter_t*>(src_ptr + offsets[buff_index]);
+        uint32_t copy_size = static_cast<uint32_t>(sizeof(bbox_count) + bbox_count * info.bbox_size);
+        assert(offsets[buff_index] + copy_size <= buffer.size());
+        assert(dst_offset + copy_size <= dst.size());
+        std::copy_n(src_ptr + offsets[buff_index], copy_size, dst.data() + dst_offset);
+        offsets[buff_index] += copy_size;
+        dst_offset += copy_size;
+    }
+
+    return HAILO_SUCCESS;
+}
+
+Expected<std::vector<hailo_mux_info_t>> OutputDemuxerBase::get_mux_infos_from_layer_info(const LayerInfo &layer_info)
+{
+    // Setting the first mux
+    std::vector<hailo_mux_info_t> res;
+    res.reserve(HailoRTCommon::MUX_INFO_COUNT);
+    res.push_back({});
+    uint32_t offset = 0;
+    uint32_t height_ratio = 0;
+    size_t number_of_mux_infos = 1;
+
+    auto status = get_mux_info_from_layer_info_impl(res[0], layer_info, offset, height_ratio, res, number_of_mux_infos);
+    CHECK_SUCCESS_AS_EXPECTED(status);
+    res.resize(number_of_mux_infos);
+
+    return res;
+}
+
+OutputDemuxerBase::OutputDemuxerBase(size_t src_frame_size, std::vector<hailo_mux_info_t> &&mux_infos) :
+        OutputDemuxer(src_frame_size),
+        m_mux_infos(std::move(mux_infos)) {}
+
+hailo_status OutputDemuxerBase::transform_demux(const MemoryView src, std::vector<MemoryView> &raw_buffers)
+{
+    size_t raw_buffer_index = 0;
+    size_t total_mux_sizes = 0;
+
+    CHECK(raw_buffers.size() == get_edges_stream_info().size(), HAILO_INVALID_ARGUMENT,
+        "There is a missmatch between mux edges counts ({}) and raw_buffers_size ({})", get_edges_stream_info().size(),
+        raw_buffers.size());
+
+    // Reset the runtime offset
+    for (auto &mux_edge : m_mux_infos) {
+        if (!mux_edge.info.is_mux) {
+            mux_edge.buffer = (void*)((uintptr_t)raw_buffers[raw_buffer_index].data());
+            mux_edge.current_offset = 0;
+            mux_edge.row_counter = 0;
+            CHECK((mux_edge.info.hw_frame_size == raw_buffers[raw_buffer_index].size()), HAILO_INVALID_ARGUMENT,
+                "Expected buffer size of {}, got {}", mux_edge.info.hw_frame_size, raw_buffers[raw_buffer_index].size());
+            total_mux_sizes += mux_edge.info.hw_frame_size;
+            raw_buffer_index++;
+        }
+    }
+    CHECK(total_mux_sizes == src.size(), HAILO_INVALID_ARGUMENT,
+        "src_size must be: {}, passed_size: {}", total_mux_sizes, src.size());
+
+    // TODO: Optimization - Read directly to user raw buffers (in case of NO_TRANSFORM, INPLACE_TRANSFORM)
+
+    auto first_mux_info = m_mux_infos[0];
+    return transform_demux_raw_frame(src.data(), 0, &first_mux_info, first_mux_info.rows_gcd);
+}
+
+hailo_status OutputDemuxerBase::transform_demux(const MemoryView src, const std::map<std::string, MemoryView> &dst_ptrs)
+{
+    size_t total_mux_sizes = 0;
+    // Reset the runtime offset
+    for (auto &mux_edge : m_mux_infos) {
+        if (!mux_edge.info.is_mux) {
+            auto name = std::string(mux_edge.info.name);
+            CHECK(contains(dst_ptrs, name), HAILO_INVALID_ARGUMENT, "edge name {} is not in dst_ptrs", name);
+            mux_edge.buffer = const_cast<void*>(reinterpret_cast<const void*>((dst_ptrs.at(name)).data()));
+            mux_edge.current_offset = 0;
+            mux_edge.row_counter = 0;
+            CHECK((mux_edge.info.hw_frame_size == (dst_ptrs.at(name)).size()), HAILO_INVALID_ARGUMENT,
+                "Expected buffer size of {}, got {}", mux_edge.info.hw_frame_size, (dst_ptrs.at(name)).size());
+            total_mux_sizes += mux_edge.info.hw_frame_size;
+        }
+    }
+    CHECK(total_mux_sizes == src.size(), HAILO_INVALID_ARGUMENT, "src_size must be: {}, passed_size: {}",
+        total_mux_sizes, src.size());
+
+    auto first_mux_info = m_mux_infos[0];
+    return transform_demux_raw_frame(src.data(), 0, &first_mux_info, first_mux_info.rows_gcd);
+}
+
+} /* namespace hailort */
diff --git a/hailort/libhailort/src/transform_internal.hpp b/hailort/libhailort/src/transform_internal.hpp
new file mode 100644 (file)
index 0000000..f439776
--- /dev/null
@@ -0,0 +1,125 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file transform_internal.hpp
+ * @brief Pre/post infer transformations
+ **/
+
+#ifndef _TRANSFORM_INTERNAL_HPP_
+#define _TRANSFORM_INTERNAL_HPP_
+
+#include "hailo/hailort.h"
+#include "hailo/expected.hpp"
+#include "hailo/hailort_common.hpp"
+#include "hailo/buffer.hpp"
+#include "hailo/hef.hpp"
+#include "hailo/transform.hpp"
+#include "stream_internal.hpp"
+#include "layer_info.hpp"
+
+#include <map>
+#include <vector>
+
+namespace hailort
+{
+
+class HAILORTAPI TransformContextUtils final
+{
+public:
+    static bool is_transformation_required(const hailo_stream_direction_t stream_direction,
+        const hailo_3d_image_shape_t &src_image_shape, 
+        const hailo_format_t &src_format, const hailo_3d_image_shape_t &dst_image_shape, 
+        const hailo_format_t &dst_format, const hailo_quant_info_t &quant_info);
+    static bool should_quantize(const hailo_stream_direction_t stream_direction, 
+        const hailo_format_t &src_format, const hailo_format_t &dst_format, const hailo_quant_info_t &quant_info);
+    static bool should_transpose(const hailo_format_flags_t &src_flags, const hailo_format_flags_t &dst_flags);
+    static bool should_reorder(const hailo_3d_image_shape_t &src_image_shape, const hailo_format_t &src_format,
+        const hailo_3d_image_shape_t &dst_image_shape, const hailo_format_t &dst_format);
+    static std::string make_quantization_description(hailo_format_type_t src_type, hailo_format_type_t dst_type,
+                                                    hailo_quant_info_t quant_info);
+    static std::string make_reorder_description(hailo_format_order_t src_order, hailo_3d_image_shape_t src_shape,
+                                                hailo_format_order_t dst_order, hailo_3d_image_shape_t dst_shape);
+    static std::string make_transpose_description(hailo_3d_image_shape_t original_shape, hailo_3d_image_shape_t transposed_shape);
+};
+
+class OutputDemuxerBase : public OutputDemuxer {
+public:
+    static Expected<OutputDemuxerBase> create(size_t src_frame_size, const LayerInfo &layer_info);
+
+    virtual std::vector<hailo_stream_info_t> get_edges_stream_info() override
+    {
+        std::vector<hailo_stream_info_t> res;
+        for (auto &info : m_mux_infos) {
+            if (!info.info.is_mux) {
+                res.push_back(info.info);
+            }
+        }
+        return res;
+    }
+
+    virtual hailo_status transform_demux(const MemoryView src, const std::map<std::string, MemoryView> &dst_ptrs) override;
+    virtual hailo_status transform_demux(const MemoryView src, std::vector<MemoryView> &raw_buffers) override;
+
+private:
+    OutputDemuxerBase(size_t src_frame_size, std::vector<hailo_mux_info_t> &&mux_infos);
+
+    static Expected<std::vector<hailo_mux_info_t>> get_mux_infos_from_layer_info(const LayerInfo &layer_info);
+    static hailo_status get_mux_info_from_layer_info_impl(hailo_mux_info_t &mux_info, const LayerInfo &layer_info,
+    uint32_t &offset, uint32_t height_ratio, std::vector<hailo_mux_info_t> &res, size_t &number_of_mux_infos);
+
+    std::vector<hailo_mux_info_t> m_mux_infos;
+};
+
+class HAILORTAPI FrameOutputTransformContext final : public OutputTransformContext
+{
+public:
+    static Expected<std::unique_ptr<OutputTransformContext>> create(const hailo_3d_image_shape_t &src_image_shape,
+        const hailo_format_t &src_format, const hailo_3d_image_shape_t &dst_image_shape,
+        const hailo_format_t &dst_format, const hailo_quant_info_t &dst_quant_info);
+
+    FrameOutputTransformContext(size_t src_frame_size, const hailo_3d_image_shape_t &src_image_shape,
+        const hailo_format_t &src_format, size_t dst_frame_size, const hailo_3d_image_shape_t &dst_image_shape,
+        const hailo_format_t &dst_format, const hailo_quant_info_t &dst_quant_info, Buffer&& transpose_buffer,
+        const bool should_quantize, const bool should_transpose, const bool should_reorder);
+
+    hailo_status transform_inner(const void *src_ptr, void *dst_ptr, MemoryView transpose_buffer);
+
+    hailo_status quantize_stream(const void *dst_ptr);
+
+
+    virtual hailo_status transform(const MemoryView src, MemoryView dst) override;
+    virtual std::string description() const override;
+
+private:
+    const hailo_3d_image_shape_t m_src_image_shape;
+    const hailo_3d_image_shape_t m_dst_image_shape;
+    Buffer m_transpose_buffer;
+};
+
+class HAILORTAPI NMSOutputTransformContext final : public OutputTransformContext
+{
+public:
+    static Expected<std::unique_ptr<OutputTransformContext>> create(const hailo_format_t &src_format, 
+        const hailo_format_t &dst_format, const hailo_quant_info_t &dst_quant_info, const hailo_nms_info_t &nms_info);
+
+    NMSOutputTransformContext(size_t src_frame_size, const hailo_format_t &src_format, size_t dst_frame_size,
+        const hailo_format_t &dst_format, const hailo_quant_info_t &dst_quant_info, const hailo_nms_info_t &nms_info, 
+        Buffer &&quant_buffer, const bool should_quantize, const bool should_transpose);
+
+    virtual hailo_status transform(const MemoryView src, MemoryView dst) override;
+    virtual std::string description() const override;
+
+private:
+
+    const hailo_nms_info_t m_nms_info;
+
+    // For each chunk contains offset of current nms class. Used here in order to avoid run-time allocations
+    std::vector<size_t> m_chunk_offsets;
+    Buffer m_quant_buffer;
+};
+
+} /* namespace hailort */
+
+#endif /* _TRANSFORM_INTERNAL_HPP_ */
\ No newline at end of file
diff --git a/hailort/libhailort/src/udp.cpp b/hailort/libhailort/src/udp.cpp
new file mode 100644 (file)
index 0000000..d002ac5
--- /dev/null
@@ -0,0 +1,288 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file udp.cpp
+ * @brief Socket wrapper for Unix
+ **/
+
+#include <stdint.h>
+#include <errno.h>
+#include <string.h>
+
+#include <hailo/hailort.h>
+#include "common/utils.hpp"
+#include "common/logger_macros.hpp"
+#include "udp.hpp"
+#include "common/socket.hpp"
+#include "control_protocol.hpp"
+
+namespace hailort
+{
+
+#define MILLISECONDS_IN_SECOND (1000)
+#define MICROSECONDS_IN_MILLISECOND (1000)
+
+//initialize with padding
+uint8_t g_padded_buffer[MAX_UDP_PAYLOAD_SIZE] = {0,};
+
+hailo_status Udp::bind(struct in_addr host_ip, uint16_t host_port)
+{
+    m_host_address.sin_family = AF_INET;
+    m_host_address.sin_port = htons(host_port);
+    m_host_address.sin_addr = host_ip;
+    m_host_address_length = sizeof(m_host_address);
+
+    /* Bind the socket */
+    auto status = m_socket.socket_bind((struct sockaddr*)&(m_host_address), m_host_address_length);
+    CHECK_SUCCESS(status);
+
+    /* Save binded host address information */
+    return m_socket.get_sock_name((struct sockaddr*)&(m_host_address), &m_host_address_length);
+}
+
+Expected<Udp> Udp::create(struct in_addr device_ip, uint16_t device_port, struct in_addr host_ip,
+    uint16_t host_port)
+{
+    auto status = HAILO_UNINITIALIZED;
+    auto socket = Socket::create(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+    CHECK_EXPECTED(socket);
+    auto object = Udp(device_ip, device_port, host_ip, host_port, socket.release(), status);
+    CHECK_SUCCESS_AS_EXPECTED(status);
+
+    return object;
+}
+
+Udp::Udp(struct in_addr device_ip, uint16_t device_port, struct in_addr host_ip, uint16_t host_port,
+    Socket &&socket, hailo_status &status) : m_socket(std::move(socket))
+{
+    m_device_address.sin_family = AF_INET;
+    m_device_address.sin_port = htons(device_port);
+    m_device_address.sin_addr = device_ip;
+    m_device_address_length = sizeof(m_device_address);
+
+    /* Adjust socket rcv buff size */
+    status = m_socket.set_recv_buffer_size_max();
+    if (HAILO_SUCCESS != status) {
+        return;
+    }
+
+    /* Set default value timeout */
+    status = set_timeout(std::chrono::milliseconds(HAILO_DEFAULT_ETH_SCAN_TIMEOUT_MS));
+    if (HAILO_SUCCESS != status) {
+        return;
+    }
+
+    /* Set deafult max number of retries */
+    status = set_max_number_of_attempts(HAILO_DEFAULT_ETH_MAX_NUMBER_OF_RETRIES);
+    if (HAILO_SUCCESS != status) {
+        return;
+    }
+
+    /* If device address is 255.255.255.255 (broadcast), enable broadcast */
+    if (INADDR_BROADCAST == m_device_address.sin_addr.s_addr) {
+        status = m_socket.enable_broadcast();
+        if (HAILO_SUCCESS != status) {
+            return;
+        }
+    }
+
+    /* Bind socket at the host */
+    status = bind(host_ip, host_port);
+    if (HAILO_SUCCESS != status) {
+        return;
+    }
+
+    status = HAILO_SUCCESS;
+}
+
+hailo_status Udp::set_timeout(const std::chrono::milliseconds timeout_ms)
+{
+    return m_socket.set_timeout(timeout_ms, &(m_timeout));
+}
+
+hailo_status Udp::send(uint8_t *buffer, size_t *size, bool use_padding, size_t max_payload_size)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    size_t number_of_sent_bytes = 0;
+    uint8_t *send_ptr = buffer;
+
+    /* Validate arguments */
+    CHECK_ARG_NOT_NULL(buffer);
+    CHECK_ARG_NOT_NULL(size);
+
+    if (use_padding) {
+        if (*size > (max_payload_size - PADDING_BYTES_SIZE - PADDING_ALIGN_BYTES)) {
+            *size = (max_payload_size - PADDING_BYTES_SIZE - PADDING_ALIGN_BYTES);
+        }
+        /*copy the data to the padded buffer and adjust the size*/
+        memcpy((g_padded_buffer + PADDING_BYTES_SIZE), buffer, *size);
+        send_ptr = g_padded_buffer;    
+        *size += PADDING_BYTES_SIZE;
+    }
+    else if (*size > max_payload_size) {
+        *size = max_payload_size;
+    }
+
+    status = m_socket.send_to((const uint8_t*)send_ptr, *size, MSG_CONFIRM, (const struct sockaddr *) &m_device_address,
+         m_device_address_length, &number_of_sent_bytes);
+    if (HAILO_STREAM_INTERNAL_ABORT == status) {
+        LOGGER__INFO("Socket send_to was aborted!");
+        return status;
+    } 
+    CHECK_SUCCESS(status);
+
+    /*if we had to pad, omit the padding when returning the number of bytes*/
+    if (use_padding) {
+        number_of_sent_bytes -= PADDING_BYTES_SIZE;
+    }
+
+    /* number_of_sent_bytes will be positive because of the validation above */
+    *size = (size_t)number_of_sent_bytes;
+
+    return HAILO_SUCCESS;
+}
+
+hailo_status Udp::recv(uint8_t *buffer, size_t *size)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    size_t number_of_received_bytes = 0;
+
+    /* Validate arguments */
+    CHECK_ARG_NOT_NULL(buffer);
+    CHECK_ARG_NOT_NULL(size);
+
+    if (*size > MAX_UDP_PAYLOAD_SIZE) {
+        *size = MAX_UDP_PAYLOAD_SIZE;
+    }
+
+    status = m_socket.recv_from(buffer, *size,  0, (struct sockaddr *) &m_device_address, m_device_address_length,
+        &number_of_received_bytes); 
+    if (HAILO_STREAM_INTERNAL_ABORT == status) {
+        LOGGER__INFO("Socket recv_from was aborted!");
+        return status;
+    }
+    CHECK_SUCCESS(status);
+    
+    *size = number_of_received_bytes;
+    return HAILO_SUCCESS;
+}
+
+hailo_status Udp::abort()
+{
+    return m_socket.abort();
+}
+
+hailo_status Udp::has_data(bool log_timeouts_in_debug)
+{
+    return m_socket.has_data((struct sockaddr *) &m_device_address, m_device_address_length, log_timeouts_in_debug); 
+}
+
+hailo_status Udp::receive_fw_response(uint8_t *buffer, size_t *size, uint32_t expected_sequence)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    HAILO_COMMON_STATUS_t common_status = HAILO_COMMON_STATUS__UNINITIALIZED;
+
+    size_t receive_attempts = 0;
+    uint32_t received_sequence = 0;
+
+    ASSERT(NULL != buffer);
+    ASSERT(NULL != size);
+
+    for (receive_attempts = 0; receive_attempts < m_max_number_of_attempts; receive_attempts++) {
+        /* Receive a single packet */
+        status = recv(buffer, size);
+        CHECK_SUCCESS(status);
+
+        /* Get the sequence from the buffer */
+        common_status = CONTROL_PROTOCOL__get_sequence_from_response_buffer(buffer, *size, &received_sequence);
+        status = (HAILO_COMMON_STATUS__SUCCESS == common_status) ? HAILO_SUCCESS : HAILO_INTERNAL_FAILURE;
+        CHECK_SUCCESS(status);
+
+        if (received_sequence == expected_sequence) {
+            /* Received the expected response */
+            break;
+        } else {
+            /* Invalid response was received */
+            LOGGER__WARNING("Invalid sequence received (received {}, expected {}). Discarding it.", received_sequence,
+                expected_sequence);
+            continue;
+        }
+    }
+    CHECK((receive_attempts < m_max_number_of_attempts), HAILO_ETH_FAILURE,
+        "Received a response with an invalid sequence for {} time.", receive_attempts);
+
+    return HAILO_SUCCESS;
+}
+
+
+hailo_status Udp::fw_interact_impl(uint8_t *request_buffer, size_t request_size, uint8_t *response_buffer,
+    size_t *response_size, uint32_t expected_sequence)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    size_t expected_request_size = request_size;
+    /* If the response_size value is 0, we do not expect response from the fw */
+    bool expecting_response = (0 != *response_size);
+
+    ASSERT(NULL != request_buffer);
+    ASSERT(NULL != response_buffer);
+    ASSERT(NULL != response_size);
+
+    status = send(request_buffer, &request_size, false, MAX_UDP_PAYLOAD_SIZE);
+    CHECK_SUCCESS(status);
+
+    /* Validate all bytes were actually sent */
+    CHECK(expected_request_size == request_size, HAILO_ETH_FAILURE,
+        "Did not send all data at UDP__fw_interact. Expected to send: {}, actually sent: {}", expected_request_size,
+        request_size);
+
+    status = receive_fw_response(response_buffer, response_size, expected_sequence);
+    if ((HAILO_TIMEOUT == status) && !expecting_response) {
+        // This timeout was predictable
+        status = HAILO_SUCCESS;
+    }
+    return status;
+}
+
+hailo_status Udp::fw_interact(uint8_t *request_buffer, size_t request_size, uint8_t *response_buffer,
+    size_t *response_size, uint32_t expected_sequence)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+
+    /* Validate arguments */
+    CHECK_ARG_NOT_NULL(request_buffer);
+    CHECK_ARG_NOT_NULL(response_buffer);
+    CHECK_ARG_NOT_NULL(response_size);
+
+    /* Not clearing the read socket before, because the FW ignores duplicated controls,
+    so a leftover control response in the read socket is not possible */
+
+    for (size_t attempt_number = 0; attempt_number < m_max_number_of_attempts; ++attempt_number) {
+        status = fw_interact_impl(request_buffer, request_size, response_buffer, response_size, expected_sequence);
+        if ((HAILO_ETH_RECV_FAILURE == status) || (HAILO_ETH_SEND_FAILURE == status) || (HAILO_TIMEOUT == status)) {
+            LOGGER__WARN("Control response was not received, sending it again. Attempt number: {} (zero indexed)",
+                    attempt_number);
+            continue;
+        }
+        CHECK_SUCCESS(status);
+        /* Not validating amount of received bytes because we can not know how many bytes are expected */
+        break;
+    }
+
+    return HAILO_SUCCESS;
+}
+
+hailo_status Udp::set_max_number_of_attempts(uint8_t max_number_of_attempts)
+{
+    /* Validate arguments */
+    CHECK(0 < max_number_of_attempts, HAILO_INVALID_ARGUMENT,
+        "Invalid max_number_of_attempts attempt to be set. max_number_of_attempts cannot be 0.");
+
+    m_max_number_of_attempts = max_number_of_attempts;
+
+    return HAILO_SUCCESS;
+
+}
+
+} /* namespace hailort */
diff --git a/hailort/libhailort/src/udp.hpp b/hailort/libhailort/src/udp.hpp
new file mode 100644 (file)
index 0000000..900bcb0
--- /dev/null
@@ -0,0 +1,59 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file udp.hpp
+ * @brief Defines udp transport method.
+ **/
+
+#ifndef __OS_UDP_H__
+#define __OS_UDP_H__
+
+#include "common/socket.hpp"
+
+#include <hailo/hailort.h>
+#include "hailo/expected.hpp"
+
+namespace hailort
+{
+
+typedef struct sockaddr_in UDP__sockaddr_in_t;
+typedef struct timeval UDP__timeout_t;
+
+class Udp final {
+public:
+    static Expected<Udp> create(struct in_addr device_ip, uint16_t device_port, struct in_addr host_ip,
+        uint16_t host_port);
+
+    hailo_status set_timeout(const std::chrono::milliseconds timeout_ms);
+    hailo_status send(uint8_t *buffer, size_t *size, bool use_padding, size_t max_payload_size);
+    hailo_status recv(uint8_t *buffer, size_t *size);
+    hailo_status abort();
+    hailo_status has_data(bool log_timeouts_in_debug = false);
+    hailo_status fw_interact(uint8_t *request_buffer, size_t request_size, uint8_t *response_buffer,
+        size_t *response_size, uint32_t expected_sequence);
+    hailo_status set_max_number_of_attempts(uint8_t max_number_of_attempts);
+
+    UDP__sockaddr_in_t m_host_address;
+    socklen_t m_host_address_length;
+    UDP__sockaddr_in_t m_device_address;
+    socklen_t m_device_address_length;
+    UDP__timeout_t m_timeout;
+
+private:
+    Udp(struct in_addr device_ip, uint16_t device_port, struct in_addr host_ip, uint16_t host_port,
+        Socket &&socket, hailo_status &status);
+
+    hailo_status bind(struct in_addr host_ip, uint16_t host_port);
+    hailo_status receive_fw_response(uint8_t *buffer, size_t *size, uint32_t expected_sequence);
+    hailo_status fw_interact_impl(uint8_t *request_buffer, size_t request_size, uint8_t *response_buffer,
+        size_t *response_size, uint32_t expected_sequence);
+
+    uint8_t m_max_number_of_attempts;
+    Socket m_socket;
+};
+
+} /* namespace hailort */
+
+#endif /* __OS_UDP_H__ */
diff --git a/hailort/libhailort/src/vdevice.cpp b/hailort/libhailort/src/vdevice.cpp
new file mode 100644 (file)
index 0000000..aa87124
--- /dev/null
@@ -0,0 +1,100 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file vdevice.cpp
+ * @brief TODO: brief
+ *
+ * TODO: doc
+ **/
+
+#include "hailo/hailort.h"
+#include "hailo/vdevice.hpp"
+#include "vdevice_internal.hpp"
+#include "pcie_device.hpp"
+#include "hailort_defaults.hpp"
+
+namespace hailort
+{
+
+Expected<std::unique_ptr<VDevice>> VDevice::create(const hailo_vdevice_params_t &params)
+{
+    CHECK_AS_EXPECTED(0 != params.device_count, HAILO_INVALID_ARGUMENT,
+        "VDevice creation failed. invalid device_count ({}).", params.device_count);
+
+    auto vdevice = VDeviceBase::create(params);
+    CHECK_EXPECTED(vdevice);
+    // Upcasting to VDevice unique_ptr (from VDeviceBase unique_ptr)
+    auto vdevice_ptr = std::unique_ptr<VDevice>(vdevice.release());
+    return vdevice_ptr;
+}
+
+Expected<std::unique_ptr<VDevice>> VDevice::create()
+{
+    auto params = HailoRTDefaults::get_vdevice_params();
+    return create(params);
+}
+
+Expected<std::unique_ptr<VDeviceBase>> VDeviceBase::create(const hailo_vdevice_params_t &params)
+{
+    auto scan_res = PcieDevice::scan();
+    CHECK_EXPECTED(scan_res);
+
+    std::vector<std::unique_ptr<PcieDevice>> devices;
+    devices.reserve(params.device_count);
+
+    hailo_pcie_device_info_t *device_infos_ptr = params.device_infos;
+    uint32_t devices_pool_count = params.device_count;
+    if (nullptr == device_infos_ptr) {
+        /* If params.device_infos is not nullptr, we use a pool of the given device_infos.
+           Otherwise, we use all available devices */
+        device_infos_ptr = scan_res->data();
+        devices_pool_count = static_cast<uint32_t>(scan_res->size());
+    }
+
+    for (uint32_t i = 0; i < devices_pool_count; i++) {
+        if (devices.size() == params.device_count) {
+            break;
+        }
+        auto pcie_device = PcieDevice::create(device_infos_ptr[i]);
+        CHECK_EXPECTED(pcie_device);
+        auto status = pcie_device.value()->mark_as_used();
+        if ((nullptr == params.device_infos) && (HAILO_DEVICE_IN_USE == status)) {
+            // Continue only if the user didnt ask for specific devices
+            continue;
+        }
+        CHECK_SUCCESS_AS_EXPECTED(status);
+        devices.emplace_back(pcie_device.release());
+    }
+    CHECK_AS_EXPECTED(params.device_count == devices.size(), HAILO_OUT_OF_PHYSICAL_DEVICES,
+        "Failed to create vdevice. there are not enough free devices. requested: {}, found: {}",
+            params.device_count, devices.size());
+    auto vdevice = std::unique_ptr<VDeviceBase>(new (std::nothrow) VDeviceBase(std::move(devices)));
+    CHECK_AS_EXPECTED(nullptr != vdevice, HAILO_OUT_OF_HOST_MEMORY);
+
+    return vdevice;
+}
+
+Expected<ConfiguredNetworkGroupVector> VDeviceBase::configure(Hef &hef,
+    const NetworkGroupsParamsMap &configure_params)
+{
+    auto start_time = std::chrono::steady_clock::now();
+    if (!m_context_switch_manager) {
+
+        auto local_context_switch_manager = VdmaConfigManager::create(*this);
+        CHECK_EXPECTED(local_context_switch_manager);
+        m_context_switch_manager = make_unique_nothrow<VdmaConfigManager>(local_context_switch_manager.release());
+        CHECK_AS_EXPECTED(nullptr != m_context_switch_manager, HAILO_OUT_OF_HOST_MEMORY);
+    }
+
+    auto network_groups = m_context_switch_manager->add_hef(hef, configure_params);
+    CHECK_EXPECTED(network_groups);
+
+    auto elapsed_time_ms = std::chrono::duration<double, std::milli>(std::chrono::steady_clock::now() - start_time).count();
+    LOGGER__INFO("Configuring HEF on VDevice took {} milliseconds", elapsed_time_ms);
+
+    return network_groups;
+}
+
+} /* namespace hailort */
diff --git a/hailort/libhailort/src/vdevice_internal.hpp b/hailort/libhailort/src/vdevice_internal.hpp
new file mode 100644 (file)
index 0000000..abf2632
--- /dev/null
@@ -0,0 +1,73 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file vdevice_internal.hpp
+ * @brief Class declaration for VDeviceBase that implements the basic VDevice "interface".
+ *        Hence, the hiearchy is as follows:
+ *
+ * VDevice                  (External "interface")
+ * └── VDeviceBase          (Actual implementations)
+ *     |
+ *     ├── std::vector<PcieDevice>
+ **/
+
+#ifndef _HAILO_VDEVICE_INTERNAL_HPP_
+#define _HAILO_VDEVICE_INTERNAL_HPP_
+
+#include "hailo/hailort.h"
+#include "hailo/vdevice.hpp"
+#include "pcie_device.hpp"
+#include "context_switch/multi_context/vdma_config_manager.hpp"
+
+
+namespace hailort
+{
+
+class VDeviceBase : public VDevice
+{
+public:
+    static Expected<std::unique_ptr<VDeviceBase>> create(const hailo_vdevice_params_t &params);
+    VDeviceBase(VDeviceBase &&) = delete;
+    VDeviceBase(const VDeviceBase &) = delete;
+    VDeviceBase &operator=(VDeviceBase &&) = delete;
+    VDeviceBase &operator=(const VDeviceBase &) = delete;
+    virtual ~VDeviceBase() = default;
+
+    virtual Expected<ConfiguredNetworkGroupVector> configure(Hef &hef,
+        const NetworkGroupsParamsMap &configure_params={}) override;
+
+    virtual Expected<std::vector<std::reference_wrapper<Device>>> get_physical_devices() override
+    {
+        // Return Expected for future functionality
+        std::vector<std::reference_wrapper<Device>> devices_refs;
+        for (auto &device : m_devices) {
+            devices_refs.push_back(*device);
+        }
+        return devices_refs;
+    }
+
+    virtual Expected<std::vector<hailo_pcie_device_info_t>> get_physical_devices_infos() override
+    {
+        // Return Expected for future functionality
+        std::vector<hailo_pcie_device_info_t> devices_infos;
+        for (auto &device : m_devices) {
+            devices_infos.push_back(device->get_device_info());
+        }
+
+        return devices_infos;
+    }
+
+private:
+    VDeviceBase(std::vector<std::unique_ptr<PcieDevice>> &&devices) : m_devices(std::move(devices))
+        {}
+
+    std::vector<std::unique_ptr<PcieDevice>> m_devices;
+    std::unique_ptr<VdmaConfigManager> m_context_switch_manager;
+
+};
+
+} /* namespace hailort */
+
+#endif /* _HAILO_DEVICE_INTERNAL_HPP_ */
diff --git a/hailort/libhailort/src/vdevice_stream.cpp b/hailort/libhailort/src/vdevice_stream.cpp
new file mode 100644 (file)
index 0000000..c3cb113
--- /dev/null
@@ -0,0 +1,350 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file vdevice_stream.cpp
+ * @brief TODO: brief
+ *
+ * TODO: doc
+ **/
+
+#include <new>
+
+#include "hailo/hailort.h"
+#include "common/utils.hpp"
+#include "hailo/stream.hpp"
+#include "hailo/hef.hpp"
+#include "hailo/hailort_common.hpp"
+#include "vdevice_stream.hpp"
+#include "pcie_stream.hpp"
+#include "pcie_device.hpp"
+#include "context_switch/multi_context/resource_manager.hpp"
+
+namespace hailort
+{
+
+hailo_status VDeviceInputStream::deactivate_stream()
+{
+    auto status = HAILO_SUCCESS; // Best effort
+    for (auto &stream : m_streams) {
+        auto deactivate_status = stream->deactivate_stream();
+        if (HAILO_SUCCESS != deactivate_status) {
+            LOGGER__ERROR("Failed to deactivate input stream. (status: {} device: {})", deactivate_status, stream->get_dev_id());
+            status = deactivate_status;
+        }
+    }
+    m_is_stream_activated = false;
+    return status;
+}
+
+/** Input stream **/
+VDeviceInputStream::~VDeviceInputStream()
+{
+    // We want to stop the vdma channel before closing the stream in the firmware
+    // because sending data to a closed stream may terminate the dma engine
+    if (m_is_stream_activated) {
+        (void)deactivate_stream();
+    }
+}
+
+hailo_status VDeviceInputStream::activate_stream()
+{
+    for (auto &stream : m_streams) {
+        auto status = stream->activate_stream();
+        if (HAILO_SUCCESS != status) {
+            LOGGER__ERROR("Failed to activate input stream. (device: {})", stream->get_dev_id());
+            deactivate_stream();
+            return status;
+        }
+    }
+    m_is_stream_activated = true;
+    return HAILO_SUCCESS;
+}
+
+Expected<size_t> VDeviceInputStream::sync_write_raw_buffer(const MemoryView &buffer)
+{
+    auto written_bytes = m_streams[m_next_transfer_stream_index]->sync_write_raw_buffer(buffer);
+    if (HAILO_SUCCESS != written_bytes.status()) {
+        LOGGER__INFO("Write to stream has failed! status = {}", written_bytes.status());
+        return make_unexpected(written_bytes.status());
+    }
+
+    // Update m_next_transfer_stream_index only if 'batch' frames has been transferred
+    if (0 == (++m_acc_frames % m_streams[0]->get_batch_size())) {
+        m_next_transfer_stream_index = static_cast<uint32_t>((m_next_transfer_stream_index + 1) % m_streams.size());
+        m_acc_frames = 0;
+    }
+    return written_bytes.release();
+}
+
+hailo_status VDeviceInputStream::sync_write_all_raw_buffer_no_transform_impl(void *buffer, size_t offset, size_t size)
+{
+    ASSERT(NULL != buffer);
+
+    return sync_write_raw_buffer(MemoryView(static_cast<uint8_t*>(buffer) + offset, size)).status();
+}
+
+Expected<std::unique_ptr<VDeviceInputStream>> VDeviceInputStream::create_input_stream_from_net_group(
+    std::vector<std::shared_ptr<ResourcesManager>> &resources_managers,
+    const LayerInfo &edge_layer, const std::string &stream_name,
+    EventPtr &&network_group_activated_event, LatencyMeterPtr latency_meter)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+
+    const auto stream_index = edge_layer.index;
+    // Channel index is the same through all resources_managers
+    const auto channel_index = resources_managers[0]->get_boundary_channel_index(stream_index, HAILO_H2D_STREAM, stream_name);
+    CHECK_EXPECTED(channel_index, "Failed to get channel index for input stream {}", stream_index);
+
+    std::vector<PcieDevice*> devices;
+    std::vector<std::unique_ptr<PcieInputStream>> streams;
+    auto partial_network_name = edge_layer.partial_network_name;
+
+    for (auto &resources_manager : resources_managers) {
+        CHECK_AS_EXPECTED(Device::Type::PCIE == resources_manager->get_device().get_type(), HAILO_INTERNAL_FAILURE,
+            "vDevice stream is supported only with PCIe devices");
+        PcieDevice &pcie_device = reinterpret_cast<PcieDevice&>(resources_manager->get_device());
+
+        auto batch_size = resources_manager->get_network_batch_size_from_partial_name(partial_network_name);
+        auto local_stream = PcieInputStream::create(pcie_device, channel_index.value(),
+            edge_layer, batch_size.value(), network_group_activated_event, latency_meter);
+        CHECK_EXPECTED(local_stream);
+
+        devices.push_back(&pcie_device);
+        streams.emplace_back(local_stream.release());
+    }
+
+    std::unique_ptr<VDeviceInputStream> local_vdevice_stream(new (std::nothrow) VDeviceInputStream(devices,
+        std::move(streams), std::move(network_group_activated_event), edge_layer, status));
+    CHECK_AS_EXPECTED((nullptr != local_vdevice_stream), HAILO_OUT_OF_HOST_MEMORY);
+    CHECK_SUCCESS_AS_EXPECTED(status);
+
+    return local_vdevice_stream;
+}
+
+Expected<std::unique_ptr<VDeviceInputStream>> VDeviceInputStream::create(std::vector<std::shared_ptr<ResourcesManager>> &resources_managers,
+    const LayerInfo &edge_layer, const std::string &stream_name, EventPtr network_group_activated_event, LatencyMeterPtr latency_meter)
+{
+    assert(0 < resources_managers.size());
+
+    auto input_stream = create_input_stream_from_net_group(resources_managers, edge_layer,
+        stream_name, std::move(network_group_activated_event), latency_meter);
+    CHECK_EXPECTED(input_stream);
+
+    return input_stream.release();
+}
+
+hailo_status VDeviceInputStream::set_timeout(std::chrono::milliseconds timeout)
+{
+    for (auto &stream : m_streams) {
+        auto status = stream->set_timeout(timeout);
+        CHECK_SUCCESS(status, "Failed to set timeout to input stream. (device: {})", stream->get_dev_id());
+    }
+    return HAILO_SUCCESS;
+}
+
+std::chrono::milliseconds VDeviceInputStream::get_timeout() const
+{
+    // All timeout values of m_streams should be the same
+    return m_streams[0]->get_timeout();
+}
+
+hailo_status VDeviceInputStream::flush()
+{
+    auto status = HAILO_SUCCESS; // Best effort
+    for (auto &stream : m_streams) {
+        auto flush_status = stream->flush();
+        if (HAILO_SUCCESS != status) {
+            LOGGER__ERROR("Failed to flush input stream. (status: {} device: {})", status, stream->get_dev_id());
+            status = flush_status;
+        }
+    }
+    return status;
+}
+
+hailo_status VDeviceInputStream::abort()
+{
+    auto status = HAILO_SUCCESS; // Best effort
+    for (auto &stream : m_streams) {
+        auto abort_status = stream->abort();
+        if (HAILO_SUCCESS != status) {
+            LOGGER__ERROR("Failed to abort input stream. (status: {} device: {})", status, stream->get_dev_id());
+            status = abort_status;
+        }
+    }
+    return status;
+}
+
+hailo_status VDeviceInputStream::clear_abort()
+{
+    auto status = HAILO_SUCCESS; // Best effort
+    for (auto &stream : m_streams) {
+        auto clear_abort_status = stream->clear_abort();
+        if (HAILO_SUCCESS != status) {
+            LOGGER__ERROR("Failed to clear abort input stream. (status: {} device: {})", status, stream->get_dev_id());
+            status = clear_abort_status;
+        }
+    }
+    return status;
+}
+
+/** Output stream **/
+hailo_status VDeviceOutputStream::deactivate_stream()
+{
+    auto status = HAILO_SUCCESS; // Best effort
+    for (auto &stream : m_streams) {
+        auto deactivate_status = stream->deactivate_stream();
+        if (HAILO_SUCCESS != status) {
+            LOGGER__ERROR("Failed to deactivate output stream. (status: {} device: {})", status, stream->get_dev_id());
+            status = deactivate_status;
+        }
+    }
+    m_is_stream_activated = false;
+    return status;
+}
+
+VDeviceOutputStream::~VDeviceOutputStream()
+{
+    // We want to stop the vdma channel before closing the stream in the firmware
+    // because sending data to a closed stream may terminate the dma engine
+    if (m_is_stream_activated) {
+        (void)deactivate_stream();
+    }
+}
+
+hailo_status VDeviceOutputStream::activate_stream()
+{
+    for (auto &stream : m_streams) {
+        auto status = stream->activate_stream();
+        if (HAILO_SUCCESS != status) {
+            LOGGER__ERROR("Failed to activate output stream. (device: {})", stream->get_dev_id());
+            deactivate_stream();
+            return status;
+        }
+    }
+    m_is_stream_activated = true;
+    return HAILO_SUCCESS;
+}
+
+hailo_status VDeviceOutputStream::read_all(MemoryView &/*buffer*/)
+{
+    LOGGER__ERROR("read_all should not be called in vdevice flow");
+    return HAILO_INTERNAL_FAILURE;
+}
+
+Expected<size_t> VDeviceOutputStream::sync_read_raw_buffer(MemoryView &/*buffer*/)
+{
+    LOGGER__ERROR("sync_read_raw_buffer should not be called in vdevice flow");
+    return make_unexpected(HAILO_INTERNAL_FAILURE);
+}
+
+hailo_status VDeviceOutputStream::read(MemoryView buffer)
+{
+    auto status = m_streams[m_next_transfer_stream_index]->read(buffer);
+    if (HAILO_SUCCESS != status) {
+        LOGGER__INFO("Read from stream has failed! status = {}", status);
+        return status;
+    }
+
+    // Update m_next_transfer_stream_index only if 'batch' frames has been transferred
+    if (0 == (++m_acc_frames % m_streams[0]->get_batch_size())) {
+        m_next_transfer_stream_index = static_cast<uint32_t>((m_next_transfer_stream_index + 1) % m_streams.size());
+        m_acc_frames = 0;
+    }
+    return HAILO_SUCCESS;
+}
+
+Expected<std::unique_ptr<VDeviceOutputStream>> VDeviceOutputStream::create_output_stream_from_net_group(
+    std::vector<std::shared_ptr<ResourcesManager>> &resources_managers,
+    const LayerInfo &edge_layer, const std::string &stream_name,
+    EventPtr &&network_group_activated_event, LatencyMeterPtr latency_meter)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+
+    const auto stream_index = edge_layer.index;
+
+    // Channel index is the same through all resources_managers
+    const auto channel_index = resources_managers[0]->get_boundary_channel_index(stream_index, HAILO_D2H_STREAM, stream_name);
+    CHECK_EXPECTED(channel_index, "Failed to get channel index for output stream {}", stream_index);
+
+    std::vector<PcieDevice*> devices;
+    std::vector<std::unique_ptr<PcieOutputStream>> streams;
+    auto partial_network_name = edge_layer.partial_network_name;
+
+    for (auto &resources_manager : resources_managers) {
+        CHECK_AS_EXPECTED(Device::Type::PCIE == resources_manager->get_device().get_type(), HAILO_INTERNAL_FAILURE,
+            "vDevice stream is supported only with PCIe devices");
+        PcieDevice &pcie_device = reinterpret_cast<PcieDevice&>(resources_manager->get_device());
+
+        auto batch_size = resources_manager->get_network_batch_size_from_partial_name(partial_network_name);
+        auto local_stream = PcieOutputStream::create(pcie_device, channel_index.value(),
+            edge_layer, batch_size.value(), network_group_activated_event, latency_meter);
+        CHECK_EXPECTED(local_stream);
+
+        devices.push_back(&pcie_device);
+        streams.emplace_back(local_stream.release());
+    }
+
+    std::unique_ptr<VDeviceOutputStream> local_vdevice_stream(new (std::nothrow) VDeviceOutputStream(devices, std::move(streams),
+        edge_layer, std::move(network_group_activated_event), status));
+    CHECK_AS_EXPECTED((nullptr != local_vdevice_stream), HAILO_OUT_OF_HOST_MEMORY);
+    CHECK_SUCCESS_AS_EXPECTED(status);
+
+    return local_vdevice_stream;
+}
+
+Expected<std::unique_ptr<VDeviceOutputStream>> VDeviceOutputStream::create(std::vector<std::shared_ptr<ResourcesManager>> &resources_managers,
+    const LayerInfo &edge_layer, const std::string &stream_name, EventPtr network_group_activated_event, LatencyMeterPtr latency_meter)
+{
+    assert(0 < resources_managers.size());
+
+    auto output_stream = create_output_stream_from_net_group(resources_managers, edge_layer,
+        stream_name, std::move(network_group_activated_event), latency_meter);
+    CHECK_EXPECTED(output_stream);
+
+    return output_stream.release();
+}
+
+hailo_status VDeviceOutputStream::set_timeout(std::chrono::milliseconds timeout)
+{
+    for (auto &stream : m_streams) {
+        auto status = stream->set_timeout(timeout);
+        CHECK_SUCCESS(status, "Failed to set timeout to output stream. (device: {})", stream->get_dev_id());
+    }
+    return HAILO_SUCCESS;
+}
+
+std::chrono::milliseconds VDeviceOutputStream::get_timeout() const
+{
+    // All timeout values of m_streams should be the same
+    return m_streams[0]->get_timeout();
+}
+
+hailo_status VDeviceOutputStream::abort()
+{
+    auto status = HAILO_SUCCESS; // Best effort
+    for (auto &stream : m_streams) {
+        auto abort_status = stream->abort();
+        if (HAILO_SUCCESS != status) {
+            LOGGER__ERROR("Failed to abort output stream. (status: {} device: {})", status, stream->get_dev_id());
+            status = abort_status;
+        }
+    }
+    return status;
+}
+
+hailo_status VDeviceOutputStream::clear_abort()
+{
+    auto status = HAILO_SUCCESS; // Best effort
+    for (auto &stream : m_streams) {
+        auto clear_abort_status = stream->clear_abort();
+        if (HAILO_SUCCESS != status) {
+            LOGGER__ERROR("Failed to clear abort output stream. (status: {} device: {})", status, stream->get_dev_id());
+            status = clear_abort_status;
+        }
+    }
+    return status;
+}
+
+} /* namespace hailort */
diff --git a/hailort/libhailort/src/vdevice_stream.hpp b/hailort/libhailort/src/vdevice_stream.hpp
new file mode 100644 (file)
index 0000000..4b8d817
--- /dev/null
@@ -0,0 +1,141 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file vdevice_stream.hpp
+ * @brief Internal stream implementation for VDevice - holds PcieStream per physical device
+ *
+ * TODO: doc
+ **/
+
+#ifndef HAILO_VDEVICE_STREAM_HPP_
+#define HAILO_VDEVICE_STREAM_HPP_
+
+#include "stream_internal.hpp"
+#include "hailo/hailort.h"
+#include "pcie_device.hpp"
+#include "pcie_stream.hpp"
+#include "hailo/expected.hpp"
+
+namespace hailort
+{
+
+class VDeviceInputStream : public InputStreamBase {
+public:
+    VDeviceInputStream(VDeviceInputStream &&other) :
+        InputStreamBase(std::move(other)),
+        m_devices(std::move(other.m_devices)),
+        m_streams(std::move(other.m_streams)),
+        m_is_stream_activated(std::exchange(other.m_is_stream_activated, false)),
+        m_next_transfer_stream_index(other.m_next_transfer_stream_index),
+        m_acc_frames(other.m_acc_frames)
+    {}
+
+    virtual ~VDeviceInputStream();
+
+    static Expected<std::unique_ptr<VDeviceInputStream>> create(std::vector<std::shared_ptr<ResourcesManager>> &resources_managers,
+        const LayerInfo &edge_layer, const std::string &stream_name, EventPtr network_group_activated_event,
+        LatencyMeterPtr latency_meter = nullptr);
+
+    virtual hailo_status activate_stream() override;
+    virtual hailo_status deactivate_stream() override;
+    virtual hailo_stream_interface_t get_interface() const override { return HAILO_STREAM_INTERFACE_PCIE; }
+    virtual std::chrono::milliseconds get_timeout() const override;
+    virtual hailo_status abort() override;
+    virtual hailo_status clear_abort() override;
+
+protected:
+    virtual Expected<size_t> sync_write_raw_buffer(const MemoryView &buffer) override;
+    virtual hailo_status sync_write_all_raw_buffer_no_transform_impl(void *buffer, size_t offset, size_t size) override;
+
+private:
+    explicit VDeviceInputStream(
+        std::vector<PcieDevice*> devices,
+        std::vector<std::unique_ptr<PcieInputStream>> &&streams,
+        EventPtr &&network_group_activated_event,
+        const LayerInfo &layer_info,
+        hailo_status &status) :
+            InputStreamBase(layer_info, HAILO_STREAM_INTERFACE_PCIE, std::move(network_group_activated_event), status),
+            m_devices(devices),
+            m_streams(std::move(streams)),
+            m_is_stream_activated(false),
+            m_next_transfer_stream_index(0),
+            m_acc_frames(0)
+    {}
+
+    hailo_status set_timeout(std::chrono::milliseconds timeout);
+    virtual hailo_status flush() override;
+
+    static Expected<std::unique_ptr<VDeviceInputStream>> create_input_stream_from_net_group(
+        std::vector<std::shared_ptr<ResourcesManager>> &resources_managers,
+        const LayerInfo &edge_layer, const std::string &stream_name,
+        EventPtr &&network_group_activated_event, LatencyMeterPtr latency_meter);
+
+    std::vector<PcieDevice*> m_devices;
+    std::vector<std::unique_ptr<PcieInputStream>> m_streams;
+    bool m_is_stream_activated;
+    uint32_t m_next_transfer_stream_index;
+    uint32_t m_acc_frames;
+};
+
+class VDeviceOutputStream : public OutputStreamBase {
+public:
+    VDeviceOutputStream(VDeviceOutputStream &&other) :
+        OutputStreamBase(std::move(other)),
+        m_devices(std::move(other.m_devices)),
+        m_streams(std::move(other.m_streams)),
+        m_is_stream_activated(std::exchange(other.m_is_stream_activated, false)),
+        m_next_transfer_stream_index(other.m_next_transfer_stream_index),
+        m_acc_frames(other.m_acc_frames)
+    {}
+
+    virtual ~VDeviceOutputStream();
+
+    static Expected<std::unique_ptr<VDeviceOutputStream>> create(std::vector<std::shared_ptr<ResourcesManager>> &resources_managers,
+        const LayerInfo &edge_layer, const std::string &stream_name,
+        EventPtr network_group_activated_event, LatencyMeterPtr latency_meter = nullptr);
+
+    virtual hailo_status activate_stream() override;
+    virtual hailo_status deactivate_stream() override;
+    virtual hailo_stream_interface_t get_interface() const override { return HAILO_STREAM_INTERFACE_PCIE; }
+    virtual std::chrono::milliseconds get_timeout() const override;
+    virtual hailo_status abort() override;
+    virtual hailo_status clear_abort() override;
+
+protected:
+    virtual Expected<size_t> sync_read_raw_buffer(MemoryView &buffer) override;
+
+private:
+    explicit VDeviceOutputStream(
+        std::vector<PcieDevice*> devices,
+        std::vector<std::unique_ptr<PcieOutputStream>> &&streams,
+        const LayerInfo &layer_info,
+        EventPtr &&network_group_activated_event,
+        hailo_status &status) :
+            OutputStreamBase(layer_info, std::move(network_group_activated_event), status),
+            m_devices(devices),
+            m_streams(std::move(streams)),
+            m_is_stream_activated(false),
+            m_next_transfer_stream_index(0),
+            m_acc_frames(0)
+    {}
+
+    hailo_status set_timeout(std::chrono::milliseconds timeout);
+    virtual hailo_status read_all(MemoryView &buffer) override;
+    virtual hailo_status read(MemoryView buffer) override;
+    
+    static Expected<std::unique_ptr<VDeviceOutputStream>> create_output_stream_from_net_group(
+        std::vector<std::shared_ptr<ResourcesManager>> &resources_managers, const LayerInfo &edge_layer,
+        const std::string &stream_name, EventPtr &&network_group_activated_event, LatencyMeterPtr latency_meter);
+
+    std::vector<PcieDevice*> m_devices;
+    std::vector<std::unique_ptr<PcieOutputStream>> m_streams;
+    bool m_is_stream_activated;
+    uint32_t m_next_transfer_stream_index;
+    uint32_t m_acc_frames;
+};
+
+} /* namespace hailort */
+
+#endif /* HAILO_VDEVICE_STREAM_HPP_ */
diff --git a/hailort/libhailort/src/vdma_buffer.cpp b/hailort/libhailort/src/vdma_buffer.cpp
new file mode 100644 (file)
index 0000000..6dd9565
--- /dev/null
@@ -0,0 +1,160 @@
+#include "vdma_buffer.hpp"
+
+namespace hailort
+{
+
+Expected<VdmaBuffer> VdmaBuffer::create(size_t required_size, HailoRTDriver::DmaDirection data_direction,
+    HailoRTDriver &driver)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    VdmaBuffer object(required_size, data_direction, driver, status);
+    if (HAILO_SUCCESS != status) {
+        return make_unexpected(status);
+    }
+
+    return object;
+}
+
+VdmaBuffer::VdmaBuffer(
+    size_t required_size, HailoRTDriver::DmaDirection data_direction, HailoRTDriver &driver, hailo_status &status)
+    : m_user_address(), m_size(required_size), m_driver(driver),
+      m_driver_buff_handle(HailoRTDriver::INVALID_DRIVER_BUFFER_HANDLE_VALUE)
+{
+    auto buffer = allocate_vdma_buffer(driver, required_size, m_driver_buff_handle);
+    if (! buffer) {
+        status = buffer.status();
+        return;
+    }
+
+    auto expected_handle = m_driver.vdma_buffer_map(buffer->get(), required_size, data_direction, m_driver_buff_handle);
+    if (!expected_handle) {
+        status = expected_handle.status();
+        return;
+    }
+    
+    m_handle = expected_handle.release();
+    m_user_address = buffer.release();
+    status = HAILO_SUCCESS;
+}
+
+VdmaBuffer::~VdmaBuffer()
+{
+    if (m_user_address) {
+        m_driver.vdma_buffer_unmap(m_handle);
+
+        if (HailoRTDriver::INVALID_DRIVER_BUFFER_HANDLE_VALUE != m_driver_buff_handle) {
+            m_driver.vdma_low_memory_buffer_free(m_driver_buff_handle);
+        }
+    }
+}
+
+hailo_status VdmaBuffer::write(const void *buf_src, size_t count, size_t offset)
+{
+    if ((count + offset) > m_size) {
+        LOGGER__ERROR("Requested size {} from offset {} is more than the VdmaBuffer size {}", count, offset, m_size);
+        return HAILO_INSUFFICIENT_BUFFER;
+    }
+
+    if (count > 0) {
+        auto dst_vdma_address = (uint8_t*)m_user_address.get() + offset;
+        memcpy(dst_vdma_address, buf_src, count);
+
+        auto status = m_driver.vdma_buffer_sync(m_handle, HailoRTDriver::DmaDirection::H2D, dst_vdma_address, count);
+        if (HAILO_SUCCESS != status) {
+            LOGGER__ERROR("Failed synching vdma buffer on write");
+            return status;
+        }
+    }
+    
+    return HAILO_SUCCESS;
+}
+
+hailo_status VdmaBuffer::read(void *buf_dst, size_t count, size_t offset)
+{
+    if ((count + offset) > m_size) {
+        LOGGER__ERROR("Requested size {} from offset {} is more than the VdmaBuffer size {}", count, offset, m_size);
+        return HAILO_INSUFFICIENT_BUFFER;
+    }
+
+    if (count > 0) {
+        auto dst_vdma_address = (uint8_t*)m_user_address.get() + offset;
+        auto status = m_driver.vdma_buffer_sync(m_handle, HailoRTDriver::DmaDirection::D2H, dst_vdma_address, count);
+        if (HAILO_SUCCESS != status) {
+            LOGGER__ERROR("Failed synching vdma buffer on read");
+            return status;
+        }
+
+        memcpy(buf_dst, dst_vdma_address, count);
+    }
+    
+    return HAILO_SUCCESS;
+}
+
+hailo_status VdmaBuffer::write_cyclic(const void *buf_src, size_t count, size_t offset)
+{
+    if (count > m_size) {
+        LOGGER__ERROR("Requested size({}) is more than the VdmaBuffer size {}", count, m_size);
+        return HAILO_INSUFFICIENT_BUFFER;
+    }
+
+    auto size_to_end = m_size - offset;
+    auto copy_size = std::min(size_to_end, count);
+    auto status = write(buf_src, copy_size, offset);
+    if (HAILO_SUCCESS != status) {
+        return status;
+    }
+
+    auto remaining_size = count - copy_size;
+    if (remaining_size > 0) {
+        status = write((uint8_t*)buf_src + copy_size, remaining_size, 0);
+        if (HAILO_SUCCESS != status) {
+            return status;
+        }
+    }
+
+    return HAILO_SUCCESS;
+}
+
+hailo_status VdmaBuffer::read_cyclic(void *buf_dst, size_t count, size_t offset)
+{
+    if (count > m_size) {
+        LOGGER__ERROR("Requested size({}) is more than the VdmaBuffer size {}", count, m_size);
+        return HAILO_INSUFFICIENT_BUFFER;
+    }
+
+    auto size_to_end = m_size - offset;
+    auto copy_size = std::min(size_to_end, count);
+    auto status = read(buf_dst, copy_size, offset);
+    if (HAILO_SUCCESS != status) {
+        return status;
+    }
+
+    auto remaining_size = count - copy_size;
+    if (remaining_size > 0) {
+        status = read((uint8_t*)buf_dst + copy_size, remaining_size, 0);
+        if (HAILO_SUCCESS != status) {
+            return status;
+        }
+    }
+
+    return HAILO_SUCCESS;
+}
+
+Expected<MmapBuffer<void>> VdmaBuffer::allocate_vdma_buffer(HailoRTDriver &driver, size_t required_size,
+    uintptr_t &driver_buff_handle)
+{
+    // Check if driver should be allocated from driver or from user
+    if (driver.allocate_driver_buffer()) {
+        auto driver_buffer_handle = driver.vdma_low_memory_buffer_alloc(required_size);
+        CHECK_EXPECTED(driver_buffer_handle);
+
+        driver_buff_handle = driver_buffer_handle.release();
+
+        return MmapBuffer<void>::create_file_map(required_size, driver.fd(), driver_buff_handle);
+    }
+    else {
+        return MmapBuffer<void>::create_shared_memory(required_size);
+    }
+}
+
+} /* namespace hailort */
diff --git a/hailort/libhailort/src/vdma_buffer.hpp b/hailort/libhailort/src/vdma_buffer.hpp
new file mode 100644 (file)
index 0000000..b11d4e6
--- /dev/null
@@ -0,0 +1,104 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file vdma_buffer.hpp
+ * @brief Provides a buffer that can be used for VDMA
+ *
+ **/
+
+#ifndef _HAILO_VDMA_BUFFER_HPP_
+#define _HAILO_VDMA_BUFFER_HPP_
+
+#include "os/mmap_buffer.hpp"
+#include "os/hailort_driver.hpp"
+#include "hailo/expected.hpp"
+
+namespace hailort
+{
+
+class VdmaBuffer final
+{
+public:
+    static Expected<VdmaBuffer> create(size_t required_size, HailoRTDriver::DmaDirection data_direction,
+        HailoRTDriver &driver);
+
+    VdmaBuffer(size_t required_size, HailoRTDriver::DmaDirection data_direction,
+        HailoRTDriver &driver, hailo_status &status);
+    ~VdmaBuffer();
+
+    VdmaBuffer(const VdmaBuffer &other) = delete;
+    VdmaBuffer &operator=(const VdmaBuffer &other) = delete;
+    VdmaBuffer(VdmaBuffer &&other) noexcept = default;
+    VdmaBuffer &operator=(VdmaBuffer &&other) = delete;
+
+    void *user_address() { return m_user_address.get(); }
+    size_t handle() { return m_handle; }
+    size_t size() const { return m_size; }
+
+    /**
+     * Copy data from buf_src parameter to this VdmaBuffer.
+     *
+     * @note (offset + count) MUST be smaller than this VdmaBuffer size
+     *
+     * @param[in] buf_src The buffer to copy the data from
+     * @param[in] count Number of bytes to copy from buf_src
+     * @param[in] offset The offset relative to this VdmaBuffer to copy the data to
+     */
+    hailo_status write(const void *buf_src, size_t count, size_t offset);
+
+    /**
+     * Copy data from this VdmaBuffer to buf_dst.
+     *
+     * @note (offset + count) MUST be smaller than this VdmaBuffer size
+     *
+     * @param[out] buf_dst The buffer to copy the data to
+     * @param[in] count Number of bytes to copy to buf_dst
+     * @param[in] offset The offset relative to this VdmaBuffer to copy the data from
+     */
+    hailo_status read(void *buf_dst, size_t count, size_t offset);
+
+    /**
+     * Copy data from buf_src parameter to this VdmaBuffer.
+     * 
+     * Similar to 'write' but if (offset + count) is larger than the VdmaBuffer size, the copy continues
+     * from the start of the VdmaBuffer.
+     *
+     * @note count MUST be smaller than this VdmaBuffer size
+     *
+     * @param[in] buf_src The buffer to copy the data from
+     * @param[in] count Number of bytes to copy from buf_src
+     * @param[in] offset The offset relative to this VdmaBuffer to copy the data to
+     */
+    hailo_status write_cyclic(const void *buf_src, size_t count, size_t offset);
+
+    /**
+     * Copy data from this VdmaBuffer to buf_dst.
+     *
+     * Similar to 'read' but if (offset + count) is larger than the VdmaBuffer size, the copy continues
+     * from the start of the VdmaBuffer.
+     *
+     * @note count MUST be smaller than this VdmaBuffer size
+     *
+     * @param[out] buf_dst The buffer to copy the data to
+     * @param[in] count Number of bytes to copy to buf_dst
+     * @param[in] offset The offset relative to this VdmaBuffer to copy the data from
+     */
+    hailo_status read_cyclic(void *buf_dst, size_t count, size_t offset);
+
+private:
+
+    static Expected<MmapBuffer<void>> allocate_vdma_buffer(HailoRTDriver &driver, size_t required_size,
+        uintptr_t &driver_buff_handle);
+
+    MmapBuffer<void> m_user_address;
+    HailoRTDriver::VdmaBufferHandle m_handle;
+    size_t m_size;
+    HailoRTDriver &m_driver;
+    uintptr_t m_driver_buff_handle;
+};
+
+} /* namespace hailort */
+
+#endif /* _HAILO_VDMA_BUFFER_HPP_ */
\ No newline at end of file
diff --git a/hailort/libhailort/src/vdma_channel.cpp b/hailort/libhailort/src/vdma_channel.cpp
new file mode 100644 (file)
index 0000000..cfbe70d
--- /dev/null
@@ -0,0 +1,938 @@
+#include "vdma_channel.hpp"
+#include "vdma_channel_regs.hpp"
+#include "hw_consts.hpp"
+#include "common/logger_macros.hpp"
+#include "common/utils.hpp"
+
+#include <list>
+#include <chrono>
+#include <thread>
+
+namespace hailort
+{
+
+#define FD_READ_SIZE (8)
+#define MIN_TIMEOUT_DDR (1000)
+
+/* PLDA descriptor control */
+#define PCIE_DESCRIPTOR_CONTROL_CLR(src)\
+    src = (src & (~(uint32_t)0xFF))
+#define PCIE_DESCRIPTOR_CONTROL_SET_DESC_STATUS_REQ(src)\
+    src = ((src) | 0x01)
+#define PCIE_DESCRIPTOR_CONTROL_SET_DESC_STATUS_REQ_ERR(src)\
+    src = ((src) | 0x02)
+#define PCIE_DESCRIPTOR_CONTROL_SET_DESC_SET_IRQ_ON_ERROR(src)\
+    src = ((src) | 0x04)
+#define PCIE_DESCRIPTOR_CONTROL_SET_DESC_SET_IRQ_ON_AXI_DOMAIN(src)\
+    src = ((src) | 0x10)
+
+
+void VdmaChannel::State::lock()
+{
+#ifndef _MSC_VER
+    int err = pthread_mutex_lock(&m_state_lock);
+    if (0 != err) {
+        LOGGER__ERROR("Failed destory vdma channel mutex, errno {}", err);
+        assert(true);
+    }
+#else
+    EnterCriticalSection(&m_state_lock);
+#endif
+}
+
+void VdmaChannel::State::unlock()
+{
+#ifndef _MSC_VER
+    int err = pthread_mutex_unlock(&m_state_lock);
+    assert(0 == err);
+    (void)err;
+#else
+    LeaveCriticalSection(&m_state_lock);
+#endif
+}
+
+Expected<VdmaChannel> VdmaChannel::create(uint8_t channel_index, Direction direction, HailoRTDriver &driver,
+    uint16_t requested_desc_page_size, uint32_t stream_index, LatencyMeterPtr latency_meter, uint16_t transfers_per_axi_intr)
+{
+    CHECK_AS_EXPECTED(Direction::BOTH != direction, HAILO_INVALID_ARGUMENT);
+
+    hailo_status status = HAILO_UNINITIALIZED;
+    auto desc_page_size_value = driver.calc_desc_page_size(requested_desc_page_size);
+    CHECK_AS_EXPECTED(is_powerof2(desc_page_size_value), HAILO_INVALID_ARGUMENT,
+        "Descriptor page_size must be a power of two.");
+
+    VdmaChannel object(channel_index, direction, driver, stream_index, latency_meter, desc_page_size_value, 
+        transfers_per_axi_intr, status);
+    if (HAILO_SUCCESS != status) {
+        LOGGER__ERROR("Failed creating VdmaChannel");
+        return make_unexpected(status);
+    }
+    return object;
+}
+
+VdmaChannel::VdmaChannel(uint8_t channel_index, Direction direction, HailoRTDriver &driver, 
+    uint32_t stream_index, LatencyMeterPtr latency_meter, uint16_t desc_page_size, uint16_t transfers_per_axi_intr, 
+    hailo_status &status)
+    : channel_index(channel_index), m_direction(direction), m_driver(driver),
+      m_host_registers(driver, channel_index, direction),
+      m_device_registers(driver, channel_index, other_direction(direction)), m_desc_page_size(desc_page_size),
+      m_stream_index(stream_index), m_latency_meter(latency_meter), m_channel_enabled(false), 
+      m_transfers_per_axi_intr(transfers_per_axi_intr)
+{
+    // channel index invalid
+    if (channel_index >= MAX_HOST_CHANNELS_COUNT) {
+        LOGGER__ERROR("Invalid DMA index");
+        status = HAILO_INVALID_ARGUMENT;
+        return;
+    }
+
+    if (m_transfers_per_axi_intr == 0) {
+        LOGGER__ERROR("Invalid transfers per axi interrupt");
+        status = HAILO_INVALID_ARGUMENT;
+        return;
+    }
+
+    auto channel_handle_memory = MmapBuffer<HailoRTDriver::VdmaChannelHandle>::create_shared_memory(sizeof(HailoRTDriver::VdmaChannelHandle));
+    if (!channel_handle_memory) {
+        LOGGER__ERROR("Failed allocating shared memory for channel, err = {}", channel_handle_memory.status());
+        status = channel_handle_memory.status();
+        return;
+    }
+    m_channel_handle = channel_handle_memory.release();
+    *m_channel_handle = HailoRTDriver::INVALID_VDMA_CHANNEL_HANDLE;
+
+    // The channel will become active after calling start_allocated_channel().
+    // The driver cleans the channel's state, in case the last shutdown wasn't successful.
+    m_channel_enabled = true;
+
+    status = HAILO_SUCCESS;
+}
+
+VdmaChannel::~VdmaChannel()
+{
+    if (m_channel_enabled) {
+        stop_channel();
+        m_channel_enabled = false;
+    }
+
+    if (m_state) {
+#ifndef _MSC_VER
+        int err = pthread_mutex_destroy(&m_state->m_state_lock);
+        if (0 != err) {
+            LOGGER__ERROR("Failed destory vdma channel mutex, errno {}", err);
+        }
+#else
+        DeleteCriticalSection(&m_state->m_state_lock);
+#endif
+    }
+}
+
+VdmaChannel::VdmaChannel(VdmaChannel &&other) noexcept:
+channel_index(other.channel_index),
+ m_direction(other.m_direction),
+ m_driver(other.m_driver),
+ m_host_registers(std::move(other.m_host_registers)),
+ m_device_registers(std::move(other.m_device_registers)),
+ m_desc_page_size(other.m_desc_page_size),
+ m_mapped_user_buffer(std::move(other.m_mapped_user_buffer)),
+ m_descriptors_buffer(std::move(other.m_descriptors_buffer)),
+ m_stream_index(std::move(other.m_stream_index)),
+ m_latency_meter(std::move(other.m_latency_meter)),
+ m_state(std::move(other.m_state)),
+ m_channel_handle(std::move(other.m_channel_handle)),
+ m_channel_enabled(std::exchange(other.m_channel_enabled, false)),
+ m_transfers_per_axi_intr(std::move(other.m_transfers_per_axi_intr))
+{}
+
+hailo_status VdmaChannel::stop_channel()
+{
+    assert(m_channel_handle);
+    if (HailoRTDriver::INVALID_VDMA_CHANNEL_HANDLE != *m_channel_handle) {
+        // The driver also stops the channels
+        const auto status = unregister_fw_controlled_channel();
+        CHECK_SUCCESS(status, "Failed to disable channel {}", channel_index);
+    }
+    
+    return HAILO_SUCCESS;
+}
+
+uint16_t VdmaChannel::get_page_size() 
+{
+    return m_desc_page_size;
+}
+
+
+hailo_status VdmaChannel::abort()
+{
+    return m_driver.vdma_channel_abort(channel_index, *m_channel_handle);
+}
+
+hailo_status VdmaChannel::clear_abort()
+{
+    return m_driver.vdma_channel_clear_abort(channel_index, *m_channel_handle);
+}
+
+hailo_status VdmaChannel::prepare_d2h_pending_descriptors(uint32_t descs_count, uint32_t transfer_size)
+{
+    uint32_t descs_in_transfer = m_descriptors_buffer->descriptors_in_buffer(transfer_size);
+    uint32_t transfers_count = std::min(((descs_count - 1) / descs_in_transfer),
+        static_cast<uint32_t>(CB_SIZE(m_state->m_buffers) - 1));
+
+    // on D2H no need for interrupt of first descriptor
+    const auto first_desc_interrupts_domain = VdmaInterruptsDomain::NONE;
+    for (uint32_t i = 0; i < transfers_count; i++) {
+        /* Provide FW interrupt only in the end of the last transfer in the batch */
+        auto last_desc_interrutps_domain = 
+            (static_cast<uint32_t>(m_transfers_per_axi_intr -1) == (i % m_transfers_per_axi_intr)) ? 
+                VdmaInterruptsDomain::BOTH : VdmaInterruptsDomain::HOST;
+        auto status = prepare_descriptors(transfer_size, first_desc_interrupts_domain, last_desc_interrutps_domain);
+        CHECK_SUCCESS(status, "Failed prepare desc status={}", status);
+    }
+
+    /* We assume each output transfer is in the same size */
+    m_state->m_accumulated_transfers += ((m_state->m_accumulated_transfers + transfers_count) % m_transfers_per_axi_intr);
+
+    return HAILO_SUCCESS;
+}
+
+hailo_status VdmaChannel::allocate_resources(uint32_t descs_count)
+{
+    // TODO (HRT-3762) : Move channel's state to driver to avoid using shared memory
+    auto state = MmapBuffer<VdmaChannel::State>::create_shared_memory(sizeof(VdmaChannel::State));
+    CHECK_EXPECTED_AS_STATUS(state, "Failed to allocate channel's resources");
+
+#ifndef _MSC_VER
+    // Make sharable mutex
+    pthread_mutexattr_t mutex_attrs{};
+    int err = pthread_mutexattr_init(&mutex_attrs);
+    CHECK(0 == err, HAILO_INTERNAL_FAILURE, "pthread_mutexattr_init failed with {}", err);
+
+    err = pthread_mutexattr_setpshared(&mutex_attrs, PTHREAD_PROCESS_SHARED);
+    if (0 != err) {
+        (void)pthread_mutexattr_destroy(&mutex_attrs);
+        LOGGER__ERROR("pthread_mutexattr_setpshared failed with {}", err);
+        return HAILO_INTERNAL_FAILURE;
+    }
+
+    err = pthread_mutex_init(&state.value()->m_state_lock, &mutex_attrs);
+    if (0 != pthread_mutexattr_destroy(&mutex_attrs)) {
+        LOGGER__ERROR("pthread_mutexattr_destroy failed");
+        // continue
+    }
+    CHECK(0 == err, HAILO_INTERNAL_FAILURE, "Mutex init failed with {}", err);
+#else
+    InitializeCriticalSection(&state.value()->m_state_lock);
+#endif
+
+    m_state = state.release();
+
+    // If measuring latency, max_active_transfer is limited to 16 (see hailort_driver.hpp doc for further information)
+    int pending_buffers_size = (nullptr == m_latency_meter) ? static_cast<int>(m_state->m_pending_buffers.size()) :
+        (static_cast<int>(m_state->m_pending_buffers.size()) / 2);
+
+    if (MAX_DESCS_COUNT < descs_count) {
+        LOGGER__ERROR("Vdma channel descs_count mustn't be larger than {}", MAX_DESCS_COUNT);
+        return HAILO_INVALID_ARGUMENT;
+    }
+
+    CB_INIT(m_state->m_descs, descs_count);
+    CB_INIT(m_state->m_buffers, pending_buffers_size);
+
+    // Allocate descriptor list (host side)
+    auto status = allocate_buffer(descs_count * m_desc_page_size);
+    CHECK_SUCCESS(status, "Failed to allocate vDMA buffer for channel transfer! status={}", status);
+
+    clear_descriptor_list();
+
+    return HAILO_SUCCESS;
+}
+
+void VdmaChannel::reset_internal_counters()
+{
+    assert(m_state);
+    CB_RESET(m_state->m_descs);
+    CB_RESET(m_state->m_buffers);
+    m_state->m_d2h_read_desc_index = 0;
+    m_state->m_last_timestamp_num_processed = 0;
+    m_state->m_accumulated_transfers = 0;
+}
+
+hailo_status VdmaChannel::start_allocated_channel(uint32_t transfer_size)
+{
+    /* descriptor buffer must be allocated */
+    assert(m_descriptors_buffer);
+    assert(m_state);
+
+    std::lock_guard<State> state_guard(*m_state);
+
+    reset_internal_counters();
+
+    auto status = start_channel(*m_descriptors_buffer);
+    CHECK_SUCCESS(status, "failed to start channel {}", channel_index);
+
+    if ((Direction::D2H == m_direction) && (transfer_size != 0)) {
+        auto descs_count = CB_SIZE(m_state->m_descs);
+        status = prepare_d2h_pending_descriptors(descs_count, transfer_size);
+        if (HAILO_SUCCESS != status) {
+            stop_channel();
+        }
+        return status;
+    }
+    return HAILO_SUCCESS;
+}
+
+hailo_status VdmaChannel::register_fw_controlled_channel()
+{
+    return register_channel_to_driver(HailoRTDriver::INVALID_DRIVER_BUFFER_HANDLE_VALUE);
+}
+
+hailo_status VdmaChannel::wait(size_t buffer_size, const std::chrono::milliseconds &timeout)
+{
+    if (!m_mapped_user_buffer) {
+        LOGGER__ERROR("Wait called without allocating buffers");
+        return HAILO_INVALID_OPERATION;
+    }
+
+    CHECK(buffer_size < m_mapped_user_buffer->size(), HAILO_INVALID_ARGUMENT,
+        "Requested transfer size ({}) must be smaller than ({})", buffer_size, m_mapped_user_buffer->size());
+
+    auto is_ready_for_transfer = (Direction::H2D == m_direction)
+                                     ? std::bind(&VdmaChannel::is_ready_for_transfer_h2d, this, buffer_size)
+                                     : std::bind(&VdmaChannel::is_ready_for_transfer_d2h, this, buffer_size);
+
+    return wait_for_condition(is_ready_for_transfer, timeout);
+}
+
+hailo_status VdmaChannel::transfer(void *buf, size_t count)
+{
+    if ((nullptr == buf) || (0 == count)) {
+        return HAILO_INVALID_ARGUMENT;
+    }
+
+    if (!m_mapped_user_buffer) {
+        LOGGER__ERROR("Transfer called without allocating buffers");
+        return HAILO_INVALID_OPERATION;
+    }
+
+    hailo_status status = HAILO_UNINITIALIZED;
+    assert(m_state);
+    std::lock_guard<State> state_guard(*m_state);
+
+    if (Direction::H2D == m_direction) {
+        status = transfer_h2d(buf, count);
+        if (HAILO_SUCCESS != status) {
+            LOGGER__ERROR("Transfer failed for channel {}", channel_index);
+            return status;
+        }
+        return HAILO_SUCCESS;
+    } else {
+        status = transfer_d2h(buf, count);
+        if (HAILO_SUCCESS != status) {
+            LOGGER__ERROR("Transfer failed for channel {} status {}", channel_index, status);
+            return status;
+        }
+        return HAILO_SUCCESS;
+    }
+
+    return HAILO_SUCCESS;
+}
+
+hailo_status VdmaChannel::flush(const std::chrono::milliseconds &timeout)
+{
+    assert(m_state);
+
+    if (Direction::D2H == m_direction) {
+        // We are not buffering user data
+        return HAILO_SUCCESS;
+    }
+
+    if (!m_mapped_user_buffer) {
+        LOGGER__ERROR("VdmaChannel::flush is called on a channel without allocated resources");
+        return HAILO_INVALID_OPERATION;
+    }
+
+    return wait_for_condition([this] { return CB_HEAD(m_state->m_buffers) == CB_TAIL(m_state->m_buffers); }, timeout);
+}
+
+hailo_status VdmaChannel::transfer_h2d(void *buf, size_t count)
+{
+    uint16_t descNum = 0;
+    const uint16_t pageSize = m_desc_page_size;
+    uint32_t desc_avail = 0;
+    hailo_status status = HAILO_UNINITIALIZED;
+    // For h2d, only the host need to get transfer done interrupts
+    VdmaInterruptsDomain last_desc_interrupts_domain = VdmaInterruptsDomain::HOST;
+    // If we measure latecy, we need interrupt on the first descriptor
+    VdmaInterruptsDomain first_desc_interrupts_domain = (m_latency_meter != nullptr) ?
+        VdmaInterruptsDomain::HOST : VdmaInterruptsDomain::NONE;
+
+    assert(buf != nullptr);
+    assert(m_state);
+
+    desc_avail = get_num_available();
+
+    /* calculate desired descriptors for the buffer */
+    size_t desiredDescNum = ((count + pageSize - 1) / pageSize);
+    if (desiredDescNum > UINT16_MAX) {
+        return HAILO_OUT_OF_DESCRIPTORS;
+    }
+    descNum = (uint16_t)(desiredDescNum);
+    int num_available = desc_avail;
+    int num_processed = CB_TAIL(m_state->m_descs);
+    int num_free = CB_AVAIL(m_state->m_descs, num_available, num_processed);
+    if (num_free < descNum) {
+        return HAILO_OUT_OF_DESCRIPTORS;
+    }
+
+    /* Copy buffer into the PLDA data struct */
+    auto offset = desc_avail * pageSize;
+    status = m_mapped_user_buffer->write_cyclic(buf, count, offset);
+    CHECK_SUCCESS(status);
+
+    status = prepare_descriptors(count, first_desc_interrupts_domain, last_desc_interrupts_domain);
+    CHECK_SUCCESS(status);
+
+    m_state->m_accumulated_transfers = (m_state->m_accumulated_transfers + 1) % m_transfers_per_axi_intr;
+
+    return HAILO_SUCCESS;
+}
+
+hailo_status VdmaChannel::transfer_d2h(void *buf, size_t count)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    /* Provide FW interrupt only in the end of the last transfer in the batch */
+    VdmaInterruptsDomain first_desc_interrupts_domain = VdmaInterruptsDomain::NONE;
+    VdmaInterruptsDomain last_desc_interrupts_domain = (m_state->m_accumulated_transfers + 1 == m_transfers_per_axi_intr) ? 
+        VdmaInterruptsDomain::BOTH : VdmaInterruptsDomain::HOST;
+    assert(m_state);
+
+    auto desired_desc_num = m_descriptors_buffer->descriptors_in_buffer(count);
+    assert(desired_desc_num <= MAX_DESCS_COUNT);
+    int desc_num = static_cast<int>(desired_desc_num);
+
+    int num_processes = CB_TAIL(m_state->m_descs);
+    int num_ready = CB_PROG(m_state->m_descs, num_processes, m_state->m_d2h_read_desc_index);
+    if (num_ready < desc_num) {
+        return HAILO_OUT_OF_DESCRIPTORS;
+    }
+
+    size_t offset = m_state->m_d2h_read_desc_index * m_desc_page_size;
+    status = m_mapped_user_buffer->read_cyclic(buf, count, offset);
+    if (status != HAILO_SUCCESS) {
+        return status;
+    }
+
+    m_state->m_d2h_read_desc_index = (m_state->m_d2h_read_desc_index + desc_num) & m_state->m_descs.size_mask;
+
+    // prepare descriptors for next recv
+    status = prepare_descriptors(count, first_desc_interrupts_domain, last_desc_interrupts_domain);
+    CHECK_SUCCESS(status);
+
+    m_state->m_accumulated_transfers = (m_state->m_accumulated_transfers + 1) % m_transfers_per_axi_intr;
+
+    return HAILO_SUCCESS;
+
+}
+
+uint16_t VdmaChannel::get_num_available()
+{
+    assert(m_state);
+
+    uint16_t num_available = (uint16_t)CB_HEAD(m_state->m_descs);
+
+#ifndef NDEBUG
+    // Validate synchronization with HW
+    auto hw_num_avail = m_host_registers.get_num_available();
+    assert(hw_num_avail);
+    // On case of channel aborted, the num_available is set to 0 (so we don't accept sync)
+    assert((hw_num_avail.value() == num_available) || is_aborted().value());
+#endif
+    return num_available;
+}
+
+Expected<uint16_t> VdmaChannel::get_hw_num_processed()
+{
+    assert(m_state);
+
+    auto hw_num_processed = m_host_registers.get_num_processed();
+    CHECK_EXPECTED(hw_num_processed, "Fail to read vdma num processed register");
+
+    // Although the hw_num_processed should be a number between 0 and m_descs.size-1, if
+    // m_desc.size < 0x10000 (the maximum desc size), the actual hw_num_processed is a number
+    // between 1 and m_descs.size. Therefore the value can be m_descs.size, in this case we change it
+    // to zero.
+    return static_cast<uint16_t>(hw_num_processed.value() & m_state->m_descs.size_mask);
+}
+
+Expected<uint16_t> VdmaChannel::get_hw_num_processed_ddr(uint32_t size_mask)
+{
+    auto hw_num_processed = m_host_registers.get_num_processed();
+    CHECK_EXPECTED(hw_num_processed, "Fail to read vdma num processed register");
+
+    // Although the hw_num_processed should be a number between 0 and m_descs.size-1, if
+    // m_desc.size < 0x10000 (the maximum desc size), the actual hw_num_processed is a number
+    // between 1 and m_descs.size. Therefore the value can be m_descs.size, in this case we change it
+    // to zero.
+    return static_cast<uint16_t>(hw_num_processed.value() & size_mask);
+}
+
+hailo_status VdmaChannel::wait_channel_interrupts_for_ddr(const std::chrono::milliseconds &timeout)
+{
+    auto irq_data = m_driver.wait_channel_interrupts(channel_index, *m_channel_handle, timeout);
+    return irq_data.status();
+}
+
+hailo_status VdmaChannel::set_num_avail_value(uint16_t new_value)
+{
+    auto status = m_host_registers.set_num_available(new_value);
+    CHECK_SUCCESS(status, "Fail to write vdma num available register");
+
+#ifndef NDEBUG
+    // Validate synchronization with HW
+    auto hw_num_avail = m_host_registers.get_num_available();
+    assert(hw_num_avail);
+    assert(hw_num_avail.value() == new_value);
+#endif
+
+    return HAILO_SUCCESS;
+}
+
+hailo_status VdmaChannel::inc_num_available_for_ddr(uint16_t value, uint32_t size_mask)
+{
+    //TODO: validate that count is added.
+    auto num_available = m_host_registers.get_num_available();
+    CHECK_EXPECTED_AS_STATUS(num_available, "Fail to read vdma num available register");
+
+    uint16_t new_value =  static_cast<uint16_t>((num_available.value() + value) & size_mask);
+    return set_num_avail_value(new_value);
+}
+
+hailo_status VdmaChannel::inc_num_available(uint16_t value)
+{
+    assert(m_state);
+
+    //TODO: validate that count is added.
+    int num_available = get_num_available();
+    int num_processed = CB_TAIL(m_state->m_descs);
+    int num_free = CB_AVAIL(m_state->m_descs, num_available, num_processed);
+    uint16_t hw_num_avail = 0;
+    
+    (void) hw_num_avail;
+    if (value > num_free) {
+        return HAILO_OUT_OF_DESCRIPTORS;
+    }
+
+    CB_ENQUEUE(m_state->m_descs, value);
+    num_available = (num_available + value) & m_state->m_descs.size_mask;
+    return set_num_avail_value(static_cast<uint16_t>(num_available));
+}
+
+void VdmaChannel::add_pending_buffer(uint32_t first_desc, uint32_t last_desc)
+{
+    assert(m_state);
+
+    int head = CB_HEAD(m_state->m_buffers);
+    int tail = CB_TAIL(m_state->m_buffers);
+    if (!CB_AVAIL(m_state->m_buffers, head, tail)) {
+        LOGGER__ERROR("no avail space");
+    }
+    m_state->m_pending_buffers[head].last_desc = last_desc;
+    m_state->m_pending_buffers[head].latency_measure_desc = (m_direction == Direction::H2D) ? first_desc : last_desc;
+    CB_ENQUEUE(m_state->m_buffers, 1);
+}
+
+VdmaChannel::Direction VdmaChannel::other_direction(Direction direction)
+{
+    return (Direction::H2D == direction) ? Direction::D2H : Direction::H2D;
+}
+
+hailo_status VdmaChannel::unregister_fw_controlled_channel()
+{
+    auto status = m_driver.vdma_channel_disable(channel_index, *m_channel_handle);
+    *m_channel_handle = HailoRTDriver::INVALID_VDMA_CHANNEL_HANDLE;
+    CHECK_SUCCESS(status, "Failed to disable channel {}", channel_index);
+
+    return HAILO_SUCCESS;
+}
+
+hailo_status VdmaChannel::register_channel_to_driver(uintptr_t desc_list_handle)
+{
+    const bool measure_latency = (nullptr != m_latency_meter);
+    const uintptr_t desc_handle = desc_list_handle;
+    auto channel_handle = m_driver.vdma_channel_enable(channel_index, m_direction, desc_handle, measure_latency);
+    CHECK_EXPECTED_AS_STATUS(channel_handle, "Failed to enable channel {}", channel_index);
+
+    *m_channel_handle = channel_handle.release();
+    return HAILO_SUCCESS;
+}
+
+hailo_status VdmaChannel::start_channel(VdmaDescriptorList &desc_list)
+{
+    assert(is_aborted());
+
+    auto status = register_channel_to_driver(desc_list.handle());
+    CHECK_SUCCESS(status, "Failed to enable channel {}", channel_index);
+
+    return HAILO_SUCCESS;
+}
+
+void VdmaChannel::clear_descriptor_list()
+{
+    assert(m_descriptors_buffer);
+
+    size_t desc_number = m_descriptors_buffer->count();
+    size_t page_size = m_mapped_user_buffer->size() / desc_number;
+
+    // Config Descriptors value in SG-List Host side
+    for (uint32_t j = 0; j < desc_number; j++) {
+        VdmaDescriptor &descInfo = (*m_descriptors_buffer)[j];
+        descInfo.PageSize_DescControl = static_cast<uint32_t>((page_size << 8) + 0x2);
+        descInfo.RemainingPageSize_Status = 0x0;
+    }
+}
+
+hailo_status VdmaChannel::allocate_buffer(const size_t buffer_size)
+{
+    assert((buffer_size % m_desc_page_size) == 0);
+    size_t desc_count = buffer_size / m_desc_page_size;
+
+    if (m_mapped_user_buffer) {
+        LOGGER__ERROR("m_mapped_user_buffer is not NULL");
+        return HAILO_INVALID_OPERATION;
+    }
+
+    auto mapped_buffer = VdmaBuffer::create(buffer_size, m_direction, m_driver);
+    if(!mapped_buffer) {
+        LOGGER__ERROR("create mapped buffer failed");
+        return mapped_buffer.status();
+    }
+
+    auto descriptors = VdmaDescriptorList::create(desc_count, m_desc_page_size, m_driver);
+    if (!descriptors) {
+        LOGGER__ERROR("create descriptor list failed");
+        return descriptors.status();
+    }
+
+    auto status = descriptors->configure_to_use_buffer(mapped_buffer.value(), channel_index);
+    if (status != HAILO_SUCCESS) {
+        LOGGER__ERROR("connect descriptor list to buffer failed");
+        return status;
+    }
+
+    m_mapped_user_buffer = make_unique_nothrow<VdmaBuffer>(mapped_buffer.release());
+    CHECK_NOT_NULL(m_mapped_user_buffer, HAILO_OUT_OF_HOST_MEMORY);
+
+    m_descriptors_buffer = make_unique_nothrow<VdmaDescriptorList>(descriptors.release());
+    CHECK_NOT_NULL(m_descriptors_buffer, HAILO_OUT_OF_HOST_MEMORY);
+
+    return HAILO_SUCCESS;
+}
+
+uint32_t VdmaChannel::calculate_buffer_size(const HailoRTDriver &driver, uint32_t transfer_size,
+    uint32_t transfers_count, uint16_t requested_desc_page_size) {
+    auto desc_page_size = driver.calc_desc_page_size(requested_desc_page_size);
+    uint32_t descs_per_transfer = VdmaDescriptorList::descriptors_in_buffer(transfer_size, desc_page_size);
+    uint32_t descs_count = descs_per_transfer * transfers_count;
+
+    if (descs_count > MAX_DESCS_COUNT) {
+        descs_count = MAX_DESCS_COUNT;
+    }
+    else if (descs_count < MIN_DESCS_COUNT) {
+        descs_count = MIN_DESCS_COUNT;
+    }
+
+    return descs_count * desc_page_size;
+}
+
+hailo_status VdmaChannel::trigger_channel_completion(uint16_t hw_num_processed)
+{
+    // NOTE: right now, we can retake the 'completion' descriptor for a new transfer before handling the interrupt.
+    //      we should have our own pointers indicating whats free instead of reading from HW.
+    // TODO: consider calculating the last descriptor using the src_desc_avail and src_desc_proc instead of using
+    // status?
+    // TODO: we might free a pending buffer which we didn't get an interrupt for yet. we should still handle this
+    // situation correctly.
+
+    assert(m_state);
+
+    int processed_no = 0;
+    int head = CB_HEAD(m_state->m_buffers);
+    int tail = CB_TAIL(m_state->m_buffers);
+    int prog = CB_PROG(m_state->m_buffers, head, tail);
+    int last_tail = -1;
+
+    auto channel_error = m_host_registers.get_channel_error();
+    CHECK_EXPECTED_AS_STATUS(channel_error, "Fail to read vdma channel error register");
+    CHECK(0 == channel_error.value(), HAILO_INTERNAL_FAILURE, "Vdma channel {} in error state {}", channel_index, channel_error.value());
+
+    uint16_t last_num_processed = static_cast<uint16_t>(CB_TAIL(m_state->m_descs));
+
+    for (; prog > 0; prog--) {
+        uint16_t last_desc_index = static_cast<uint16_t>(m_state->m_pending_buffers[tail].last_desc);
+        // Transfer is complete if its last descriptor is in [last_num_processed, hw_num_processed) or
+        // the the buffer is empty (hw_num_processed == get_num_available())
+        bool is_complete = (is_desc_between(last_num_processed, hw_num_processed, last_desc_index) ||
+                            hw_num_processed == get_num_available());
+
+#ifndef NDEBUG
+        auto status = (*m_descriptors_buffer)[last_desc_index].RemainingPageSize_Status & 0xFF;
+        // Verify if a DMA Descriptor error occurred.
+        if (status & 0x2) {
+            LOGGER__ERROR("Error while processing descriptor {} of DMA {} on board {}.", last_desc_index, channel_index,
+                m_driver.dev_path());
+            return HAILO_INTERNAL_FAILURE;
+        }
+
+        // status is read after hw_num_processed, so we want is_complete -> (status == 1).
+        assert(!is_complete || ((status & 0x1) == 1));
+#endif
+
+        if (!is_complete) {
+            break;
+        }
+
+        processed_no++;
+        last_tail = tail;
+        tail = ((tail + 1) & m_state->m_buffers.size_mask);
+    }
+
+    if (0 < processed_no) {
+        // TODO: use a different macro instead?
+        _CB_SET(m_state->m_descs.tail, (m_state->m_pending_buffers[last_tail].last_desc + 1) & m_state->m_descs.size_mask);
+        CB_DEQUEUE(m_state->m_buffers, processed_no);
+    }
+
+    return HAILO_SUCCESS;
+}
+
+bool VdmaChannel::is_ready_for_transfer_h2d(size_t buffer_size)
+{   
+    assert(m_state);
+
+    size_t desired_desc_num = m_descriptors_buffer->descriptors_in_buffer(buffer_size);
+    assert(desired_desc_num <= MAX_DESCS_COUNT);
+    int desc_num = static_cast<int>(desired_desc_num);
+
+    int buffers_head = CB_HEAD(m_state->m_buffers);
+    int buffers_tail = CB_TAIL(m_state->m_buffers);
+    if (!CB_AVAIL(m_state->m_buffers, buffers_head, buffers_tail)) {
+        return false;
+    }
+
+    int num_available = get_num_available();
+    int num_processed = CB_TAIL(m_state->m_descs);
+
+    if (desc_num == m_state->m_descs.size) {
+        // Special case when the checking if the buffer is empty
+        return num_available == num_processed; 
+    }
+
+    int num_free = CB_AVAIL(m_state->m_descs, num_available, num_processed);
+    if (num_free < desc_num) {
+        return false;
+    }
+
+    return true;
+}
+
+bool VdmaChannel::is_ready_for_transfer_d2h(size_t buffer_size)
+{
+    assert(m_state);
+
+    size_t desired_desc_num = m_descriptors_buffer->descriptors_in_buffer(buffer_size);
+    assert(desired_desc_num <= MAX_DESCS_COUNT);
+    int desc_num = static_cast<int>(desired_desc_num);
+
+    int buffers_head = CB_HEAD(m_state->m_buffers);
+    int buffers_tail = CB_TAIL(m_state->m_buffers);
+    if (!CB_AVAIL(m_state->m_buffers, buffers_head, buffers_tail)) {
+        return false;
+    }
+
+    int num_processed = CB_TAIL(m_state->m_descs);
+    int num_ready = CB_PROG(m_state->m_descs, num_processed, m_state->m_d2h_read_desc_index);
+    if (num_ready < desc_num) {
+        return false;
+    }
+
+    return true;
+}
+
+hailo_status VdmaChannel::prepare_descriptors(size_t transfer_size, VdmaInterruptsDomain first_desc_interrupts_domain,
+    VdmaInterruptsDomain last_desc_interrupts_domain)
+{
+    assert(m_descriptors_buffer);
+    assert(m_state);
+    auto &desc_info = *m_descriptors_buffer;
+
+    /* calculate desired descriptors for the buffer */
+    size_t desired_desc_num = m_descriptors_buffer->descriptors_in_buffer(transfer_size);
+    assert(desired_desc_num <= MAX_DESCS_COUNT);
+    uint16_t desc_num = static_cast<uint16_t>(desired_desc_num);
+
+    int num_available = get_num_available();
+    int num_processed = CB_TAIL(m_state->m_descs);
+    int num_free = CB_AVAIL(m_state->m_descs, num_available, num_processed);
+    if (num_free < desc_num) {
+        return HAILO_OUT_OF_DESCRIPTORS;
+    }
+
+    auto actual_desc_count = desc_info.program_descriptors(transfer_size, first_desc_interrupts_domain,
+        last_desc_interrupts_domain, num_available, true);
+    if (!actual_desc_count) {
+        LOGGER__ERROR("Failed to program desc_list for channel {}", channel_index);
+        return actual_desc_count.status();
+    }
+    assert (actual_desc_count.value() == desc_num);
+    int last_desc_avail = ((num_available + desc_num - 1) & m_state->m_descs.size_mask);
+
+    add_pending_buffer(num_available, last_desc_avail);
+    return inc_num_available(desc_num);
+}
+
+uint32_t VdmaChannel::calculate_descriptors_count(uint32_t buffer_size)
+{
+    return VdmaDescriptorList::calculate_descriptors_count(buffer_size, 1, m_desc_page_size);
+}
+
+bool VdmaChannel::is_desc_between(uint16_t begin, uint16_t end, uint16_t desc)
+{
+    if (begin == end) {
+        // There is nothing between
+        return false;
+    }
+    if (begin < end) {
+        // desc needs to be in [begin, end)
+        return (begin <= desc) && (desc < end);
+    }
+    else {
+        // desc needs to be in [0, end) or [begin, m_state->m_descs.size()-1]
+        return (desc < end) || (begin <= desc);
+    }
+}
+
+Expected<bool> VdmaChannel::is_aborted()
+{
+    // Checking if either src side or dst side of the channel are aborted
+    auto host_control = m_host_registers.get_control();
+    CHECK_EXPECTED(host_control, "Fail to read vdma control register");
+    if (vdma_channel_control_is_aborted(host_control.value()) ||
+        vdma_channel_control_is_paused(host_control.value())) {
+        return true;
+    }
+
+    auto device_control = m_device_registers.get_control();
+    CHECK_EXPECTED(device_control, "Fail to read vdma control register");
+    if (vdma_channel_control_is_aborted(device_control.value()) ||
+        vdma_channel_control_is_paused(device_control.value())) {
+        return true;
+    }
+
+    return false;
+}
+
+hailo_status VdmaChannel::wait_for_condition(std::function<bool()> condition, std::chrono::milliseconds timeout)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    auto start_time = std::chrono::steady_clock::now();
+    std::chrono::milliseconds time_elapsed(0);
+    while (timeout > time_elapsed) {
+        if (condition()) {
+            return HAILO_SUCCESS;
+        }
+
+        auto hw_num_processed = wait_interrupts(timeout);
+        if ((hw_num_processed.status() == HAILO_TIMEOUT) ||
+            (hw_num_processed &&  hw_num_processed.value() == 0)) {
+            // We need to check for channel abort in this 2 cases:
+            //  1. TIMEOUT - maybe the timeout is a result of channel aborted.
+            //  2. hw_num_processed == 0 - In this case we receive an interrupt, but the channel may be
+            //     aborted. When the channel is aborted, num processed is set to 0.
+            auto is_aborted_exp = is_aborted();
+            CHECK_EXPECTED_AS_STATUS(is_aborted_exp);
+            if (is_aborted_exp.value()) {
+                LOGGER__CRITICAL("Channel {} was aborted by an external source!", channel_index);
+                return HAILO_STREAM_ABORTED;
+            }
+        }
+        if ((HAILO_STREAM_INTERNAL_ABORT == hw_num_processed.status()) ||
+            (HAILO_STREAM_NOT_ACTIVATED == hw_num_processed.status())) {
+            return hw_num_processed.status();
+        }
+        CHECK_EXPECTED_AS_STATUS(hw_num_processed);
+
+        status = trigger_channel_completion(hw_num_processed.value());
+        CHECK_SUCCESS(status);
+
+        time_elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - start_time);
+    }
+
+    return condition() ? HAILO_SUCCESS : HAILO_TIMEOUT;
+}
+
+Expected<uint16_t> VdmaChannel::wait_interrupts(std::chrono::milliseconds timeout)
+{
+    assert(m_state);
+
+    auto irq_data = m_driver.wait_channel_interrupts(channel_index, *m_channel_handle, timeout);
+    if ((HAILO_STREAM_INTERNAL_ABORT == irq_data.status()) ||
+        (HAILO_STREAM_NOT_ACTIVATED == irq_data.status())) {
+        LOGGER__INFO("Wait channel interrupts was aborted!");
+        return make_unexpected(irq_data.status());
+    }
+    CHECK_EXPECTED(irq_data);
+
+    if (m_latency_meter == nullptr) {
+        return get_hw_num_processed();
+    }
+    else {
+        // Fixing desc num_processed (it may be equal to m_state->m_descs.size, in this case we will make it zero)
+        for (size_t i = 0; i < irq_data->count; i++) {
+            irq_data->timestamp_list[i].desc_num_processed = static_cast<uint16_t>(
+                irq_data->timestamp_list[i].desc_num_processed & m_state->m_descs.size_mask);
+        }
+        return update_latency_meter(irq_data.value());
+    }
+}
+
+Expected<uint16_t> VdmaChannel::update_latency_meter(const ChannelInterruptTimestampList &timestamp_list)
+{
+    assert(m_state);
+
+    uint16_t last_num_processed = m_state->m_last_timestamp_num_processed;
+    if (timestamp_list.count == 0) {
+        // TODO: handle this in the driver level.
+        return last_num_processed;
+    }
+
+    // TODO: now we have more iterations than we need. We know that the pending buffers + the timestamp list
+    // are ordered. If pending_buffer[i] is not in any of the timestamps_list[0, 1, ... k], then also pending_buffer[i+1,i+2,...]
+    // not in those timestamps
+
+    int head = CB_HEAD(m_state->m_buffers);
+    int tail = CB_TAIL(m_state->m_buffers);
+    int prog = CB_PROG(m_state->m_buffers, head, tail);
+
+    for (; prog > 0; prog--, tail = ((tail + 1) & m_state->m_buffers.size_mask)) {
+        uint16_t latency_desc = static_cast<uint16_t>(m_state->m_pending_buffers[tail].latency_measure_desc);
+        for (size_t i = 0; i < timestamp_list.count; i++) {
+            const auto &irq_timestamp = timestamp_list.timestamp_list[i];
+            if (is_desc_between(last_num_processed, irq_timestamp.desc_num_processed, latency_desc)) {
+                if (m_direction == Direction::H2D) {
+                    m_latency_meter->add_start_sample(irq_timestamp.timestamp);
+                }
+                else {
+                    m_latency_meter->add_end_sample(m_stream_index, irq_timestamp.timestamp);
+                }
+                break;
+            }
+        }
+    }
+
+    m_state->m_last_timestamp_num_processed = timestamp_list.timestamp_list[timestamp_list.count-1].desc_num_processed;
+    return std::move(static_cast<uint16_t>(m_state->m_last_timestamp_num_processed));
+}
+
+} /* namespace hailort */
diff --git a/hailort/libhailort/src/vdma_channel.hpp b/hailort/libhailort/src/vdma_channel.hpp
new file mode 100644 (file)
index 0000000..d2a9f92
--- /dev/null
@@ -0,0 +1,178 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file vdma_channel.hpp
+ * @brief Cordinator of everything related to one channel of Vdma
+ *
+ * <doc>
+ **/
+
+#ifndef _HAILO_VDMA_CHANNEL_HPP_
+#define _HAILO_VDMA_CHANNEL_HPP_
+
+#include "hailo/hailort.h"
+#include "common/circular_buffer.hpp"
+#include "common/latency_meter.hpp"
+#include "vdma_channel_regs.hpp"
+#include "hailo/expected.hpp"
+#include "os/hailort_driver.hpp"
+#include "vdma_buffer.hpp"
+#include "vdma_descriptor_list.hpp"
+
+#include <mutex>
+#include <array>
+
+namespace hailort
+{
+
+class VdmaChannel final
+{
+public:
+    using Direction = HailoRTDriver::DmaDirection;
+
+    static Expected<VdmaChannel> create(uint8_t channel_index, Direction direction, HailoRTDriver &driver,
+        uint16_t requested_desc_page_size, uint32_t stream_index = 0, LatencyMeterPtr latency_meter = nullptr, 
+        uint16_t transfers_per_axi_intr = 1);
+    ~VdmaChannel();
+
+    /**
+     * Waits until the channel is ready for transfer `buffer_size` bytes.
+     * For now only supported in H2D stream.
+     * TODO: SDK-15831 support D2H
+     *
+     * @param[in] buffer_size
+     * @param[in] timeout
+     */
+    hailo_status wait(size_t buffer_size, const std::chrono::milliseconds &timeout);
+
+    hailo_status transfer(void *buf, size_t count);
+    hailo_status trigger_channel_completion(uint16_t hw_num_processed);
+    hailo_status allocate_resources(uint32_t descs_count);
+    /* For channels controlled by the HailoRT, the HailoRT needs to use this function to start the channel (it registers the channel to driver 
+       and starts the vDMA channel. Used for boundary channels */
+    hailo_status start_allocated_channel(uint32_t transfer_size);
+    hailo_status register_fw_controlled_channel();
+    hailo_status unregister_fw_controlled_channel();
+    hailo_status flush(const std::chrono::milliseconds &timeout);
+    hailo_status set_num_avail_value(uint16_t new_value);
+    hailo_status inc_num_available_for_ddr(uint16_t value, uint32_t size_mask);
+    hailo_status wait_channel_interrupts_for_ddr(const std::chrono::milliseconds &timeout);
+    Expected<uint16_t> get_hw_num_processed_ddr(uint32_t size_mask);
+    /*Used for DDR channels only. TODO - remove */
+    hailo_status start_channel(VdmaDescriptorList &desc_list);
+    /* For channels controlled by the FW (inter context and cfg channels), the hailort needs only to register the channel to the driver.
+       The FW would be responsible to open and close the channel */
+    hailo_status register_channel_to_driver(uintptr_t desc_list_handle);
+    hailo_status stop_channel();
+    uint16_t get_page_size();
+
+    hailo_status abort();
+    hailo_status clear_abort();
+
+    VdmaChannel(const VdmaChannel &other) = delete;
+    VdmaChannel &operator=(const VdmaChannel &other) = delete;
+    VdmaChannel(VdmaChannel &&other) noexcept;
+    VdmaChannel &operator=(VdmaChannel &&other) = delete;
+
+    static uint32_t calculate_buffer_size(const HailoRTDriver &driver, uint32_t transfer_size, uint32_t transfers_count,
+        uint16_t requested_desc_page_size);
+
+
+    const uint8_t channel_index;
+
+private:
+    struct PendingBuffer {
+        uint32_t last_desc;
+        uint32_t latency_measure_desc;
+    };
+
+    // TODO (HRT-3762) : Move channel's state to driver to avoid using shared memory
+    class State {
+    public:
+
+        void lock();
+        void unlock();
+
+#ifndef _MSC_VER
+        pthread_mutex_t m_state_lock;
+#else
+        CRITICAL_SECTION m_state_lock;
+#endif
+        std::array<PendingBuffer, PENDING_BUFFERS_SIZE> m_pending_buffers;
+        circbuf_t m_buffers;
+        // TODO: describe why we must have our own num_available and num_proc.
+        // it's not just for efficiency but its critical to avoid a potential bug - see Avigail email.
+        // TODO: Consider C11 stdatomic
+        circbuf_t m_descs;
+        int m_d2h_read_desc_index;
+        // Contains the last num_processed of the last interrupt (only used on latency measurement)
+        uint16_t m_last_timestamp_num_processed;
+        size_t m_accumulated_transfers;
+    };
+
+    VdmaChannel(uint8_t channel_index, Direction direction, HailoRTDriver &driver, uint32_t stream_index,
+        LatencyMeterPtr latency_meter, uint16_t desc_page_size, uint16_t transfers_per_axi_intr, hailo_status &status);
+
+    hailo_status allocate_buffer(const size_t buffer_size);
+    void clear_descriptor_list();
+    hailo_status release_buffer();
+    static Direction other_direction(const Direction direction);
+    hailo_status transfer_h2d(void *buf, size_t count);
+    uint16_t get_num_available();
+    Expected<uint16_t> get_hw_num_processed();
+    void add_pending_buffer(uint32_t first_desc, uint32_t last_desc);
+    hailo_status inc_num_available(uint16_t value);
+    hailo_status transfer_d2h(void *buf, size_t count);
+    bool is_ready_for_transfer_h2d(size_t buffer_size);
+    bool is_ready_for_transfer_d2h(size_t buffer_size);
+    hailo_status prepare_descriptors(size_t transfer_size, VdmaInterruptsDomain first_desc_interrupts_domain,
+        VdmaInterruptsDomain last_desc_interrupts_domain);
+    hailo_status prepare_d2h_pending_descriptors(uint32_t descs_count, uint32_t transfer_size);
+    void reset_internal_counters();
+
+    uint32_t calculate_descriptors_count(uint32_t buffer_size);
+
+    hailo_status wait_for_condition(std::function<bool()> condition, std::chrono::milliseconds timeout);
+
+    /**
+     * Returns the new hw num_processed of the irq
+     */
+    Expected<uint16_t> wait_interrupts(std::chrono::milliseconds timeout);
+
+    /**
+     * Returns the new hw num processed. 
+     */
+    Expected<uint16_t> update_latency_meter(const ChannelInterruptTimestampList &timestamp_list);
+    static bool is_desc_between(uint16_t begin, uint16_t end, uint16_t desc);
+    Expected<bool> is_aborted();
+
+    Direction m_direction;
+    HailoRTDriver &m_driver;
+    VdmaChannelRegs m_host_registers;
+    VdmaChannelRegs m_device_registers;
+
+    // TODO: use m_descriptors_buffer.desc_page_size()
+    const uint16_t m_desc_page_size;
+
+    // TODO: remove the unique_ptr, instead allocate the buffer in the ctor (needs to move ddr channel to
+    // other class)
+    std::unique_ptr<VdmaBuffer> m_mapped_user_buffer;
+    std::unique_ptr<VdmaDescriptorList> m_descriptors_buffer;
+    uint32_t m_stream_index;
+    LatencyMeterPtr m_latency_meter;
+
+    MmapBuffer<State> m_state;
+    // Unique channel handle, may be changed between registration to driver. This object is shared
+    // because multiple processes can enable/disable vdma channel (which changes the channel)
+    MmapBuffer<HailoRTDriver::VdmaChannelHandle> m_channel_handle;
+
+    bool m_channel_enabled;
+    
+    uint16_t m_transfers_per_axi_intr;
+};
+
+} /* namespace hailort */
+
+#endif  // _HAILO_VDMA_CHANNEL_HPP_
\ No newline at end of file
diff --git a/hailort/libhailort/src/vdma_channel_regs.hpp b/hailort/libhailort/src/vdma_channel_regs.hpp
new file mode 100644 (file)
index 0000000..f035e2e
--- /dev/null
@@ -0,0 +1,111 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file vdma_channel_regs.hpp
+ * @brief utilties used to parse/modify PLDA Vdma channel registers
+ **/
+
+#ifndef _HAILO_VDMA_CHANNEL__REGS_HPP_
+#define _HAILO_VDMA_CHANNEL__REGS_HPP_
+
+#include "hw_consts.hpp"
+#include "hailo/expected.hpp"
+#include "os/hailort_driver.hpp"
+
+#include <cstdint>
+
+namespace hailort
+{
+
+#define DESCPRIPTOR_LIST_MAX_DEPTH (16)
+
+inline bool vdma_channel_control_is_aborted(uint8_t control_reg)
+{
+    return (control_reg & 1) == 0;
+}
+
+inline bool vdma_channel_control_is_paused(uint8_t control_reg)
+{
+    return (control_reg & 2) == 2;
+}
+
+class VdmaChannelRegs final {
+public:
+    VdmaChannelRegs(HailoRTDriver &driver, uint32_t channel_index, HailoRTDriver::DmaDirection direction) :
+        m_driver(driver),
+        m_channel_offset(VDMA_CHANNEL_OFFSET(channel_index, (HailoRTDriver::DmaDirection::H2D == direction)))
+    {}
+
+    Expected<uint8_t> get_control()
+    {
+        return read_integer<uint8_t>(VDMA_CHANNEL_CONTROL_OFFSET);
+    }
+
+    Expected<uint16_t> get_num_available()
+    {
+        return read_integer<uint16_t>(VDMA_CHANNEL_NUM_AVAIL_OFFSET);
+    }
+
+    hailo_status set_num_available(uint16_t value)
+    {
+        return write_integer<uint16_t>(VDMA_CHANNEL_NUM_AVAIL_OFFSET, value);
+    }
+
+    Expected<uint16_t> get_num_processed()
+    {
+        return read_integer<uint16_t>(VDMA_CHANNEL_NUM_PROC_OFFSET);
+    }
+
+    Expected<uint8_t> get_channel_error()
+    {
+        return read_integer<uint8_t>(VDMA_CHANNEL_ERROR_OFFSET);
+    }
+
+    hailo_status stop_channel()
+    {
+        auto reg_control = get_control();
+        CHECK_EXPECTED_AS_STATUS(reg_control, "Fail to read vdma control register");
+
+        // First pause channel
+        auto status = set_control((reg_control.value() & 0xFC) | 0x3);
+        CHECK_SUCCESS(status, "Fail to write vdma control register");
+
+        std::this_thread::sleep_for(std::chrono::microseconds(2));
+
+        // Then abort
+        status = set_control((reg_control.value() & 0xFC) | 0x0);
+        CHECK_SUCCESS(status, "Fail to write vdma control register");
+
+        return HAILO_SUCCESS;
+    }
+
+private:
+
+    template<typename IntegerType>
+    Expected<IntegerType> read_integer(uint32_t offset)
+    {
+        auto value = m_driver.read_vdma_channel_registers(m_channel_offset + offset, sizeof(IntegerType));
+        CHECK_EXPECTED(value);
+        return static_cast<IntegerType>(value.release());
+    }
+
+    hailo_status set_control(uint8_t value)
+    {
+        return write_integer<uint8_t>(VDMA_CHANNEL_CONTROL_OFFSET, value);
+    }
+
+    template<typename IntegerType>
+    hailo_status write_integer(uint32_t offset, IntegerType value)
+    {
+        return m_driver.write_vdma_channel_registers(m_channel_offset + offset,  sizeof(value), value);
+    }
+
+    HailoRTDriver &m_driver;
+    uint32_t m_channel_offset;
+};
+
+} /* namespace hailort */
+
+#endif /*_HAILO_VDMA_CHANNEL__REGS_HPP_ */
\ No newline at end of file
diff --git a/hailort/libhailort/src/vdma_descriptor_list.cpp b/hailort/libhailort/src/vdma_descriptor_list.cpp
new file mode 100644 (file)
index 0000000..6d02368
--- /dev/null
@@ -0,0 +1,337 @@
+#include "vdma_descriptor_list.hpp"
+
+#define DESC_STATUS_REQ                       (1 << 0)
+#define DESC_STATUS_REQ_ERR                   (1 << 1)
+#define DESC_REQUREST_IRQ_PROCESSED           (1 << 2)
+#define DESC_REQUREST_IRQ_ERR                 (1 << 3)
+
+#define PCIE_DMA_HOST_INTERRUPTS_BITMASK      (1 << 5)
+#define PCIE_DMA_DEVICE_INTERRUPTS_BITMASK    (1 << 4)
+
+#define DRAM_DMA_HOST_INTERRUPTS_BITMASK      (1 << 4)
+#define DRAM_DMA_DEVICE_INTERRUPTS_BITMASK    (1 << 5)
+
+#define DESC_PAGE_SIZE_SHIFT                  (8)
+#define DESC_PAGE_SIZE_MASK                   (0xFFFFFF00)
+
+namespace hailort
+{
+
+Expected<VdmaDescriptorList> VdmaDescriptorList::create(size_t desc_count, uint16_t requested_desc_page_size,
+    HailoRTDriver &driver)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    auto desc_page_size_value = driver.calc_desc_page_size(requested_desc_page_size);
+    VdmaDescriptorList object(desc_count, driver, desc_page_size_value, status);
+    if (HAILO_SUCCESS != status) {
+        return make_unexpected(status);
+    }
+
+    return object;
+}
+
+VdmaDescriptorList::VdmaDescriptorList(size_t desc_count, HailoRTDriver &driver, uint16_t desc_page_size,
+                                       hailo_status &status) :
+    m_mapped_list(),
+    m_count(desc_count),
+    m_depth(0),
+    m_desc_handle(0),
+    m_dma_address(0),
+    m_driver(driver),
+    m_desc_page_size(desc_page_size)
+{
+    if (!is_powerof2(desc_count)) {
+        LOGGER__ERROR("Descriptor count ({}) must be power of 2", desc_count);
+        status = HAILO_INVALID_ARGUMENT;
+        return;
+    }
+
+    auto depth = calculate_desc_list_depth(desc_count);
+    if (!depth) {
+        status = depth.status();
+        return;
+    }
+    m_depth = depth.value();
+
+    auto desc_handle_phys_addr_pair = m_driver.descriptors_list_create(desc_count);
+    if (!desc_handle_phys_addr_pair) {
+        status = desc_handle_phys_addr_pair.status();
+        return;
+    }
+
+    m_desc_handle = desc_handle_phys_addr_pair->first;
+    m_dma_address = desc_handle_phys_addr_pair->second;
+    
+    auto mapped_list = MmapBuffer<VdmaDescriptor>::create_file_map(desc_count * sizeof(VdmaDescriptor), m_driver.fd(), m_desc_handle);
+    if (!mapped_list) {
+        LOGGER__ERROR("Failed to memory map descriptors. desc handle: {:X}", m_desc_handle);
+        status = mapped_list.status();
+        return;
+    }
+
+    m_mapped_list = mapped_list.release();
+    status = HAILO_SUCCESS;
+}
+
+VdmaDescriptorList::~VdmaDescriptorList()
+{
+    if (HAILO_SUCCESS != m_mapped_list.unmap()) {
+        LOGGER__ERROR("Failed to release descriptors mapping");
+    }
+
+    // Note: The descriptors_list is freed by the desc_handle (no need to use the phys_address to free)
+    if (0 != m_desc_handle) {
+        if(HAILO_SUCCESS != m_driver.descriptors_list_release(m_desc_handle)) {
+            LOGGER__ERROR("Failed to release descriptor list {}", m_desc_handle);
+        }
+    }
+}
+
+VdmaDescriptorList::VdmaDescriptorList(VdmaDescriptorList &&other) noexcept : 
+    m_mapped_list(std::move(other.m_mapped_list)),
+    m_count(std::move(other.m_count)),
+    m_depth(std::move(other.m_depth)),
+    m_desc_handle(std::exchange(other.m_desc_handle, 0)),
+    m_dma_address(std::exchange(other.m_dma_address, 0)),
+    m_driver(other.m_driver),
+    m_desc_page_size(other.m_desc_page_size)  {}
+
+Expected<uint8_t> VdmaDescriptorList::calculate_desc_list_depth(size_t count)
+{
+    // Calculate log2 of m_count (by finding the offset of the MSB)
+    uint32_t depth = 0;
+    while (count >>= 1) {
+        ++depth;
+    }
+    CHECK_AS_EXPECTED(IS_FIT_IN_UINT8(depth), HAILO_INTERNAL_FAILURE, "Calculated desc_list_depth is too big: {}", depth);
+    return static_cast<uint8_t>(depth);
+}
+
+hailo_status VdmaDescriptorList::configure_to_use_buffer(VdmaBuffer& buffer, uint8_t channel_index)
+{
+    return m_driver.descriptors_list_bind_vdma_buffer(m_desc_handle, buffer.handle(), m_desc_page_size,
+        channel_index);
+}
+
+hailo_status VdmaDescriptorList::configure_to_use_buffer(VdmaBuffer& buffer)
+{
+    return configure_to_use_buffer(buffer, HailoRTDriver::INVALID_VDMA_CHANNEL_INDEX);
+}
+
+Expected<uint16_t> VdmaDescriptorList::program_descriptors(size_t transfer_size,
+    VdmaInterruptsDomain first_desc_interrupts_domain, VdmaInterruptsDomain last_desc_interrupts_domain,
+    size_t desc_offset, bool is_circular)
+{
+    const auto required_descriptors = descriptors_in_buffer(transfer_size);
+    // Required_descriptors + desc_offset can't reach m_count. We need to keep at least 1 free desc at all time.
+    if ((!is_circular) && ((required_descriptors + desc_offset) >= m_count)){
+        LOGGER__ERROR("Requested transfer size ({}) result in more descrptors than available ({})", transfer_size, m_count);
+        return make_unexpected(HAILO_OUT_OF_DESCRIPTORS);
+    }
+
+    size_t desc_index = desc_offset;
+    for (size_t i = 0; i < required_descriptors - 1; ++i) {
+        const auto interrupts_domain = (i == 0) ? first_desc_interrupts_domain : VdmaInterruptsDomain::NONE;
+        program_single_descriptor((*this)[desc_index], m_desc_page_size, interrupts_domain);
+        desc_index = (desc_index + 1) & (m_count - 1);
+    }
+
+    /* write residue page with the remaining buffer size*/
+    auto resuide = transfer_size - (required_descriptors - 1) * m_desc_page_size;
+    assert(IS_FIT_IN_UINT16(resuide));
+    program_single_descriptor((*this)[desc_index], static_cast<uint16_t>(resuide), last_desc_interrupts_domain);
+
+    return std::move(static_cast<uint16_t>(required_descriptors));
+}
+
+Expected<uint16_t> VdmaDescriptorList::program_descs_for_ddr_transfers(uint32_t row_size, bool should_raise_interrupt,
+    uint32_t number_of_rows_per_intrpt, uint32_t buffered_rows, uint16_t initial_descs_offset, bool is_circular)
+{
+    uint16_t programmed_descs = 0;
+    size_t offset = initial_descs_offset;
+    assert(0 == (buffered_rows % number_of_rows_per_intrpt));
+
+    auto first_desc_interrupts_mask = VdmaInterruptsDomain::NONE;
+    auto last_desc_interrupts_mask = (should_raise_interrupt) ? VdmaInterruptsDomain::HOST : VdmaInterruptsDomain::NONE;
+    for (uint32_t rows_count = 0; rows_count < buffered_rows; rows_count += number_of_rows_per_intrpt) {
+        auto desc_count_local = program_descriptors((row_size * number_of_rows_per_intrpt),
+            first_desc_interrupts_mask, last_desc_interrupts_mask, offset, is_circular);
+        CHECK_EXPECTED(desc_count_local);
+        offset = (offset + desc_count_local.value()) & (m_count - 1);
+        programmed_descs = static_cast<uint16_t>(programmed_descs + desc_count_local.value());
+    }
+    return programmed_descs;
+}
+
+uint32_t VdmaDescriptorList::descriptors_in_buffer(size_t buffer_size) const
+{
+    return descriptors_in_buffer(buffer_size, m_desc_page_size);
+}
+
+uint32_t VdmaDescriptorList::descriptors_in_buffer(size_t buffer_size, uint16_t desc_page_size)
+{
+    assert(buffer_size < std::numeric_limits<uint32_t>::max());
+    return static_cast<uint32_t>(((buffer_size) + desc_page_size - 1) / desc_page_size);
+}
+
+uint32_t VdmaDescriptorList::calculate_descriptors_count(uint32_t buffer_size, uint16_t batch_size, uint16_t desc_page_size)
+{
+    // Because we use cyclic buffer, the amount of active descs is lower by one that the amount
+    // of descs given  (Otherwise we won't be able to determine if the buffer is empty or full).
+    // Therefore we add 1 in order to compensate.
+    uint32_t descs_count = std::min(((descriptors_in_buffer(buffer_size, desc_page_size) * batch_size) + 1),
+        MAX_DESCS_COUNT);
+
+    return get_nearest_powerof_2(descs_count, MIN_DESCS_COUNT);
+}
+
+Expected<std::pair<uint16_t, uint32_t>> VdmaDescriptorList::get_desc_buffer_sizes_for_single_transfer(
+    const HailoRTDriver &driver, uint16_t min_batch_size, uint16_t max_batch_size, uint32_t transfer_size)
+{
+    // Note: If the pages pointed to by the descriptors are copied in their entirety, then DEFAULT_DESC_PAGE_SIZE
+    //       is the optimal value. For transfer_sizes smaller than DEFAULT_DESC_PAGE_SIZE using smaller descriptor page
+    //       sizes will save memory consuption without harming performance. In the case of nms for example, only one bbox
+    //       is copied from each page. Hence, we'll use MIN_DESC_PAGE_SIZE for nms.
+    const uint32_t initial_desc_page_size = (DEFAULT_DESC_PAGE_SIZE > transfer_size) ? 
+        get_nearest_powerof_2(transfer_size, MIN_DESC_PAGE_SIZE) : DEFAULT_DESC_PAGE_SIZE;
+    if (DEFAULT_DESC_PAGE_SIZE != initial_desc_page_size) {
+        LOGGER__INFO("Using non-default initial_desc_page_size of {}, due to a small transfer size ({})",
+            initial_desc_page_size, transfer_size);
+    }
+    CHECK_AS_EXPECTED(IS_FIT_IN_UINT16(initial_desc_page_size), HAILO_INTERNAL_FAILURE,
+        "Descriptor page size needs to fit in 16B");
+    
+    return get_desc_buffer_sizes_for_single_transfer_impl(driver, min_batch_size, max_batch_size, transfer_size,
+        static_cast<uint16_t>(initial_desc_page_size));
+}
+
+Expected<std::pair<uint16_t, uint32_t>> VdmaDescriptorList::get_desc_buffer_sizes_for_multiple_transfers(
+    const HailoRTDriver &driver, uint16_t batch_size, const std::vector<uint32_t> &transfer_sizes)
+{
+    return get_desc_buffer_sizes_for_multiple_transfers_impl(driver, batch_size, transfer_sizes,
+        DEFAULT_DESC_PAGE_SIZE);
+}
+
+Expected<std::pair<uint16_t, uint32_t>> VdmaDescriptorList::get_desc_buffer_sizes_for_single_transfer_impl(
+    const HailoRTDriver &driver, uint16_t min_batch_size, uint16_t max_batch_size, uint32_t transfer_size,
+    uint16_t initial_desc_page_size)
+{
+    auto results =  VdmaDescriptorList::get_desc_buffer_sizes_for_multiple_transfers_impl(driver, min_batch_size,
+        {transfer_size}, initial_desc_page_size);
+    CHECK_EXPECTED(results);
+
+    auto page_size = results->first;
+
+    auto desc_count = std::min(MAX_DESCS_COUNT,
+            VdmaDescriptorList::calculate_descriptors_count(transfer_size, max_batch_size, page_size));
+
+    return std::make_pair(page_size, desc_count);
+}
+
+Expected<std::pair<uint16_t, uint32_t>> VdmaDescriptorList::get_desc_buffer_sizes_for_multiple_transfers_impl(
+    const HailoRTDriver &driver, uint16_t batch_size, const std::vector<uint32_t> &transfer_sizes,
+    uint16_t initial_desc_page_size)
+{
+    const uint16_t min_desc_page_size = driver.calc_desc_page_size(MIN_DESC_PAGE_SIZE);
+    const uint16_t max_desc_page_size = driver.calc_desc_page_size(MAX_DESC_PAGE_SIZE);
+    // Defined as uint32_t to prevent overflow (as we multiply it by two in each iteration of the while loop bellow)
+    uint32_t local_desc_page_size = driver.calc_desc_page_size(initial_desc_page_size);
+    CHECK_AS_EXPECTED(IS_FIT_IN_UINT16(local_desc_page_size), HAILO_INTERNAL_FAILURE,
+        "Descriptor page size needs to fit in 16B");
+    CHECK_AS_EXPECTED(local_desc_page_size <= max_desc_page_size, HAILO_INTERNAL_FAILURE,
+        "Initial descriptor page size ({}) is larger than maximum descriptor page size ({})",
+        local_desc_page_size, max_desc_page_size);
+    CHECK_AS_EXPECTED(local_desc_page_size >= min_desc_page_size, HAILO_INTERNAL_FAILURE,
+        "Initial descriptor page size ({}) is smaller than minimum descriptor page size ({})",
+        local_desc_page_size, min_desc_page_size);
+
+    uint32_t acc_desc_count = 0;
+    for (const auto &transfer_size : transfer_sizes) {
+        acc_desc_count +=
+            VdmaDescriptorList::descriptors_in_buffer(transfer_size, static_cast<uint16_t>(local_desc_page_size));
+    }
+
+    // Too many descriptors; try a larger desc_page_size which will lead to less descriptors used
+    while ((acc_desc_count * batch_size) > (MAX_DESCS_COUNT - 1)) {
+        local_desc_page_size <<= 1;
+
+        CHECK_AS_EXPECTED(local_desc_page_size <= max_desc_page_size, HAILO_OUT_OF_DESCRIPTORS,
+            "Network shapes and batch size exceeds driver descriptors capabilities. "
+            "Required descriptors count: {}, max allowed on the driver: {}.",
+            (batch_size * acc_desc_count), MAX_DESCS_COUNT);
+
+        CHECK_AS_EXPECTED(IS_FIT_IN_UINT16(local_desc_page_size), HAILO_INTERNAL_FAILURE,
+            "Descriptor page size needs to fit in 16B");
+
+        acc_desc_count = 0;
+        for (auto &transfer_size : transfer_sizes) {
+            acc_desc_count +=
+                VdmaDescriptorList::descriptors_in_buffer(transfer_size, static_cast<uint16_t>(local_desc_page_size));
+        }
+    }
+
+    // Found desc_page_size and acc_desc_count
+    const auto desc_page_size = static_cast<uint16_t>(local_desc_page_size);
+
+    // Find descs_count
+    const auto descs_count = get_nearest_powerof_2(acc_desc_count, MIN_DESCS_COUNT);
+    CHECK_AS_EXPECTED(descs_count <= MAX_DESCS_COUNT, HAILO_OUT_OF_DESCRIPTORS);
+
+    if (initial_desc_page_size != desc_page_size) {
+        LOGGER__WARNING("Desc page size value ({}) is not optimal for performance.", desc_page_size);
+    }
+
+    return std::make_pair(desc_page_size, descs_count);
+}
+
+uint32_t VdmaDescriptorList::get_interrupts_bitmask(VdmaInterruptsDomain interrupts_domain)
+{
+    uint32_t host_bitmask = 0;
+    uint32_t device_bitmask = 0;
+
+    switch (m_driver.dma_type()) {
+    case HailoRTDriver::DmaType::PCIE:
+        host_bitmask = PCIE_DMA_HOST_INTERRUPTS_BITMASK;
+        device_bitmask = PCIE_DMA_DEVICE_INTERRUPTS_BITMASK;
+        break;
+    case HailoRTDriver::DmaType::DRAM:
+        host_bitmask = DRAM_DMA_HOST_INTERRUPTS_BITMASK;
+        device_bitmask = DRAM_DMA_DEVICE_INTERRUPTS_BITMASK;
+        break;
+    default:
+        assert(true);
+    }
+
+    uint32_t bitmask = 0;
+    if (host_interuptes_enabled(interrupts_domain)) {
+        bitmask |= host_bitmask;
+    }
+    if (device_interuptes_enabled(interrupts_domain)) {
+        bitmask |= device_bitmask;
+    }
+
+    return bitmask;
+}
+
+void VdmaDescriptorList::program_single_descriptor(VdmaDescriptor &descriptor, uint16_t page_size,
+    VdmaInterruptsDomain interrupts_domain)
+{
+    descriptor.PageSize_DescControl = 0;
+    // Update the descriptor's PAGE_SIZE field in the control register with the maximum size of the DMA page.
+    descriptor.PageSize_DescControl |=
+        (uint32_t)(page_size << DESC_PAGE_SIZE_SHIFT) & (uint32_t)DESC_PAGE_SIZE_MASK;
+
+    if (VdmaInterruptsDomain::NONE != interrupts_domain) {
+        // update the desc_control
+        descriptor.PageSize_DescControl |= (DESC_REQUREST_IRQ_PROCESSED | DESC_REQUREST_IRQ_ERR);
+#ifndef NDEBUG
+        descriptor.PageSize_DescControl |= (DESC_STATUS_REQ | DESC_STATUS_REQ_ERR);
+#endif
+        descriptor.PageSize_DescControl |= get_interrupts_bitmask(interrupts_domain);
+    }
+
+    // Clear status
+    descriptor.RemainingPageSize_Status = 0;
+}
+
+} /* namespace hailort */
diff --git a/hailort/libhailort/src/vdma_descriptor_list.hpp b/hailort/libhailort/src/vdma_descriptor_list.hpp
new file mode 100644 (file)
index 0000000..f09a3a3
--- /dev/null
@@ -0,0 +1,168 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file vdma_descriptor_list.hpp
+ * @brief Allocates a list of buffer descriptors used for VDMA
+ *
+ **/
+
+#ifndef _HAILO_VDMA_DESCRIPTOR_LIST_HPP_
+#define _HAILO_VDMA_DESCRIPTOR_LIST_HPP_
+
+#include "os/hailort_driver.hpp"
+#include "hailo/expected.hpp"
+#include "os/mmap_buffer.hpp"
+#include "vdma_buffer.hpp"
+#include "common/utils.hpp"
+
+namespace hailort
+{
+
+// HW doens't support more than 32 channels
+#define MAX_HOST_CHANNELS_COUNT (32)
+
+#define MAX_DESCS_COUNT (64 * 1024u)
+#define MIN_DESCS_COUNT (2u)
+#define DEFAULT_DESC_COUNT (64 * 1024u)
+
+static_assert(is_powerof2(MAX_DESCS_COUNT), "MAX_DESCS_COUNT must be a power of 2");
+static_assert(is_powerof2(MIN_DESCS_COUNT), "MIN_DESCS_COUNT must be a power of 2");
+static_assert(is_powerof2(DEFAULT_DESC_COUNT), "DEFAULT_DESC_COUNT must be a power of 2");
+static_assert(DEFAULT_DESC_COUNT <= MAX_DESCS_COUNT && DEFAULT_DESC_COUNT >= MIN_DESCS_COUNT,
+    "DEFAULT_DESC_COUNT not in range");
+
+// From PLDA's vDMA controller reference:
+// - Addresses of pages pointed to by vDMA descriptors need to be on a 64B boundry.
+//   Hence, we require a minimum page size of 64B.
+// - G_PAGE_SIZE_MAX dictates the maximum desc page size:
+//     max_page_size = 2 ^ (G_PAGE_SIZE_MAX - 1)
+//   In our case max_page_size = 2 ^ (13 - 1) = 4096
+#define MIN_DESC_PAGE_SIZE (64u)
+// TODO: Calculate from G_PAGE_SIZE_MAX (I.e. read the reg etc.)
+#define MAX_DESC_PAGE_SIZE (4096u)
+#define DEFAULT_DESC_PAGE_SIZE (512u)
+
+static_assert(is_powerof2(MIN_DESC_PAGE_SIZE), "MIN_DESC_PAGE_SIZE must be a power of 2");
+static_assert(MIN_DESC_PAGE_SIZE > 0, "MIN_DESC_PAGE_SIZE must be larger then 0");
+static_assert(is_powerof2(MAX_DESC_PAGE_SIZE), "MAX_DESC_PAGE_SIZE must be a power of 2");
+static_assert(MAX_DESC_PAGE_SIZE > 0, "MAX_DESC_PAGE_SIZE must be larger then 0");
+static_assert(is_powerof2(DEFAULT_DESC_PAGE_SIZE), "DEFAULT_DESC_PAGE_SIZE must be a power of 2");
+static_assert(DEFAULT_DESC_PAGE_SIZE > 0, "DEFAULT_DESC_PAGE_SIZE must be larger then 0");
+
+
+struct VdmaDescriptor 
+{
+    uint32_t PageSize_DescControl;
+    uint32_t AddrL_rsvd_DataID;
+    uint32_t AddrH;
+    uint32_t RemainingPageSize_Status;
+};
+
+enum class VdmaInterruptsDomain 
+{
+    NONE    = 0,
+    DEVICE  = 1 << 0,
+    HOST    = 1 << 1,
+    BOTH    = DEVICE | HOST
+};
+
+inline bool host_interuptes_enabled(VdmaInterruptsDomain interrupts_domain)
+{
+    return 0 != (static_cast<uint32_t>(interrupts_domain) & static_cast<uint32_t>(VdmaInterruptsDomain::HOST));
+}
+
+inline bool device_interuptes_enabled(VdmaInterruptsDomain interrupts_domain)
+{
+    return 0 != (static_cast<uint32_t>(interrupts_domain) & static_cast<uint32_t>(VdmaInterruptsDomain::DEVICE));
+}
+
+class VdmaDescriptorList
+{
+public:
+    static Expected<VdmaDescriptorList> create(size_t desc_count, uint16_t requested_desc_page_size,
+        HailoRTDriver &driver);
+
+    ~VdmaDescriptorList();
+
+    VdmaDescriptorList(const VdmaDescriptorList &other) = delete;
+    VdmaDescriptorList &operator=(const VdmaDescriptorList &other) = delete;
+    VdmaDescriptorList(VdmaDescriptorList &&other) noexcept;
+    VdmaDescriptorList &operator=(VdmaDescriptorList &&other) = delete;
+
+    uint8_t depth() const
+    {
+        return m_depth;
+    }
+
+    size_t count() const
+    {
+        return m_count;
+    }
+
+    uint64_t dma_address() const
+    {
+        return m_dma_address;
+    }
+
+    VdmaDescriptor& operator[](size_t i)
+    {
+        assert(i < m_count);
+        return m_mapped_list[i];
+    }
+
+    uint16_t desc_page_size() const
+    {
+        return m_desc_page_size;
+    }
+
+    uintptr_t handle() const
+    {
+        return m_desc_handle;
+    }
+
+    hailo_status configure_to_use_buffer(VdmaBuffer& buffer, uint8_t channel_index);
+    // On hailo8, we allow configuring buffer without specific channel index.
+    hailo_status configure_to_use_buffer(VdmaBuffer& buffer);
+
+    Expected<uint16_t> program_descriptors(size_t transfer_size, VdmaInterruptsDomain first_desc_interrupts_domain,
+        VdmaInterruptsDomain last_desc_interrupts_domain, size_t desc_offset, bool is_circular);
+
+    Expected<uint16_t> program_descs_for_ddr_transfers(uint32_t row_size, bool should_raise_interrupt, 
+        uint32_t number_of_rows_per_intrpt, uint32_t buffered_rows, uint16_t initial_descs_offset, bool is_circular);
+
+    uint32_t descriptors_in_buffer(size_t buffer_size) const;
+    static uint32_t descriptors_in_buffer(size_t buffer_size, uint16_t desc_page_size);
+    static uint32_t calculate_descriptors_count(uint32_t buffer_size, uint16_t batch_size, uint16_t desc_page_size);
+    static Expected<std::pair<uint16_t, uint32_t>> get_desc_buffer_sizes_for_single_transfer(const HailoRTDriver &driver,
+        uint16_t min_batch_size, uint16_t max_batch_size, uint32_t transfer_size);
+    static Expected<std::pair<uint16_t, uint32_t>> get_desc_buffer_sizes_for_multiple_transfers(const HailoRTDriver &driver,
+        uint16_t batch_size, const std::vector<uint32_t> &transfer_sizes);
+
+private:
+    VdmaDescriptorList(size_t desc_count, HailoRTDriver &driver, uint16_t desc_page_size, hailo_status &status);
+    uint32_t get_interrupts_bitmask(VdmaInterruptsDomain interrupts_domain);
+    void program_single_descriptor(VdmaDescriptor &descriptor, uint16_t page_size,
+        VdmaInterruptsDomain interrupts_domain);
+    static Expected<uint8_t> calculate_desc_list_depth(size_t count);
+    // Note: initial_desc_page_size should be the optimal descriptor page size.
+    static Expected<std::pair<uint16_t, uint32_t>> get_desc_buffer_sizes_for_single_transfer_impl(
+        const HailoRTDriver &driver, uint16_t min_batch_size, uint16_t max_batch_size, uint32_t transfer_size,
+        uint16_t initial_desc_page_size);
+    static Expected<std::pair<uint16_t, uint32_t>> get_desc_buffer_sizes_for_multiple_transfers_impl(
+        const HailoRTDriver &driver, uint16_t batch_size, const std::vector<uint32_t> &transfer_sizes,
+        uint16_t initial_desc_page_size);
+
+    MmapBuffer<VdmaDescriptor> m_mapped_list;
+    size_t m_count;
+    uint8_t m_depth;
+    uintptr_t m_desc_handle;
+    uint64_t m_dma_address;
+    HailoRTDriver &m_driver;
+    const uint16_t m_desc_page_size;
+};
+
+} /* namespace hailort */
+
+#endif //_HAILO_VDMA_DESCRIPTOR_LIST_HPP_
\ No newline at end of file
diff --git a/hailort/libhailort/src/vdma_device.cpp b/hailort/libhailort/src/vdma_device.cpp
new file mode 100644 (file)
index 0000000..22d7374
--- /dev/null
@@ -0,0 +1,93 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file vdma_device.cpp
+ * @brief TODO: brief
+ *
+ * TODO: doc
+ **/
+
+#include "vdma_device.hpp"
+#include "vdma_descriptor_list.hpp"
+#include "context_switch/multi_context/vdma_config_manager.hpp"
+
+
+#include <new>
+#include <algorithm>
+
+namespace hailort
+{
+
+VdmaDevice::VdmaDevice(HailoRTDriver &&driver, Device::Type type) :
+    DeviceBase::DeviceBase(type),
+    m_driver(std::move(driver)),
+    m_context_switch_manager(nullptr)
+{
+}
+
+hailo_status VdmaDevice::wait_for_wakeup()
+{
+    return HAILO_SUCCESS;
+}
+
+Expected<D2H_EVENT_MESSAGE_t> VdmaDevice::read_notification()
+{
+    return m_driver.read_notification();
+}
+
+hailo_status VdmaDevice::disable_notifications()
+{
+    return m_driver.disable_notifications();
+}
+
+ExpectedRef<ConfigManager> VdmaDevice::get_config_manager()
+{
+    auto status = mark_as_used();
+    CHECK_SUCCESS_AS_EXPECTED(status);
+
+    if (!m_context_switch_manager) {
+        auto local_context_switch_manager = VdmaConfigManager::create(*this);
+        CHECK_EXPECTED(local_context_switch_manager);
+
+        m_context_switch_manager = make_unique_nothrow<VdmaConfigManager>(local_context_switch_manager.release());
+        CHECK_AS_EXPECTED(nullptr != m_context_switch_manager, HAILO_OUT_OF_HOST_MEMORY);
+    }
+
+    return std::ref(*m_context_switch_manager);
+}
+
+Expected<size_t> VdmaDevice::read_log(MemoryView &buffer, hailo_cpu_id_t cpu_id)
+{
+    size_t read_bytes = 0;
+    hailo_status status = HAILO_UNINITIALIZED;
+    status = m_driver.read_log(buffer.data(), buffer.size(), &read_bytes, cpu_id);
+    CHECK_SUCCESS_AS_EXPECTED(status);
+    return read_bytes;
+}
+
+void VdmaDevice::increment_control_sequence()
+{
+    // To support multiprocess the sequence must remain 0 which is a number the FW ignores.
+    // Otherwise the FW might get the same sequence number from several processes which
+    // cause the command to be discarded.
+    m_control_sequence = 0;
+}
+
+hailo_reset_device_mode_t VdmaDevice::get_default_reset_mode()
+{
+    return HAILO_RESET_DEVICE_MODE_SOFT;
+}
+
+uint16_t VdmaDevice::get_default_desc_page_size() const
+{
+    return m_driver.calc_desc_page_size(DEFAULT_DESC_PAGE_SIZE);
+}
+
+hailo_status VdmaDevice::mark_as_used()
+{
+    return m_driver.mark_as_used();
+}
+
+} /* namespace hailort */
diff --git a/hailort/libhailort/src/vdma_device.hpp b/hailort/libhailort/src/vdma_device.hpp
new file mode 100644 (file)
index 0000000..0809828
--- /dev/null
@@ -0,0 +1,52 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file vdma_device.hpp
+ * @brief Base class for devices that uses vdma and comunicate using HailoRTDriver
+ *
+ **/
+
+#ifndef HAILO_VDMA_DEVICE_H_
+#define HAILO_VDMA_DEVICE_H_
+
+#include "hailo/hailort.h"
+#include "hailo/expected.hpp"
+#include "device_internal.hpp"
+#include "os/hailort_driver.hpp"
+
+namespace hailort
+{
+
+class VdmaDevice : public DeviceBase {
+public:
+
+    virtual ~VdmaDevice() = default;
+
+    virtual hailo_status wait_for_wakeup() override;
+    virtual void increment_control_sequence() override;
+    virtual hailo_reset_device_mode_t get_default_reset_mode() override;
+    uint16_t get_default_desc_page_size() const;
+
+    hailo_status mark_as_used();
+    virtual Expected<size_t> read_log(MemoryView &buffer, hailo_cpu_id_t cpu_id) override;
+
+    HailoRTDriver &get_driver() {
+        return std::ref(m_driver);
+    };
+
+protected:
+    VdmaDevice(HailoRTDriver &&driver, Type type);
+
+    virtual Expected<D2H_EVENT_MESSAGE_t> read_notification() override;
+    virtual hailo_status disable_notifications() override;
+    virtual ExpectedRef<ConfigManager> get_config_manager() override;
+
+    HailoRTDriver m_driver;
+    std::unique_ptr<ConfigManager> m_context_switch_manager;
+};
+
+} /* namespace hailort */
+
+#endif /* HAILO_VDMA_DEVICE_H_ */
diff --git a/hailort/libhailort/src/vdma_stream.cpp b/hailort/libhailort/src/vdma_stream.cpp
new file mode 100644 (file)
index 0000000..e1aa94d
--- /dev/null
@@ -0,0 +1,339 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file vdma_stream.cpp
+ **/
+
+#include "vdma_stream.hpp"
+
+namespace hailort
+{
+
+VdmaInputStream::VdmaInputStream(VdmaDevice &device, uint8_t channel_index, const LayerInfo &edge_layer,
+                                 EventPtr network_group_activated_event, uint16_t batch_size,
+                                 LatencyMeterPtr latency_meter, std::chrono::milliseconds transfer_timeout,
+                                 hailo_stream_interface_t stream_interface, hailo_status &status) :
+    InputStreamBase(edge_layer, stream_interface, std::move(network_group_activated_event), status),
+    m_device(&device),
+    m_channel_index(channel_index),
+    is_stream_activated(false),
+    m_channel_timeout(transfer_timeout),
+    m_latency_meter(latency_meter),
+    m_batch_size(batch_size)
+{
+    // Checking status for base class c'tor
+    if (HAILO_SUCCESS != status) {
+        return;
+    }
+
+    status = config_stream();
+    if (HAILO_SUCCESS != status) {
+        return;
+    }
+
+    status = HAILO_SUCCESS;
+}
+
+VdmaInputStream::~VdmaInputStream()
+{
+    auto status = HAILO_UNINITIALIZED;
+    // We want to stop the vdma channel before closing the stream in the firmware
+    // because sending data to a closed stream may terminate the dma engine
+    if (this->is_stream_activated) {
+        status = VdmaInputStream::deactivate_stream();
+        if (HAILO_SUCCESS != status) {
+            LOGGER__ERROR("Failed to deactivate stream with error status {}", status);
+        }
+    }
+}
+
+VdmaInputStream::VdmaInputStream(VdmaInputStream &&other) :
+    InputStreamBase(std::move(other)),
+    m_device(std::move(other.m_device)),
+    m_channel_index(std::move(other.m_channel_index)),
+    m_channel(std::move(other.m_channel)),
+    is_stream_activated(std::exchange(other.is_stream_activated, false)),
+    m_channel_timeout(std::move(other.m_channel_timeout)),
+    m_latency_meter(std::move(other.m_latency_meter)),
+    m_batch_size(other.m_batch_size)
+{}
+
+std::chrono::milliseconds VdmaInputStream::get_timeout() const
+{
+    return this->m_channel_timeout;
+}
+
+hailo_status VdmaInputStream::set_timeout(std::chrono::milliseconds timeout)
+{
+    this->m_channel_timeout = timeout;
+    return HAILO_SUCCESS;
+}
+
+hailo_status VdmaInputStream::abort()
+{
+    return m_channel->abort();
+}
+
+hailo_status VdmaInputStream::clear_abort()
+{
+    return m_channel->clear_abort();
+}
+
+hailo_status VdmaInputStream::flush()
+{
+    return m_channel->flush((m_channel_timeout * m_batch_size));
+}
+
+hailo_status VdmaInputStream::activate_stream()
+{
+    auto status = m_channel->start_allocated_channel(0);
+    CHECK_SUCCESS(status);
+
+    this->is_stream_activated = true;
+    return HAILO_SUCCESS;
+}
+
+hailo_status VdmaInputStream::deactivate_stream()
+{
+    if (!is_stream_activated) {
+        return HAILO_SUCCESS;
+    }
+
+    /* Flush is best effort */
+    auto status = m_channel->flush(VDMA_FLUSH_TIMEOUT);
+    if (HAILO_STREAM_INTERNAL_ABORT == status) {
+        LOGGER__INFO("Flush input_channel is not needed because channel was aborted. (channel {})", m_channel_index);
+        status = HAILO_SUCCESS;
+    } else if (HAILO_SUCCESS != status) {
+        LOGGER__ERROR("Failed to flush input_channel. (status {} channel {})", status, m_channel_index);
+    }
+
+    /* Close channel is best effort. */
+    auto stop_channel_status = m_channel->stop_channel();
+    if (HAILO_SUCCESS != stop_channel_status) {
+        LOGGER__ERROR("Failed to stop channel with error status {}", stop_channel_status);
+        status = (status == HAILO_SUCCESS) ? stop_channel_status : status;
+    }
+
+    this->is_stream_activated = false;
+    return status;
+}
+
+Expected<size_t> VdmaInputStream::sync_write_raw_buffer(const MemoryView &buffer)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+
+    status = m_channel->wait(buffer.size(), m_channel_timeout);
+    if ((HAILO_STREAM_INTERNAL_ABORT == status) || (HAILO_STREAM_NOT_ACTIVATED == status)) {
+        return make_unexpected(status);
+    }
+    CHECK_SUCCESS_AS_EXPECTED(status);
+
+    status = m_channel->transfer((void*)buffer.data(), buffer.size());
+    CHECK_SUCCESS_AS_EXPECTED(status);
+
+    return buffer.size();
+}
+
+hailo_status VdmaInputStream::sync_write_all_raw_buffer_no_transform_impl(void *buffer, size_t offset, size_t size)
+{
+    ASSERT(NULL != buffer);
+
+    return sync_write_raw_buffer(MemoryView(static_cast<uint8_t*>(buffer) + offset, size)).status();
+}
+
+hailo_status VdmaInputStream::config_stream()
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+    uint32_t descs_count = 0;
+    uint16_t page_size = 0;
+    uint32_t min_active_trans = MIN_ACTIVE_TRANSFERS_SCALE * m_batch_size;
+    uint32_t max_active_trans = MAX_ACTIVE_TRANSFERS_SCALE * m_batch_size;
+    assert(min_active_trans <= max_active_trans);
+
+    CHECK(IS_FIT_IN_UINT16(min_active_trans), HAILO_INVALID_ARGUMENT, 
+        "calculated min_active_trans for vdma descriptor list is out of UINT16 range");
+
+    CHECK(IS_FIT_IN_UINT16(max_active_trans), HAILO_INVALID_ARGUMENT, 
+        "calculated min_active_trans for vdma descriptor list is out of UINT16 range");
+
+    auto desc_sizes_pair = VdmaDescriptorList::get_desc_buffer_sizes_for_single_transfer(m_device->get_driver(),
+        static_cast<uint16_t>(min_active_trans), static_cast<uint16_t>(max_active_trans), m_stream_info.hw_frame_size);
+    CHECK_EXPECTED_AS_STATUS(desc_sizes_pair);
+
+    page_size = desc_sizes_pair->first;
+    descs_count = desc_sizes_pair->second;
+
+    auto channel = VdmaChannel::create(m_channel_index, VdmaChannel::Direction::H2D, m_device->get_driver(), page_size,
+        m_stream_info.index, m_latency_meter, m_batch_size);
+
+    m_channel = make_unique_nothrow<VdmaChannel>(channel.release());
+    CHECK(nullptr != m_channel, HAILO_OUT_OF_HOST_MEMORY);
+
+    status = m_channel->allocate_resources(descs_count);
+    CHECK_SUCCESS(status);
+
+    return HAILO_SUCCESS;
+}
+
+/** Output stream **/
+VdmaOutputStream::VdmaOutputStream(VdmaDevice &device, uint8_t channel_index, const LayerInfo &edge_layer,
+                                   EventPtr network_group_activated_event, uint16_t batch_size,
+                                   LatencyMeterPtr latency_meter, std::chrono::milliseconds transfer_timeout,
+                                   hailo_status &status) :
+    OutputStreamBase(edge_layer, std::move(network_group_activated_event), status),
+    m_device(&device),
+    m_channel_index(channel_index),
+    is_stream_activated(false),
+    m_transfer_timeout(transfer_timeout),
+    m_latency_meter(latency_meter),
+    m_batch_size(batch_size),
+    m_transfer_size(get_transfer_size(m_stream_info))
+{
+    // Check status for base class c'tor
+    if (HAILO_SUCCESS != status) {
+        return;
+    }
+
+    status = config_stream();
+    if (HAILO_SUCCESS != status) {
+        return;
+    }
+
+    status = HAILO_SUCCESS;
+}
+
+VdmaOutputStream::VdmaOutputStream(VdmaOutputStream &&other) :
+    OutputStreamBase(std::move(other)),
+    m_device(std::move(other.m_device)),
+    m_channel_index(std::move(other.m_channel_index)),
+    m_channel(std::move(other.m_channel)),
+    is_stream_activated(std::exchange(other.is_stream_activated, false)),
+    m_transfer_timeout(std::move(other.m_transfer_timeout)),
+    m_latency_meter(std::move(other.m_latency_meter)),
+    m_batch_size(other.m_batch_size),
+    m_transfer_size(other.m_transfer_size)
+{}
+
+VdmaOutputStream::~VdmaOutputStream()
+{
+    // We want to stop the vdma channel before closing the stream in the firmware
+    // because sending data to a closed stream may terminate the dma engine
+    auto status = HAILO_UNINITIALIZED;
+
+    if (this->is_stream_activated) {
+        status = VdmaOutputStream::deactivate_stream();
+        if (HAILO_SUCCESS != status) {
+            LOGGER__ERROR("Failed to deactivate stream with error status {}", status);
+        }
+    }
+}
+
+hailo_status VdmaOutputStream::set_timeout(std::chrono::milliseconds timeout)
+{
+    this->m_transfer_timeout = timeout;
+    return HAILO_SUCCESS;
+}
+
+std::chrono::milliseconds VdmaOutputStream::get_timeout() const
+{
+    return this->m_transfer_timeout;
+}
+
+hailo_status VdmaOutputStream::abort()
+{
+    return m_channel->abort();
+}
+
+hailo_status VdmaOutputStream::clear_abort()
+{
+    return m_channel->clear_abort();
+}
+
+hailo_status VdmaOutputStream::activate_stream()
+{
+    auto status = m_channel->start_allocated_channel(m_transfer_size);
+    CHECK_SUCCESS(status);
+
+    this->is_stream_activated = true;
+
+    return HAILO_SUCCESS;
+}
+
+hailo_status VdmaOutputStream::deactivate_stream()
+{
+    if (!is_stream_activated) {
+        return HAILO_SUCCESS;
+    }
+
+    auto status = m_channel->stop_channel();
+    CHECK_SUCCESS(status);
+
+    this->is_stream_activated = false;
+    return HAILO_SUCCESS;
+}
+
+Expected<size_t> VdmaOutputStream::sync_read_raw_buffer(MemoryView &buffer)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+
+    status = m_channel->wait(buffer.size(), m_transfer_timeout);
+    if ((HAILO_STREAM_INTERNAL_ABORT == status) || (HAILO_STREAM_NOT_ACTIVATED == status)) {
+        return make_unexpected(status);
+    }
+    CHECK_SUCCESS_AS_EXPECTED(status);
+
+    status = m_channel->transfer(buffer.data(), buffer.size());
+    CHECK_SUCCESS_AS_EXPECTED(status);
+
+    return buffer.size();
+}
+
+hailo_status VdmaOutputStream::config_stream()
+{
+    const uint32_t min_active_trans = MIN_ACTIVE_TRANSFERS_SCALE * m_batch_size;
+    const uint32_t max_active_trans = MAX_ACTIVE_TRANSFERS_SCALE * m_batch_size;
+    assert(min_active_trans <= max_active_trans);
+
+    CHECK(IS_FIT_IN_UINT16(min_active_trans), HAILO_INVALID_ARGUMENT, 
+        "calculated min_active_trans for vdma descriptor list is out of UINT16 range");
+
+    CHECK(IS_FIT_IN_UINT16(max_active_trans), HAILO_INVALID_ARGUMENT, 
+        "calculated min_active_trans for vdma descriptor list is out of UINT16 range");
+
+    auto desc_sizes_pair = VdmaDescriptorList::get_desc_buffer_sizes_for_single_transfer(m_device->get_driver(),
+        static_cast<uint16_t>(min_active_trans), static_cast<uint16_t>(max_active_trans), m_transfer_size);
+    CHECK_EXPECTED_AS_STATUS(desc_sizes_pair);
+    
+    const auto page_size = desc_sizes_pair->first;
+    auto channel = VdmaChannel::create(m_channel_index, VdmaChannel::Direction::D2H, m_device->get_driver(), page_size,
+        m_stream_info.index, m_latency_meter, m_batch_size);
+
+    m_channel = make_unique_nothrow<VdmaChannel>(channel.release());
+    CHECK(nullptr != m_channel, HAILO_OUT_OF_HOST_MEMORY);
+
+    const auto descs_count = desc_sizes_pair->second;
+    const auto status = m_channel->allocate_resources(descs_count);
+    CHECK_SUCCESS(status);
+
+    return HAILO_SUCCESS;
+}
+
+hailo_status VdmaOutputStream::read_all(MemoryView &buffer)
+{
+    CHECK((buffer.size() % HailoRTCommon::HW_DATA_ALIGNMENT) == 0, HAILO_INVALID_ARGUMENT, 
+        "Size must be aligned to {} (got {})", HailoRTCommon::HW_DATA_ALIGNMENT, buffer.size());
+
+    return sync_read_raw_buffer(buffer).status();
+}
+
+uint32_t VdmaOutputStream::get_transfer_size(const hailo_stream_info_t &stream_info)
+{
+    // The ppu outputs one bbox per vdma buffer in the case of nms
+    return (HAILO_FORMAT_ORDER_HAILO_NMS == stream_info.format.order) ?
+        stream_info.nms_info.bbox_size : stream_info.hw_frame_size;
+}
+
+} /* namespace hailort */
diff --git a/hailort/libhailort/src/vdma_stream.hpp b/hailort/libhailort/src/vdma_stream.hpp
new file mode 100644 (file)
index 0000000..4f07b22
--- /dev/null
@@ -0,0 +1,116 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file vdma_stream.hpp
+ * @brief Stream object over vDMA channel
+ **/
+
+#ifndef _HAILO_VDMA_STREAM_HPP_
+#define _HAILO_VDMA_STREAM_HPP_
+
+#include "stream_internal.hpp"
+#include "vdma_device.hpp"
+#include "vdma_channel.hpp"
+#include "hailo/hailort.h"
+#include "hailo/expected.hpp"
+
+namespace hailort
+{
+constexpr std::chrono::seconds VDMA_FLUSH_TIMEOUT(10);
+
+class VdmaInputStream : public InputStreamBase {
+public:
+    VdmaInputStream(VdmaInputStream &&other);
+    virtual ~VdmaInputStream();
+
+    virtual std::chrono::milliseconds get_timeout() const override;
+    virtual hailo_status set_timeout(std::chrono::milliseconds timeout) override;
+    virtual hailo_status abort() override;
+    virtual hailo_status clear_abort() override;
+    virtual hailo_status flush() override;
+
+    uint16_t get_batch_size() const
+    {
+        return m_batch_size;
+    }
+
+    const char* get_dev_id() const
+    {
+        return m_device->get_dev_id();
+    }
+
+protected:
+    VdmaInputStream(VdmaDevice &device, uint8_t channel_index, const LayerInfo &edge_layer,
+                    EventPtr network_group_activated_event, uint16_t batch_size, LatencyMeterPtr latency_meter,
+                    std::chrono::milliseconds transfer_timeout, hailo_stream_interface_t stream_interface,
+                    hailo_status &status);
+
+    virtual hailo_status activate_stream() override;
+    virtual hailo_status deactivate_stream() override;
+    virtual Expected<size_t> sync_write_raw_buffer(const MemoryView &buffer) override;
+    virtual hailo_status sync_write_all_raw_buffer_no_transform_impl(void *buffer, size_t offset, size_t size) override;
+
+    VdmaDevice *m_device;
+    const uint8_t m_channel_index;
+    std::unique_ptr<VdmaChannel> m_channel;
+
+private:
+    hailo_status config_stream();
+
+    bool is_stream_activated;
+    std::chrono::milliseconds m_channel_timeout;
+    LatencyMeterPtr m_latency_meter;
+    uint16_t m_batch_size;
+};
+
+class VdmaOutputStream : public OutputStreamBase {
+public:
+    VdmaOutputStream(VdmaOutputStream &&other);
+    virtual ~VdmaOutputStream();
+
+    virtual std::chrono::milliseconds get_timeout() const override;
+    virtual hailo_status set_timeout(std::chrono::milliseconds timeout) override;
+    virtual hailo_status abort() override;
+    virtual hailo_status clear_abort() override;
+
+    uint16_t get_batch_size() const
+    {
+        return m_batch_size;
+    }
+
+    const char* get_dev_id() const
+    {
+        return m_device->get_dev_id();
+    }
+
+protected:
+    VdmaOutputStream(VdmaDevice &device, uint8_t channel_index, const LayerInfo &edge_layer,
+                     EventPtr network_group_activated_event, uint16_t batch_size, LatencyMeterPtr latency_meter,
+                     std::chrono::milliseconds transfer_timeout, hailo_status &status);
+
+    virtual hailo_status activate_stream() override;
+    virtual hailo_status deactivate_stream() override;
+    virtual Expected<size_t> sync_read_raw_buffer(MemoryView &buffer);
+
+    VdmaDevice *m_device;
+    const uint8_t m_channel_index;
+    std::unique_ptr<VdmaChannel> m_channel;
+
+private:
+    hailo_status config_stream();
+    hailo_status read_all(MemoryView &buffer) override;
+    static uint32_t get_transfer_size(const hailo_stream_info_t &stream_info);
+
+    bool is_stream_activated;
+    std::chrono::milliseconds m_transfer_timeout;
+    LatencyMeterPtr m_latency_meter;
+    uint16_t m_batch_size;
+    const uint32_t m_transfer_size;
+};
+
+
+} /* namespace hailort */
+
+#endif /* _HAILO_VDMA_STREAM_HPP_ */
diff --git a/hailort/libhailort/src/vstream.cpp b/hailort/libhailort/src/vstream.cpp
new file mode 100644 (file)
index 0000000..080c20c
--- /dev/null
@@ -0,0 +1,1605 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file vstream.cpp
+ * @brief Implemention of the virtual stream
+ **/
+
+#include "hailo/vstream.hpp"
+#include "hailort_defaults.hpp"
+#include "vstream_internal.hpp"
+#include "common/runtime_statistics_internal.hpp"
+
+#include <unordered_set>
+
+namespace hailort
+{
+
+static std::map<std::string, AccumulatorPtr> get_pipeline_accumulators_by_type(
+    const std::vector<std::shared_ptr<PipelineElement>> &pipeline, AccumulatorType accumulator_type);
+
+static std::map<std::string, std::vector<AccumulatorPtr>> get_pipeline_queue_size_accumulators(
+    const std::vector<std::shared_ptr<PipelineElement>> &pipeline);
+
+Expected<std::shared_ptr<PreInferElement>> PreInferElement::create(const hailo_3d_image_shape_t &src_image_shape, const hailo_format_t &src_format,
+    const hailo_3d_image_shape_t &dst_image_shape, const hailo_format_t &dst_format, const hailo_quant_info_t &dst_quant_info,
+    const std::string &name, std::chrono::milliseconds timeout, size_t buffer_pool_size, hailo_pipeline_elem_stats_flags_t elem_flags,
+    hailo_vstream_stats_flags_t vstream_flags, EventPtr shutdown_event, std::shared_ptr<std::atomic<hailo_status>> pipeline_status)
+{
+    auto transform_context = InputTransformContext::create(src_image_shape, src_format, dst_image_shape, dst_format,
+        dst_quant_info);
+    CHECK_EXPECTED(transform_context, "Failed Creating InputTransformContext");
+
+    auto buffer_pool = BufferPool::create(transform_context.value()->get_dst_frame_size(), buffer_pool_size, shutdown_event, elem_flags,
+        vstream_flags);
+    CHECK_EXPECTED(buffer_pool, "Failed creating BufferPool for {}", name);
+
+    auto duration_collector = DurationCollector::create(elem_flags);
+    CHECK_EXPECTED(duration_collector);
+
+    auto pre_infer_elem_ptr = make_shared_nothrow<PreInferElement>(transform_context.release(),
+        buffer_pool.release(), name, timeout, duration_collector.release(), std::move(pipeline_status));
+    CHECK_AS_EXPECTED(nullptr != pre_infer_elem_ptr, HAILO_OUT_OF_HOST_MEMORY);
+
+    LOGGER__INFO("Created {}", pre_infer_elem_ptr->name());
+
+    return pre_infer_elem_ptr;
+}
+
+Expected<std::shared_ptr<PreInferElement>> PreInferElement::create(const hailo_3d_image_shape_t &src_image_shape, const hailo_format_t &src_format,
+        const hailo_3d_image_shape_t &dst_image_shape, const hailo_format_t &dst_format, const hailo_quant_info_t &dst_quant_info, const std::string &name,
+        const hailo_vstream_params_t &vstream_params, EventPtr shutdown_event, std::shared_ptr<std::atomic<hailo_status>> pipeline_status)
+{
+    return PreInferElement::create(src_image_shape, src_format, dst_image_shape, dst_format, dst_quant_info, name,
+        std::chrono::milliseconds(vstream_params.timeout_ms), vstream_params.queue_size, vstream_params.pipeline_elements_stats_flags,
+        vstream_params.vstream_stats_flags, shutdown_event, pipeline_status);
+}
+
+PreInferElement::PreInferElement(std::unique_ptr<InputTransformContext> &&transform_context, BufferPoolPtr buffer_pool,
+                                const std::string &name, std::chrono::milliseconds timeout, DurationCollector &&duration_collector,
+                                std::shared_ptr<std::atomic<hailo_status>> &&pipeline_status) :
+    FilterElement(name, std::move(duration_collector), std::move(pipeline_status)),
+    m_transform_context(std::move(transform_context)),
+    m_pool(buffer_pool),
+    m_timeout(timeout)
+{}
+
+Expected<PipelineBuffer> PreInferElement::run_pull(PipelineBuffer &&/*optional*/, const PipelinePad &/*source*/)
+{
+    LOGGER__ERROR("PreInferElement does not support run_pull operation");
+    return make_unexpected(HAILO_INVALID_OPERATION);
+}
+
+std::vector<AccumulatorPtr> PreInferElement::get_queue_size_accumulators()
+{
+    if (nullptr == m_pool->get_queue_size_accumulator()) {
+        return std::vector<AccumulatorPtr>();
+    }
+    return {m_pool->get_queue_size_accumulator()};
+}
+
+PipelinePad &PreInferElement::next_pad()
+{
+    // Note: The next elem to be run is downstream from this elem (i.e. buffers are pushed)
+    return *m_sources[0].next();
+}
+
+std::string PreInferElement::description() const
+{
+    std::stringstream element_description;
+    element_description << "(" << this->name() << " | " << m_transform_context->description() << ")";
+    return element_description.str();
+}
+
+Expected<PipelineBuffer> PreInferElement::action(PipelineBuffer &&input, PipelineBuffer &&optional)
+{
+    if (PipelineBuffer::Type::FLUSH == input.get_type()) {
+        return std::move(input);
+    }
+
+    auto transformed_buffer = m_pool->get_available_buffer(std::move(optional), m_timeout);
+    if (HAILO_SHUTDOWN_EVENT_SIGNALED == transformed_buffer.status()) {
+        return make_unexpected(transformed_buffer.status());
+    }
+    CHECK_EXPECTED(transformed_buffer);
+
+    auto dst = transformed_buffer->as_view();
+    m_duration_collector.start_measurement();
+    const auto status = m_transform_context->transform(input.as_view(), dst);
+    m_duration_collector.complete_measurement();
+    CHECK_SUCCESS_AS_EXPECTED(status);
+
+    // Note: The latency to be measured starts as the input buffer is sent to the InputVStream (via write())
+    transformed_buffer->set_metadata(input.get_metadata());
+
+    return transformed_buffer.release();
+}
+
+Expected<std::shared_ptr<PostInferElement>> PostInferElement::create(const hailo_3d_image_shape_t &src_image_shape,
+    const hailo_format_t &src_format, const hailo_3d_image_shape_t &dst_image_shape, const hailo_format_t &dst_format,
+    const hailo_quant_info_t &dst_quant_info, const hailo_nms_info_t &nms_info, const std::string &name,
+    hailo_pipeline_elem_stats_flags_t elem_flags, std::shared_ptr<std::atomic<hailo_status>> pipeline_status)
+{
+    auto transform_context = OutputTransformContext::create(src_image_shape, src_format, dst_image_shape, dst_format,
+        dst_quant_info, nms_info);
+    CHECK_EXPECTED(transform_context, "Failed Creating OutputTransformContext");
+
+    auto duration_collector = DurationCollector::create(elem_flags);
+    CHECK_EXPECTED(duration_collector);
+
+    auto post_infer_elem_ptr = make_shared_nothrow<PostInferElement>(transform_context.release(),
+    name, duration_collector.release(), std::move(pipeline_status));
+    CHECK_AS_EXPECTED(nullptr != post_infer_elem_ptr, HAILO_OUT_OF_HOST_MEMORY);
+
+    LOGGER__INFO("Created {}", post_infer_elem_ptr->name());
+
+    return post_infer_elem_ptr;
+}
+
+Expected<std::shared_ptr<PostInferElement>> PostInferElement::create(const hailo_3d_image_shape_t &src_image_shape, const hailo_format_t &src_format,
+        const hailo_3d_image_shape_t &dst_image_shape, const hailo_format_t &dst_format, const hailo_quant_info_t &dst_quant_info, const hailo_nms_info_t &nms_info,
+        const std::string &name, const hailo_vstream_params_t &vstream_params, std::shared_ptr<std::atomic<hailo_status>> pipeline_status)
+{
+    return PostInferElement::create(src_image_shape, src_format, dst_image_shape, dst_format, dst_quant_info, nms_info,
+        name, vstream_params.pipeline_elements_stats_flags, pipeline_status);
+}
+
+PostInferElement::PostInferElement(std::unique_ptr<OutputTransformContext> &&transform_context, const std::string &name,
+                                   DurationCollector &&duration_collector,
+                                   std::shared_ptr<std::atomic<hailo_status>> &&pipeline_status) :
+    FilterElement(name, std::move(duration_collector), std::move(pipeline_status)),
+    m_transform_context(std::move(transform_context))
+{}
+
+hailo_status PostInferElement::run_push(PipelineBuffer &&/*buffer*/)
+{
+    LOGGER__ERROR("PostInferElement does not support run_push operation");
+    return HAILO_INVALID_OPERATION;
+}
+
+PipelinePad &PostInferElement::next_pad()
+{
+    // Note: The next elem to be run is upstream from this elem (i.e. buffers are pulled)
+    return *m_sinks[0].prev();
+}
+
+std::string PostInferElement::description() const
+{
+    std::stringstream element_description;
+    element_description << "(" << this->name() << " | " << m_transform_context->description() << ")";
+    return element_description.str();
+}
+
+Expected<PipelineBuffer> PostInferElement::action(PipelineBuffer &&input, PipelineBuffer &&optional)
+{
+    CHECK_AS_EXPECTED(optional, HAILO_INVALID_ARGUMENT, "Optional buffer must be valid in {}!", name());
+
+    // Note: The latency to be measured starts as the buffer is read from the HW (it's 'input' in this case)
+    optional.set_metadata(input.get_metadata());
+
+    auto dst = optional.as_view();
+    m_duration_collector.start_measurement();
+    const auto status = m_transform_context->transform(input.as_view(), dst);
+    m_duration_collector.complete_measurement();
+    CHECK_SUCCESS_AS_EXPECTED(status);
+
+    return std::move(optional);
+}
+
+static hailo_nms_info_t fuse_nms_info(const std::vector<hailo_nms_info_t> &nms_infos)
+{
+    hailo_nms_info_t fused_info = nms_infos[0];
+    fused_info.is_defused = false;
+    fused_info.number_of_classes = 0;
+    for (const auto &nms_info : nms_infos) {
+        fused_info.number_of_classes += nms_info.number_of_classes;
+    }
+
+    return fused_info;
+}
+
+Expected<std::shared_ptr<NmsMuxElement>> NmsMuxElement::create(const std::vector<hailo_nms_info_t> &nms_infos,
+    const std::string &name, std::chrono::milliseconds timeout, size_t buffer_pool_size,
+    hailo_pipeline_elem_stats_flags_t elem_flags, hailo_vstream_stats_flags_t vstream_flags, EventPtr shutdown_event,
+    std::shared_ptr<std::atomic<hailo_status>> pipeline_status)
+{
+    const auto &fused_info = fuse_nms_info(nms_infos);
+    auto buffer_pool = BufferPool::create(HailoRTCommon::get_nms_hw_frame_size(fused_info),
+        buffer_pool_size, shutdown_event, elem_flags, vstream_flags);
+    CHECK_EXPECTED(buffer_pool, "Failed creating BufferPool");
+
+    auto duration_collector = DurationCollector::create(elem_flags);
+    CHECK_EXPECTED(duration_collector);
+
+    auto nms_elem_ptr = make_shared_nothrow<NmsMuxElement>(nms_infos, fused_info, buffer_pool.release(),
+        name, timeout, duration_collector.release(), std::move(pipeline_status));
+    CHECK_AS_EXPECTED(nullptr != nms_elem_ptr, HAILO_OUT_OF_HOST_MEMORY);
+
+    LOGGER__INFO("Created {}", nms_elem_ptr->name());
+
+    return nms_elem_ptr;
+}
+
+Expected<std::shared_ptr<NmsMuxElement>> NmsMuxElement::create(const std::vector<hailo_nms_info_t> &nms_infos, const std::string &name,
+        const hailo_vstream_params_t &vstream_params, EventPtr shutdown_event, std::shared_ptr<std::atomic<hailo_status>> pipeline_status)
+{
+    return NmsMuxElement::create(nms_infos, name, std::chrono::milliseconds(vstream_params.timeout_ms), vstream_params.queue_size,
+        vstream_params.pipeline_elements_stats_flags, vstream_params.vstream_stats_flags, shutdown_event, pipeline_status);
+}
+
+NmsMuxElement::NmsMuxElement(const std::vector<hailo_nms_info_t> &nms_infos, const hailo_nms_info_t &fused_nms_info, BufferPoolPtr &&pool,
+                             const std::string &name, std::chrono::milliseconds timeout, DurationCollector &&duration_collector,
+                             std::shared_ptr<std::atomic<hailo_status>> &&pipeline_status) :
+    BaseMuxElement(nms_infos.size(), name, timeout, std::move(duration_collector), std::move(pipeline_status)),
+    m_nms_infos(nms_infos),
+    m_fused_nms_info(fused_nms_info),
+    m_pool(std::move(pool))
+{}
+
+const hailo_nms_info_t &NmsMuxElement::get_fused_nms_info() const
+{
+    return m_fused_nms_info;
+}
+
+std::vector<AccumulatorPtr> NmsMuxElement::get_queue_size_accumulators()
+{
+    if (nullptr == m_pool->get_queue_size_accumulator()) {
+        return std::vector<AccumulatorPtr>();
+    }
+    return {m_pool->get_queue_size_accumulator()};
+}
+
+Expected<PipelineBuffer> NmsMuxElement::action(std::vector<PipelineBuffer> &&inputs, PipelineBuffer &&optional)
+{
+    std::vector<MemoryView> input_views;
+
+    input_views.reserve(inputs.size());
+    for (auto &input_buf : inputs) {
+        input_views.push_back(input_buf.as_view());
+    }
+
+    auto acquired_buffer = m_pool->get_available_buffer(std::move(optional), m_timeout);
+    if (HAILO_SHUTDOWN_EVENT_SIGNALED == acquired_buffer.status()) {
+        return make_unexpected(acquired_buffer.status());
+    }
+    CHECK_EXPECTED(acquired_buffer);
+
+    m_duration_collector.start_measurement();
+    const auto status = fuse_buffers(input_views, m_nms_infos, acquired_buffer.value().as_view());
+    m_duration_collector.complete_measurement();
+    CHECK_SUCCESS_AS_EXPECTED(status);
+
+    return acquired_buffer.release();
+}
+
+Expected<std::shared_ptr<TransformDemuxElement>> TransformDemuxElement::create(std::shared_ptr<OutputDemuxer> demuxer,
+    const std::string &name, std::chrono::milliseconds timeout, size_t buffer_pool_size, hailo_pipeline_elem_stats_flags_t elem_flags,
+    hailo_vstream_stats_flags_t vstream_flags, EventPtr shutdown_event, std::shared_ptr<std::atomic<hailo_status>> pipeline_status)
+{
+    std::vector<BufferPoolPtr> pools;
+    pools.reserve(demuxer->get_edges_stream_info().size());
+
+    for (const auto& mux_edge : demuxer->get_edges_stream_info()) {
+        auto buffer_pool = BufferPool::create(mux_edge.hw_frame_size, buffer_pool_size, shutdown_event, elem_flags, vstream_flags);
+        CHECK_EXPECTED(buffer_pool, "Failed creating BufferPool");
+        pools.push_back(buffer_pool.release());
+    }
+
+    auto duration_collector = DurationCollector::create(elem_flags);
+    CHECK_EXPECTED(duration_collector);
+
+    auto demux_elem_ptr = make_shared_nothrow<TransformDemuxElement>(demuxer, std::move(pools), name, timeout,
+        duration_collector.release(), std::move(pipeline_status));
+    CHECK_AS_EXPECTED(nullptr != demux_elem_ptr, HAILO_OUT_OF_HOST_MEMORY);
+
+    return demux_elem_ptr;
+}
+
+TransformDemuxElement::TransformDemuxElement(std::shared_ptr<OutputDemuxer> demuxer, std::vector<BufferPoolPtr> &&pools,
+                                             const std::string &name, std::chrono::milliseconds timeout,
+                                             DurationCollector &&duration_collector,
+                                             std::shared_ptr<std::atomic<hailo_status>> &&pipeline_status) :
+    BaseDemuxElement(demuxer->get_edges_stream_info().size(), name, timeout, std::move(duration_collector),
+                     std::move(pipeline_status)),
+    m_demuxer(demuxer),
+    m_pools(std::move(pools))
+{}
+
+std::vector<AccumulatorPtr> TransformDemuxElement::get_queue_size_accumulators()
+{
+    std::vector<AccumulatorPtr> result;
+    for (const auto& pool : m_pools) {
+        if (nullptr != pool->get_queue_size_accumulator()) {
+            result.emplace_back(pool->get_queue_size_accumulator());
+        }
+    }
+    return result;
+}
+
+Expected<std::vector<PipelineBuffer>> TransformDemuxElement::action(PipelineBuffer &&input)
+{
+    std::vector<PipelineBuffer> outputs;
+    std::vector<MemoryView> raw_buffers;
+
+    auto mux_edges = m_demuxer->get_edges_stream_info();
+    outputs.reserve(mux_edges.size());
+    raw_buffers.reserve(mux_edges.size());
+
+    for (uint32_t i = 0; i < mux_edges.size(); i++) {
+        auto acquired_buffer = m_pools[i]->acquire_buffer(m_timeout);
+        if (HAILO_SHUTDOWN_EVENT_SIGNALED == acquired_buffer.status()) {
+            return make_unexpected(acquired_buffer.status());
+        }
+        CHECK_EXPECTED(acquired_buffer, "Failed to acquire buffer");
+        outputs.emplace_back(acquired_buffer.release());
+        
+        raw_buffers.push_back(outputs.back().as_view());
+    }
+
+    m_duration_collector.start_measurement();
+    const auto status = m_demuxer->transform_demux(input.as_view(), raw_buffers);
+    m_duration_collector.complete_measurement();
+    CHECK_SUCCESS_AS_EXPECTED(status);
+
+    return outputs;
+}
+
+BaseVStream::BaseVStream(const hailo_vstream_info_t &vstream_info, const hailo_vstream_params_t &vstream_params,
+                         std::shared_ptr<PipelineElement> pipeline_entry, std::vector<std::shared_ptr<PipelineElement>> &&pipeline,
+                         std::shared_ptr<std::atomic<hailo_status>> &&pipeline_status, EventPtr shutdown_event,
+                         AccumulatorPtr pipeline_latency_accumulator, EventPtr &&network_group_activated_event, hailo_status &output_status) :
+    m_vstream_info(vstream_info),
+    m_vstream_params(vstream_params),
+    m_measure_pipeline_latency((vstream_params.vstream_stats_flags & HAILO_VSTREAM_STATS_MEASURE_LATENCY) != 0),
+    m_entry_element(pipeline_entry),
+    m_pipeline(std::move(pipeline)),
+    m_is_activated(false),
+    m_pipeline_status(std::move(pipeline_status)),
+    m_shutdown_event(shutdown_event),
+    m_network_group_activated_event(std::move(network_group_activated_event)),
+    m_fps_accumulators(get_pipeline_accumulators_by_type(m_pipeline, AccumulatorType::FPS)),
+    m_latency_accumulators(get_pipeline_accumulators_by_type(m_pipeline, AccumulatorType::LATENCY)),
+    m_queue_size_accumulators(get_pipeline_queue_size_accumulators(m_pipeline)),
+    m_pipeline_latency_accumulator(pipeline_latency_accumulator)
+{
+    output_status = start_vstream();
+}
+
+BaseVStream::BaseVStream(BaseVStream &&other) noexcept :
+    m_vstream_info(std::move(other.m_vstream_info)),
+    m_vstream_params(std::move(other.m_vstream_params)),
+    m_measure_pipeline_latency(std::move(other.m_measure_pipeline_latency)),
+    m_entry_element(std::move(other.m_entry_element)),
+    m_pipeline(std::move(other.m_pipeline)),
+    m_is_activated(std::exchange(other.m_is_activated, false)),
+    m_pipeline_status(std::move(other.m_pipeline_status)),
+    m_shutdown_event(std::move(other.m_shutdown_event)),
+    m_network_group_activated_event(std::move(other.m_network_group_activated_event)),
+    m_fps_accumulators(std::move(other.m_fps_accumulators)),
+    m_latency_accumulators(std::move(other.m_latency_accumulators)),
+    m_queue_size_accumulators(std::move(other.m_queue_size_accumulators)),
+    m_pipeline_latency_accumulator(std::move(other.m_pipeline_latency_accumulator))
+{}
+
+BaseVStream& BaseVStream::operator=(BaseVStream &&other) noexcept
+{
+    if (this != &other) {
+        // operator= is used only for vstream creation BEFORE activation. otherwise we should deactivate vstream here
+        assert(!m_is_activated);
+        m_vstream_info = std::move(other.m_vstream_info);
+        m_vstream_params = std::move(other.m_vstream_params);
+        m_measure_pipeline_latency = std::move(other.m_measure_pipeline_latency);
+        m_entry_element = std::move(other.m_entry_element);
+        m_pipeline = std::move(other.m_pipeline);
+        m_is_activated = std::exchange(other.m_is_activated, false);
+        m_pipeline_status = std::move(other.m_pipeline_status);
+        m_shutdown_event = std::move(other.m_shutdown_event);
+        m_network_group_activated_event = std::move(other.m_network_group_activated_event);
+        m_fps_accumulators = std::move(other.m_fps_accumulators);
+        m_latency_accumulators = std::move(other.m_latency_accumulators);
+        m_queue_size_accumulators = std::move(other.m_queue_size_accumulators);
+        m_pipeline_latency_accumulator = std::move(other.m_pipeline_latency_accumulator);
+    }
+    return *this;
+}
+
+BaseVStream::~BaseVStream()
+{
+    (void)stop_vstream();
+}
+
+hailo_status BaseVStream::start_vstream()
+{
+    auto status = m_shutdown_event->reset();
+    CHECK_SUCCESS(status);
+
+    LOGGER__DEBUG("Activating {}...", name());
+    status = m_entry_element->activate();
+    CHECK_SUCCESS(status);
+
+    m_is_activated = true;
+    return HAILO_SUCCESS;
+}
+
+hailo_status BaseVStream::stop_vstream()
+{
+    hailo_status status = HAILO_SUCCESS;
+    if (m_is_activated) {
+        m_is_activated = false;
+        status = m_entry_element->deactivate();
+        if (HAILO_SUCCESS != status) {
+            LOGGER__WARNING("Failed deactivate of vstream {} status {}", name(), status);
+        }
+
+        status = m_entry_element->post_deactivate();
+        if (HAILO_SUCCESS != status) {
+            LOGGER__WARNING("Failed post deactivate of vstream {} status {}", name(), status);
+        }
+    }
+    return status;
+}
+
+hailo_status BaseVStream::stop_and_clear()
+{
+     auto status = m_network_group_activated_event->wait(std::chrono::milliseconds(0));
+     CHECK(HAILO_TIMEOUT == status, HAILO_INVALID_OPERATION,
+        "Trying to clear {} vstream before its network group is deactivated", name());
+     status = stop_vstream();
+     CHECK_SUCCESS(status);
+    
+    status = m_entry_element->clear();
+    CHECK_SUCCESS(status, "Failed clearing vstream {}", name());
+    
+    return HAILO_SUCCESS;
+}
+
+size_t BaseVStream::get_frame_size() const
+{
+    if (HAILO_FORMAT_ORDER_HAILO_NMS == m_vstream_info.format.order) {
+        return HailoRTCommon::get_nms_host_frame_size(m_vstream_info.nms_shape, m_vstream_params.user_buffer_format);
+    }
+    return HailoRTCommon::get_frame_size(m_vstream_info.shape, m_vstream_params.user_buffer_format);
+}
+
+const hailo_vstream_info_t &BaseVStream::get_info() const
+{
+    return m_vstream_info;
+}
+
+const hailo_format_t &BaseVStream::get_user_buffer_format() const
+{
+    return m_vstream_params.user_buffer_format;
+}
+
+std::string BaseVStream::name() const
+{
+    return std::string(m_vstream_info.name);
+}
+
+std::string BaseVStream::network_name() const
+{
+    return std::string(m_vstream_info.network_name);
+}
+
+const std::map<std::string, AccumulatorPtr> &BaseVStream::get_fps_accumulators() const
+{
+    return m_fps_accumulators;
+}
+
+const std::map<std::string, AccumulatorPtr> &BaseVStream::get_latency_accumulators() const
+{
+    return m_latency_accumulators;
+}
+
+const std::map<std::string, std::vector<AccumulatorPtr>> &BaseVStream::get_queue_size_accumulators() const
+{
+    return m_queue_size_accumulators;
+}
+
+AccumulatorPtr BaseVStream::get_pipeline_latency_accumulator() const
+{
+    return m_pipeline_latency_accumulator;
+}
+
+
+const std::vector<std::shared_ptr<PipelineElement>> &BaseVStream::get_pipeline() const
+{
+    return m_pipeline;
+}
+
+
+std::map<std::string, AccumulatorPtr> get_pipeline_accumulators_by_type(
+    const std::vector<std::shared_ptr<PipelineElement>> &pipeline, AccumulatorType accumulator_type)
+{
+    std::map<std::string, AccumulatorPtr> result;
+    for (const auto &elem : pipeline) {
+        if (nullptr == elem) {
+            continue;
+        }
+
+        AccumulatorPtr accumulator = nullptr;
+        if (AccumulatorType::FPS == accumulator_type) {
+            accumulator = elem->get_fps_accumulator();
+        } else if (AccumulatorType::LATENCY == accumulator_type) {
+            accumulator = elem->get_latency_accumulator();
+        } else {
+            continue;
+        }
+
+        if (nullptr != accumulator) {
+            result.emplace(elem->name(), accumulator);
+        }
+    }
+
+    return result;
+}
+
+std::map<std::string, std::vector<AccumulatorPtr>> get_pipeline_queue_size_accumulators(
+    const std::vector<std::shared_ptr<PipelineElement>> &pipeline)
+{
+    std::map<std::string, std::vector<AccumulatorPtr>> result;
+    for (const auto &elem : pipeline) {
+        if (nullptr == elem) {
+            continue;
+        }
+
+        const auto accumulators = elem->get_queue_size_accumulators();
+        if (0 != accumulators.size()) {
+            result.emplace(elem->name(), accumulators);
+        }
+    }
+
+    return result;
+}
+
+Expected<InputVStream> InputVStream::create(const hailo_vstream_info_t &vstream_info,
+    const hailo_vstream_params_t &vstream_params, std::shared_ptr<PipelineElement> pipeline_entry,
+    std::shared_ptr<SinkElement> pipeline_exit, std::vector<std::shared_ptr<PipelineElement>> &&pipeline,
+    std::shared_ptr<std::atomic<hailo_status>> &&pipeline_status, EventPtr shutdown_event, EventPtr network_group_activated_event,
+    AccumulatorPtr pipeline_latency_accumulator)
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+
+    if (nullptr != pipeline_latency_accumulator) {
+        pipeline_exit->sink().set_push_complete_callback([pipeline_latency_accumulator](const PipelineBuffer::Metadata& metadata) {
+                const auto duration_sec = std::chrono::duration_cast<std::chrono::duration<double>>(
+                    std::chrono::steady_clock::now() - metadata.get_start_time()).count();
+                pipeline_latency_accumulator->add_data_point(duration_sec);
+            });
+    }
+
+    InputVStream vstream(vstream_info, vstream_params, std::move(pipeline_entry), std::move(pipeline),
+        std::move(pipeline_status), shutdown_event, pipeline_latency_accumulator, std::move(network_group_activated_event), status);
+    CHECK_SUCCESS_AS_EXPECTED(status, "Failed to create virtual stream");
+
+    return vstream;
+}
+
+InputVStream::InputVStream(const hailo_vstream_info_t &vstream_info, const hailo_vstream_params_t &vstream_params,
+                           std::shared_ptr<PipelineElement> pipeline_entry,
+                           std::vector<std::shared_ptr<PipelineElement>> &&pipeline,
+                           std::shared_ptr<std::atomic<hailo_status>> &&pipeline_status, EventPtr shutdown_event,
+                           AccumulatorPtr pipeline_latency_accumulator,
+                           EventPtr network_group_activated_event, hailo_status &output_status) :
+    BaseVStream(vstream_info, vstream_params, pipeline_entry, std::move(pipeline), std::move(pipeline_status), shutdown_event,
+                pipeline_latency_accumulator, std::move(network_group_activated_event), output_status)
+{
+    if (HAILO_SUCCESS != output_status) {
+        return;
+    }
+    LOGGER__INFO("Creating {}...", name());
+}
+
+hailo_status InputVStream::write(const MemoryView &buffer)
+{
+    CHECK(m_is_activated, HAILO_VSTREAM_PIPELINE_NOT_ACTIVATED, "Failed to write buffer! Virtual stream {} is not activated!", name());
+    auto status = m_network_group_activated_event->wait(std::chrono::milliseconds(0));
+    CHECK(HAILO_TIMEOUT != status, HAILO_NETWORK_GROUP_NOT_ACTIVATED,
+        "Trying to write to vstream {} before its network group is activated", name());
+    status = m_entry_element->run_push(PipelineBuffer(buffer, m_measure_pipeline_latency));
+    if (HAILO_SHUTDOWN_EVENT_SIGNALED == status) {
+        LOGGER__INFO("Sending to VStream was shutdown!");
+        status = m_pipeline_status->load();
+    }
+    if (HAILO_STREAM_INTERNAL_ABORT == status) {
+        LOGGER__INFO("Sending to VStream was aborted!");
+        return HAILO_STREAM_INTERNAL_ABORT;
+    }
+    return status;
+}
+
+hailo_status InputVStream::flush()
+{
+    auto status = m_entry_element->run_push(PipelineBuffer(PipelineBuffer::Type::FLUSH));
+    CHECK_SUCCESS(status);
+    
+    status = m_entry_element->flush();
+    CHECK_SUCCESS(status);
+
+    return HAILO_SUCCESS;
+}
+
+hailo_status InputVStream::clear(std::vector<InputVStream> &vstreams)
+{
+    for (auto &vstream : vstreams) {
+        auto status = vstream.stop_and_clear();
+        CHECK_SUCCESS(status);
+    }
+    for (auto &vstream : vstreams) {
+        auto status = vstream.start_vstream();
+        CHECK_SUCCESS(status);
+    }
+
+    return HAILO_SUCCESS;
+}
+
+hailo_status InputVStream::clear(std::vector<std::reference_wrapper<InputVStream>> &vstreams)
+{
+    for (auto &vstream : vstreams) {
+        auto status = vstream.get().stop_and_clear();
+        CHECK_SUCCESS(status);
+    }
+    for (auto &vstream : vstreams) {
+        auto status = vstream.get().start_vstream();
+        CHECK_SUCCESS(status);
+    }
+
+    return HAILO_SUCCESS;
+}
+
+std::string InputVStream::get_pipeline_description() const
+{
+    std::stringstream pipeline_str;
+    pipeline_str << "Input pipeline '" << name() << "': ";
+    for (const auto &element : m_pipeline) {
+        pipeline_str << element->description() << " >> ";
+    }
+    pipeline_str << "HW";
+    return pipeline_str.str();
+}
+
+Expected<OutputVStream> OutputVStream::create(const hailo_vstream_info_t &vstream_info, const hailo_vstream_params_t &vstream_params,
+    std::shared_ptr<PipelineElement> pipeline_entry, std::vector<std::shared_ptr<PipelineElement>> &&pipeline,
+    std::shared_ptr<std::atomic<hailo_status>> &&pipeline_status, EventPtr shutdown_event,
+    EventPtr network_group_activated_event, AccumulatorPtr pipeline_latency_accumulator)
+
+{
+    hailo_status status = HAILO_UNINITIALIZED;
+
+    CHECK_AS_EXPECTED(1 == pipeline_entry->sources().size(), HAILO_INVALID_ARGUMENT, 
+        "OutputVStream's entry element is expected to have one source");
+
+    if (nullptr != pipeline_latency_accumulator) {
+        pipeline_entry->sources()[0].set_pull_complete_callback([pipeline_latency_accumulator](const PipelineBuffer::Metadata& metadata) { 
+                const auto duration_sec = std::chrono::duration_cast<std::chrono::duration<double>>(
+                    std::chrono::steady_clock::now() - metadata.get_start_time()).count();
+                pipeline_latency_accumulator->add_data_point(duration_sec);
+            });
+    }
+
+    OutputVStream vstream(vstream_info, vstream_params, std::move(pipeline_entry), std::move(pipeline),
+        std::move(pipeline_status), shutdown_event, pipeline_latency_accumulator, std::move(network_group_activated_event), status);
+    CHECK_SUCCESS_AS_EXPECTED(status, "Failed to create virtual stream");
+
+    return vstream;
+}
+
+OutputVStream::OutputVStream(const hailo_vstream_info_t &vstream_info, const hailo_vstream_params_t &vstream_params,
+                             std::shared_ptr<PipelineElement> pipeline_entry,
+                             std::vector<std::shared_ptr<PipelineElement>> &&pipeline,
+                             std::shared_ptr<std::atomic<hailo_status>> &&pipeline_status, EventPtr shutdown_event,
+                             AccumulatorPtr pipeline_latency_accumulator,
+                             EventPtr network_group_activated_event, hailo_status &output_status) :
+    BaseVStream(vstream_info, vstream_params, pipeline_entry, std::move(pipeline), std::move(pipeline_status), shutdown_event,
+                pipeline_latency_accumulator, std::move(network_group_activated_event), output_status)
+{
+    if (HAILO_SUCCESS != output_status) {
+        return;
+    }
+
+    LOGGER__INFO("Creating {}...", name());
+}
+
+hailo_status OutputVStream::read(MemoryView buffer)
+{
+    CHECK(m_is_activated, HAILO_VSTREAM_PIPELINE_NOT_ACTIVATED, "read() failed! Virtual stream {} is not activated!", name());
+    auto status = m_network_group_activated_event->wait(std::chrono::milliseconds(0));
+    if (HAILO_TIMEOUT == status) {
+        LOGGER__INFO("Trying to read from vstream {} before its network_group is activated", name());
+        return HAILO_NETWORK_GROUP_NOT_ACTIVATED;
+    }
+    CHECK_SUCCESS(status);
+
+    assert(1 == m_entry_element->sources().size());
+    auto recv_buffer = m_entry_element->sources()[0].run_pull(PipelineBuffer(buffer, m_measure_pipeline_latency));
+    status = recv_buffer.status();
+    if (HAILO_SHUTDOWN_EVENT_SIGNALED == status) {
+        LOGGER__INFO("Receiving to VStream was shutdown!");
+        status = m_pipeline_status->load();
+    }
+    if (HAILO_STREAM_INTERNAL_ABORT == status) {
+        LOGGER__INFO("Receiving to VStream was aborted!");
+        return HAILO_STREAM_INTERNAL_ABORT;
+    }
+    return status;
+}
+
+hailo_status OutputVStream::clear(std::vector<OutputVStream> &vstreams)
+{
+    for (auto &vstream : vstreams) {
+        auto status = vstream.stop_and_clear();
+        CHECK_SUCCESS(status);
+    }
+
+    for (auto &vstream : vstreams) {
+        auto status = vstream.start_vstream();
+        CHECK_SUCCESS(status);
+    }
+
+    return HAILO_SUCCESS;
+}
+
+hailo_status OutputVStream::clear(std::vector<std::reference_wrapper<OutputVStream>> &vstreams)
+{
+    for (auto &vstream : vstreams) {
+        auto status = vstream.get().stop_and_clear();
+        CHECK_SUCCESS(status);
+    }
+
+    for (auto &vstream : vstreams) {
+        auto status = vstream.get().start_vstream();
+        CHECK_SUCCESS(status);
+    }
+
+    return HAILO_SUCCESS;
+}
+
+std::string OutputVStream::get_pipeline_description() const
+{
+    std::stringstream pipeline_str;
+    pipeline_str << "Output pipeline '" << name() << "': HW";
+    for (const auto &element : m_pipeline) {
+        pipeline_str << " >> " << element->description();
+    }
+    return pipeline_str.str();
+}
+
+Expected<std::shared_ptr<HwReadElement>> HwReadElement::create(OutputStream &stream, const std::string &name, std::chrono::milliseconds timeout,
+    size_t buffer_pool_size, hailo_pipeline_elem_stats_flags_t elem_flags, hailo_vstream_stats_flags_t vstream_flags, EventPtr shutdown_event,
+    std::shared_ptr<std::atomic<hailo_status>> pipeline_status)
+{
+    auto buffer_pool = BufferPool::create(stream.get_frame_size(), buffer_pool_size, shutdown_event, elem_flags, vstream_flags);
+    CHECK_EXPECTED(buffer_pool, "Failed creating BufferPool for {}", name);
+
+    auto duration_collector = DurationCollector::create(elem_flags);
+    CHECK_EXPECTED(duration_collector);
+
+    auto hw_read_elem_ptr = make_shared_nothrow<HwReadElement>(stream, buffer_pool.release(), name, timeout,
+        duration_collector.release(), shutdown_event, std::move(pipeline_status));
+    CHECK_AS_EXPECTED(nullptr != hw_read_elem_ptr, HAILO_OUT_OF_HOST_MEMORY);
+
+    LOGGER__INFO("Created {}", hw_read_elem_ptr->name());
+
+    return hw_read_elem_ptr;
+}
+
+HwReadElement::HwReadElement(OutputStream &stream, BufferPoolPtr buffer_pool, const std::string &name,
+                             std::chrono::milliseconds timeout, DurationCollector &&duration_collector,
+                             EventPtr shutdown_event, std::shared_ptr<std::atomic<hailo_status>> &&pipeline_status) :
+    SourceElement(name, std::move(duration_collector), std::move(pipeline_status)),
+    m_stream(stream),
+    m_pool(buffer_pool),
+    m_timeout(timeout),
+    m_shutdown_event(shutdown_event),
+    m_activation_wait_or_shutdown(stream.get_network_group_activated_event(), shutdown_event)
+{}
+
+uint32_t HwReadElement::get_invalid_frames_count()
+{
+    return m_stream.get_invalid_frames_count();
+}
+
+std::string HwReadElement::description() const
+{
+    std::stringstream element_description;
+    element_description << "(" << this->name() << " | hw_frame_size: " << m_stream.get_info().hw_frame_size << ")";   
+
+    return element_description.str();
+}
+
+hailo_status HwReadElement::post_deactivate()
+{
+    auto status = m_stream.clear_abort();
+    CHECK(((HAILO_SUCCESS == status) || (HAILO_STREAM_NOT_ACTIVATED == status)), status,
+        "Failed to clear abort stream in {}", name());
+    return HAILO_SUCCESS;
+}
+
+hailo_status HwReadElement::clear()
+{
+    return HAILO_SUCCESS;
+}
+
+hailo_status HwReadElement::flush()
+{
+    return HAILO_INVALID_OPERATION;
+}
+
+std::vector<AccumulatorPtr> HwReadElement::get_queue_size_accumulators()
+{
+    if (nullptr == m_pool->get_queue_size_accumulator()) {
+        return std::vector<AccumulatorPtr>();
+    }
+    return {m_pool->get_queue_size_accumulator()};
+}
+
+hailo_status HwReadElement::run_push(PipelineBuffer &&/*buffer*/)
+{
+    return HAILO_INVALID_OPERATION;
+}
+
+Expected<PipelineBuffer> HwReadElement::run_pull(PipelineBuffer &&optional, const PipelinePad &/*source*/)
+{
+    auto buffer = m_pool->get_available_buffer(std::move(optional), m_timeout);
+    if (HAILO_SHUTDOWN_EVENT_SIGNALED == buffer.status()) {
+        return make_unexpected(buffer.status());
+    }
+    CHECK_EXPECTED(buffer);
+
+    while (true) {
+        auto status = m_activation_wait_or_shutdown.wait(m_timeout);
+        if (HAILO_SHUTDOWN_EVENT_SIGNALED == status) {
+            return make_unexpected(HAILO_SHUTDOWN_EVENT_SIGNALED);
+        }
+        if (HAILO_TIMEOUT == status) {
+            return make_unexpected(HAILO_NETWORK_GROUP_NOT_ACTIVATED);
+        }
+        CHECK_SUCCESS_AS_EXPECTED(status);
+
+        MemoryView buffer_view(buffer.value().as_view());
+        m_duration_collector.start_measurement();
+        status = m_stream.read(buffer_view);
+        m_duration_collector.complete_measurement();
+        if (HAILO_INVALID_FRAME == status) {
+            m_stream.increase_invalid_frames_count(1);
+            status = HAILO_SUCCESS;
+        }
+        if (HAILO_STREAM_NOT_ACTIVATED == status) {
+            // Try again
+            continue;
+        }
+        if (HAILO_STREAM_INTERNAL_ABORT == status) {
+            LOGGER__INFO("Reading from stream was aborted!");
+            return make_unexpected(HAILO_STREAM_INTERNAL_ABORT);
+        }
+        CHECK_SUCCESS_AS_EXPECTED(status);
+
+        return buffer.release();
+    }
+}
+
+hailo_status HwReadElement::activate()
+{
+    return HAILO_SUCCESS;
+}
+
+hailo_status HwReadElement::deactivate()
+{
+    auto signal_shutdown_status = m_shutdown_event->signal();
+    if (HAILO_SUCCESS != signal_shutdown_status) {
+        LOGGER__ERROR("Signaling {} shutdown event failed with {}", name(), signal_shutdown_status);
+    }
+
+    auto abort_status = m_stream.abort();
+    if ((HAILO_SUCCESS != abort_status) && (HAILO_STREAM_NOT_ACTIVATED != abort_status)) {
+        LOGGER__ERROR("Abort {} failed with {}", name(), abort_status);
+        return abort_status;
+    }
+
+    return signal_shutdown_status;
+}
+
+Expected<std::shared_ptr<HwWriteElement>> HwWriteElement::create(InputStream &stream, const std::string &name,
+    hailo_pipeline_elem_stats_flags_t elem_flags, std::shared_ptr<std::atomic<hailo_status>> pipeline_status)
+{
+
+    auto duration_collector = DurationCollector::create(elem_flags);
+    CHECK_EXPECTED(duration_collector);
+
+    auto got_flush_event = Event::create_shared(Event::State::not_signalled);
+    CHECK_AS_EXPECTED(nullptr != got_flush_event, HAILO_OUT_OF_HOST_MEMORY);
+
+    auto hw_write_elem_ptr = make_shared_nothrow<HwWriteElement>(stream, name,
+        duration_collector.release(), std::move(pipeline_status), got_flush_event);
+    CHECK_AS_EXPECTED(nullptr != hw_write_elem_ptr, HAILO_OUT_OF_HOST_MEMORY);
+
+    LOGGER__INFO("Created {}", hw_write_elem_ptr->name());
+
+    return hw_write_elem_ptr;
+}
+
+HwWriteElement::HwWriteElement(InputStream &stream, const std::string &name, DurationCollector &&duration_collector,
+                               std::shared_ptr<std::atomic<hailo_status>> &&pipeline_status, EventPtr got_flush_event) :
+    SinkElement(name, std::move(duration_collector), std::move(pipeline_status)),
+    m_stream(stream), m_got_flush_event(got_flush_event)
+{}
+
+Expected<PipelineBuffer> HwWriteElement::run_pull(PipelineBuffer &&/*optional*/, const PipelinePad &/*source*/)
+{
+    return make_unexpected(HAILO_INVALID_OPERATION);
+}
+
+hailo_status HwWriteElement::run_push(PipelineBuffer &&buffer)
+{
+    if (PipelineBuffer::Type::FLUSH == buffer.get_type()) {
+        hailo_status flush_status = m_stream.flush();
+        if (HAILO_STREAM_INTERNAL_ABORT == flush_status) {
+            LOGGER__INFO("Failed flushing input stream {} because stream was aborted", m_stream.to_string());
+        } else if (HAILO_SUCCESS != flush_status) {
+            LOGGER__ERROR("flush has failed in {} with status {}", name(), flush_status);
+        }
+        hailo_status status = m_got_flush_event->signal();
+        CHECK_SUCCESS(status);
+        return HAILO_SUCCESS;
+    }
+
+    m_duration_collector.start_measurement();
+    const auto status = m_stream.write(MemoryView(buffer.data(), buffer.size()));
+    m_duration_collector.complete_measurement();
+
+    return status;
+}
+
+hailo_status HwWriteElement::activate()
+{
+    return HAILO_SUCCESS;
+}
+
+hailo_status HwWriteElement::deactivate()
+{
+    // The flush operation will block until all buffers currently in the pipeline will be processed.
+    // We assume that no buffers are sent after the call for deactivate.
+    hailo_status flush_status = m_stream.flush();
+    if (HAILO_STREAM_INTERNAL_ABORT == flush_status) {
+        LOGGER__INFO("Failed flushing input stream {} because stream was aborted", m_stream.to_string());
+        // TODO: HRT-3621
+        return HAILO_SUCCESS;
+    } else if (HAILO_SUCCESS != flush_status) {
+        LOGGER__ERROR("flush has failed in {} with status {}", name(), flush_status);
+    }
+
+    auto abort_status = m_stream.abort();
+    CHECK(((HAILO_SUCCESS == abort_status) || (HAILO_STREAM_NOT_ACTIVATED == abort_status)), abort_status,
+        "Failed to abort stream in {}", name());
+    return HAILO_SUCCESS;
+}
+
+hailo_status HwWriteElement::post_deactivate()
+{
+    auto status = m_stream.clear_abort();
+    CHECK(((HAILO_SUCCESS == status) || (HAILO_STREAM_NOT_ACTIVATED == status)), status,
+        "Failed to clear abort stream in {}", name());
+    return HAILO_SUCCESS;
+}
+
+hailo_status HwWriteElement::clear()
+{
+    return HAILO_SUCCESS;
+}
+
+hailo_status HwWriteElement::flush()
+{
+    hailo_status status = m_got_flush_event->wait(std::chrono::milliseconds(HAILO_DEFAULT_VSTREAM_TIMEOUT_MS));
+    CHECK_SUCCESS(status);
+
+    status = m_got_flush_event->reset();
+    CHECK_SUCCESS(status);
+
+    return HAILO_SUCCESS;
+}
+
+std::string HwWriteElement::description() const
+{
+    std::stringstream element_description;
+    element_description << "(" << this->name() << " | hw_frame_size: " << m_stream.get_info().hw_frame_size << ")";   
+
+    return element_description.str();
+}
+
+Expected<std::shared_ptr<CopyBufferElement>> CopyBufferElement::create(const std::string &name,
+    std::shared_ptr<std::atomic<hailo_status>> pipeline_status)
+{
+    auto duration_collector = DurationCollector::create(HAILO_PIPELINE_ELEM_STATS_NONE);
+    CHECK_EXPECTED(duration_collector);
+    auto elem_ptr = make_shared_nothrow<CopyBufferElement>(name, duration_collector.release(), std::move(pipeline_status));
+    CHECK_AS_EXPECTED(nullptr != elem_ptr, HAILO_OUT_OF_HOST_MEMORY);
+
+    LOGGER__INFO("Created {}", elem_ptr->name());
+
+    return elem_ptr;
+}
+
+CopyBufferElement::CopyBufferElement(const std::string &name, DurationCollector &&duration_collector, 
+                                     std::shared_ptr<std::atomic<hailo_status>> pipeline_status) :
+    FilterElement(name, std::move(duration_collector), std::move(pipeline_status))
+{}
+
+PipelinePad &CopyBufferElement::next_pad()
+{
+    // Note: The next elem to be run is downstream from this elem (i.e. buffers are pushed)
+    return *m_sources[0].next();
+}
+
+Expected<PipelineBuffer> CopyBufferElement::action(PipelineBuffer &&input, PipelineBuffer &&optional)
+{
+    CHECK_AS_EXPECTED(optional, HAILO_INVALID_ARGUMENT, "Optional buffer must be passed to CopyBufferElement!");
+
+    CHECK_AS_EXPECTED(optional.size() == input.size(), HAILO_INVALID_ARGUMENT, "Optional buffer size does not equal to the input buffer size!");
+    memcpy(optional.data(), input.data(), optional.size());
+
+    return std::move(optional);
+}
+
+Expected<std::pair<std::vector<InputVStream>, std::vector<OutputVStream>>> VStreamsBuilder::create_vstreams(
+    ConfiguredNetworkGroup &net_group, bool quantized, hailo_format_type_t format_type,
+    const std::string &network_name)
+{
+    const auto params = HailoRTDefaults::get_vstreams_params(quantized, format_type);
+    return create_vstreams(net_group, params, network_name);
+}
+
+Expected<std::pair<std::vector<InputVStream>, std::vector<OutputVStream>>> VStreamsBuilder::create_vstreams(
+    ConfiguredNetworkGroup &net_group, const hailo_vstream_params_t &vstreams_params,
+    const std::string &network_name)
+{
+    std::map<std::string, hailo_vstream_params_t> vstreams_params_by_input_stream_name;
+    auto input_vstream_params = net_group.make_input_vstream_params(true, HAILO_FORMAT_TYPE_AUTO, 
+        HAILO_DEFAULT_VSTREAM_TIMEOUT_MS, HAILO_DEFAULT_VSTREAM_QUEUE_SIZE, network_name);
+    CHECK_EXPECTED(input_vstream_params);
+
+    for (auto params_pair : input_vstream_params.release()) {
+        vstreams_params_by_input_stream_name.emplace(std::make_pair(params_pair.first, vstreams_params));
+    }
+
+    auto expected_all_inputs = create_input_vstreams(net_group, vstreams_params_by_input_stream_name);
+    CHECK_EXPECTED(expected_all_inputs);
+
+    std::map<std::string, hailo_vstream_params_t> vstreams_params_by_output_stream_name;
+    auto output_vstream_params = net_group.make_output_vstream_params(true, HAILO_FORMAT_TYPE_AUTO, 
+        HAILO_DEFAULT_VSTREAM_TIMEOUT_MS, HAILO_DEFAULT_VSTREAM_QUEUE_SIZE, network_name);
+    CHECK_EXPECTED(output_vstream_params);
+
+    for (auto params_pair : output_vstream_params.release()) {
+        vstreams_params_by_output_stream_name.emplace(std::make_pair(params_pair.first, vstreams_params));
+    }
+
+    auto expected_all_outputs = create_output_vstreams(net_group, vstreams_params_by_output_stream_name);
+    CHECK_EXPECTED(expected_all_outputs);
+
+    return std::pair<std::vector<InputVStream>, std::vector<OutputVStream>>(
+            expected_all_inputs.release(), expected_all_outputs.release());
+}
+
+static std::map<std::string, hailo_vstream_info_t> vstream_infos_vector_to_map(std::vector<hailo_vstream_info_t> &&vstream_info_vector)
+{
+    std::map<std::string, hailo_vstream_info_t> vstream_infos_map;
+    for (const auto &vstream_info : vstream_info_vector) {
+        vstream_infos_map.emplace(std::string(vstream_info.name), vstream_info);
+    }
+
+    return vstream_infos_map;
+}
+
+static Expected<std::map<std::string, hailo_vstream_info_t>> get_input_vstream_infos_map(ConfiguredNetworkGroup &net_group)
+{
+    auto input_vstream_infos = net_group.get_input_vstream_infos();
+    CHECK_EXPECTED(input_vstream_infos);
+
+    return vstream_infos_vector_to_map(input_vstream_infos.release());
+}
+
+static Expected<std::map<std::string, hailo_vstream_info_t>> get_output_vstream_infos_map(ConfiguredNetworkGroup &net_group)
+{
+    auto output_vstream_infos = net_group.get_output_vstream_infos();
+    CHECK_EXPECTED(output_vstream_infos);
+
+    return vstream_infos_vector_to_map(output_vstream_infos.release());
+}
+
+static hailo_vstream_params_t expand_vstream_params_autos(const hailo_vstream_info_t &vstream_info,
+    const hailo_vstream_params_t &vstream_params)
+{
+    auto local_vstream_params = vstream_params;
+    local_vstream_params.user_buffer_format = HailoRTDefaults::expand_auto_format(vstream_params.user_buffer_format,
+        vstream_info.format);
+    return local_vstream_params;
+}
+
+Expected<std::vector<InputVStream>> VStreamsBuilder::create_input_vstreams(ConfiguredNetworkGroup &net_group,
+    const std::map<std::string, hailo_vstream_params_t> &inputs_params)
+{
+    auto input_vstream_infos = get_input_vstream_infos_map(net_group);
+    CHECK_EXPECTED(input_vstream_infos);
+
+    std::vector<InputVStream> vstreams;
+    vstreams.reserve(inputs_params.size());
+    for (const auto &name_params_pair : inputs_params) {
+        auto input_stream_ref = net_group.get_input_stream_by_name(name_params_pair.first);
+        CHECK_EXPECTED(input_stream_ref);
+
+        const auto vstream_info = input_vstream_infos->find(name_params_pair.first);
+        CHECK_AS_EXPECTED(vstream_info != input_vstream_infos->end(), HAILO_NOT_FOUND,
+            "Failed to find vstream info of {}", name_params_pair.first);
+
+        const auto vstream_params = expand_vstream_params_autos(vstream_info->second, name_params_pair.second);
+        auto inputs = VStreamsBuilderUtils::create_inputs(input_stream_ref->get(), vstream_info->second, vstream_params);
+        CHECK_EXPECTED(inputs);
+
+        vstreams.insert(vstreams.end(), std::make_move_iterator(inputs->begin()), std::make_move_iterator(inputs->end()));
+    }
+    return vstreams;
+}
+
+static bool is_defused_nms(const hailo_stream_info_t &info)
+{
+    return (HAILO_FORMAT_ORDER_HAILO_NMS == info.format.order && info.nms_info.is_defused);
+}
+
+Expected<std::vector<OutputVStream>> VStreamsBuilder::create_output_vstreams(ConfiguredNetworkGroup &net_group,
+    const std::map<std::string, hailo_vstream_params_t> &outputs_params)
+{
+    std::vector<OutputVStream> vstreams;
+    vstreams.reserve(outputs_params.size());
+    auto output_streams = net_group.get_output_streams_from_vstream_names(outputs_params);
+    CHECK_EXPECTED(output_streams);
+
+    auto output_vstream_infos = get_output_vstream_infos_map(net_group);
+    CHECK_EXPECTED(output_vstream_infos);
+
+    // We iterate through all output streams, and if they are nms, we collect them together by their original stream name.
+    // We need this step because all nms output streams of the same original stream need to be fused together
+    std::map<std::string, std::pair<OutputStreamRefVector, hailo_vstream_params_t>> nms_output_streams;
+    for (auto &stream_params_pair: output_streams.value()) {
+        auto &out_stream = stream_params_pair.first.get();
+        if (is_defused_nms(out_stream.get_info()) &&
+            (outputs_params.end() != outputs_params.find(out_stream.get_info().nms_info.defuse_info.original_name))) {
+                auto original_name = stream_params_pair.first.get().get_info().nms_info.defuse_info.original_name;
+                nms_output_streams.emplace(original_name, std::pair<OutputStreamRefVector, hailo_vstream_params_t>(
+                    OutputStreamRefVector(), outputs_params.at(original_name)));
+                nms_output_streams[original_name].first.push_back(stream_params_pair.first.get());
+        } else {
+            auto outputs = VStreamsBuilderUtils::create_outputs(stream_params_pair.first.get(), stream_params_pair.second, output_vstream_infos.value());
+            CHECK_EXPECTED(outputs);
+            vstreams.insert(vstreams.end(), std::make_move_iterator(outputs->begin()), std::make_move_iterator(outputs->end()));
+        }
+    }
+    for (auto &nms_output_stream_pair : nms_output_streams) {
+        auto outputs = VStreamsBuilderUtils::create_output_nms(nms_output_stream_pair.second.first, nms_output_stream_pair.second.second,
+            output_vstream_infos.value());
+        CHECK_EXPECTED(outputs);
+        vstreams.insert(vstreams.end(), std::make_move_iterator(outputs->begin()), std::make_move_iterator(outputs->end()));
+    }
+    return vstreams;
+}
+
+Expected<std::vector<InputVStream>> VStreamsBuilderUtils::create_inputs(InputStream &input_stream, const hailo_vstream_info_t &vstream_info,
+    const hailo_vstream_params_t &vstream_params)
+{
+    // TODO (HRT-4522): Support this measurement
+    CHECK_AS_EXPECTED(!(vstream_params.vstream_stats_flags & HAILO_VSTREAM_STATS_MEASURE_FPS), HAILO_NOT_IMPLEMENTED,
+        "Pipeline FPS statistics measurement is not implemented");
+
+    std::vector<std::shared_ptr<PipelineElement>> elements;
+    std::vector<InputVStream> vstreams;
+
+    auto network_group_activated_event = input_stream.get_network_group_activated_event();
+
+    auto shutdown_event = Event::create_shared(Event::State::not_signalled);
+    CHECK_AS_EXPECTED(nullptr != shutdown_event, HAILO_OUT_OF_HOST_MEMORY);
+
+    auto pipeline_status = make_shared_nothrow<std::atomic<hailo_status>>(HAILO_SUCCESS);
+    CHECK_AS_EXPECTED(nullptr != pipeline_status, HAILO_OUT_OF_HOST_MEMORY);
+
+    auto pipeline_latency_accumulator = create_pipeline_latency_accumulator(vstream_params);
+    CHECK_EXPECTED(pipeline_latency_accumulator);
+
+    auto user_timeout = std::chrono::milliseconds(vstream_params.timeout_ms);
+
+    auto hw_write_elem = HwWriteElement::create(input_stream,
+        PipelineObject::create_element_name("HwWriteElement", input_stream.name(), input_stream.get_info().index),
+        vstream_params.pipeline_elements_stats_flags, pipeline_status);
+    CHECK_EXPECTED(hw_write_elem);
+    elements.insert(elements.begin(), hw_write_elem.value());
+
+    auto should_transform = InputTransformContext::is_transformation_required(input_stream.get_info().shape, 
+        vstream_params.user_buffer_format, input_stream.get_info().hw_shape, input_stream.get_info().format, 
+        input_stream.get_info().quant_info);
+
+    if (should_transform) {
+        std::shared_ptr<SinkElement> elem_after_post_infer = hw_write_elem.value();
+        auto queue_elem = PushQueueElement::create(
+            PipelineObject::create_element_name("PushQueueElement", input_stream.get_info().name, input_stream.get_info().index),
+            vstream_params, shutdown_event, pipeline_status);
+        CHECK_EXPECTED(queue_elem);
+        elements.insert(elements.begin(), queue_elem.value());
+        CHECK_SUCCESS_AS_EXPECTED(PipelinePad::link_pads(queue_elem.value(), hw_write_elem.value()));
+
+        auto pre_infer_elem = PreInferElement::create(input_stream.get_info().shape, vstream_params.user_buffer_format,
+             input_stream.get_info().hw_shape, input_stream.get_info().format, input_stream.get_info().quant_info, 
+            PipelineObject::create_element_name("PreInferElement", input_stream.get_info().name, input_stream.get_info().index),
+            vstream_params, shutdown_event, pipeline_status);
+        CHECK_EXPECTED(pre_infer_elem);
+        elements.insert(elements.begin(), pre_infer_elem.value());
+        CHECK_SUCCESS_AS_EXPECTED(PipelinePad::link_pads(pre_infer_elem.value(), queue_elem.value()));
+
+        input_stream.set_timeout(user_timeout);
+        auto vstream = InputVStream::create(vstream_info, vstream_params, pre_infer_elem.release(), hw_write_elem.release(), std::move(elements),
+            std::move(pipeline_status), shutdown_event, network_group_activated_event, pipeline_latency_accumulator.release());
+        CHECK_EXPECTED(vstream);
+        vstreams.emplace_back(vstream.release());
+    } else {
+        input_stream.set_timeout(user_timeout);
+        auto vstream = InputVStream::create(vstream_info, vstream_params, hw_write_elem.value(), hw_write_elem.value(), std::move(elements),
+            std::move(pipeline_status), shutdown_event, network_group_activated_event, pipeline_latency_accumulator.release());
+        CHECK_EXPECTED(vstream);
+        vstreams.emplace_back(vstream.release());
+    }
+
+    for (const auto &vstream : vstreams) {
+       LOGGER__INFO("{}", vstream.get_pipeline_description()); 
+    }
+
+    return vstreams;
+}
+
+Expected<std::vector<OutputVStream>> VStreamsBuilderUtils::create_outputs(OutputStream &output_stream,
+    NameToVStreamParamsMap &vstreams_params_map, const std::map<std::string, hailo_vstream_info_t> &output_vstream_infos)
+{
+    std::vector<std::shared_ptr<PipelineElement>> elements;
+    std::vector<OutputVStream> vstreams;
+
+    auto network_group_activated_event = output_stream.get_network_group_activated_event();
+
+    auto shutdown_event = Event::create_shared(Event::State::not_signalled);
+    CHECK_AS_EXPECTED(nullptr != shutdown_event, HAILO_OUT_OF_HOST_MEMORY);
+
+    auto pipeline_status = make_shared_nothrow<std::atomic<hailo_status>>(HAILO_SUCCESS);
+    CHECK_AS_EXPECTED(nullptr != pipeline_status, HAILO_OUT_OF_HOST_MEMORY);
+
+    assert(!vstreams_params_map.empty());
+
+    // Note: In case of multiple values in vstreams_params_map (e.g. in the case of demux), we'll set the
+    //       pipeline_elements_stats_flags for the hw_read_element as bitwise or of all the flags.
+    hailo_pipeline_elem_stats_flags_t hw_read_element_stats_flags = HAILO_PIPELINE_ELEM_STATS_NONE;
+    hailo_vstream_stats_flags_t hw_read_stream_stats_flags = HAILO_VSTREAM_STATS_NONE;
+    size_t buffer_pool_size = 0;
+    for (const auto &elem_name_params : vstreams_params_map) {
+        hw_read_element_stats_flags |= elem_name_params.second.pipeline_elements_stats_flags;
+        hw_read_stream_stats_flags |= elem_name_params.second.vstream_stats_flags;
+        buffer_pool_size += elem_name_params.second.queue_size;
+    }
+
+    // TODO (HRT-4522): Support this measurement
+    CHECK_AS_EXPECTED(!(hw_read_stream_stats_flags & HAILO_VSTREAM_STATS_MEASURE_FPS), HAILO_NOT_IMPLEMENTED,
+        "Pipeline FPS statistics measurement is not implemented");
+
+    auto hw_read_elem = HwReadElement::create(output_stream,
+        PipelineObject::create_element_name("HwReadElement", output_stream.name(), output_stream.get_info().index),
+        HAILO_INFINITE_TIMEOUT, buffer_pool_size, hw_read_element_stats_flags, hw_read_stream_stats_flags, shutdown_event, pipeline_status);
+    CHECK_EXPECTED(hw_read_elem);
+    elements.push_back(hw_read_elem.value());
+
+    if (output_stream.get_info().is_mux) {
+        hailo_status status = add_demux(output_stream, vstreams_params_map, std::move(elements), vstreams, hw_read_elem.value(),
+            shutdown_event, pipeline_status, output_vstream_infos);
+        CHECK_SUCCESS_AS_EXPECTED(status);
+    } else {
+        auto vstream_info = output_vstream_infos.find(output_stream.name());
+        CHECK_AS_EXPECTED(vstream_info != output_vstream_infos.end(), HAILO_NOT_FOUND,
+            "Failed to find vstream info of {}", output_stream.name());
+
+        assert(1 == vstreams_params_map.size());
+        auto vstream_params = expand_vstream_params_autos(vstream_info->second, vstreams_params_map.begin()->second);
+
+        auto pipeline_latency_accumulator = create_pipeline_latency_accumulator(vstream_params);
+        CHECK_EXPECTED(pipeline_latency_accumulator);
+
+        auto should_transform = OutputTransformContext::is_transformation_required(output_stream.get_info().hw_shape, 
+            output_stream.get_info().format, output_stream.get_info().shape, 
+            vstream_params.user_buffer_format, output_stream.get_info().quant_info);
+
+        if (should_transform) {
+            auto hw_read_queue_elem = PullQueueElement::create(
+                PipelineObject::create_element_name("PullQueueElement_hw_read", output_stream.name(), output_stream.get_info().index),
+                vstream_params, shutdown_event, pipeline_status);
+            CHECK_EXPECTED(hw_read_queue_elem);
+            elements.push_back(hw_read_queue_elem.value());
+            CHECK_SUCCESS_AS_EXPECTED(PipelinePad::link_pads(hw_read_elem.value(), hw_read_queue_elem.value()));
+
+            auto post_infer_elem = PostInferElement::create(output_stream.get_info().hw_shape, output_stream.get_info().format, 
+                output_stream.get_info().shape, vstream_params.user_buffer_format, output_stream.get_info().quant_info, output_stream.get_info().nms_info,
+                PipelineObject::create_element_name("PostInferElement", output_stream.name(), output_stream.get_info().index),
+                vstream_params, pipeline_status);
+            CHECK_EXPECTED(post_infer_elem);
+            elements.push_back(post_infer_elem.value());
+            CHECK_SUCCESS_AS_EXPECTED(PipelinePad::link_pads(hw_read_queue_elem.value(), post_infer_elem.value()));
+
+            auto post_infer_queue_elem = UserBufferQueueElement::create(
+                PipelineObject::create_element_name("UserBufferQueueElement_post_infer", output_stream.name(), output_stream.get_info().index),
+                vstream_params, shutdown_event, pipeline_status);
+            CHECK_EXPECTED(post_infer_queue_elem);
+            elements.push_back(post_infer_queue_elem.value());
+            CHECK_SUCCESS_AS_EXPECTED(PipelinePad::link_pads(post_infer_elem.value(), post_infer_queue_elem.value()));
+
+            output_stream.set_timeout(std::chrono::milliseconds(HAILO_INFINITE));
+            hw_read_queue_elem->get()->set_timeout(std::chrono::milliseconds(HAILO_INFINITE));
+            auto vstream = OutputVStream::create(vstream_info->second, vstream_params, post_infer_queue_elem.release(),
+                std::move(elements), std::move(pipeline_status), shutdown_event, network_group_activated_event, pipeline_latency_accumulator.release());
+            CHECK_EXPECTED(vstream);
+            vstreams.emplace_back(vstream.release());
+        } else {
+            output_stream.set_timeout(std::chrono::milliseconds(vstream_params.timeout_ms));
+            auto vstream = OutputVStream::create(vstream_info->second, vstream_params, hw_read_elem.release(), std::move(elements),
+                std::move(pipeline_status), shutdown_event, network_group_activated_event, pipeline_latency_accumulator.release());
+            CHECK_EXPECTED(vstream);
+            vstreams.emplace_back(vstream.release());
+        }
+    }
+
+    for (const auto &vstream : vstreams) {
+        LOGGER__INFO("{}", vstream.get_pipeline_description());
+    }
+
+    return vstreams;
+}
+
+static bool are_formats_equal(const hailo_format_t &format1, const hailo_format_t &format2) {
+    return ((format1.order == format2.order) && (format1.flags == format2.flags) && (format1.type == format2.type));
+}
+
+Expected<std::vector<OutputVStream>> VStreamsBuilderUtils::create_output_nms(OutputStreamRefVector &output_streams,
+    hailo_vstream_params_t vstreams_params,
+    const std::map<std::string, hailo_vstream_info_t> &output_vstream_infos)
+{
+    for (const auto &out_stream : output_streams) {
+        CHECK_AS_EXPECTED(are_formats_equal(output_streams[0].get().get_info().format, out_stream.get().get_info().format),
+            HAILO_INVALID_ARGUMENT, "All nms streams of the same virtual output must have the same format");
+    }
+
+    auto shutdown_event = Event::create_shared(Event::State::not_signalled);
+    CHECK_AS_EXPECTED(nullptr != shutdown_event, HAILO_OUT_OF_HOST_MEMORY);
+
+    auto pipeline_status = make_shared_nothrow<std::atomic<hailo_status>>(HAILO_SUCCESS);
+    CHECK_AS_EXPECTED(nullptr != pipeline_status, HAILO_OUT_OF_HOST_MEMORY);
+
+    std::vector<std::shared_ptr<PipelineElement>> elements;
+    std::vector<OutputVStream> vstreams;
+
+    hailo_status status = add_nms_fuse(output_streams, vstreams_params, elements, vstreams, shutdown_event,
+        pipeline_status, output_vstream_infos);
+    CHECK_SUCCESS_AS_EXPECTED(status);
+
+    for (const auto &vstream : vstreams) {
+        LOGGER__INFO("{}", vstream.get_pipeline_description());
+    }
+
+    return vstreams;
+}
+
+hailo_status VStreamsBuilderUtils::add_demux(OutputStream &output_stream, NameToVStreamParamsMap &vstreams_params_map,
+    std::vector<std::shared_ptr<PipelineElement>> &&base_elements, std::vector<OutputVStream> &vstreams,
+    std::shared_ptr<HwReadElement> hw_read_elem, EventPtr shutdown_event, std::shared_ptr<std::atomic<hailo_status>> pipeline_status,
+    const std::map<std::string, hailo_vstream_info_t> &output_vstream_infos)
+{
+    auto expected_demuxer = OutputDemuxer::create(output_stream);
+    CHECK_EXPECTED_AS_STATUS(expected_demuxer);
+
+    std::shared_ptr<OutputDemuxer> demuxer_ptr = expected_demuxer.release();
+    CHECK(nullptr != demuxer_ptr, HAILO_OUT_OF_HOST_MEMORY);
+
+    auto status = output_stream.set_timeout(HAILO_INFINITE_TIMEOUT);
+    CHECK_SUCCESS(status);
+
+    // Note: In case of multiple values in vstreams_params_map (e.g. in the case of demux), we'll set the
+    //       pipeline_elements_stats_flags for the demux_elem as bitwise or of all the flags.
+    hailo_pipeline_elem_stats_flags_t demux_elem_stats_flags = HAILO_PIPELINE_ELEM_STATS_NONE;
+    hailo_vstream_stats_flags_t demux_vstream_stats_flags = HAILO_VSTREAM_STATS_NONE;
+    size_t buffer_pool_size = 0;
+    for (const auto &elem_name_params : vstreams_params_map) {
+        demux_elem_stats_flags |= elem_name_params.second.pipeline_elements_stats_flags;
+        demux_vstream_stats_flags |= elem_name_params.second.vstream_stats_flags;
+        buffer_pool_size += elem_name_params.second.queue_size;
+    }
+
+    auto demux_elem = TransformDemuxElement::create(demuxer_ptr,
+        PipelineObject::create_element_name("TransformDemuxElement", output_stream.name(), output_stream.get_info().index),
+        std::chrono::milliseconds(HAILO_INFINITE), buffer_pool_size, demux_elem_stats_flags, demux_vstream_stats_flags, shutdown_event, pipeline_status);
+    CHECK_EXPECTED_AS_STATUS(demux_elem);
+    base_elements.push_back(demux_elem.value());
+    CHECK_SUCCESS(PipelinePad::link_pads(hw_read_elem, demux_elem.value()));
+
+    auto network_group_activated_event = output_stream.get_network_group_activated_event();
+
+    uint32_t i = 0;
+    for (auto &edge_info : demuxer_ptr->get_edges_stream_info()) {
+        auto name_params_pair = vstreams_params_map.find(edge_info.name);
+        CHECK(name_params_pair != vstreams_params_map.end(), HAILO_NOT_FOUND,
+            "Failed to find vstreams params of edge {}", edge_info.name);
+
+        const auto vstream_info = output_vstream_infos.find(edge_info.name);
+        CHECK(vstream_info != output_vstream_infos.end(), HAILO_NOT_FOUND,
+            "Failed to find vstream info of {}", edge_info.name);
+
+        const auto vstream_params = expand_vstream_params_autos(vstream_info->second, name_params_pair->second);
+
+        // For each mux vstream, we create a copy of the previous elements
+        auto current_vstream_elements = base_elements;
+
+        // For muxed VStreams we use the same pipeline_status for all
+        auto pipeline_status_copy = pipeline_status;
+        auto demux_queue_elem = PullQueueElement::create(
+            PipelineObject::create_element_name("PullQueueElement_demux", edge_info.name, edge_info.index),
+            vstream_params, shutdown_event, pipeline_status);
+        CHECK_EXPECTED_AS_STATUS(demux_queue_elem);
+        current_vstream_elements.push_back(demux_queue_elem.value());
+        CHECK_SUCCESS(PipelinePad::link_pads(demux_elem.value(), demux_queue_elem.value(), i, 0));
+
+        demux_queue_elem.value()->set_timeout(HAILO_INFINITE_TIMEOUT);
+
+        auto pipeline_latency_accumulator = create_pipeline_latency_accumulator(vstream_params);
+        CHECK_EXPECTED_AS_STATUS(pipeline_latency_accumulator);
+
+        auto should_transform = OutputTransformContext::is_transformation_required(edge_info.hw_shape, 
+            edge_info.format, edge_info.shape, vstream_params.user_buffer_format, edge_info.quant_info);
+
+        if (should_transform) {
+            auto post_infer_elem = PostInferElement::create(edge_info.hw_shape, edge_info.format, 
+                edge_info.shape, vstream_params.user_buffer_format, edge_info.quant_info, edge_info.nms_info,
+                PipelineObject::create_element_name("PostInferElement", edge_info.name, edge_info.index),
+                vstream_params, pipeline_status);
+            CHECK_EXPECTED_AS_STATUS(post_infer_elem);
+            current_vstream_elements.push_back(post_infer_elem.value());
+            CHECK_SUCCESS(PipelinePad::link_pads(demux_queue_elem.value(), post_infer_elem.value()));
+
+            auto post_infer_queue_elem = UserBufferQueueElement::create(
+                PipelineObject::create_element_name("UserBufferQueueElement_post_infer", edge_info.name, edge_info.index),
+                vstream_params, shutdown_event, pipeline_status);
+            CHECK_EXPECTED_AS_STATUS(post_infer_queue_elem);
+            current_vstream_elements.push_back(post_infer_queue_elem.value());
+            CHECK_SUCCESS(PipelinePad::link_pads(post_infer_elem.value(), post_infer_queue_elem.value()));
+
+            auto vstream = OutputVStream::create(vstream_info->second, vstream_params, post_infer_queue_elem.release(),
+                std::move(current_vstream_elements), std::move(pipeline_status_copy), shutdown_event, network_group_activated_event,
+                pipeline_latency_accumulator.release());
+            CHECK_EXPECTED_AS_STATUS(vstream);
+            vstreams.emplace_back(vstream.release());
+        } else {
+            // TODO: HRT-4179
+            auto user_copy_elem = CopyBufferElement::create(
+                PipelineObject::create_element_name("CopyBufferElement", edge_info.name, edge_info.index),
+                pipeline_status);
+            CHECK_EXPECTED_AS_STATUS(user_copy_elem);
+            current_vstream_elements.push_back(user_copy_elem.value());
+            CHECK_SUCCESS(PipelinePad::link_pads(demux_queue_elem.value(), user_copy_elem.value()));
+
+            auto vstream = OutputVStream::create(vstream_info->second, vstream_params, user_copy_elem.release(),
+                std::move(current_vstream_elements), std::move(pipeline_status_copy), shutdown_event, network_group_activated_event,
+                pipeline_latency_accumulator.release());
+            CHECK_EXPECTED_AS_STATUS(vstream);
+            vstreams.emplace_back(vstream.release());
+        }
+        i++;
+    }
+    return HAILO_SUCCESS;
+}
+
+hailo_status VStreamsBuilderUtils::add_nms_fuse(OutputStreamRefVector &output_streams, hailo_vstream_params_t &vstreams_params,
+    std::vector<std::shared_ptr<PipelineElement>> &elements, std::vector<OutputVStream> &vstreams,
+    EventPtr shutdown_event, std::shared_ptr<std::atomic<hailo_status>> pipeline_status,
+    const std::map<std::string, hailo_vstream_info_t> &output_vstream_infos)
+{
+    std::vector<hailo_nms_info_t> nms_infos;
+    nms_infos.reserve(output_streams.size());
+    for (const auto &out_stream : output_streams) {
+        CHECK(out_stream.get().get_info().nms_info.defuse_info.class_group_index <= output_streams.size(),
+            HAILO_INVALID_ARGUMENT, "Not all defused nms outputs were grouped correctly!");
+        nms_infos.emplace_back(out_stream.get().get_info().nms_info);
+    }
+
+    // To get the fused layer name and src stream format, we use the stream info of one of the defuses
+    auto first_defused_stream_info = output_streams[0].get().get_info();
+    auto fused_layer_name = first_defused_stream_info.nms_info.defuse_info.original_name;
+    auto src_stream_format = first_defused_stream_info.format;
+
+    auto vstream_info = output_vstream_infos.find(fused_layer_name);
+    CHECK(vstream_info != output_vstream_infos.end(), HAILO_NOT_FOUND,
+        "Failed to find vstream info of {}", fused_layer_name);
+
+    vstreams_params = expand_vstream_params_autos(vstream_info->second, vstreams_params);
+    auto nms_elem = NmsMuxElement::create(nms_infos,
+        PipelineObject::create_element_name("NmsMuxElement", fused_layer_name, 0),
+        vstreams_params, shutdown_event, pipeline_status);
+    CHECK_EXPECTED_AS_STATUS(nms_elem);
+    auto fused_layer_nms_info = nms_elem.value()->get_fused_nms_info();
+
+    for (uint32_t i = 0; i < output_streams.size(); ++i) {
+        const auto &curr_stream_info = output_streams[i].get().get_info();
+
+        auto hw_read_elem = HwReadElement::create(output_streams[i],
+            PipelineObject::create_element_name("HwReadElement", curr_stream_info.name, curr_stream_info.index),
+            HAILO_INFINITE_TIMEOUT, vstreams_params.queue_size, vstreams_params.pipeline_elements_stats_flags,
+            vstreams_params.vstream_stats_flags, shutdown_event, pipeline_status);
+        CHECK_EXPECTED_AS_STATUS(hw_read_elem);
+        elements.push_back(hw_read_elem.value());
+
+        auto nms_source_queue_elem = PullQueueElement::create(
+            PipelineObject::create_element_name("PullQueueElement_nms_source", curr_stream_info.name, curr_stream_info.index),
+            vstreams_params, shutdown_event, pipeline_status);
+        CHECK_EXPECTED_AS_STATUS(nms_source_queue_elem);
+        elements.push_back(nms_source_queue_elem.value());
+        CHECK_SUCCESS(PipelinePad::link_pads(hw_read_elem.value(), nms_source_queue_elem.value()));
+        CHECK_SUCCESS(PipelinePad::link_pads(nms_source_queue_elem.value(), nms_elem.value(), 0, i));
+    }
+    elements.push_back(nms_elem.value());
+
+    auto pipeline_latency_accumulator = create_pipeline_latency_accumulator(vstreams_params);
+    CHECK_EXPECTED_AS_STATUS(pipeline_latency_accumulator);
+
+    auto should_transform = OutputTransformContext::is_transformation_required({}, src_stream_format, {},
+        vstreams_params.user_buffer_format, vstream_info->second.quant_info);
+
+    if (should_transform) {
+        auto nms_queue_elem = PullQueueElement::create(
+            PipelineObject::create_element_name("PullQueueElement_nms", fused_layer_name, 0),
+            vstreams_params, shutdown_event, pipeline_status);
+        CHECK_EXPECTED_AS_STATUS(nms_queue_elem);
+        elements.push_back(nms_queue_elem.value());
+        CHECK_SUCCESS(PipelinePad::link_pads(nms_elem.value(), nms_queue_elem.value()));
+
+        auto post_infer_elem = PostInferElement::create({}, src_stream_format,
+            {}, vstreams_params.user_buffer_format, vstream_info->second.quant_info, fused_layer_nms_info,
+            PipelineObject::create_element_name("PostInferElement", fused_layer_name, 0), vstreams_params, pipeline_status);
+        CHECK_EXPECTED_AS_STATUS(post_infer_elem);
+
+        elements.push_back(post_infer_elem.value());
+        CHECK_SUCCESS(PipelinePad::link_pads(nms_queue_elem.value(), post_infer_elem.value()));
+
+        auto post_infer_queue_elem = UserBufferQueueElement::create(
+            PipelineObject::create_element_name("UserBufferQueueElement_post_infer", fused_layer_name, 0),
+            vstreams_params, shutdown_event, pipeline_status);
+        CHECK_EXPECTED_AS_STATUS(post_infer_queue_elem);
+        elements.push_back(post_infer_queue_elem.value());
+        CHECK_SUCCESS(PipelinePad::link_pads(post_infer_elem.value(), post_infer_queue_elem.value()));
+
+        auto vstream = OutputVStream::create(vstream_info->second, vstreams_params, post_infer_queue_elem.release(),
+                std::move(elements), std::move(pipeline_status), shutdown_event, output_streams[0].get().get_network_group_activated_event(),
+                pipeline_latency_accumulator.release());
+        CHECK_EXPECTED_AS_STATUS(vstream);
+        vstreams.emplace_back(vstream.release());
+    } else {
+        auto vstream = OutputVStream::create(vstream_info->second, vstreams_params, nms_elem.release(),
+                std::move(elements), std::move(pipeline_status), shutdown_event, output_streams[0].get().get_network_group_activated_event(),
+                pipeline_latency_accumulator.release());
+        CHECK_EXPECTED_AS_STATUS(vstream);
+        vstreams.emplace_back(vstream.release());
+    }
+
+    return HAILO_SUCCESS;
+}
+
+Expected<AccumulatorPtr> VStreamsBuilderUtils::create_pipeline_latency_accumulator(const hailo_vstream_params_t &vstreams_params)
+{
+    AccumulatorPtr pipeline_latency_accumulator = nullptr;
+    const auto measure_latency = ((vstreams_params.vstream_stats_flags & HAILO_VSTREAM_STATS_MEASURE_LATENCY) != 0);
+    if (measure_latency) {
+        pipeline_latency_accumulator = make_shared_nothrow<FullAccumulator<double>>("latency");
+        CHECK_AS_EXPECTED(nullptr != pipeline_latency_accumulator, HAILO_OUT_OF_HOST_MEMORY);
+    }
+
+    return pipeline_latency_accumulator;
+}
+
+} /* namespace hailort */
diff --git a/hailort/libhailort/src/vstream_internal.hpp b/hailort/libhailort/src/vstream_internal.hpp
new file mode 100644 (file)
index 0000000..64d7544
--- /dev/null
@@ -0,0 +1,204 @@
+/**
+ * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved.
+ * Distributed under the MIT license (https://opensource.org/licenses/MIT)
+ **/
+/**
+ * @file vstream.hpp
+ * @brief Virtual Stream
+ **/
+
+#ifndef _HAILO_VSTREAM_INTERNAL_HPP_
+#define _HAILO_VSTREAM_INTERNAL_HPP_
+
+#include "pipeline.hpp"
+#include "hailo/transform.hpp"
+#include "hailo/stream.hpp"
+#include "hailo/network_group.hpp"
+
+namespace hailort
+{
+
+class PreInferElement : public FilterElement
+{
+public:
+    static Expected<std::shared_ptr<PreInferElement>> create(const hailo_3d_image_shape_t &src_image_shape, const hailo_format_t &src_format,
+        const hailo_3d_image_shape_t &dst_image_shape, const hailo_format_t &dst_format, const hailo_quant_info_t &dst_quant_info,
+        const std::string &name, std::chrono::milliseconds timeout, size_t buffer_pool_size, hailo_pipeline_elem_stats_flags_t elem_flags,
+        hailo_vstream_stats_flags_t vstream_flags, EventPtr shutdown_event, std::shared_ptr<std::atomic<hailo_status>> pipeline_status);
+    static Expected<std::shared_ptr<PreInferElement>> create(const hailo_3d_image_shape_t &src_image_shape, const hailo_format_t &src_format,
+        const hailo_3d_image_shape_t &dst_image_shape, const hailo_format_t &dst_format, const hailo_quant_info_t &dst_quant_info, const std::string &name,
+        const hailo_vstream_params_t &vstream_params, EventPtr shutdown_event, std::shared_ptr<std::atomic<hailo_status>> pipeline_status);
+    PreInferElement(std::unique_ptr<InputTransformContext> &&transform_context, BufferPoolPtr buffer_pool,
+        const std::string &name, std::chrono::milliseconds timeout, DurationCollector &&duration_collector,
+        std::shared_ptr<std::atomic<hailo_status>> &&pipeline_status);
+    virtual ~PreInferElement() = default;
+
+    virtual Expected<PipelineBuffer> run_pull(PipelineBuffer &&optional, const PipelinePad &source) override;
+    virtual std::vector<AccumulatorPtr> get_queue_size_accumulators() override;
+    virtual PipelinePad &next_pad() override;
+    virtual std::string description() const override;
+
+protected:
+    virtual Expected<PipelineBuffer> action(PipelineBuffer &&input, PipelineBuffer &&optional) override;
+
+private:
+    std::unique_ptr<InputTransformContext> m_transform_context;
+    BufferPoolPtr m_pool;
+    std::chrono::milliseconds m_timeout;
+};
+
+class PostInferElement : public FilterElement
+{
+public:
+    static Expected<std::shared_ptr<PostInferElement>> create(const hailo_3d_image_shape_t &src_image_shape,
+        const hailo_format_t &src_format, const hailo_3d_image_shape_t &dst_image_shape, const hailo_format_t &dst_format,
+        const hailo_quant_info_t &dst_quant_info, const hailo_nms_info_t &nms_info, const std::string &name,
+        hailo_pipeline_elem_stats_flags_t elem_flags, std::shared_ptr<std::atomic<hailo_status>> pipeline_status);
+    static Expected<std::shared_ptr<PostInferElement>> create(const hailo_3d_image_shape_t &src_image_shape, const hailo_format_t &src_format,
+        const hailo_3d_image_shape_t &dst_image_shape, const hailo_format_t &dst_format, const hailo_quant_info_t &dst_quant_info, const hailo_nms_info_t &nms_info,
+        const std::string &name, const hailo_vstream_params_t &vstream_params, std::shared_ptr<std::atomic<hailo_status>> pipeline_status);
+    PostInferElement(std::unique_ptr<OutputTransformContext> &&transform_context, const std::string &name,
+        DurationCollector &&duration_collector, std::shared_ptr<std::atomic<hailo_status>> &&pipeline_status);
+    virtual ~PostInferElement() = default;
+    virtual hailo_status run_push(PipelineBuffer &&buffer) override;
+    virtual PipelinePad &next_pad() override;
+    virtual std::string description() const override;
+
+protected:
+    virtual Expected<PipelineBuffer> action(PipelineBuffer &&input, PipelineBuffer &&optional) override;
+
+private:
+    std::unique_ptr<OutputTransformContext> m_transform_context;
+};
+
+class NmsMuxElement : public BaseMuxElement
+{
+public:
+    static Expected<std::shared_ptr<NmsMuxElement>> create(const std::vector<hailo_nms_info_t> &nms_infos,
+        const std::string &name, std::chrono::milliseconds timeout, size_t buffer_pool_size, hailo_pipeline_elem_stats_flags_t elem_flags,
+        hailo_vstream_stats_flags_t vstream_flags, EventPtr shutdown_event, std::shared_ptr<std::atomic<hailo_status>> pipeline_status);
+    static Expected<std::shared_ptr<NmsMuxElement>> create(const std::vector<hailo_nms_info_t> &nms_infos, const std::string &name,
+        const hailo_vstream_params_t &vstream_params, EventPtr shutdown_event, std::shared_ptr<std::atomic<hailo_status>> pipeline_status);
+    NmsMuxElement(const std::vector<hailo_nms_info_t> &nms_infos, const hailo_nms_info_t &fused_nms_info, BufferPoolPtr &&pool, const std::string &name,
+        std::chrono::milliseconds timeout, DurationCollector &&duration_collector, std::shared_ptr<std::atomic<hailo_status>> &&pipeline_status);
+    const hailo_nms_info_t &get_fused_nms_info() const;
+
+    virtual std::vector<AccumulatorPtr> get_queue_size_accumulators() override;
+
+protected:
+    virtual Expected<PipelineBuffer> action(std::vector<PipelineBuffer>  &&inputs, PipelineBuffer &&optional) override;
+
+private:
+    std::vector<hailo_nms_info_t> m_nms_infos;
+    hailo_nms_info_t m_fused_nms_info;
+    BufferPoolPtr m_pool;
+};
+
+class TransformDemuxElement : public BaseDemuxElement
+{
+public:
+    static Expected<std::shared_ptr<TransformDemuxElement>> create(std::shared_ptr<OutputDemuxer> demuxer,
+        const std::string &name, std::chrono::milliseconds timeout, size_t buffer_pool_size, hailo_pipeline_elem_stats_flags_t elem_flags,
+        hailo_vstream_stats_flags_t vstream_flags, EventPtr shutdown_event, std::shared_ptr<std::atomic<hailo_status>> pipeline_status);
+    TransformDemuxElement(std::shared_ptr<OutputDemuxer> demuxer, std::vector<BufferPoolPtr> &&pools, const std::string &name,
+        std::chrono::milliseconds timeout, DurationCollector &&duration_collector, std::shared_ptr<std::atomic<hailo_status>> &&pipeline_status);
+
+    virtual std::vector<AccumulatorPtr> get_queue_size_accumulators() override;
+
+protected:
+    virtual Expected<std::vector<PipelineBuffer>> action(PipelineBuffer &&input) override;
+
+private:
+    std::shared_ptr<OutputDemuxer> m_demuxer;
+    std::vector<BufferPoolPtr> m_pools;
+};
+
+class HwReadElement : public SourceElement
+{
+public:
+    static Expected<std::shared_ptr<HwReadElement>> create(OutputStream &stream, const std::string &name, std::chrono::milliseconds timeout,
+        size_t buffer_pool_size, hailo_pipeline_elem_stats_flags_t elem_flags, hailo_vstream_stats_flags_t vstream_flags, EventPtr shutdown_event,
+        std::shared_ptr<std::atomic<hailo_status>> pipeline_status);
+    HwReadElement(OutputStream &stream, BufferPoolPtr buffer_pool, const std::string &name, std::chrono::milliseconds timeout,
+        DurationCollector &&duration_collector, EventPtr shutdown_event, std::shared_ptr<std::atomic<hailo_status>> &&pipeline_status);
+    virtual ~HwReadElement() = default;
+
+    virtual std::vector<AccumulatorPtr> get_queue_size_accumulators() override;
+
+    virtual hailo_status run_push(PipelineBuffer &&buffer) override;
+    virtual Expected<PipelineBuffer> run_pull(PipelineBuffer &&optional, const PipelinePad &source) override;
+    virtual hailo_status activate() override;
+    virtual hailo_status deactivate() override;
+    virtual hailo_status post_deactivate() override;
+    virtual hailo_status clear() override;
+    virtual hailo_status flush() override;
+    uint32_t get_invalid_frames_count();
+    virtual std::string description() const override;
+
+private:
+    OutputStream &m_stream;
+    BufferPoolPtr m_pool;
+    std::chrono::milliseconds m_timeout;
+    EventPtr m_shutdown_event;
+    WaitOrShutdown m_activation_wait_or_shutdown;
+};
+
+class HwWriteElement : public SinkElement
+{
+public:
+    static Expected<std::shared_ptr<HwWriteElement>> create(InputStream &stream, const std::string &name,
+        hailo_pipeline_elem_stats_flags_t elem_flags, std::shared_ptr<std::atomic<hailo_status>> pipeline_status);
+    HwWriteElement(InputStream &stream, const std::string &name, DurationCollector &&duration_collector,
+        std::shared_ptr<std::atomic<hailo_status>> &&pipeline_status, EventPtr got_flush_event);
+    virtual ~HwWriteElement() = default;
+
+    virtual hailo_status run_push(PipelineBuffer &&buffer) override;
+    virtual Expected<PipelineBuffer> run_pull(PipelineBuffer &&optional, const PipelinePad &source) override;
+    virtual hailo_status activate() override;
+    virtual hailo_status deactivate() override;
+    virtual hailo_status post_deactivate() override;
+    virtual hailo_status clear() override;
+    virtual hailo_status flush() override;
+    virtual std::string description() const override;
+
+private:
+    InputStream &m_stream;
+    EventPtr m_got_flush_event;
+};
+
+class CopyBufferElement : public FilterElement
+{
+public:
+    static Expected<std::shared_ptr<CopyBufferElement>> create(const std::string &name, std::shared_ptr<std::atomic<hailo_status>> pipeline_status);
+    CopyBufferElement(const std::string &name, DurationCollector &&duration_collector, std::shared_ptr<std::atomic<hailo_status>> pipeline_status);
+    virtual ~CopyBufferElement() = default;
+    virtual PipelinePad &next_pad() override;
+
+protected:
+    virtual Expected<PipelineBuffer> action(PipelineBuffer &&input, PipelineBuffer &&optional) override;
+};
+
+class VStreamsBuilderUtils
+{
+public:
+    static Expected<std::vector<InputVStream>> create_inputs(InputStream &input_stream, const hailo_vstream_info_t &input_vstream_infos,
+        const hailo_vstream_params_t &vstreams_params);
+    static Expected<std::vector<OutputVStream>> create_outputs(OutputStream &output_stream,
+        NameToVStreamParamsMap &vstreams_params_map, const std::map<std::string, hailo_vstream_info_t> &output_vstream_infos);
+    static Expected<std::vector<OutputVStream>> create_output_nms(OutputStreamRefVector &output_streams,
+        hailo_vstream_params_t vstreams_params,
+        const std::map<std::string, hailo_vstream_info_t> &output_vstream_infos);
+    static hailo_status add_demux(OutputStream &output_stream, NameToVStreamParamsMap &vstreams_params_map,
+        std::vector<std::shared_ptr<PipelineElement>> &&elements, std::vector<OutputVStream> &vstreams,
+        std::shared_ptr<HwReadElement> hw_read_elem, EventPtr shutdown_event, std::shared_ptr<std::atomic<hailo_status>> pipeline_status,
+        const std::map<std::string, hailo_vstream_info_t> &output_vstream_infos);
+    static hailo_status add_nms_fuse(OutputStreamRefVector &output_streams, hailo_vstream_params_t &vstreams_params,
+        std::vector<std::shared_ptr<PipelineElement>> &elements, std::vector<OutputVStream> &vstreams,
+        EventPtr shutdown_event, std::shared_ptr<std::atomic<hailo_status>> pipeline_status,
+        const std::map<std::string, hailo_vstream_info_t> &output_vstream_infos);
+    static Expected<AccumulatorPtr> create_pipeline_latency_accumulator(const hailo_vstream_params_t &vstreams_params);
+};
+
+} /* namespace hailort */
+
+#endif /* _HAILO_VSTREAM_INTERNAL_HPP_ */
diff --git a/hailort/pre_build/CMakeLists.txt b/hailort/pre_build/CMakeLists.txt
new file mode 100644 (file)
index 0000000..6eb2276
--- /dev/null
@@ -0,0 +1,14 @@
+cmake_minimum_required(VERSION 3.0.0)
+project(hailort_prebuild)
+
+set(HAILO_PRE_BUILD_EXTERNAL_DIR ${CMAKE_CURRENT_LIST_DIR}/external)
+include(../libhailort/cmake/execute_cmake.cmake)
+message("Downloading dependencies to ${HAILO_EXTERNAL_DIR} ...")
+execute_cmake(
+    SOURCE_DIR "${HAILO_PRE_BUILD_EXTERNAL_DIR}"
+    BUILD_DIR "${HAILO_PRE_BUILD_EXTERNAL_DIR}/build"
+    CONFIGURE_ARGS "-DHAILO_EXTERNAL_DIR=${HAILO_EXTERNAL_DIR}"
+)
+message("Finished downloading dependencies")
+
+add_subdirectory(tools)
\ No newline at end of file
diff --git a/hailort/pre_build/external/CMakeLists.txt b/hailort/pre_build/external/CMakeLists.txt
new file mode 100644 (file)
index 0000000..11c34b0
--- /dev/null
@@ -0,0 +1,27 @@
+cmake_minimum_required(VERSION 3.0.0)
+project(hailort_prebuild_external)
+
+include(ExternalProject)
+
+function(git_clone proj repo tag)
+    ExternalProject_Add(git_clone_${proj}
+        GIT_REPOSITORY  ${repo}
+        GIT_TAG         ${tag}
+        # GIT_SHALLOW     TRUE
+        SOURCE_DIR      ${HAILO_EXTERNAL_DIR}/${proj}
+        CONFIGURE_COMMAND ""
+        BUILD_COMMAND ""
+        INSTALL_COMMAND ""
+        TEST_COMMAND ""
+    )
+endfunction()
+
+git_clone(pybind11          https://github.com/pybind/pybind11.git                                  502ffe50a9f22c04637bbc8ec0019488458ba948)
+git_clone(Catch2            https://github.com/catchorg/Catch2.git                                  c4e3767e265808590986d5db6ca1b5532a7f3d13)
+git_clone(CLI11             https://github.com/CLIUtils/CLI11.git                                   706b14fb14444e68ace232eb9a0ef106bf99343b)
+git_clone(spdlog            https://github.com/gabime/spdlog.git                                    e2789531912a5c6ab28a90387f97c52963eec08a)
+git_clone(protobuf          https://github.com/protocolbuffers/protobuf.git                         d0bfd5221182da1a7cc280f3337b5e41a89539cf)
+git_clone(readerwriterqueue https://github.com/cameron314/readerwriterqueue.git                     435e36540e306cac40fcfeab8cc0a22d48464509)
+git_clone(json              https://github.com/ArthurSonzogni/nlohmann_json_cmake_fetchcontent.git  391786c6c3abdd3eeb993a3154f1f2a4cfe137a0)
+git_clone(DotWriter         https://github.com/hailo-ai/DotWriter.git                               e5fa8f281adca10dd342b1d32e981499b8681daf)
+git_clone(benchmark         https://github.com/google/benchmark.git                                 f91b6b42b1b9854772a90ae9501464a161707d1e)
\ No newline at end of file
diff --git a/hailort/pre_build/tools/CMakeLists.txt b/hailort/pre_build/tools/CMakeLists.txt
new file mode 100644 (file)
index 0000000..1bb7f3f
--- /dev/null
@@ -0,0 +1,10 @@
+cmake_minimum_required(VERSION 3.0.0)
+
+message(STATUS "Building protobuf::protoc...")
+if(NOT protobuf_BUILD_TESTS)
+    set(protobuf_BUILD_TESTS OFF CACHE BOOL "Build protobuf tests")
+endif()
+if(MSVC AND NOT protobuf_MSVC_STATIC_RUNTIME)
+    set(protobuf_MSVC_STATIC_RUNTIME OFF CACHE BOOL "Protobuf MSVC static runtime")
+endif()
+add_subdirectory(${HAILO_EXTERNAL_DIR}/protobuf/cmake build_protoc)
\ No newline at end of file
diff --git a/hailort/scripts/configure_ethernet_buffers.sh b/hailort/scripts/configure_ethernet_buffers.sh
new file mode 100755 (executable)
index 0000000..e2b8204
--- /dev/null
@@ -0,0 +1,89 @@
+#!/usr/bin/env bash
+
+# Setting the script to exit when command returns a non-zero status
+set -e
+
+declare -A increase_target_configurations=(
+    ["rmem_max"]=26214400
+    ["rmem_default"]=212992
+    ["netdev_max_backlog"]=5000
+)
+
+declare -A decrease_target_configurations=(
+    ["wmem_max"]=212992
+    ["wmem_default"]=212992
+)
+
+function validate_interface_exists() {
+    if [ -z $1 ] ; then
+        echo "Usage: $0 interface"
+        return 1
+    fi
+    if ip a show $1 up > /dev/null 2>&1; then
+        echo "Working on interface $1"
+        return 0
+    else
+        echo "Interface $1 not found"
+        return 2
+    fi
+}
+
+function update_kernel_network_parameters() {
+    echo "Updating kernel parameters. Resetting default values back in case someone changed them by accident"
+    # These values should be increased if they are lower than expected
+    for target_configuration in "${!increase_target_configurations[@]}"; do
+        actual_configuration_value=`sysctl -b net.core.$target_configuration`
+        target_configuration_value=${increase_target_configurations[$target_configuration]}
+        if [[ $actual_configuration_value -gt $target_configuration_value ]]; then
+            echo "Skipping $target_configuration, it is already configured to a higher value ($actual_configuration_value) than target ($target_configuration_value)";
+            continue
+        fi
+        sysctl -w net.core.$target_configuration=${increase_target_configurations[$target_configuration]}
+    done
+
+    # These values should be decreased if they are higher than expected
+    for target_configuration in "${!decrease_target_configurations[@]}"; do
+        actual_configuration_value=`sysctl -b net.core.$target_configuration`
+        target_configuration_value=${decrease_target_configurations[$target_configuration]}
+        if [[ $actual_configuration_value -lt $target_configuration_value ]]; then
+            echo "Skipping $target_configuration, it is already configured to a lower value ($actual_configuration_value) than target ($target_configuration_value)";
+            continue
+        fi
+        sysctl -w net.core.$target_configuration=${decrease_target_configurations[$target_configuration]}
+    done
+}
+
+function validate_ring_parameters_can_be_modified() {
+    if ethtool -g $1 > /dev/null 2>&1; then
+        return 0;
+    else
+        echo "NIC for interface $1 doesn't support modifying buffer sizes."
+        echo "Successfully finished configuring interface $1 to the best of our ability"
+        return 1
+    fi
+}
+
+function update_network_driver_configuration() {
+    # Make sure the ring parameters can be modified
+    validate_ring_parameters_can_be_modified "$1"
+
+    CURRENT_RX_VALUE=$(ethtool -g $1 | grep "RX:" | cut -f 3 | sed -n 2p)
+    EXPECTED_RX_VALUE=$(ethtool -g $1 | grep "RX:" | cut -f 3 | sed -n 1p)
+
+    if [[ ${CURRENT_RX_VALUE} -ne ${EXPECTED_RX_VALUE} ]]; then
+       echo "Updating RX ring size to $EXPECTED_RX_VALUE"
+       ethtool -G $1 rx $EXPECTED_RX_VALUE
+    else
+       echo "RX ring size is $EXPECTED_RX_VALUE. No change is required"
+    fi
+}
+
+function update_ethernet_configuration() {
+    validate_interface_exists "$@"
+    INTERFACE=$1
+    update_kernel_network_parameters
+    update_network_driver_configuration $INTERFACE
+    echo "Successfully finished configuring interface $INTERFACE"
+}
+
+(update_ethernet_configuration "$@")
diff --git a/hailort/scripts/configure_interface.sh b/hailort/scripts/configure_interface.sh
new file mode 100755 (executable)
index 0000000..9e4996d
--- /dev/null
@@ -0,0 +1,68 @@
+#!/usr/bin/env bash
+
+# Setting the script to exit when command returns a non-zero status
+set -e
+# Default interface IP
+INTERFACE_IP="10.0.0.50/24"
+
+function parse_commandline_arguments() {
+    if [ -z $1 ] ; then
+        echo "Usage: $0 interface [-i|--ip INTERFACE_IP]"
+        return 1
+    fi
+
+    INTERFACE_NAME=$1
+    # Shifting since we already processed the interface name
+    shift
+
+    while [[ "$#" -gt 0 ]]; do
+        case $1 in
+            -i|--ip) INTERFACE_IP=$2; shift ;;
+            *) echo "Unknown parameter passed: $1"; exit 1;;
+        esac
+        shift
+    done
+}
+
+function validate_interface_exists() {
+    if ip a show $1 up > /dev/null 2>&1; then
+        echo "Configuring interface $1"
+        return 0
+    else
+        echo "Interface $1 not found"
+        return 2
+    fi
+}
+
+function set_interface_ip() {
+    # Piping to true because we don't care about the return value here
+    CURRENT_INTERFACE_IP=`ip -4 addr show dev $INTERFACE | grep -oP '(?<=inet\s)\d+(\.\d+){3}/\d+' || true`
+    # The interface might have multiple addresses, so we'll check if the address to set is one of them
+    if [[ $CURRENT_INTERFACE_IP = *$INTERFACE_IP* ]]; then
+        echo "Skipping setting an IP address since it is already configured"
+        return 0
+    fi
+    echo "Configuring $INTERFACE IP to: $INTERFACE_IP"
+    sudo ip -4 address add dev $INTERFACE $INTERFACE_IP
+}
+
+
+function configure_interface() {
+    parse_commandline_arguments "$@"
+    validate_interface_exists $INTERFACE_NAME
+    INTERFACE=$INTERFACE_NAME
+
+    # Shutting down the interface in order for kernel parameter changes to register
+    sudo ip link set $INTERFACE down
+    sudo sysctl -w net.ipv6.conf.$INTERFACE.disable_ipv6=1
+    sudo sysctl -w net.ipv6.conf.$INTERFACE.autoconf=0
+
+    set_interface_ip
+    # After we had finished configuration, turn the interface back on
+    sudo ip link set $INTERFACE up
+
+    echo "interface $INTERFACE configured successfully"
+}
+
+configure_interface "$@"
+
diff --git a/hailort/scripts/download_hefs.cmd b/hailort/scripts/download_hefs.cmd
new file mode 100644 (file)
index 0000000..aea8c58
--- /dev/null
@@ -0,0 +1,20 @@
+:: cmd
+@ECHO OFF
+set BASE_URI=https://hailo-hailort.s3.eu-west-2.amazonaws.com
+set HRT_VERSION=4.6.0
+set REMOTE_HEF_DIR=Hailo8/%HRT_VERSION%/HEFS
+set LOCAL_EXAMPLES_HEF_DIR=..\libhailort\examples\hefs
+set LOCAL_TUTORIALS_HEF_DIR=..\libhailort\bindings\python\platform\tutorials\hefs
+set EXAMPLES_HEFS=(multi_network_shortcut_net.hef shortcut_net.hef)
+set TUTORIALS_HEFS=(resnet_v1_18.hef)
+
+if not exist %LOCAL_EXAMPLES_HEF_DIR% mkdir %LOCAL_EXAMPLES_HEF_DIR%
+if not exist %LOCAL_TUTORIALS_HEF_DIR% mkdir %LOCAL_TUTORIALS_HEF_DIR%
+
+ECHO Downloading HEFs from S3
+(for %%h in %EXAMPLES_HEFS% do (
+       powershell -c "wget %BASE_URI%/%REMOTE_HEF_DIR%/%%h -outfile %LOCAL_EXAMPLES_HEF_DIR%\%%h"
+))
+(for %%h in %TUTORIALS_HEFS% do (
+       powershell -c "wget %BASE_URI%/%REMOTE_HEF_DIR%/%%h -outfile %LOCAL_TUTORIALS_HEF_DIR%\%%h"
+))
diff --git a/hailort/scripts/download_hefs.sh b/hailort/scripts/download_hefs.sh
new file mode 100755 (executable)
index 0000000..9431218
--- /dev/null
@@ -0,0 +1,39 @@
+#!/bin/bash
+set -e
+
+readonly BASE_URI="https://hailo-hailort.s3.eu-west-2.amazonaws.com"
+readonly HRT_VERSION=4.6.0
+readonly REMOTE_HEF_DIR="Hailo8/${HRT_VERSION}/HEFS"
+readonly LOCAL_EXAMPLES_HEF_DIR="../libhailort/examples/hefs"
+readonly LOCAL_TUTORIALS_HEF_DIR="../libhailort/bindings/python/platform/tutorials/hefs/"
+readonly EXAMPLES_HEFS=(
+    "shortcut_net.hef"
+    "multi_network_shortcut_net.hef"
+)
+readonly TUTORIALS_HEFS=(
+    "resnet_v1_18.hef"
+)
+
+function create_hef_dir(){
+    for d in $LOCAL_EXAMPLES_HEF_DIR $LOCAL_TUTORIALS_HEF_DIR; do
+        if ! [ -d ${d} ]; then
+            mkdir ${d}
+        fi
+    done
+}
+
+function download_hefs(){
+    for hef in "${EXAMPLES_HEFS[@]}"; do
+        wget -N ${BASE_URI}/${REMOTE_HEF_DIR}/${hef} -O ${LOCAL_EXAMPLES_HEF_DIR}/${hef}
+    done
+    for hef in "${TUTORIALS_HEFS[@]}"; do
+        wget -N ${BASE_URI}/${REMOTE_HEF_DIR}/${hef} -O ${LOCAL_TUTORIALS_HEF_DIR}/${hef}
+    done
+}
+
+function main(){
+    create_hef_dir
+    download_hefs
+}
+
+main
diff --git a/hailort/scripts/ubuntu_ethernet_statistics.sh b/hailort/scripts/ubuntu_ethernet_statistics.sh
new file mode 100755 (executable)
index 0000000..17c6ae3
--- /dev/null
@@ -0,0 +1,35 @@
+#!/usr/bin/env bash
+
+# Set bash to exit if function fails
+set -e
+
+function validate_interface_exists() {
+    if [[ -z $1 ]] ; then
+        echo "Usage: $0 interface_name"
+        return 1
+    fi
+    if ip a show $1 up > /dev/null 2>&1; then
+        echo "Working on interface $1"
+        return 0
+    else
+        echo "Interface $1 not found"
+        return 2
+    fi
+}
+
+
+function main() {
+    validate_interface_exists "$@"
+
+    echo
+    echo "Network stack errors"
+    nstat -az | grep "Udp.*.Errors\|Discards" | awk 'NF{NF--};1'
+
+
+    echo
+    echo "Driver rx & tx errors"
+    ethtool -S $1 | grep -G ".*.err\|.*.fail\|.*.drop\|.*.miss" | awk '{$1=$1};1'
+}
+
+
+main "$@"