From 3506da8c3f1b121d103d5a9cc6458d99135c06e5 Mon Sep 17 00:00:00 2001 From: HailoRT-Automation Date: Thu, 30 Jun 2022 17:21:15 +0300 Subject: [PATCH] v4.8.0 --- .hailort.png | Bin 0 -> 146846 bytes .logo.svg | 212 -- CMakeLists.txt | 50 +- README.md | 2 +- common/include/context_switch_defs.h | 66 +- common/include/control_protocol.h | 80 +- common/include/firmware_status.h | 40 + hailort/CMakeLists.txt | 65 +- hailort/LICENSE-3RD-PARTY.md | 6 +- hailort/common/circular_buffer.hpp | 5 + hailort/common/latency_meter.hpp | 1 + hailort/common/os/posix/filesystem.cpp | 2 +- hailort/common/utils.hpp | 30 + hailort/drivers/common/hailo_ioctl_common.h | 66 +- hailort/drivers/win/include/Public.h | 124 + .../drivers/win/include/hailo_pcie_version.h | 10 + hailort/hailortcli/CMakeLists.txt | 10 +- hailort/hailortcli/benchmark_command.cpp | 13 +- hailort/hailortcli/benchmark_command.hpp | 1 - hailort/hailortcli/command.cpp | 37 +- hailort/hailortcli/command.hpp | 18 +- .../download_action_list_command.cpp | 27 +- .../download_action_list_command.hpp | 8 +- .../example_config.json | 0 hailort/hailortcli/fw_control_command.cpp | 12 +- hailort/hailortcli/fw_control_command.hpp | 2 - hailort/hailortcli/hailortcli.cpp | 39 +- hailort/hailortcli/hailortcli.hpp | 2 +- .../hailortcli/power_measurement_command.cpp | 15 +- hailort/hailortcli/run_command.cpp | 64 +- hailort/hailortcli/run_command.hpp | 3 +- hailort/hailortcli/scan_command.cpp | 25 +- hailort/libhailort/CMakeLists.txt | 2 +- hailort/libhailort/bindings/CMakeLists.txt | 1 + .../bindings/gstreamer/CMakeLists.txt | 5 +- .../gstreamer/cmake/FindHailoRT.cmake | 28 - .../bindings/gstreamer/gst-hailo/common.hpp | 3 + .../gst-hailo/gsthailodevicestats.cpp | 7 +- .../gstreamer/gst-hailo/gsthailonet.cpp | 167 +- .../gstreamer/gst-hailo/gsthailonet.hpp | 17 +- .../gstreamer/gst-hailo/gsthailorecv.cpp | 6 +- .../metadata/hailo_buffer_flag_meta.cpp | 3 +- .../gst-hailo/metadata/tensor_meta.cpp | 3 +- .../gst-hailo/network_group_handle.cpp | 87 +- .../gst-hailo/network_group_handle.hpp | 35 +- .../libhailort/bindings/python/CMakeLists.txt | 8 + .../examples/hef_infer_pipeline_vstream.py | 4 +- .../platform/hailo_platform/__init__.py | 57 +- .../common/compatibility/__init__.py | 27 - .../common/paths_manager/config.py | 52 - .../common/paths_manager/paths.py | 207 -- .../common/paths_manager/version.py | 15 - .../common/targets/inference_targets.py | 111 - .../hailo_platform/common/tools/__init__.py | 0 .../common/tools/cmd_utils/__init__.py | 0 .../hailo_platform/drivers/__init__.py | 6 +- .../hailo_platform/drivers/control_object.py | 656 +---- .../hailo_platform/drivers/ethernet_utils.py | 67 +- .../hailo_control_protocol.py | 331 +-- .../drivers/hailo_controller/i2c_slaves.py | 191 +- .../hailo_controller/power_measurement.py | 12 +- .../drivers/hailort/pyhailort.py | 2124 +---------------- .../hailo_platform/drivers/hw_object.py | 466 +--- .../hailo_platform/paths_manager/__init__.py | 0 .../hailo_platform/paths_manager/paths.py | 10 - .../paths_manager => pyhailort}/__init__.py | 0 .../pyhailort/control_object.py | 745 ++++++ .../pyhailort/ethernet_utils.py | 66 + .../pyhailort/hailo_control_protocol.py | 323 +++ .../hailo_platform/pyhailort/hw_object.py | 478 ++++ .../hailo_platform/pyhailort/i2c_slaves.py | 191 ++ .../pyhailort/power_measurement.py | 14 + .../hailo_platform/pyhailort/pyhailort.py | 1805 ++++++++++++++ .../hailo_platform/tools/benchmark_command.py | 5 - .../tools/cmd_utils/__init__.py | 0 .../hailo_platform/tools/firmware/__init__.py | 0 .../tools/firmware/configure_firmware.py | 15 - .../tools/firmware/firmware_config.py | 286 --- .../tools/firmware/sensor_config.py | 9 - .../tools/firmware/update_firmware.py | 9 - .../tools/firmware/update_second_stage.py | 9 - .../hailo_platform/tools/fw_control.py | 20 - .../targets => tools/hailocli}/__init__.py | 0 .../hailocli}/base_utils.py | 0 .../hailocli}/config_definitions.json | 0 .../tools/hailocli/example_config.json | 14 + .../hailocli}/hailo_device_utils.py | 6 +- .../tools/hailocli/hailocli_commands.py | 70 + .../tools/{cmd_utils => hailocli}/main.py | 17 +- .../hailo_platform/tools/infer_cli.py | 54 - .../hailo_platform/tools/run_command.py | 5 - .../hailo_platform/tools/udp_rate_limiter.py | 13 +- .../platform/hailo_platform/tools/watchdog.py | 33 - .../notebooks/HRT_0_Inference_Tutorial.ipynb} | 0 .../HRT_1_Power_Measurement_Tutorial.ipynb} | 11 +- .../bindings/python/platform/requirements.txt | 1 - .../bindings/python/platform/setup.py | 30 +- .../bindings/python/src/CMakeLists.txt | 24 +- .../bindings/python/src/bindings_common.hpp | 80 +- .../bindings/python/src/device_api.cpp | 568 +++++ .../bindings/python/src/device_api.hpp | 132 + .../bindings/python/src/hef_api.cpp | 318 +++ .../bindings/python/src/hef_api.hpp | 348 +-- .../python/src/internal/CMakeLists.txt | 9 +- .../python/src/internal/control_api.cpp | 154 +- .../python/src/internal/control_api.hpp | 46 +- .../src/internal/pyhailort_internal.cpp | 2 +- .../bindings/python/src/pyhailort.cpp | 1301 +++------- .../bindings/python/src/vdevice_api.hpp | 1 - .../bindings/python/src/vstream_api.cpp | 335 +++ .../bindings/python/src/vstream_api.hpp | 341 +-- .../cmake/toolchains/qnx.aarch64.cmake | 54 +- .../cmake/toolchains/qnx.x86_64.cmake | 52 +- .../cmake/toolchains/toolchains.yaml | 7 + hailort/libhailort/examples/CMakeLists.txt | 6 +- hailort/libhailort/examples/README.md | 36 +- hailort/libhailort/examples/c/CMakeLists.txt | 22 +- hailort/libhailort/examples/c/common.h | 2 +- .../examples/c/data_quantization_example.c | 2 +- hailort/libhailort/examples/c/hailo_thread.h | 2 +- .../examples/c/infer_pipeline_example.c | 2 +- .../examples/c/multi_device_example.c | 2 +- .../examples/c/power_measurement_example.c | 135 ++ .../examples/c/raw_streams_example.c | 7 +- ...mple.c => switch_network_groups_example.c} | 84 +- ...ngle_io_network_groups_manually_example.c} | 2 +- .../libhailort/examples/c/vstreams_example.c | 2 +- .../examples/cmake/FindHailoRT.cmake | 35 - .../libhailort/examples/cpp/CMakeLists.txt | 22 +- .../examples/cpp/infer_pipeline_example.cpp | 12 +- .../examples/cpp/multi_device_example.cpp | 14 +- .../cpp/multi_network_vstream_example.cpp | 13 +- .../cpp/power_measurement_example.cpp | 138 ++ .../examples/cpp/raw_streams_example.cpp | 21 +- .../examples/cpp/switch_hefs_example.cpp | 196 -- .../cpp/switch_network_groups_example.cpp | 170 ++ ...witch_network_groups_manually_example.cpp} | 16 +- .../examples/cpp/vstreams_example.cpp | 14 +- hailort/libhailort/hef.proto | 3 + hailort/libhailort/include/hailo/device.hpp | 56 +- hailort/libhailort/include/hailo/event.hpp | 56 +- hailort/libhailort/include/hailo/expected.hpp | 38 +- hailort/libhailort/include/hailo/hailort.h | 190 +- .../include/hailo/hailort_common.hpp | 1 + .../include/hailo/network_group.hpp | 42 +- hailort/libhailort/include/hailo/platform.h | 6 +- .../libhailort/include/hailo/quantization.hpp | 1 + hailort/libhailort/include/hailo/stream.hpp | 14 +- .../libhailort/include/hailo/transform.hpp | 4 - hailort/libhailort/src/CMakeLists.txt | 61 +- hailort/libhailort/src/Config.cmake.in | 10 + hailort/libhailort/src/config_buffer.cpp | 143 ++ hailort/libhailort/src/config_buffer.hpp | 63 + .../src/context_switch/config_manager.hpp | 2 +- .../hcp_config_activated_network_group.cpp | 33 +- .../src/context_switch/hcp_config_manager.cpp | 12 +- .../hcp_config_network_group.cpp | 35 +- .../src/context_switch/hef_metadata.cpp | 120 +- .../src/context_switch/hef_metadata.hpp | 59 +- .../multi_context/resource_manager.hpp | 97 +- .../vdma_config_activated_network_group.hpp | 8 +- .../multi_context/vdma_config_manager.hpp | 12 +- .../vdma_config_network_group.hpp | 29 +- .../src/context_switch/network_group.cpp | 224 +- .../context_switch/network_group_internal.hpp | 43 +- .../context_switch/network_group_wrapper.cpp | 180 ++ .../context_switch/network_group_wrapper.hpp | 87 + .../context_switch/pipeline_multiplexer.cpp | 74 + .../context_switch/pipeline_multiplexer.hpp | 52 + .../src/context_switch/resource_manager.cpp | 426 ++-- .../hcp_config_activated_network_group.hpp | 7 +- .../single_context/hcp_config_manager.hpp | 4 +- .../hcp_config_network_group.hpp | 9 +- .../vdma_config_activated_network_group.cpp | 243 +- .../context_switch/vdma_config_manager.cpp | 64 +- .../vdma_config_network_group.cpp | 109 +- hailort/libhailort/src/control.cpp | 63 +- hailort/libhailort/src/control.hpp | 15 +- hailort/libhailort/src/control_protocol.cpp | 39 +- hailort/libhailort/src/control_protocol.hpp | 9 +- hailort/libhailort/src/core_stream.cpp | 30 +- hailort/libhailort/src/core_stream.hpp | 18 +- hailort/libhailort/src/device.cpp | 40 +- hailort/libhailort/src/device_internal.cpp | 11 +- hailort/libhailort/src/device_internal.hpp | 4 +- hailort/libhailort/src/eth_stream.cpp | 6 +- hailort/libhailort/src/eth_stream.hpp | 4 +- hailort/libhailort/src/event_internal.hpp | 4 +- hailort/libhailort/src/hailort.cpp | 137 +- hailort/libhailort/src/hailort_common.cpp | 1 + hailort/libhailort/src/hailort_defaults.hpp | 9 +- hailort/libhailort/src/hef.cpp | 696 +++--- hailort/libhailort/src/hef_internal.hpp | 141 +- hailort/libhailort/src/inference_pipeline.cpp | 3 +- .../libhailort/src/intermediate_buffer.cpp | 239 +- .../libhailort/src/intermediate_buffer.hpp | 123 +- hailort/libhailort/src/layer_info.hpp | 19 +- hailort/libhailort/src/mipi_stream.cpp | 3 +- hailort/libhailort/src/mipi_stream.hpp | 2 +- .../src/network_group_scheduler.cpp | 640 +++++ .../src/network_group_scheduler.hpp | 143 ++ hailort/libhailort/src/os/CMakeLists.txt | 9 +- hailort/libhailort/src/os/hailort_driver.hpp | 21 +- .../src/os/posix/hailort_driver.cpp | 76 +- .../libhailort/src/os/posix/mmap_buffer.cpp | 2 +- ..._driver_sysfs.hpp => pcie_driver_scan.hpp} | 10 +- hailort/libhailort/src/os/posix/qnx/event.cpp | 223 +- .../src/os/posix/qnx/pcie_driver_scan.cpp | 86 + .../libhailort/src/os/posix/unix/event.cpp | 14 +- .../pcie_driver_scan.cpp} | 6 +- hailort/libhailort/src/os/windows/event.cpp | 10 +- .../src/os/windows/hailort_driver.cpp | 26 +- hailort/libhailort/src/pcie_stream.cpp | 30 +- hailort/libhailort/src/pcie_stream.hpp | 18 +- hailort/libhailort/src/pipeline.cpp | 35 +- hailort/libhailort/src/stream.cpp | 11 +- hailort/libhailort/src/stream_internal.cpp | 10 + hailort/libhailort/src/stream_internal.hpp | 2 + hailort/libhailort/src/transform.cpp | 14 +- hailort/libhailort/src/vdevice.cpp | 27 +- hailort/libhailort/src/vdevice_internal.hpp | 11 +- hailort/libhailort/src/vdevice_stream.cpp | 197 +- hailort/libhailort/src/vdevice_stream.hpp | 40 +- .../libhailort/src/vdma/continuous_buffer.cpp | 152 ++ .../libhailort/src/vdma/continuous_buffer.hpp | 82 + .../mapped_buffer.cpp} | 36 +- hailort/libhailort/src/vdma/mapped_buffer.hpp | 115 + hailort/libhailort/src/vdma/sg_buffer.cpp | 88 + hailort/libhailort/src/vdma/sg_buffer.hpp | 74 + hailort/libhailort/src/vdma/vdma_buffer.hpp | 65 + hailort/libhailort/src/vdma_buffer.hpp | 104 - hailort/libhailort/src/vdma_channel.cpp | 345 ++- hailort/libhailort/src/vdma_channel.hpp | 43 +- .../libhailort/src/vdma_descriptor_list.cpp | 61 +- .../libhailort/src/vdma_descriptor_list.hpp | 19 +- hailort/libhailort/src/vdma_stream.cpp | 193 +- hailort/libhailort/src/vdma_stream.hpp | 56 +- hailort/libhailort/src/vstream.cpp | 124 +- hailort/pre_build/CMakeLists.txt | 22 +- hailort/pre_build/external/CMakeLists.txt | 6 +- hailort/scripts/download_hefs.cmd | 2 +- hailort/scripts/download_hefs.sh | 2 +- 242 files changed, 12308 insertions(+), 9874 deletions(-) create mode 100644 .hailort.png delete mode 100644 .logo.svg create mode 100644 hailort/drivers/win/include/Public.h create mode 100644 hailort/drivers/win/include/hailo_pcie_version.h rename hailort/{libhailort/bindings/python/platform/hailo_platform/tools/firmware => hailortcli}/example_config.json (100%) delete mode 100644 hailort/libhailort/bindings/gstreamer/cmake/FindHailoRT.cmake delete mode 100644 hailort/libhailort/bindings/python/platform/hailo_platform/common/compatibility/__init__.py delete mode 100644 hailort/libhailort/bindings/python/platform/hailo_platform/common/paths_manager/config.py delete mode 100644 hailort/libhailort/bindings/python/platform/hailo_platform/common/paths_manager/paths.py delete mode 100644 hailort/libhailort/bindings/python/platform/hailo_platform/common/paths_manager/version.py delete mode 100644 hailort/libhailort/bindings/python/platform/hailo_platform/common/targets/inference_targets.py delete mode 100644 hailort/libhailort/bindings/python/platform/hailo_platform/common/tools/__init__.py delete mode 100644 hailort/libhailort/bindings/python/platform/hailo_platform/common/tools/cmd_utils/__init__.py delete mode 100644 hailort/libhailort/bindings/python/platform/hailo_platform/paths_manager/__init__.py delete mode 100644 hailort/libhailort/bindings/python/platform/hailo_platform/paths_manager/paths.py rename hailort/libhailort/bindings/python/platform/hailo_platform/{common/paths_manager => pyhailort}/__init__.py (100%) create mode 100644 hailort/libhailort/bindings/python/platform/hailo_platform/pyhailort/control_object.py create mode 100644 hailort/libhailort/bindings/python/platform/hailo_platform/pyhailort/ethernet_utils.py create mode 100644 hailort/libhailort/bindings/python/platform/hailo_platform/pyhailort/hailo_control_protocol.py create mode 100644 hailort/libhailort/bindings/python/platform/hailo_platform/pyhailort/hw_object.py create mode 100644 hailort/libhailort/bindings/python/platform/hailo_platform/pyhailort/i2c_slaves.py create mode 100644 hailort/libhailort/bindings/python/platform/hailo_platform/pyhailort/power_measurement.py create mode 100644 hailort/libhailort/bindings/python/platform/hailo_platform/pyhailort/pyhailort.py delete mode 100644 hailort/libhailort/bindings/python/platform/hailo_platform/tools/benchmark_command.py delete mode 100644 hailort/libhailort/bindings/python/platform/hailo_platform/tools/cmd_utils/__init__.py delete mode 100644 hailort/libhailort/bindings/python/platform/hailo_platform/tools/firmware/__init__.py delete mode 100644 hailort/libhailort/bindings/python/platform/hailo_platform/tools/firmware/configure_firmware.py delete mode 100644 hailort/libhailort/bindings/python/platform/hailo_platform/tools/firmware/firmware_config.py delete mode 100644 hailort/libhailort/bindings/python/platform/hailo_platform/tools/firmware/sensor_config.py delete mode 100644 hailort/libhailort/bindings/python/platform/hailo_platform/tools/firmware/update_firmware.py delete mode 100644 hailort/libhailort/bindings/python/platform/hailo_platform/tools/firmware/update_second_stage.py delete mode 100644 hailort/libhailort/bindings/python/platform/hailo_platform/tools/fw_control.py rename hailort/libhailort/bindings/python/platform/hailo_platform/{common/targets => tools/hailocli}/__init__.py (100%) rename hailort/libhailort/bindings/python/platform/hailo_platform/{common/tools/cmd_utils => tools/hailocli}/base_utils.py (100%) rename hailort/libhailort/bindings/python/platform/hailo_platform/{ => tools/hailocli}/config_definitions.json (100%) create mode 100644 hailort/libhailort/bindings/python/platform/hailo_platform/tools/hailocli/example_config.json rename hailort/libhailort/bindings/python/platform/hailo_platform/{common/tools/cmd_utils => tools/hailocli}/hailo_device_utils.py (91%) create mode 100644 hailort/libhailort/bindings/python/platform/hailo_platform/tools/hailocli/hailocli_commands.py rename hailort/libhailort/bindings/python/platform/hailo_platform/tools/{cmd_utils => hailocli}/main.py (79%) delete mode 100644 hailort/libhailort/bindings/python/platform/hailo_platform/tools/infer_cli.py delete mode 100644 hailort/libhailort/bindings/python/platform/hailo_platform/tools/run_command.py delete mode 100644 hailort/libhailort/bindings/python/platform/hailo_platform/tools/watchdog.py rename hailort/libhailort/bindings/python/platform/{tutorials/notebooks/Inference Tutorial.ipynb => hailo_tutorials/notebooks/HRT_0_Inference_Tutorial.ipynb} (100%) rename hailort/libhailort/bindings/python/platform/{tutorials/notebooks/Power Measurement Tutorial.ipynb => hailo_tutorials/notebooks/HRT_1_Power_Measurement_Tutorial.ipynb} (90%) create mode 100644 hailort/libhailort/bindings/python/src/device_api.cpp create mode 100644 hailort/libhailort/bindings/python/src/device_api.hpp create mode 100644 hailort/libhailort/bindings/python/src/hef_api.cpp create mode 100644 hailort/libhailort/bindings/python/src/vstream_api.cpp create mode 100644 hailort/libhailort/examples/c/power_measurement_example.c rename hailort/libhailort/examples/c/{switch_hefs_example.c => switch_network_groups_example.c} (78%) rename hailort/libhailort/examples/c/{switch_single_io_hefs_example.c => switch_single_io_network_groups_manually_example.c} (99%) delete mode 100644 hailort/libhailort/examples/cmake/FindHailoRT.cmake create mode 100644 hailort/libhailort/examples/cpp/power_measurement_example.cpp delete mode 100644 hailort/libhailort/examples/cpp/switch_hefs_example.cpp create mode 100644 hailort/libhailort/examples/cpp/switch_network_groups_example.cpp rename hailort/libhailort/examples/cpp/{switch_hefs_example_threads_reuse.cpp => switch_network_groups_manually_example.cpp} (96%) create mode 100644 hailort/libhailort/src/Config.cmake.in create mode 100644 hailort/libhailort/src/config_buffer.cpp create mode 100644 hailort/libhailort/src/config_buffer.hpp create mode 100644 hailort/libhailort/src/context_switch/network_group_wrapper.cpp create mode 100644 hailort/libhailort/src/context_switch/network_group_wrapper.hpp create mode 100644 hailort/libhailort/src/context_switch/pipeline_multiplexer.cpp create mode 100644 hailort/libhailort/src/context_switch/pipeline_multiplexer.hpp create mode 100644 hailort/libhailort/src/network_group_scheduler.cpp create mode 100644 hailort/libhailort/src/network_group_scheduler.hpp rename hailort/libhailort/src/os/posix/{pcie_driver_sysfs.hpp => pcie_driver_scan.hpp} (51%) create mode 100644 hailort/libhailort/src/os/posix/qnx/pcie_driver_scan.cpp rename hailort/libhailort/src/os/posix/{pcie_driver_sysfs.cpp => unix/pcie_driver_scan.cpp} (96%) create mode 100644 hailort/libhailort/src/vdma/continuous_buffer.cpp create mode 100644 hailort/libhailort/src/vdma/continuous_buffer.hpp rename hailort/libhailort/src/{vdma_buffer.cpp => vdma/mapped_buffer.cpp} (77%) create mode 100644 hailort/libhailort/src/vdma/mapped_buffer.hpp create mode 100644 hailort/libhailort/src/vdma/sg_buffer.cpp create mode 100644 hailort/libhailort/src/vdma/sg_buffer.hpp create mode 100644 hailort/libhailort/src/vdma/vdma_buffer.hpp delete mode 100644 hailort/libhailort/src/vdma_buffer.hpp diff --git a/.hailort.png b/.hailort.png new file mode 100644 index 0000000000000000000000000000000000000000..fdc84976f3453aa8686afc64e88078f775d43716 GIT binary patch literal 146846 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

{|65GqzI>dc38yCp2E2FG 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 - - -image/svg+xml \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 68b135b..06f1154 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,11 +1,13 @@ 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_PYHAILORT_VENV "Build pyhailort in venv. Only used if HAILO_BUILD_PYBIND is on" 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) +option(HAILO_OFFLINE_COMPILATION "Don't download external dependencies" OFF) +option(HAILO_MICROPROFILE "Microprofile code" OFF) find_program(CCACHE_PROGRAM ccache) if(CCACHE_PROGRAM) @@ -14,6 +16,13 @@ endif() project(HailoRT) +# 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() + # Check build type if (NOT CMAKE_BUILD_TYPE) message(STATUS "No build type selected, default to Debug") @@ -35,23 +44,11 @@ if(WIN32) /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("$<$>:/DEBUG>") - add_link_options("$<$>:/OPT:REF>") - add_link_options("$<$>:/OPT:ICF>") - elseif(UNIX) - if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR CMAKE_CXX_COMPILER_ID STREQUAL "QCC") 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 + set(HAILORT_COMPILE_OPTIONS ${HAILORT_COMPILE_OPTIONS} -Werror -Wall -Wextra # TODO: remove me warnings -Wno-conversion -Wno-deprecated-declarations @@ -60,14 +57,6 @@ elseif(UNIX) 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() @@ -80,23 +69,8 @@ if(HAILO_BUILD_EMULATOR) 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 index edc09bf..69fb8cb 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@

- +

diff --git a/common/include/context_switch_defs.h b/common/include/context_switch_defs.h index b993a3b..ef432a4 100644 --- a/common/include/context_switch_defs.h +++ b/common/include/context_switch_defs.h @@ -23,7 +23,6 @@ extern "C" { #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) @@ -57,7 +56,7 @@ typedef enum : uint8_t { #else typedef enum __attribute__((packed)) { #endif - CONTEXT_SWITCH_DEFS__ACTION_TYPE_FETCH_VDMA_DESCRIPTORS = 0, + CONTEXT_SWITCH_DEFS__ACTION_TYPE_FETCH_CFG_CHANNEL_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, @@ -85,6 +84,8 @@ typedef enum __attribute__((packed)) { 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, + CONTEXT_SWITCH_DEFS__ACTION_TYPE_VALIDATE_VDMA_CHANNEL, + CONTEXT_SWITCH_DEFS__ACTION_TYPE_BURST_CREDITS_TASK_START, /* Must be last */ CONTEXT_SWITCH_DEFS__ACTION_TYPE_COUNT @@ -116,9 +117,18 @@ typedef struct { * | | | .last_executed = ; | * | | | .sub_action_type = CONTEXT_SWITCH_DEFS__ACTION_TYPE_ENABLE_LCU_DEFAULT; | * | | | } | - * | | | CONTEXT_SWITCH_DEFS__enable_lcu_action_default_data_t { .packed_lcu_id=; } | - * | | | CONTEXT_SWITCH_DEFS__enable_lcu_action_default_data_t { .packed_lcu_id=; } | - * | V | CONTEXT_SWITCH_DEFS__enable_lcu_action_default_data_t { .packed_lcu_id=; } | + * | | | CONTEXT_SWITCH_DEFS__enable_lcu_action_default_data_t { | + * | | | .packed_lcu_id=; | + * | | | .network_index= | + * | | | } | + * | | | CONTEXT_SWITCH_DEFS__enable_lcu_action_default_data_t { | + * | | | .packed_lcu_id=; | + * | | | .network_index= | + * | | | } | + * | | | CONTEXT_SWITCH_DEFS__enable_lcu_action_default_data_t { | + * | | | .packed_lcu_id=; | + * | | | .network_index= | + * | V | } | * | ... | (Next action starting with CONTEXT_SWITCH_DEFS__common_action_header_t) | * |-------------------------------------------------------------------------------------------------------| * See also: "CONTROL_PROTOCOL__REPEATED_ACTION_t" in "control_protocol.h" @@ -132,7 +142,7 @@ typedef struct { typedef struct { uint16_t descriptors_count; uint8_t cfg_channel_number; -} CONTEXT_SWITCH_DEFS__read_vdma_action_data_t; +} CONTEXT_SWITCH_DEFS__fetch_cfg_channel_descriptors_action_data_t; typedef struct { uint16_t ccw_bursts; @@ -146,6 +156,7 @@ typedef struct { typedef struct { uint8_t packed_lcu_id; + uint8_t network_index; uint16_t kernel_done_address; uint32_t kernel_done_count; } CONTEXT_SWITCH_DEFS__enable_lcu_action_non_default_data_t; @@ -153,6 +164,7 @@ typedef struct { /* Default action - kernel_done_address and kernel_done_count has default values */ typedef struct { uint8_t packed_lcu_id; + uint8_t network_index; } CONTEXT_SWITCH_DEFS__enable_lcu_action_default_data_t; typedef struct { @@ -163,14 +175,27 @@ typedef struct { uint8_t vdma_channel_index; uint8_t edge_layer_direction; bool is_inter_context; + uint8_t host_buffer_type; // CONTROL_PROTOCOL__HOST_BUFFER_TYPE_t + uint32_t initial_credit_size; } CONTEXT_SWITCH_DEFS__deactivate_vdma_channel_action_data_t; +typedef struct { + uint8_t vdma_channel_index; + uint8_t edge_layer_direction; + bool is_inter_context; + bool is_single_context_network_group; + uint8_t host_buffer_type; // CONTROL_PROTOCOL__HOST_BUFFER_TYPE_t + uint32_t initial_credit_size; +} CONTEXT_SWITCH_DEFS__validate_vdma_channel_action_data_t; + typedef struct { uint8_t vdma_channel_index; uint8_t stream_index; - uint32_t channel_credits; + uint8_t network_index; + uint32_t frame_periph_size; uint8_t credit_type; uint16_t periph_bytes_per_buffer; + uint8_t host_buffer_type; // CONTROL_PROTOCOL__HOST_BUFFER_TYPE_t, relevant only for descriptors credit. } CONTEXT_SWITCH_DEFS__fetch_data_action_data_t; typedef struct { @@ -182,7 +207,8 @@ typedef struct { typedef struct { uint8_t h2d_vdma_channel_index; uint8_t d2h_vdma_channel_index; - uint32_t descriptors_per_batch; + uint8_t network_index; + uint32_t descriptors_per_frame; uint16_t programmed_descriptors_count; } CONTEXT_SWITCH_DEFS__add_ddr_pair_info_action_data_t; @@ -227,16 +253,17 @@ typedef struct { uint8_t stream_index; uint8_t vdma_channel_index; CONTEXT_SWITCH_DEFS__stream_reg_info_t stream_reg_info; + uint32_t initial_credit_size; bool is_single_context_app; } CONTEXT_SWITCH_DEFS__activate_boundary_input_data_t; typedef struct { uint8_t stream_index; uint8_t vdma_channel_index; + uint8_t network_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; + CONTROL_PROTOCOL__host_buffer_info_t host_buffer_info; + uint32_t initial_credit_size; } CONTEXT_SWITCH_DEFS__activate_inter_context_input_data_t; typedef struct { @@ -244,9 +271,8 @@ typedef struct { 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; + uint32_t initial_credit_size; } CONTEXT_SWITCH_DEFS__activate_ddr_buffer_input_data_t; typedef struct { @@ -260,13 +286,9 @@ typedef struct { typedef struct { uint8_t stream_index; uint8_t vdma_channel_index; + uint8_t network_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; + CONTROL_PROTOCOL__host_buffer_info_t host_buffer_info; } CONTEXT_SWITCH_DEFS__activate_inter_context_output_data_t; typedef struct { @@ -275,16 +297,14 @@ typedef struct { 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; + uint32_t buffered_rows_count; } 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; + CONTROL_PROTOCOL__host_buffer_info_t host_buffer_info; } CONTEXT_SWITCH_DEFS__activate_cfg_channel_t; typedef struct { diff --git a/common/include/control_protocol.h b/common/include/control_protocol.h index 627ee04..a5d9e36 100644 --- a/common/include/control_protocol.h +++ b/common/include/control_protocol.h @@ -59,6 +59,11 @@ extern "C" { #define CONTROL_PROTOCOL__REQUEST_BASE_SIZE (sizeof(CONTROL_PROTOCOL__request_header_t) + sizeof(uint32_t)) #define CONTROL_PROTOCOL__OPCODE_INVALID 0xFFFFFFFF +/* If a control accepts a dynamic_batch_size and this value is passed, the + * dynamic_batch_size will be ignored. The pre-configured batch_size will be used. + */ +#define CONTROL_PROTOCOL__IGNORE_DYNAMIC_BATCH_SIZE (0) + #define CONTROL_PROTOCOL__TRIGGER_SUB_INDEX_SHIFT (0) #define CONTROL_PROTOCOL__TRIGGER_SUB_INDEX_BIT_MASK (0x000000FF) #define CONTROL_PROTOCOL__TRIGGER_INDEX_SHIFT (16) @@ -862,21 +867,24 @@ typedef enum { CONTROL_PROTOCOL__CONTEXT_SWITCH_VER_V1_0_0 = 0x010000, } CONTROL_PROTOCOL__CONTEXT_SWITCH_VERSION_t; +typedef struct { + bool is_abbale_supported; +} CONTROL_PROTOCOL__VALIDATION_FEATURE_LIST_t; + +typedef struct { + bool preliminary_run_asap; +} CONTROL_PROTOCOL__INFER_FEATURE_LIST_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 + CONTROL_PROTOCOL__INFER_FEATURE_LIST_t infer_features; 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; @@ -960,6 +968,7 @@ typedef enum { CONTROL_PROTOCOL__CONTEXT_SWITCH_ACTION_ENABLE_LCU_DEFAULT, CONTROL_PROTOCOL__CONTEXT_SWITCH_ACTION_ADD_REPEATED, CONTROL_PROTOCOL__CONTEXT_SWITCH_ACTION_FETCH_CCW_BURSTS, + CONTROL_PROTOCOL__CONTEXT_SWITCH_ACTION_BURST_CREDITS_TASK_START, /* must be last*/ CONTROL_PROTOCOL__CONTEXT_SWITCH_ACTION_COUNT, @@ -1026,6 +1035,25 @@ typedef enum { CONTROL_PROTOCOL__EDGE_CONNECTION_TYPE_COUNT } CONTROL_PROTOCOL__EDGE_CONNECTION_TYPE_t; +typedef enum { + CONTROL_PROTOCOL__HOST_BUFFER_TYPE_EXTERNAL_DESC = 0, + CONTROL_PROTOCOL__HOST_BUFFER_TYPE_CCB, + + // The buffer uses external descriptors that is host managed - the firmware don't need to config this buffer + CONTROL_PROTOCOL__HOST_BUFFER_TYPE_HOST_MANAGED_EXTERNAL_DESC, + + /* must be last*/ + CONTROL_PROTOCOL__HOST_BUFFER_TYPE_COUNT +} CONTROL_PROTOCOL__HOST_BUFFER_TYPE_t; + +typedef struct { + uint8_t buffer_type; // CONTROL_PROTOCOL__HOST_BUFFER_TYPE_t + uint64_t dma_address; + uint16_t desc_page_size; + uint32_t total_desc_count; + uint32_t bytes_in_pattern; +} CONTROL_PROTOCOL__host_buffer_info_t; + typedef struct { uint8_t communication_type; uint8_t edge_connection_type; @@ -1040,7 +1068,6 @@ typedef struct { 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; @@ -1052,9 +1079,7 @@ typedef struct { 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__host_buffer_info_t host_buffer_info; } CONTROL_PROTOCOL__inter_context_output_t; typedef struct { @@ -1062,7 +1087,7 @@ typedef struct { 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; + uint32_t buffered_rows_count; } CONTROL_PROTOCOL__ddr_buffer_output_t; @@ -1073,19 +1098,19 @@ typedef struct { typedef struct { CONTROL_PROTOCOL__edge_layer_common_info_t common_info; uint16_t desc_page_size; + uint32_t initial_credit_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__host_buffer_info_t host_buffer_info; + uint32_t initial_credit_size; } 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; + uint32_t initial_credit_size; } CONTROL_PROTOCOL__ddr_buffer_input_t; typedef struct { @@ -1106,10 +1131,10 @@ typedef struct { 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 cfg_channels_count_length; + uint8_t cfg_channels_count; + uint32_t config_buffer_infos_length; + CONTROL_PROTOCOL__host_buffer_info_t config_buffer_infos[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; @@ -1155,16 +1180,19 @@ typedef struct { * | | | .header = { CONTROL_PROTOCOL__CONTEXT_SWITCH_ACTION_ENABLE_LCU_DEFAULT, true }; | * | | | .cluster_index = ; | * | | | .lcu_index = ; | + * | | | .network_index = ; | * | | | } | * | | | CONTROL_PROTOCOL__ENABLE_LCU_DEFAULT_ACTION_t { | * | | | .header = { CONTROL_PROTOCOL__CONTEXT_SWITCH_ACTION_ENABLE_LCU_DEFAULT, true }; | * | | | .cluster_index = ; | * | | | .lcu_index = ; | + * | | | .network_index = ; | * | | | } | * | | | CONTROL_PROTOCOL__ENABLE_LCU_DEFAULT_ACTION_t { | * | | | .header = { CONTROL_PROTOCOL__CONTEXT_SWITCH_ACTION_ENABLE_LCU_DEFAULT, true }; | * | | | .cluster_index = ; | * | | | .lcu_index = ; | + * | | | .network_index = ; | * | V | } | * | ... | (Next action control) | * |--------------------------------------------------------------------------------------------------| @@ -1236,6 +1264,7 @@ typedef struct { CONTROL_PROTOCOL__ACTION_HEADER_t header; uint8_t cluster_index; uint8_t lcu_index; + uint8_t network_index; } CONTROL_PROTOCOL__ENABLE_LCU_DEFAULT_ACTION_t; typedef struct { @@ -1265,6 +1294,11 @@ typedef struct { CONTROL_PROTOCOL__ACTION_HEADER_t header; } CONTROL_PROTOCOL__ADD_DDR_BUFFERING_START_ACTION_t; +typedef struct { + /* Must be first */ + CONTROL_PROTOCOL__ACTION_HEADER_t header; +} CONTROL_PROTOCOL__BURST_CREDITS_TASK_START_ACTION_T; + typedef struct { CONTROL_PROTOCOL__TRIGGER_t trigger; uint16_t triggers_action_count; @@ -1324,6 +1358,8 @@ typedef struct { uint8_t state_machine_status; uint32_t application_index_length; uint8_t application_index; + uint32_t dynamic_batch_size_length; + uint16_t dynamic_batch_size; } CONTROL_PROTOCOL__change_context_switch_status_request_t; typedef struct { @@ -1338,6 +1374,8 @@ typedef struct { typedef struct { uint32_t application_index_length; uint8_t application_index; + uint32_t dynamic_batch_size_length; + uint16_t dynamic_batch_size; } CONTROL_PROTOCOL__switch_application_request_t; typedef struct { @@ -1674,8 +1712,8 @@ typedef struct { 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]; + uint8_t cfg_channels_count; + CONTROL_PROTOCOL__host_buffer_info_t config_buffer_infos[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; diff --git a/common/include/firmware_status.h b/common/include/firmware_status.h index 391213e..88350aa 100644 --- a/common/include/firmware_status.h +++ b/common/include/firmware_status.h @@ -395,6 +395,10 @@ Updating rules: 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_STATUS__X(CONTROL_PROTOCOL_STATUS_INVALID_CONTEXT_SWITCH_HOST_BUFFER_INFO)\ + FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_INVALID_CFG_CHANNELS_COUNT_LENGTH)\ + FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_INVALID_DYNAMIC_BATCH_SIZE_LENGTH)\ + FIRMWARE_STATUS__X(CONTROL_PROTOCOL_STATUS_INVALID_INFER_FEATURES_LENGTH) /* DEPRECATED */\ \ FIRMWARE_MODULE__X(FIRMWARE_MODULE__POWER_MEASUREMENT)\ FIRMWARE_STATUS__X(HAILO_POWER_MEASUREMENT_STATUS_POWER_INIT_ERROR)\ @@ -537,6 +541,7 @@ Updating rules: 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_STATUS__X(PCIE_SERVICE_INVALID_INITIAL_CREDIT_SIZE)\ \ FIRMWARE_MODULE__X(FIRMWARE_MODULE__FIRMWARE_UPDATE)\ FIRMWARE_STATUS__X(FIRMWARE_UPDATE_STATUS_INVALID_PARAMETERS)\ @@ -726,6 +731,10 @@ Updating rules: 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_STATUS__X(CONTEXT_SWITCH_STATUS_ACTION_IS_NOT_SUPPORTED)\ + FIRMWARE_STATUS__X(CONTEXT_SWITCH_STATUS_INVALID_CFG_CHANNELS_COUNT)\ + FIRMWARE_STATUS__X(CONTEXT_SWITCH_STATUS_INVALID_HOST_BUFFER_TYPE)\ + FIRMWARE_STATUS__X(CONTEXT_SWITCH_STATUS_BURST_CREDITS_TASK_IS_NOT_IDLE)\ \ FIRMWARE_MODULE__X(FIRMWARE_MODULE__D2H_EVENT_MANAGER)\ FIRMWARE_STATUS__X(HAILO_D2H_EVENT_MANAGER_STATUS_MESSAGE_HIGH_PRIORITY_QUEUE_CREATE_FAILED)\ @@ -940,6 +949,7 @@ Updating rules: 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_STATUS__X(CSM_CONFIG_MANAGER_STATUS_CSM_FIFO_NOT_EMPTY)\ \ FIRMWARE_MODULE__X(FIRMWARE_MODULE__PCIE_CONFIG_MANAGER)\ FIRMWARE_STATUS__X(PCIE_CONFIG_MANAGER_STATUS_NOT_IMPLEMENTED)\ @@ -964,6 +974,16 @@ Updating rules: 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_STATUS__X(VDMA_SERVICE_STATUS_EXTERNAL_DESC_COUNT_MUST_BE_POWER_OF_2)\ + FIRMWARE_STATUS__X(VDMA_SERVICE_STATUS_TOO_MANY_DESCRIPTORS)\ + FIRMWARE_STATUS__X(VDMA_SERVICE_STATUS_INVALID_HOST_BUFFER_TYPE)\ + FIRMWARE_STATUS__X(VDMA_SERVICE_STATUS_DESC_PAGE_SIZE_MUST_BE_POWER_OF_2)\ + FIRMWARE_STATUS__X(VDMA_SERVICE_STATUS_INITIAL_DESC_BIGGER_EQ_THAN_TOTAL)\ + FIRMWARE_STATUS__X(VDMA_SERVICE_STATUS_CCB_NOT_IMPLEMENTED_OVER_PCIE)\ + FIRMWARE_STATUS__X(VDMA_SERVICE_STATUS_CCB_BASE_ADDRESS_IS_NOT_IN_MASK)\ + FIRMWARE_STATUS__X(VDMA_SERVICE_STATUS_INITIAL_DESC_BIGGER_THAN_TOTAL)\ + FIRMWARE_STATUS__X(VDMA_SERVICE_STATUS_INVALID_INITIAL_CREDIT_SIZE)\ + FIRMWARE_STATUS__X(VDMA_SERVICE_STATUS_TOO_LARGE_BYTES_IN_PATTERN)\ \ FIRMWARE_MODULE__X(FIRMWARE_MODULE__MEMORY_LOGGER)\ FIRMWARE_STATUS__X(MEMORY_LOGGER_STATUS_DEBUG_INSUFFICIENT_MEMORY)\ @@ -980,6 +1000,13 @@ Updating rules: 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_STATUS__X(DRAM_DMA_SERVICE_STATUS_INVALID_DESC_PAGE_SIZE)\ + FIRMWARE_STATUS__X(DRAM_DMA_SERVICE_NUM_PAGES_IS_OUT_OF_RANGE)\ + FIRMWARE_STATUS__X(DRAM_DMA_SERVICE_INVALID_INITIAL_CREDIT_SIZE)\ + FIRMWARE_STATUS__X(DRAM_DMA_SERVICE_TOTAL_DESCS_COUNT_IS_OUT_OF_RANGE)\ + FIRMWARE_STATUS__X(DRAM_DMA_SERVICE_TOTAL_DESCS_COUNT_MUST_BE_POWER_OF_2)\ + FIRMWARE_STATUS__X(DRAM_DMA_SERVICE_INVALID_DESCS_COUNT)\ + FIRMWARE_STATUS__X(DRAM_DMA_SERVICE_DESC_PER_INTERRUPT_NOT_IN_MASK)\ \ FIRMWARE_MODULE__X(FIRMWARE_MODULE__NN_CORE_SERVICE)\ FIRMWARE_STATUS__X(NN_CORE_SERVICE_STATUS_INVALID_ARG_PASSED)\ @@ -989,6 +1016,19 @@ Updating rules: 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)\ + FIRMWARE_STATUS__X(DATA_STREAM_WRAPPER_STATUS_INVALID_CHANNEL_INDEX)\ + FIRMWARE_STATUS__X(DATA_STREAM_WRAPPER_STATUS_INVALID_STREAM_INDEX)\ + FIRMWARE_STATUS__X(DATA_STREAM_MANAGER_STATUS_INVALID_CREDIT_TYPE)\ + FIRMWARE_STATUS__X(DATA_STREAM_MANAGER_WRAPPER_STATUS_INVALID_HOST_BUFFER_TYPE)\ + \ + FIRMWARE_MODULE__X(FIRMWARE_MODULE__BURST_CREDITS_TASK)\ + FIRMWARE_STATUS__X(BURST_CREDITS_TASK_STATUS_TRYING_TO_ADD_ACTION_WHILE_NOT_IN_IDLE_STATE)\ + FIRMWARE_STATUS__X(BURST_CREDITS_TASK_STATUS_TOO_MANY_ACTIONS)\ + FIRMWARE_STATUS__X(BURST_CREDITS_TASK_STATUS_TRYING_TO_CHANGE_STATE_TO_INFER_WHILE_ALREADY_IN_INFER)\ + FIRMWARE_STATUS__X(BURST_CREDITS_TASK_STATUS_INFER_REACHED_TIMEOUT)\ + FIRMWARE_STATUS__X(BURST_CREDITS_TASK_STATUS_TASK_DEACTIVATED)\ + \ + typedef enum { #define FIRMWARE_MODULE__X(module) module, diff --git a/hailort/CMakeLists.txt b/hailort/CMakeLists.txt index c189d11..63edd68 100644 --- a/hailort/CMakeLists.txt +++ b/hailort/CMakeLists.txt @@ -2,7 +2,7 @@ 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_MINOR=8 ) add_definitions( -DFIRMWARE_VERSION_REVISION=0 ) message(STATUS "Building pre_build") @@ -15,13 +15,14 @@ execute_cmake( -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=${CMAKE_CURRENT_LIST_DIR}/pre_build/install -DHAILO_EXTERNAL_DIR=${HAILO_EXTERNAL_DIR} + -DHAILO_OFFLINE_COMPILATION=${HAILO_OFFLINE_COMPILATION} 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 +# 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) @@ -61,6 +62,17 @@ set(COMMON_INC_DIR ${PROJECT_SOURCE_DIR}/common/include) set(DRIVER_INC_DIR ${PROJECT_SOURCE_DIR}/hailort/drivers/common) if(HAILO_BUILD_PYBIND) + if(NOT PYTHON_EXECUTABLE AND PYBIND11_PYTHON_VERSION) + # PYBIND11_PYTHON_VERSION is prioritized (not virtual environment) if PYTHON_EXECUTABLE is not set. + # See https://pybind11.readthedocs.io/en/stable/changelog.html#v2-6-0-oct-21-2020 + if(${CMAKE_VERSION} VERSION_LESS "3.12.0") + find_package(PythonInterp ${PYBIND11_PYTHON_VERSION} REQUIRED) + set(PYTHON_EXECUTABLE ${Python_EXECUTABLE}) + else() + find_package(Python3 ${PYBIND11_PYTHON_VERSION} REQUIRED EXACT COMPONENTS Interpreter Development) + set(PYTHON_EXECUTABLE ${Python3_EXECUTABLE}) + endif() + endif() add_subdirectory(external/pybind11 EXCLUDE_FROM_ALL) endif() add_subdirectory(external/Catch2 EXCLUDE_FROM_ALL) @@ -69,28 +81,39 @@ 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) +if(CMAKE_SYSTEM_NAME STREQUAL QNX) + add_library(pevents STATIC EXCLUDE_FROM_ALL external/pevents/src/pevents.cpp) + target_include_directories(pevents PUBLIC external/pevents/src) + target_compile_definitions(pevents PRIVATE -DWFMO) +endif() + +# microprofile +if(HAILO_MICROPROFILE) + add_library(microprofile STATIC EXCLUDE_FROM_ALL external/microprofile/microprofile.cpp) + set_target_properties(microprofile PROPERTIES + CXX_STANDARD 11 + CXX_STANDARD_REQUIRED YES + POSITION_INDEPENDENT_CODE ON + ) + target_compile_definitions(microprofile + PRIVATE + -DMICROPROFILE_WEBSERVER=1 + -DMICROPROFILE_GPU_TIMERS=0 + -DMICROPROFILE_NAME_MAX_LEN=256 + PUBLIC + -DMICROPROFILE_ENABLED=1 + ) + target_include_directories(microprofile PUBLIC external/microprofile) +else() + add_library(microprofile INTERFACE) + target_compile_definitions(microprofile INTERFACE -DMICROPROFILE_ENABLED=0) + target_include_directories(microprofile INTERFACE external/microprofile) +endif() + 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 - $ - ) - set(VENV_PYHAILORT_INTERNAL_DIR ${CMAKE_SOURCE_DIR}/platform_internals/hailo_platform_internals/pyhailort/) - set(PYHAILORT_INTERNAL_FILES_TO_COPY - $ - ) - 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) diff --git a/hailort/LICENSE-3RD-PARTY.md b/hailort/LICENSE-3RD-PARTY.md index c7e4576..9c86db7 100644 --- a/hailort/LICENSE-3RD-PARTY.md +++ b/hailort/LICENSE-3RD-PARTY.md @@ -3,11 +3,13 @@ | 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 | +| pybind11 | Wenzel Jakob | BSD | 2.6.2 | 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 +| md5 | Alexander Peslyak | cut-down BSD | - | Copied code from website | http://openwall.info/wiki/people/solar/software/public-domain-source-code/md5 | +| pevents | Mahmoud Al-Qudsi | MIT License | master | Cloned entire package | https://github.com/neosmart/pevents.git | +| microprofile | Jonas Meyer | Unlicense License | 3.1 | Cloned entire package | https://github.com/jonasmr/microprofile | diff --git a/hailort/common/circular_buffer.hpp b/hailort/common/circular_buffer.hpp index c50b971..8df5461 100644 --- a/hailort/common/circular_buffer.hpp +++ b/hailort/common/circular_buffer.hpp @@ -97,6 +97,11 @@ public: return CB_HEAD(m_circ) == CB_TAIL(m_circ); } + bool full() + { + return 0 == CB_AVAIL(m_circ, CB_HEAD(m_circ), CB_TAIL(m_circ)); + } + private: circbuf_t m_circ; std::vector m_array; diff --git a/hailort/common/latency_meter.hpp b/hailort/common/latency_meter.hpp index c839a0f..eaed098 100644 --- a/hailort/common/latency_meter.hpp +++ b/hailort/common/latency_meter.hpp @@ -125,6 +125,7 @@ private: }; using LatencyMeterPtr = std::shared_ptr; +using LatencyMetersMap = std::map; } /* namespace hailort */ diff --git a/hailort/common/os/posix/filesystem.cpp b/hailort/common/os/posix/filesystem.cpp index 00fce95..fd96ce2 100644 --- a/hailort/common/os/posix/filesystem.cpp +++ b/hailort/common/os/posix/filesystem.cpp @@ -52,7 +52,7 @@ dirent* Filesystem::DirWalker::next_file() return readdir(m_dir); } -#if defined(__unix__) +#if defined(__linux__) Expected> Filesystem::get_files_in_dir_flat(const std::string &dir_path) { diff --git a/hailort/common/utils.hpp b/hailort/common/utils.hpp index f4f74d7..a93c6aa 100644 --- a/hailort/common/utils.hpp +++ b/hailort/common/utils.hpp @@ -52,6 +52,24 @@ static inline bool contains(const std::set &container, T value) return (container.find(value) != container.end()); } +template +class unlock_guard { +public: + unlock_guard(T &lock) : m_lock(lock) { + m_lock.unlock(); + } + + ~unlock_guard() { + m_lock.lock(); + } + + unlock_guard(const unlock_guard&) = delete; + unlock_guard& operator=(const unlock_guard&) = delete; + +private: + T &m_lock; +}; + // From https://stackoverflow.com/questions/57092289/do-stdmake-shared-and-stdmake-unique-have-a-nothrow-version template static inline std::unique_ptr make_unique_nothrow(Args&&... args) @@ -241,6 +259,18 @@ constexpr uint32_t get_nearest_powerof_2(uint32_t value, uint32_t min_power_of_2 return power_of_2; } +template +static uint32_t get_max_value_of_unordered_map(const std::unordered_map &map) +{ + uint32_t max_count = 0; + for (auto &name_counter_pair : map) { + if (name_counter_pair.second > max_count) { + max_count = name_counter_pair.second; + } + } + return max_count; +} + } /* 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 index 1177130..64603a1 100644 --- a/hailort/drivers/common/hailo_ioctl_common.h +++ b/hailort/drivers/common/hailo_ioctl_common.h @@ -29,11 +29,13 @@ #ifdef _MSC_VER #if !defined(bool) && !defined(__cplusplus) typedef uint8_t bool; -#endif +#endif // !defined(bool) && !defined(__cplusplus) + #if !defined(INT_MAX) #define INT_MAX 0x7FFFFFFF -#endif -#else +#endif // !defined(INT_MAX) + +#elif defined(__linux__) // #ifdef _MSC_VER #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/) @@ -43,11 +45,9 @@ typedef uint8_t bool; #include #include #include -#endif +#endif // ifndef __KERNEL__ -#if defined(__unix__) #include -#endif #define _IOW_ _IOW #define _IOR_ _IOR @@ -57,6 +57,21 @@ typedef uint8_t bool; #define HAILO_GENERAL_IOCTL_MAGIC 'g' #define HAILO_VDMA_IOCTL_MAGIC 'v' #define HAILO_WINDOWS_IOCTL_MAGIC 'w' + +#elif defined(__QNX__) // #ifdef _MSC_VER +#include +#include +#include +// defines for devctl +#define _IOW_ __DIOF +#define _IOR_ __DIOT +#define _IOWR_ __DIOTF +#define _IO_ __DION +#define HAILO_GENERAL_IOCTL_MAGIC _DCMD_ALL +#define HAILO_VDMA_IOCTL_MAGIC _DCMD_MISC + +#else // #ifdef _MSC_VER +#error "unsupported platform!" #endif #pragma pack(push, 1) @@ -181,6 +196,9 @@ struct hailo_fw_control { }; /* structure used in ioctl HAILO_BAR_TRANSFER */ +// Max bar transfer size gotten from ATR0_TABLE_SIZE +#define MAX_BAR_TRANSFER_LENGTH (4096) + enum hailo_transfer_direction { TRANSFER_READ = 0, TRANSFER_WRITE, @@ -194,7 +212,7 @@ struct hailo_bar_transfer_params { uint32_t bar_index; // in off_t offset; // in size_t count; // in - void* buffer; // in/out + uint8_t buffer[MAX_BAR_TRANSFER_LENGTH]; // in/out }; /* structure used in ioctl HAILO_VDMA_CHANNEL_REGISTERS */ @@ -222,9 +240,11 @@ struct hailo_vdma_buffer_sync_params { }; /* structure used in ioctl HAILO_READ_NOTIFICATION */ +#define MAX_NOTIFICATION_LENGTH (1500) + struct hailo_d2h_notification { size_t buffer_len; // out - uint8_t buffer[MAX_CONTROL_LENGTH]; // out + uint8_t buffer[MAX_NOTIFICATION_LENGTH]; // out }; enum hailo_board_type { @@ -254,14 +274,18 @@ struct hailo_driver_info { uint32_t minor_version; uint32_t revision_version; }; + +/* structure used in ioctl HAILO_READ_LOG */ +#define MAX_FW_LOG_BUFFER_LENGTH (512) + 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 + enum hailo_cpu_id cpu_id; // in + uint8_t buffer[MAX_FW_LOG_BUFFER_LENGTH]; // out + size_t buffer_size; // in + size_t read_bytes; // out }; -struct hailo_allocate_buffer_params { +struct hailo_allocate_low_memory_buffer_params { size_t buffer_size; // in uintptr_t buffer_handle; // out }; @@ -270,6 +294,12 @@ struct hailo_mark_as_in_use_params { bool in_use; // out }; +struct hailo_allocate_continuous_buffer_params { + size_t buffer_size; // in + uintptr_t buffer_handle; // out + uint64_t dma_address; // out +}; + #pragma pack(pop) enum hailo_general_ioctl_code { @@ -286,7 +316,7 @@ enum hailo_general_ioctl_code { 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_BAR_TRANSFER _IOWR_(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) @@ -311,6 +341,8 @@ enum hailo_vdma_ioctl_code { HAILO_VDMA_LOW_MEMORY_BUFFER_ALLOC_CODE, HAILO_VDMA_LOW_MEMORY_BUFFER_FREE_CODE, HAILO_MARK_AS_IN_USE_CODE, + HAILO_VDMA_CONTINUOUS_BUFFER_ALLOC_CODE, + HAILO_VDMA_CONTINUOUS_BUFFER_FREE_CODE, // Must be last HAILO_VDMA_IOCTL_MAX_NR, @@ -331,11 +363,15 @@ enum hailo_vdma_ioctl_code { #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_ALLOC _IOWR_(HAILO_VDMA_IOCTL_MAGIC, HAILO_VDMA_LOW_MEMORY_BUFFER_ALLOC_CODE, struct hailo_allocate_low_memory_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) +#define HAILO_VDMA_CONTINUOUS_BUFFER_ALLOC _IOWR_(HAILO_VDMA_IOCTL_MAGIC, HAILO_VDMA_CONTINUOUS_BUFFER_ALLOC_CODE, struct hailo_allocate_continuous_buffer_params) +#define HAILO_VDMA_CONTINUOUS_BUFFER_FREE _IO_(HAILO_VDMA_IOCTL_MAGIC, HAILO_VDMA_CONTINUOUS_BUFFER_FREE_CODE) + + enum hailo_windows_ioctl_code { HAILO_WINDOWS_DESC_LIST_MMAP_CODE, diff --git a/hailort/drivers/win/include/Public.h b/hailort/drivers/win/include/Public.h new file mode 100644 index 0000000..96b442a --- /dev/null +++ b/hailort/drivers/win/include/Public.h @@ -0,0 +1,124 @@ +/*++ + +Module Name: + + public.h + +Abstract: + + This module contains the common declarations shared by driver + and user applications. + +Environment: + + user and kernel + +--*/ + +// +// Define an Interface Guid so that apps can find the device and talk to it. +// + +DEFINE_GUID (GUID_DEVINTERFACE_HailoKM, + 0xd88d31f1,0xfede,0x4e71,0xac,0x2a,0x6c,0xe0,0x01,0x8c,0x15,0x01); +// {d88d31f1-fede-4e71-ac2a-6ce0018c1501} + +#define HAILO_IOCTL_COMMON CTL_CODE(FILE_DEVICE_UNKNOWN, 0x801, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define IOCTL_FUNC(x) (((x) >> 2) & 0xfff) +#define NUMBER_OF_PARAMETERS(code) ((code) & 0xf) +#define NUMBER_OF_PARAMETERS_FLEXIBLE 0xf + +struct tCommonHailoIoctlParam +{ + ULONG ulCode : 24; + ULONG fResponse : 1; + ULONG fUseLL : 1; + ULONG ulParamNum : 4; + union { + ULONG ulInputs[4]; + ULONGLONG llInputs[2]; + }; + union { + ULONG ulOutputs[4]; + ULONGLONG llOutputs[2]; + }; +}; + +#define HAILO_CMD_FW_LOAD 0x0010 +#define HAILO_CMD_READ_CFG 0x0011 +#define HAILO_CMD_SW_RESET 0x0020 +#define HAILO_CMD_READ_INTERRUPT_BAR 0x0021 +#define HAILO_CMD_READ_FW_STATUS 0x0030 +#define HAILO_CMD_READ_FIRMWARE_BAR 0x0031 +#define HAILO_CMD_CANCEL_READ 0x0040 +#define HAILO_CMD_READ_RP_CFG 0x0041 +#define HAILO_CMD_UNMAP_BUFFER 0x0050 +#define HAILO_CMD_MAP_BUFFER 0x0051 +#define HAILO_CMD_FREE_MEMORY 0x0060 +#define HAILO_CMD_ALLOC_MEMORY 0x0061 +#define HAILO_CMD_ABORT_ALL 0x0070 + +#define HAILO_IOCTL_COMPATIBLE CTL_CODE(FILE_DEVICE_UNKNOWN, 0x802, METHOD_BUFFERED, FILE_ANY_ACCESS) +struct tCompatibleHailoIoctlParam +{ + union { + struct { + ULONG Size : 16; + ULONG Code : 8; + ULONG Type : 6; + ULONG Read : 1; + ULONG Write : 1; + } bits; + ULONG value; + } u; +}; + +#define HAILO_GENERAL_IOCTL_MAGIC 0 +#define HAILO_VDMA_IOCTL_MAGIC 1 +#define HAILO_WINDOWS_IOCTL_MAGIC 2 + + + +static ULONG FORCEINLINE _IOC_(ULONG nr, ULONG type, ULONG size, bool read, bool write) +{ + tCompatibleHailoIoctlParam param; + param.u.bits.Code = nr; + param.u.bits.Size = size; + param.u.bits.Type = type; + param.u.bits.Read = read ? 1 : 0; + param.u.bits.Write = write ? 1 : 0; + return param.u.value; +} + +#define _IOW_(type,nr,size) _IOC_(nr, type, sizeof(size), true, false) +#define _IOR_(type,nr,size) _IOC_(nr, type, sizeof(size), false, true) +#define _IOWR_(type,nr,size) _IOC_(nr, type, sizeof(size), true, true) +#define _IO_(type,nr) _IOC_(nr, type, 0, false, false) + +#include "..\..\common\hailo_ioctl_common.h" + +struct tCompatibleHailoIoctlData +{ + tCompatibleHailoIoctlParam Parameters; + ULONG_PTR Value; + union { + hailo_bar_transfer_params BarTransfer; + hailo_vdma_channel_enable_params ChannelEnable; + hailo_vdma_channel_disable_params ChannelDisable; + hailo_vdma_channel_wait_params ChannelWait; + hailo_vdma_channel_abort_params ChannelAbort; + hailo_vdma_channel_clear_abort_params ChannelClearAbort; + hailo_vdma_buffer_sync_params VdmaBufferSync; + hailo_fw_control FirmwareControl; + hailo_vdma_buffer_map_params VdmaBufferMap; + hailo_desc_list_create_params DescListCreate; + hailo_desc_list_bind_vdma_buffer_params DescListBind; + hailo_d2h_notification D2HNotification; + hailo_device_properties DeviceProperties; + hailo_driver_info DriverInfo; + hailo_channel_registers_params ChannelRegisters; + hailo_windows_desc_list_mmap_params DescListMmap; + hailo_read_log_params ReadLog; + hailo_mark_as_in_use_params MarkAsInUse; + } Buffer; +}; diff --git a/hailort/drivers/win/include/hailo_pcie_version.h b/hailort/drivers/win/include/hailo_pcie_version.h new file mode 100644 index 0000000..2dfaa33 --- /dev/null +++ b/hailort/drivers/win/include/hailo_pcie_version.h @@ -0,0 +1,10 @@ +#ifndef _HAILO_PCIE_VERSION_H_ +#define _HAILO_PCIE_VERSION_H_ + +#include "..\..\common\hailo_pcie_version.h" + +#define STRINGIFY_EXPANDED( x ) #x +#define STRINGIFY_NUMBER( x ) STRINGIFY_EXPANDED(x) +#define HAILO_DRV_VER STRINGIFY_NUMBER(HAILO_DRV_VER_MAJOR) "." STRINGIFY_NUMBER(HAILO_DRV_VER_MINOR) "." STRINGIFY_NUMBER(HAILO_DRV_VER_REVISION) + +#endif /* _HAILO_PCIE_VERSION_H_ */ diff --git a/hailort/hailortcli/CMakeLists.txt b/hailort/hailortcli/CMakeLists.txt index 4fb14ec..ba3dc6a 100644 --- a/hailort/hailortcli/CMakeLists.txt +++ b/hailort/hailortcli/CMakeLists.txt @@ -1,5 +1,7 @@ cmake_minimum_required(VERSION 3.0.0) +include(GNUInstallDirs) + set(HAILORTCLI_CPP_FILES hailortcli.cpp command.cpp @@ -29,7 +31,6 @@ if(UNIX) 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() @@ -50,11 +51,16 @@ add_executable(hailortcli ) 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 +set_property(TARGET hailortcli PROPERTY INSTALL_RPATH "$ORIGIN" "../lib/") # Link with a relative libhailort target_link_libraries(hailortcli libhailort CLI11::CLI11 nlohmann_json spdlog::spdlog readerwriterqueue) target_link_libraries(hailortcli DotWriter) +# TODO: Remove microprofile after removing pipeline.cpp from hailortcli sources +target_link_libraries(hailortcli microprofile) + if(WIN32) target_link_libraries(hailortcli Ws2_32 Iphlpapi Shlwapi) +elseif(CMAKE_SYSTEM_NAME STREQUAL QNX) + target_link_libraries(hailortcli pevents) endif() target_include_directories(hailortcli PRIVATE diff --git a/hailort/hailortcli/benchmark_command.cpp b/hailort/hailortcli/benchmark_command.cpp index 7b17de5..f7260f7 100644 --- a/hailort/hailortcli/benchmark_command.cpp +++ b/hailort/hailortcli/benchmark_command.cpp @@ -28,13 +28,20 @@ BenchmarkCommand::BenchmarkCommand(CLI::App &parent_app) : 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") + m_app->add_option("-t, --time-to-run", m_params.time_to_run, "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("--power-mode", m_params.power_mode, + "Core power mode (PCIE only; ignored otherwise)") + ->transform(HailoCheckedTransformer({ + { "performance", hailo_power_mode_t::HAILO_POWER_MODE_PERFORMANCE }, + { "ultra_performance", hailo_power_mode_t::HAILO_POWER_MODE_ULTRA_PERFORMANCE } + })) + ->default_val("performance"); 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"); @@ -66,6 +73,7 @@ hailo_status BenchmarkCommand::execute() auto streaming_mode_info = fps_streaming_mode(); CHECK_EXPECTED_AS_STATUS(streaming_mode_info, "FPS in streaming mode failed"); + // TODO - HRT-6931 - measure latnecy only in the case of single device. std::cout << "Measuring HW Latency" << std::endl; auto latency_info = latency(); CHECK_EXPECTED_AS_STATUS(latency_info, "Latency measuring failed"); @@ -110,7 +118,6 @@ Expected BenchmarkCommand::hw_only_mode() 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); } @@ -121,7 +128,6 @@ Expected BenchmarkCommand::fps_streaming_mode() 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); } @@ -132,6 +138,5 @@ Expected BenchmarkCommand::latency() 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 index bffd401..38178e0 100644 --- a/hailort/hailortcli/benchmark_command.hpp +++ b/hailort/hailortcli/benchmark_command.hpp @@ -27,7 +27,6 @@ private: inference_runner_params m_params; bool m_not_measure_power; - uint32_t m_time; std::string m_csv_file_path; }; diff --git a/hailort/hailortcli/command.cpp b/hailort/hailortcli/command.cpp index 59ad875..49b1e04 100644 --- a/hailort/hailortcli/command.cpp +++ b/hailort/hailortcli/command.cpp @@ -41,6 +41,10 @@ DeviceCommand::DeviceCommand(CLI::App *app) : hailo_status DeviceCommand::execute() { + if ((DeviceType::PCIE == m_device_params.device_type ) && + ("*" == m_device_params.pcie_params.pcie_bdf)) { + return execute_on_all_pcie_devices(); + } auto device = create_device(m_device_params); if (!device) { return device.status(); @@ -49,23 +53,24 @@ hailo_status DeviceCommand::execute() return execute_on_device(*device.value()); } -PcieDeviceCommand::PcieDeviceCommand(CLI::App *app) : - Command(app) +hailo_status DeviceCommand::execute_on_all_pcie_devices() { - auto group = app->add_option_group("PCIE Device Options"); - - // PCIe options - group->add_option("-s,--bdf", m_pcie_device_params.pcie_bdf, - "Device id ([]::., 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(); + auto status = HAILO_SUCCESS; // Best effort + auto all_devices_infos = Device::scan_pcie(); + if (!all_devices_infos) { + return all_devices_infos.status(); } + for (auto &dev_info : all_devices_infos.value()) { + auto device = Device::create_pcie(dev_info); + if (!device) { + return device.status(); + } - return execute_on_device(*device.value()); + auto execute_status = execute_on_device(*device.value()); + if (HAILO_SUCCESS != execute_status) { + std::cerr << "Failed to execute on device: " << device.value()->get_dev_id() << ". status= " << execute_status << std::endl; + status = execute_status; + } + } + return status; } diff --git a/hailort/hailortcli/command.hpp b/hailort/hailortcli/command.hpp index e92e999..9ad9f9c 100644 --- a/hailort/hailortcli/command.hpp +++ b/hailort/hailortcli/command.hpp @@ -26,11 +26,6 @@ public: 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); @@ -70,21 +65,10 @@ public: protected: virtual hailo_status execute_on_device(Device &device) = 0; + hailo_status execute_on_all_pcie_devices(); 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/download_action_list_command.cpp b/hailort/hailortcli/download_action_list_command.cpp index 3832338..6cb49ce 100644 --- a/hailort/hailortcli/download_action_list_command.cpp +++ b/hailort/hailortcli/download_action_list_command.cpp @@ -13,6 +13,7 @@ #include "md5.h" #include +#include constexpr int DownloadActionListCommand::INVALID_NUMERIC_VALUE; @@ -21,7 +22,7 @@ DownloadActionListCommand::DownloadActionListCommand(CLI::App &parent_app) : { 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") + ->default_val("runtime_data.json") ->check(FileSuffixValidator(JSON_SUFFIX)); } @@ -59,6 +60,11 @@ hailo_status DownloadActionListCommand::execute(Device &device, const std::strin return HAILO_SUCCESS; } +hailo_status DownloadActionListCommand::set_batch_to_measure(Device &device, uint16_t batch_to_measure) +{ + return device.set_context_action_list_timestamp_batch(batch_to_measure); +} + hailo_status DownloadActionListCommand::execute_on_device(Device &device) { return execute(device, m_output_file_path); @@ -101,7 +107,8 @@ Expected DownloadActionListCommand::calc_md5_hexdigest(const std::s 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(md5_sum[i]); + static const int NUM_HEX_DIGITS_IN_UNIT8 = 2; + hexdigest << std::hex << std::setfill('0') << std::setw(NUM_HEX_DIGITS_IN_UNIT8) << static_cast(md5_sum[i]); } return hexdigest.str(); @@ -180,9 +187,9 @@ Expected DownloadActionListCommand::parse_action_data(uint32_t bas data_json = *reinterpret_cast(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(action); - action_length_local = sizeof(CONTEXT_SWITCH_DEFS__read_vdma_action_data_t); + case CONTEXT_SWITCH_DEFS__ACTION_TYPE_FETCH_CFG_CHANNEL_DESCRIPTORS: + data_json = *reinterpret_cast(action); + action_length_local = sizeof(CONTEXT_SWITCH_DEFS__fetch_cfg_channel_descriptors_action_data_t); break; case CONTEXT_SWITCH_DEFS__ACTION_TYPE_FETCH_CCW_BURSTS: data_json = *reinterpret_cast(action); @@ -200,6 +207,10 @@ Expected DownloadActionListCommand::parse_action_data(uint32_t bas data_json = *reinterpret_cast(action); action_length_local = sizeof(CONTEXT_SWITCH_DEFS__deactivate_vdma_channel_action_data_t); break; + case CONTEXT_SWITCH_DEFS__ACTION_TYPE_VALIDATE_VDMA_CHANNEL: + data_json = *reinterpret_cast(action); + action_length_local = sizeof(CONTEXT_SWITCH_DEFS__validate_vdma_channel_action_data_t); + break; case CONTEXT_SWITCH_DEFS__ACTION_TYPE_ENABLE_LCU_DEFAULT: data_json = *reinterpret_cast(action); action_length_local = sizeof(CONTEXT_SWITCH_DEFS__enable_lcu_action_default_data_t); @@ -248,6 +259,10 @@ Expected DownloadActionListCommand::parse_action_data(uint32_t bas data_json = json({}); action_length_local = 0; break; + case CONTEXT_SWITCH_DEFS__ACTION_TYPE_BURST_CREDITS_TASK_START: + data_json = json({}); + action_length_local = 0; + break; case CONTEXT_SWITCH_DEFS__ACTION_TYPE_ACTIVATE_CFG_CHANNEL: data_json = *reinterpret_cast(action); action_length_local = sizeof(CONTEXT_SWITCH_DEFS__activate_cfg_channel_t); @@ -391,7 +406,7 @@ Expected DownloadActionListCommand::parse_network_groups(Device &d return network_group_list_json; } -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_cfg_channel_descriptors_action_data_t& data) { j = json{{"descriptors_count", data.descriptors_count}, {"channel_index", data.cfg_channel_number}}; } diff --git a/hailort/hailortcli/download_action_list_command.hpp b/hailort/hailortcli/download_action_list_command.hpp index bbbc330..e669982 100644 --- a/hailort/hailortcli/download_action_list_command.hpp +++ b/hailort/hailortcli/download_action_list_command.hpp @@ -27,6 +27,7 @@ public: // 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=""); + static hailo_status set_batch_to_measure(Device &device, uint16_t batch_to_measure); protected: virtual hailo_status execute_on_device(Device &device) override; @@ -62,7 +63,7 @@ private: // 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_FETCH_CFG_CHANNEL_DESCRIPTORS, "fetch_cfg_channel_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"}, @@ -75,9 +76,11 @@ NLOHMANN_JSON_SERIALIZE_ENUM(CONTEXT_SWITCH_DEFS__ACTION_TYPE_t, { {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_VALIDATE_VDMA_CHANNEL, "validate_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_BURST_CREDITS_TASK_START, "burst_credits_task_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"}, @@ -97,6 +100,7 @@ NLOHMANN_JSON_SERIALIZE_ENUM(CONTEXT_SWITCH_DEFS__ACTION_TYPE_t, { 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__validate_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); @@ -118,7 +122,7 @@ NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(CONTEXT_SWITCH_DEFS__deactivate_cfg_channel_t 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_cfg_channel_descriptors_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); diff --git a/hailort/libhailort/bindings/python/platform/hailo_platform/tools/firmware/example_config.json b/hailort/hailortcli/example_config.json similarity index 100% rename from hailort/libhailort/bindings/python/platform/hailo_platform/tools/firmware/example_config.json rename to hailort/hailortcli/example_config.json diff --git a/hailort/hailortcli/fw_control_command.cpp b/hailort/hailortcli/fw_control_command.cpp index 3be6e9a..5124600 100644 --- a/hailort/hailortcli/fw_control_command.cpp +++ b/hailort/hailortcli/fw_control_command.cpp @@ -230,19 +230,9 @@ FwControlCommand::FwControlCommand(CLI::App &parent_app) : { add_subcommand(); add_subcommand(); - - // TODO: Remove scan as a subcommand of fw_control_subcommand (HRT-2676) - // Can also remove Command::set_description function after this, and the return value of `add_subcommand` - auto &scan = add_subcommand(); - scan.set_description("Alias for root-level 'scan' command (i.e. 'hailortcli scan...')\n" - "Note: 'scan' as a sub-command of 'fw-control' is deprecated; use 'hailortcli scan' instead\n" - " (or 'hailo scan' when using the 'hailo' command)."); - add_subcommand(); // TODO: Support on windows (HRT-5919) #if defined(__GNUC__) - // TODO: Unhide (HRT-6035) - static const bool HIDDEN = true; - add_subcommand(HIDDEN); + add_subcommand(); #endif } diff --git a/hailort/hailortcli/fw_control_command.hpp b/hailort/hailortcli/fw_control_command.hpp index 7ffbcd4..d475bd3 100644 --- a/hailort/hailortcli/fw_control_command.hpp +++ b/hailort/hailortcli/fw_control_command.hpp @@ -12,8 +12,6 @@ #include "hailortcli.hpp" #include "command.hpp" -// TODO: Remove scan as a subcommand of fw_control_subcommand (HRT-2676) -#include "scan_command.hpp" #if defined(__GNUC__) // TODO: Support on windows (HRT-5919) #include "download_action_list_command.hpp" diff --git a/hailort/hailortcli/hailortcli.cpp b/hailort/hailortcli/hailortcli.cpp index ceabba7..5493222 100644 --- a/hailort/hailortcli/hailortcli.cpp +++ b/hailort/hailortcli/hailortcli.cpp @@ -120,25 +120,19 @@ void add_device_options(CLI::App *app, hailo_device_params &device_params) 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 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'.") + "Default is pcie.") ->transform(device_type_transformer); - std::vector actions{ std::make_shared(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 ([]::., same as in lspci command)") + "Device id ([]::., same as in lspci command).\n" \ + "In order to run on all PCIe devices connected to the machine one-by-one, use '*' (instead of device id).") ->default_val(""); // Ethernet options @@ -194,6 +188,28 @@ void add_vdevice_options(CLI::App *app, hailo_device_params &device_params) { }); } +static bool do_versions_match() +{ + hailo_version_t libhailort_version = {}; + auto status = hailo_get_library_version(&libhailort_version); + if (HAILO_SUCCESS != status) { + std::cerr << "Failed to get libhailort version" << std::endl; + return false; + } + + bool versions_match = ((HAILORT_MAJOR_VERSION == libhailort_version.major) && + (HAILORT_MINOR_VERSION == libhailort_version.minor) && + (HAILORT_REVISION_VERSION == libhailort_version.revision)); + if (!versions_match) { + std::cerr << "libhailort version (" << + libhailort_version.major << "." << libhailort_version.minor << "." << libhailort_version.revision << + ") does not match HailoRT-CLI version (" << + HAILORT_MAJOR_VERSION << "." << HAILORT_MINOR_VERSION << "." << HAILORT_REVISION_VERSION << ")" << std::endl; + return false; + } + return true; +} + class HailoRTCLI : public ContainerCommand { public: HailoRTCLI(CLI::App *app) : ContainerCommand(app) @@ -201,7 +217,7 @@ public: m_app->add_flag_callback("-v,--version", [] () { - std::cout << "hailortcli version " << + std::cout << "HailoRT-CLI 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{}); @@ -235,6 +251,9 @@ public: }; int main(int argc, char** argv) { + if (!do_versions_match()) { + return -1; + } auto console_sink = std::make_shared(); console_sink->set_level(spdlog::level::info); console_sink->set_pattern("[%n] [%^%l%$] %v"); diff --git a/hailort/hailortcli/hailortcli.hpp b/hailort/hailortcli/hailortcli.hpp index 8edf17a..5c61bf6 100644 --- a/hailort/hailortcli/hailortcli.hpp +++ b/hailort/hailortcli/hailortcli.hpp @@ -28,7 +28,7 @@ using namespace hailort; } while (0) struct hailo_pcie_params { - std::string pcie_bdf; // if empty use the first scanned + std::string pcie_bdf; // if empty use the first scanned. if '*', run on all devices on the machine one-by-one }; struct hailo_eth_params { diff --git a/hailort/hailortcli/power_measurement_command.cpp b/hailort/hailortcli/power_measurement_command.cpp index 493e475..72c4793 100644 --- a/hailort/hailortcli/power_measurement_command.cpp +++ b/hailort/hailortcli/power_measurement_command.cpp @@ -10,9 +10,6 @@ #include "power_measurement_command.hpp" #include -#define POWER_MEASUREMENT_DELAY_MS(__sample_period, __average_factor) \ - (static_cast((__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(), @@ -120,19 +117,13 @@ Expected PowerMeasurementSubcommand::start_power_measureme return make_unexpected(status); } - status = hailo_set_power_measurement(reinterpret_cast(&device), 0, dvm, measurement_type); + status = hailo_set_power_measurement(reinterpret_cast(&device), HAILO_MEASUREMENT_BUFFER_INDEX_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(&device), measurement_delay, + status = hailo_start_power_measurement(reinterpret_cast(&device), averaging_factor_enum, sampling_period_enum); if (HAILO_SUCCESS != status) { std::cerr << "Failed to start power measurement, status " << status << std::endl; @@ -155,7 +146,7 @@ hailo_status LongPowerMeasurement::stop() return status; } - status = hailo_get_power_measurement(reinterpret_cast(&m_device), 0, true, &m_data); + status = hailo_get_power_measurement(reinterpret_cast(&m_device), HAILO_MEASUREMENT_BUFFER_INDEX_0, true, &m_data); if (HAILO_SUCCESS != status) { std::cerr << "Failed to get power measurement results, status " << status << std::endl; return status; diff --git a/hailort/hailortcli/run_command.cpp b/hailort/hailortcli/run_command.cpp index fc73096..f623b17 100644 --- a/hailort/hailortcli/run_command.cpp +++ b/hailort/hailortcli/run_command.cpp @@ -61,7 +61,7 @@ void user_signal_handler_func(int signum) hailo_status wait_for_exit_with_timeout(std::chrono::seconds time_to_run) { -#if defined(__unix__) +#if defined(__linux__) 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; @@ -100,8 +100,6 @@ static void add_run_command_params(CLI::App *run_subcommand, inference_runner_pa 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); @@ -202,20 +200,20 @@ static void add_run_command_params(CLI::App *run_subcommand, inference_runner_pa // 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"); + auto *collect_runtime_data_subcommand = run_subcommand->add_subcommand("collect-runtime-data", + "Collect runtime data to be used by the Profiler"); static const char *JSON_SUFFIX = ".json"; - download_runtime_data_subcommand->add_option("--output-path", + collect_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") + ->default_val("runtime_data.json") ->check(FileSuffixValidator(JSON_SUFFIX)); - download_runtime_data_subcommand->parse_complete_callback([¶ms]() { + static const uint32_t DEFAULT_BATCH_TO_MEASURE = 2; + collect_runtime_data_subcommand->add_option("--batch-to-measure", + params.runtime_data.batch_to_measure, "Batch to be measured") + ->default_val(DEFAULT_BATCH_TO_MEASURE); + collect_runtime_data_subcommand->parse_complete_callback([¶ms]() { // If this subcommand was parsed, then we need to download runtime_data - params.runtime_data.download_runtime_data = true; + params.runtime_data.collect_runtime_data = true; }); #endif @@ -229,9 +227,9 @@ static void add_run_command_params(CLI::App *run_subcommand, inference_runner_pa run_subcommand->add_flag("--measure-temp", params.measure_temp, "Measure chip temperature"); - run_subcommand->parse_complete_callback([¶ms, hef_new, hef_old, power_sampling_period, + run_subcommand->parse_complete_callback([¶ms, hef_new, 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"); + PARSE_CHECK(!hef_new->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)), @@ -248,16 +246,22 @@ static void add_run_command_params(CLI::App *run_subcommand, inference_runner_pa !(params.power_measurement.measure_power || params.power_measurement.measure_current || params.measure_temp), "Writing measurements in csv format is not supported for multiple devices"); + PARSE_CHECK(("*" != params.device_params.pcie_params.pcie_bdf), + "Passing '*' as BDF is not supported for 'run' command. for multiple devices inference see '--device-count'"); + if ((0 == params.time_to_run) && (0 == params.frames_count)) { // Use default params.time_to_run = DEFAULT_TIME_TO_RUN_SECONDS; } - }); - std::vector actions{ - std::make_shared(hef_old, "hef (positional)"), - }; - hailo_deprecate_options(run_subcommand, actions, false); + if (params.runtime_data.collect_runtime_data) { + if ((0 != params.frames_count) && (params.frames_count < params.runtime_data.batch_to_measure)) { + LOGGER__WARNING("--frames-count ({}) is smaller than --batch-to-measure ({}), " + "hence timestamps will not be updated in runtime data", params.frames_count, + params.runtime_data.batch_to_measure); + } + } + }); } std::map format_strings_to_key_value_pairs(const std::vector &key_value_pairs_str) { @@ -1030,18 +1034,34 @@ Expected run_command_hef_single_device(const inference_ auto network_group_list = device.value()->configure(hef.value(), configure_params.value()); CHECK_EXPECTED(network_group_list, "Failed configure device from hef"); + #if defined(__GNUC__) + // TODO: Support on windows (HRT-5919) + if (params.runtime_data.collect_runtime_data) { + DownloadActionListCommand::set_batch_to_measure(*device.value(), params.runtime_data.batch_to_measure); + } + #endif + // 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) { + if (params.runtime_data.collect_runtime_data) { + if ((0 == params.frames_count) && inference_result) { + const auto frames_count = inference_result->frames_count(); + if (frames_count && (frames_count.value() < params.runtime_data.batch_to_measure)) { + LOGGER__WARNING("Number of frames sent ({}) is smaller than --batch-to-measure ({}), " + "hence timestamps will not be updated in runtime data", frames_count.value(), + params.runtime_data.batch_to_measure); + } + } + DownloadActionListCommand::execute(*device.value(), params.runtime_data.runtime_data_output_path, network_group_list.value(), params.hef_path); } #endif - + CHECK_EXPECTED(inference_result); return inference_result; } diff --git a/hailort/hailortcli/run_command.hpp b/hailort/hailortcli/run_command.hpp index 7cfcae6..85f45d9 100644 --- a/hailort/hailortcli/run_command.hpp +++ b/hailort/hailortcli/run_command.hpp @@ -45,8 +45,9 @@ struct pipeline_stats_measurement_params { }; struct runtime_data_params { - bool download_runtime_data; + bool collect_runtime_data; std::string runtime_data_output_path; + uint16_t batch_to_measure; }; struct inference_runner_params { diff --git a/hailort/hailortcli/scan_command.cpp b/hailort/hailortcli/scan_command.cpp index 073d3ae..2d9e4e2 100644 --- a/hailort/hailortcli/scan_command.cpp +++ b/hailort/hailortcli/scan_command.cpp @@ -17,39 +17,28 @@ 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_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'.") + "Device type to use.") ->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); + ->excludes(interface_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(); + m_app->parse_complete_callback([this, device_type_option, interface_ip_option, interface_name_option]() { + bool eth_options_given = !interface_ip_option->empty() || !interface_name_option->empty(); // The user didn't put target, we can figure it ourself if (device_type_option->empty()) { @@ -71,12 +60,6 @@ ScanSubcommand::ScanSubcommand(CLI::App &parent_app) : throw CLI::ParseError("Ethernet options set on non eth device", CLI::ExitCodes::InvalidError); } }); - - std::vector actions{ - std::make_shared(device_type_option, "udp", "eth"), - std::make_shared(ip_option, "--interface-ip") - }; - hailo_deprecate_options(m_app, actions, false); } hailo_status ScanSubcommand::execute() diff --git a/hailort/libhailort/CMakeLists.txt b/hailort/libhailort/CMakeLists.txt index 393ad62..aff9879 100644 --- a/hailort/libhailort/CMakeLists.txt +++ b/hailort/libhailort/CMakeLists.txt @@ -2,7 +2,7 @@ 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_MINOR_VERSION 8) set(HAILORT_REVISION_VERSION 0) # Add the cmake folder so the modules there are found diff --git a/hailort/libhailort/bindings/CMakeLists.txt b/hailort/libhailort/bindings/CMakeLists.txt index 3388d7f..c803727 100644 --- a/hailort/libhailort/bindings/CMakeLists.txt +++ b/hailort/libhailort/bindings/CMakeLists.txt @@ -2,6 +2,7 @@ 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) diff --git a/hailort/libhailort/bindings/gstreamer/CMakeLists.txt b/hailort/libhailort/bindings/gstreamer/CMakeLists.txt index 7094c83..c9fe743 100644 --- a/hailort/libhailort/bindings/gstreamer/CMakeLists.txt +++ b/hailort/libhailort/bindings/gstreamer/CMakeLists.txt @@ -2,12 +2,13 @@ cmake_minimum_required(VERSION 3.0.0) project(gsthailo) +include(GNUInstallDirs) + 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) +find_package(HailoRT 4.8.0 EXACT REQUIRED) # GST_PLUGIN_DEFINE needs PACKAGE to be defined set(GST_HAILO_PACKAGE_NAME "hailo") diff --git a/hailort/libhailort/bindings/gstreamer/cmake/FindHailoRT.cmake b/hailort/libhailort/bindings/gstreamer/cmake/FindHailoRT.cmake deleted file mode 100644 index 85906ac..0000000 --- a/hailort/libhailort/bindings/gstreamer/cmake/FindHailoRT.cmake +++ /dev/null @@ -1,28 +0,0 @@ -# - 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.hpp b/hailort/libhailort/bindings/gstreamer/gst-hailo/common.hpp index 1ee88a3..8e8759e 100644 --- a/hailort/libhailort/bindings/gstreamer/gst-hailo/common.hpp +++ b/hailort/libhailort/bindings/gstreamer/gst-hailo/common.hpp @@ -51,6 +51,9 @@ using namespace hailort; #define HAILO_SUPPORTED_FORMATS "{ RGB, YUY2 }" #define HAILO_VIDEO_CAPS GST_VIDEO_CAPS_MAKE(HAILO_SUPPORTED_FORMATS) +#define HAILO_DEFAULT_SCHEDULER_TIMEOUT_MS (0) +#define HAILO_DEFAULT_SCHEDULER_THRESHOLD (1) + #define GST_CHECK(cond, ret_val, element, domain, ...) \ do { \ if (!(cond)) { \ diff --git a/hailort/libhailort/bindings/gstreamer/gst-hailo/gsthailodevicestats.cpp b/hailort/libhailort/bindings/gstreamer/gst-hailo/gsthailodevicestats.cpp index 1049a16..f4a2f31 100644 --- a/hailort/libhailort/bindings/gstreamer/gst-hailo/gsthailodevicestats.cpp +++ b/hailort/libhailort/bindings/gstreamer/gst-hailo/gsthailodevicestats.cpp @@ -31,7 +31,6 @@ GST_DEBUG_CATEGORY_STATIC(gst_hailodevicestats_debug_category); 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(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); @@ -226,10 +225,10 @@ hailo_status HailoDeviceStatsImpl::run_measure_loop() 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); + status = m_device->set_power_measurement(HAILO_MEASUREMENT_BUFFER_INDEX_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); + status = m_device->start_power_measurement(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); @@ -237,7 +236,7 @@ hailo_status HailoDeviceStatsImpl::run_measure_loop() const char *device_raw_string = device_string->c_str(); while (m_is_thread_running.load()) { - auto measurement = m_device->get_power_measurement(0, true); + auto measurement = m_device->get_power_measurement(HAILO_MEASUREMENT_BUFFER_INDEX_0, true); GST_CHECK_EXPECTED_AS_STATUS(measurement, m_element, RESOURCE, "Getting power measurement failed, status = %d", measurement.status()); if (!m_is_silent) { diff --git a/hailort/libhailort/bindings/gstreamer/gst-hailo/gsthailonet.cpp b/hailort/libhailort/bindings/gstreamer/gst-hailo/gsthailonet.cpp index cd1fa98..b39e4e2 100644 --- a/hailort/libhailort/bindings/gstreamer/gst-hailo/gsthailonet.cpp +++ b/hailort/libhailort/bindings/gstreamer/gst-hailo/gsthailonet.cpp @@ -30,6 +30,28 @@ GST_DEBUG_CATEGORY_STATIC(gst_hailonet_debug_category); #define GST_CAT_DEFAULT gst_hailonet_debug_category +#define GST_TYPE_SCHEDULING_ALGORITHM (gst_scheduling_algorithm_get_type ()) +static GType +gst_scheduling_algorithm_get_type (void) +{ + static GType scheduling_algorithm_type = 0; + + /* Tightly coupled to hailo_scheduling_algorithm_e */ + + if (!scheduling_algorithm_type) { + static GEnumValue algorithm_types[] = { + { HAILO_SCHEDULING_ALGORITHM_NONE, "Scheduler is not active", "HAILO_SCHEDULING_ALGORITHM_NONE" }, + { HAILO_SCHEDULING_ALGORITHM_ROUND_ROBIN, "Round robin", "HAILO_SCHEDULING_ALGORITHM_ROUND_ROBIN" }, + { HAILO_SCHEDULING_ALGORITHM_MAX_ENUM, NULL, NULL }, + }; + + scheduling_algorithm_type = + g_enum_register_static ("GstHailoSchedulingAlgorithms", algorithm_types); + } + + return scheduling_algorithm_type; +} + 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); @@ -53,7 +75,10 @@ enum PROP_OUTPUTS_MAX_POOL_SIZE, PROP_IS_ACTIVE, PROP_DEVICE_COUNT, - PROP_VDEVICE_KEY + PROP_VDEVICE_KEY, + PROP_SCHEDULING_ALGORITHM, + PROP_SCHEDULER_TIMEOUT_MS, + PROP_SCHEDULER_THRESHOLD, }; G_DEFINE_TYPE(GstHailoNet, gst_hailonet, GST_TYPE_BIN); @@ -116,9 +141,26 @@ static void gst_hailonet_class_init(GstHailoNetClass *klass) 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, + "By default, the hailonet element will not be active unless it is the only one. " + "Setting this property in combination with 'scheduling-algorithm' different than HAILO_SCHEDULING_ALGORITHM_NONE is not supported.", false, (GParamFlags)(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS))); + g_object_class_install_property(gobject_class, PROP_SCHEDULING_ALGORITHM, + g_param_spec_enum("scheduling-algorithm", "Scheduling policy for automatic network group switching", "Controls the Model Scheduler algorithm of HailoRT. " + "Gets values from the enum GstHailoSchedulingAlgorithms. " + "Using Model Scheduler algorithm different than HAILO_SCHEDULING_ALGORITHM_NONE, excludes the property 'is-active'. " + "When using the same VDevice across multiple hailonets, all should have the same 'scheduling-algorithm'. " + "Currently only supported with 1 device (e.g. device-count=1).", + GST_TYPE_SCHEDULING_ALGORITHM, HAILO_SCHEDULING_ALGORITHM_NONE, + (GParamFlags)(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS))); + g_object_class_install_property(gobject_class, PROP_SCHEDULER_TIMEOUT_MS, + g_param_spec_uint("scheduler-timeout-ms", "Timeout for for scheduler in ms", "The maximum time period that may pass before getting run time from the scheduler," + " as long as at least one send request has been sent.", + HAILO_DEFAULT_SCHEDULER_TIMEOUT_MS, std::numeric_limits::max(), HAILO_DEFAULT_SCHEDULER_TIMEOUT_MS, (GParamFlags)(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS))); + g_object_class_install_property(gobject_class, PROP_SCHEDULER_THRESHOLD, + g_param_spec_uint("scheduler-threshold", "Frames threshold for scheduler", "The minimum number of send requests required before the hailonet is considered ready to get run time from the scheduler.", + HAILO_DEFAULT_SCHEDULER_THRESHOLD, std::numeric_limits::max(), HAILO_DEFAULT_SCHEDULER_THRESHOLD, (GParamFlags)(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS))); + // See information about the "flush" signal in the element description g_signal_new( "flush", @@ -333,6 +375,11 @@ void HailoNetImpl::set_property(GObject *object, guint property_id, const GValue { gboolean new_is_active = g_value_get_boolean(value); + if (HAILO_SCHEDULING_ALGORITHM_NONE != m_props.m_scheduling_algorithm.get()) { + g_error("scheduling-algorithm different than HAILO_SCHEDULING_ALGORITHM_NONE in combination with 'is-active' is not supported."); + break; + } + 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 @@ -358,6 +405,39 @@ void HailoNetImpl::set_property(GObject *object, guint property_id, const GValue } break; } + case PROP_SCHEDULING_ALGORITHM: + if (m_was_configured) { + g_warning("The network was already configured so changing the scheduling algorithm will not take place!"); + break; + } + if (m_props.m_is_active.was_changed() && (g_value_get_enum(value) != HAILO_SCHEDULING_ALGORITHM_NONE)) { + g_error("scheduling-algorithm different than HAILO_SCHEDULING_ALGORITHM_NONE in combination with 'is-active' is not supported."); + break; + } + m_props.m_scheduling_algorithm = static_cast(g_value_get_enum(value)); + break; + case PROP_SCHEDULER_TIMEOUT_MS: + if (m_was_configured) { + g_warning("The network was already configured so changing the scheduling algorithm will not take place!"); + break; + } + if (m_props.m_is_active.was_changed()) { + g_error("scheduler usage (scheduler-timeout-ms) in combination with 'is-active' is not supported."); + break; + } + m_props.m_scheduler_timeout_ms = g_value_get_uint(value); + break; + case PROP_SCHEDULER_THRESHOLD: + if (m_was_configured) { + g_warning("The network was already configured so changing the scheduling algorithm will not take place!"); + break; + } + if (m_props.m_is_active.was_changed()) { + g_error("scheduler usage (scheduler-threshold) in combination with 'is-active' is not supported."); + break; + } + m_props.m_scheduler_threshold = g_value_get_uint(value); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); break; @@ -416,6 +496,15 @@ void HailoNetImpl::get_property(GObject *object, guint property_id, GValue *valu case PROP_IS_ACTIVE: g_value_set_boolean(value, m_props.m_is_active.get()); break; + case PROP_SCHEDULING_ALGORITHM: + g_value_set_enum(value, m_props.m_scheduling_algorithm.get()); + break; + case PROP_SCHEDULER_TIMEOUT_MS: + g_value_set_uint(value, m_props.m_scheduler_timeout_ms.get()); + break; + case PROP_SCHEDULER_THRESHOLD: + g_value_set_uint(value, m_props.m_scheduler_threshold.get()); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); break; @@ -428,7 +517,7 @@ hailo_status HailoNetImpl::set_hef() 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()); + m_props.m_scheduling_algorithm.get(), m_props.m_hef_path.get()); if (HAILO_SUCCESS != status) { return status; } @@ -487,6 +576,15 @@ hailo_status HailoNetImpl::configure_network_group() } m_was_configured = true; + if (m_props.m_scheduler_timeout_ms.was_changed()) { + status = m_net_group_handle->set_scheduler_timeout(m_props.m_network_name.get(), m_props.m_scheduler_timeout_ms.get()); + GST_CHECK_SUCCESS(status, m_element, RESOURCE, "Setting scheduler timeout failed, status = %d", status); + } + if (m_props.m_scheduler_threshold.was_changed()) { + status = m_net_group_handle->set_scheduler_threshold(m_props.m_network_name.get(), m_props.m_scheduler_threshold.get()); + GST_CHECK_SUCCESS(status, m_element, RESOURCE, "Setting scheduler threshold failed, status = %d", status); + } + 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); @@ -498,8 +596,13 @@ hailo_status HailoNetImpl::configure_network_group() return HAILO_SUCCESS; } -hailo_status HailoNetImpl::activate_network_group() +hailo_status HailoNetImpl::activate_hailonet() { + if (HAILO_SCHEDULING_ALGORITHM_NONE != m_props.m_scheduling_algorithm.get()) { + m_props.m_is_active = true; + return HAILO_SUCCESS; + } + if ((1 == m_hailonet_count) && (!m_props.m_is_active.was_changed())) { m_props.m_is_active = true; } @@ -567,15 +670,21 @@ hailo_status HailoNetImpl::deactivate_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); - } + return clear_vstreams(); + } + return HAILO_SUCCESS; +} - 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); - } +hailo_status HailoNetImpl::clear_vstreams() +{ + 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; @@ -596,7 +705,7 @@ gboolean HailoNetImpl::src_pad_event(GstEvent *event) GstPadProbeReturn HailoNetImpl::sink_probe() { - hailo_status status = activate_network_group(); + hailo_status status = activate_hailonet(); GST_CHECK(HAILO_SUCCESS == status, GST_PAD_PROBE_REMOVE, m_element, RESOURCE, "Failed activating network, status = %d", status); return GST_PAD_PROBE_REMOVE; } @@ -634,8 +743,33 @@ hailo_status HailoNetImpl::signal_was_flushed_event() return m_was_flushed_event->signal(); } +static bool do_versions_match(GstHailoNet *self) +{ + hailo_version_t libhailort_version = {}; + auto status = hailo_get_library_version(&libhailort_version); + if (HAILO_SUCCESS != status) { + GST_ELEMENT_ERROR(self, RESOURCE, FAILED, ("Fetching libhailort version has failed! status = %d", status), (NULL)); + return false; + } + + bool versions_match = ((HAILORT_MAJOR_VERSION == libhailort_version.major) && + (HAILORT_MINOR_VERSION == libhailort_version.minor) && + (HAILORT_REVISION_VERSION == libhailort_version.revision)); + if (!versions_match) { + GST_ELEMENT_ERROR(self, RESOURCE, FAILED, ("libhailort version (%d.%d.%d) does not match gsthailonet version (%d.%d.%d)", + libhailort_version.major, libhailort_version.minor, libhailort_version.revision, + HAILORT_MAJOR_VERSION, HAILORT_MINOR_VERSION, HAILORT_REVISION_VERSION), (NULL)); + return false; + } + return true; +} + static void gst_hailonet_init(GstHailoNet *self) { + if (!do_versions_match(self)) { + return; + } + 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)); @@ -700,12 +834,15 @@ static GstStateChangeReturn gst_hailonet_change_state(GstElement *element, GstSt } case GST_STATE_CHANGE_READY_TO_NULL: { + // VStreams are destructed in hailosend's/hailorecv's GST_STATE_CHANGE_READY_TO_NULL (which calls 'clear_abort' on low-level streams) // 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); + if (HAILO_SCHEDULING_ALGORITHM_NONE == hailonet->get_props().m_scheduling_algorithm.get()) { + status = hailonet->deactivate_network_group(); + GST_CHECK(HAILO_SUCCESS == status, GST_STATE_CHANGE_FAILURE, element, RESOURCE, "Deactivating network group failed, status = %d\n", status); + } // Cleanup all of hailonet memory hailonet.reset(); diff --git a/hailort/libhailort/bindings/gstreamer/gst-hailo/gsthailonet.hpp b/hailort/libhailort/bindings/gstreamer/gst-hailo/gsthailonet.hpp index 810e37d..7ff211d 100644 --- a/hailort/libhailort/bindings/gstreamer/gst-hailo/gsthailonet.hpp +++ b/hailort/libhailort/bindings/gstreamer/gst-hailo/gsthailonet.hpp @@ -52,7 +52,8 @@ 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) + m_is_active(false), m_device_count(0), m_vdevice_key(DEFAULT_VDEVICE_KEY), m_scheduling_algorithm(HAILO_SCHEDULING_ALGORITHM_NONE), + m_scheduler_timeout_ms(HAILO_DEFAULT_SCHEDULER_TIMEOUT_MS), m_scheduler_threshold(HAILO_DEFAULT_SCHEDULER_THRESHOLD) {} HailoElemProperty m_device_id; @@ -62,6 +63,9 @@ public: HailoElemProperty m_is_active; HailoElemProperty m_device_count; HailoElemProperty m_vdevice_key; + HailoElemProperty m_scheduling_algorithm; + HailoElemProperty m_scheduler_timeout_ms; + HailoElemProperty m_scheduler_threshold; }; class HailoNetImpl final @@ -76,20 +80,27 @@ public: hailo_status set_hef(); hailo_status link_elements(); hailo_status configure_network_group(); - hailo_status activate_network_group(); + hailo_status activate_hailonet(); 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(); + hailo_status deactivate_network_group(); + HailoNetProperties &get_props() { + return m_props; + } + private: void init_ghost_sink(); void init_ghost_src(); Expected get_network_group_name(const std::string &network_name); + hailo_status clear_vstreams(); + static std::atomic_uint32_t m_hailonet_count; static std::mutex m_mutex; GstHailoNet *m_element; diff --git a/hailort/libhailort/bindings/gstreamer/gst-hailo/gsthailorecv.cpp b/hailort/libhailort/bindings/gstreamer/gst-hailo/gsthailorecv.cpp index 34496e9..9c6835c 100644 --- a/hailort/libhailort/bindings/gstreamer/gst-hailo/gsthailorecv.cpp +++ b/hailort/libhailort/bindings/gstreamer/gst-hailo/gsthailorecv.cpp @@ -359,11 +359,9 @@ static GstFlowReturn gst_hailorecv_buffer_pool_acquire_callback(GstBufferPool *p ++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); + gboolean result = gst_buffer_pool_config_get_params(pool_config, NULL, NULL, NULL, &max_buffers); + gst_structure_free(pool_config); if (!result) { g_error("Failed getting config params from buffer pool!"); return GST_FLOW_ERROR; 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 index a500f17..e436de8 100644 --- 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 @@ -21,7 +21,8 @@ GType gst_hailo_buffer_flag_meta_api_get_type(void) { - static volatile GType type; + // https://github.com/vmware/open-vm-tools/commit/b2c8baeaa8ac365e1445f941cf1b80999ed89a9d + static GType type; static const gchar *tags[] = {HAILO_BUFFER_FLAG_META_TAG, NULL}; if (g_once_init_enter(&type)) { diff --git a/hailort/libhailort/bindings/gstreamer/gst-hailo/metadata/tensor_meta.cpp b/hailort/libhailort/bindings/gstreamer/gst-hailo/metadata/tensor_meta.cpp index 060ad6d..302a96d 100644 --- a/hailort/libhailort/bindings/gstreamer/gst-hailo/metadata/tensor_meta.cpp +++ b/hailort/libhailort/bindings/gstreamer/gst-hailo/metadata/tensor_meta.cpp @@ -24,7 +24,8 @@ GType gst_tensor_meta_api_get_type(void) { - static volatile GType type; + // https://github.com/vmware/open-vm-tools/commit/b2c8baeaa8ac365e1445f941cf1b80999ed89a9d + static GType type; static const gchar *tags[] = {TENSOR_META_TAG, NULL}; if (g_once_init_enter(&type)) { diff --git a/hailort/libhailort/bindings/gstreamer/gst-hailo/network_group_handle.cpp b/hailort/libhailort/bindings/gstreamer/gst-hailo/network_group_handle.cpp index eec733b..3453ebe 100644 --- a/hailort/libhailort/bindings/gstreamer/gst-hailo/network_group_handle.cpp +++ b/hailort/libhailort/bindings/gstreamer/gst-hailo/network_group_handle.cpp @@ -26,14 +26,16 @@ VDeviceManager NetworkGroupHandle::m_vdevice_manager; NetworkGroupConfigManager NetworkGroupHandle::m_net_group_config_manager; NetworkGroupActivationManager NetworkGroupHandle::m_net_group_activation_manager; -Expected> NetworkGroupHandle::create_vdevice(const std::string &device_id, uint16_t device_count, uint32_t vdevice_key) +Expected> NetworkGroupHandle::create_vdevice(const std::string &device_id, uint16_t device_count, uint32_t vdevice_key, + hailo_scheduling_algorithm_t scheduling_algorithm) { - auto expected_device = m_vdevice_manager.create_vdevice(m_element, device_id, device_count, vdevice_key); + auto expected_device = m_vdevice_manager.create_vdevice(m_element, device_id, device_count, vdevice_key, scheduling_algorithm); 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) +hailo_status NetworkGroupHandle::set_hef(const char *device_id, uint16_t device_count, uint32_t vdevice_key, + hailo_scheduling_algorithm_t scheduling_algorithm, const char *hef_path) { if (0 == device_count) { device_count = HAILO_DEFAULT_DEVICE_COUNT; @@ -41,7 +43,7 @@ hailo_status NetworkGroupHandle::set_hef(const char *device_id, uint16_t device_ std::string device_id_str = (nullptr == device_id) ? "" : device_id; - auto vdevice = create_vdevice(device_id_str, device_count, vdevice_key); + auto vdevice = create_vdevice(device_id_str, device_count, vdevice_key, scheduling_algorithm); GST_CHECK_EXPECTED_AS_STATUS(vdevice, m_element, RESOURCE, "Failed creating vdevice, status = %d", vdevice.status()); m_vdevice = vdevice.release(); @@ -77,13 +79,24 @@ hailo_status NetworkGroupHandle::configure_network_group(const char *net_group_n return HAILO_SUCCESS; } + +hailo_status NetworkGroupHandle::set_scheduler_timeout(const char *network_name, uint32_t timeout_ms) +{ + return m_cng->set_scheduler_timeout(std::chrono::milliseconds(timeout_ms), network_name); +} + +hailo_status NetworkGroupHandle::set_scheduler_threshold(const char *network_name, uint32_t threshold) +{ + return m_cng->set_scheduler_threshold(threshold, network_name); +} + Expected, std::vector>> NetworkGroupHandle::create_vstreams(const char *network_name, const std::vector &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); + hailo_status status = m_net_group_config_manager.add_network_to_shared_network_group(m_shared_device_id, 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); @@ -198,26 +211,28 @@ Expected NetworkGroupHandle::remove_network_group() Expected> VDeviceManager::create_vdevice(const void *element, const std::string &device_id, uint16_t device_count, - uint32_t vdevice_key) + uint32_t vdevice_key, hailo_scheduling_algorithm_t scheduling_algorithm) { std::unique_lock lock(m_mutex); if (!device_id.empty()) { - return create_shared_vdevice(element, device_id); + return create_shared_vdevice(element, device_id, scheduling_algorithm); } if (DEFAULT_VDEVICE_KEY != vdevice_key) { - return create_shared_vdevice(element, device_count, vdevice_key); + return create_shared_vdevice(element, device_count, vdevice_key, scheduling_algorithm); } - return create_unique_vdevice(element, device_count); + return create_unique_vdevice(element, device_count, scheduling_algorithm); } -Expected> VDeviceManager::create_shared_vdevice(const void *element, const std::string &device_id) +Expected> VDeviceManager::create_shared_vdevice(const void *element, const std::string &device_id, + hailo_scheduling_algorithm_t scheduling_algorithm) { // 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) { + auto found_vdevice = get_vdevice(device_id, scheduling_algorithm); + if (found_vdevice.status() != HAILO_NOT_FOUND) { + GST_CHECK_EXPECTED(found_vdevice, element, RESOURCE, "Failed using shared vdevice, status = %d", found_vdevice.status()); return found_vdevice.release(); } @@ -227,40 +242,48 @@ Expected> VDeviceManager::create_shared_vdevice(const v hailo_vdevice_params_t params = {}; params.device_count = device_count; params.device_infos = &(device_info_expected.value()); + params.scheduling_algorithm = scheduling_algorithm; auto vdevice = VDevice::create(params); GST_CHECK_EXPECTED(vdevice, element, RESOURCE, "Failed creating vdevice, status = %d", vdevice.status()); std::shared_ptr vdevice_ptr = std::move(vdevice.release()); m_shared_vdevices[device_id] = vdevice_ptr; + m_shared_vdevices_scheduling_algorithm[device_id] = scheduling_algorithm; return vdevice_ptr; } -Expected> VDeviceManager::create_shared_vdevice(const void *element, uint16_t device_count, uint32_t vdevice_key) +Expected> VDeviceManager::create_shared_vdevice(const void *element, uint16_t device_count, uint32_t vdevice_key, + hailo_scheduling_algorithm_t scheduling_algorithm) { 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) { + auto found_vdevice = get_vdevice(device_id, scheduling_algorithm); + if (found_vdevice.status() != HAILO_NOT_FOUND) { + GST_CHECK_EXPECTED(found_vdevice, element, RESOURCE, "Failed using shared vdevice, status = %d", found_vdevice.status()); return found_vdevice.release(); } hailo_vdevice_params_t params = {}; params.device_count = device_count; params.device_infos = nullptr; + params.scheduling_algorithm = scheduling_algorithm; auto vdevice = VDevice::create(params); GST_CHECK_EXPECTED(vdevice, element, RESOURCE, "Failed creating vdevice, status = %d", vdevice.status()); std::shared_ptr vdevice_ptr = std::move(vdevice.release()); m_shared_vdevices[device_id] = vdevice_ptr; + m_shared_vdevices_scheduling_algorithm[device_id] = scheduling_algorithm; return vdevice_ptr; } -Expected> VDeviceManager::create_unique_vdevice(const void *element, uint16_t device_count) +Expected> VDeviceManager::create_unique_vdevice(const void *element, uint16_t device_count, + hailo_scheduling_algorithm_t scheduling_algorithm) { hailo_vdevice_params_t params = {}; params.device_count = device_count; params.device_infos = nullptr; + params.scheduling_algorithm = scheduling_algorithm; auto vdevice = VDevice::create(params); GST_CHECK_EXPECTED(vdevice, element, RESOURCE, "Failed creating vdevice, status = %d", vdevice.status()); @@ -270,12 +293,23 @@ Expected> VDeviceManager::create_unique_vdevice(const v return vdevice_ptr; } -Expected> VDeviceManager::get_vdevice(const std::string &device_id) +Expected> VDeviceManager::get_vdevice(const std::string &device_id, + hailo_scheduling_algorithm_t scheduling_algorithm) { auto found = m_shared_vdevices.find(device_id); if (found == m_shared_vdevices.end()) { return make_unexpected(HAILO_NOT_FOUND); } + + // shared_vdevice is found, verify the requested scheduling_algorithm + assert(m_shared_vdevices_scheduling_algorithm.end() != m_shared_vdevices_scheduling_algorithm.find(device_id)); + if (scheduling_algorithm != m_shared_vdevices_scheduling_algorithm[device_id]) { + auto status = HAILO_INVALID_OPERATION; + g_warning("Shared vdevice with the same credentials is already exists (%s) but with a different scheduling-algorithm (requested: %d, exists: %d), status = %d", + device_id.c_str(), scheduling_algorithm, m_shared_vdevices_scheduling_algorithm[device_id], status); + return make_unexpected(HAILO_INVALID_OPERATION); + } + auto vdevice_cpy = found->second; return vdevice_cpy; } @@ -300,15 +334,24 @@ Expected> NetworkGroupConfigManager::con return std::move(network_group_list->at(0)); } -hailo_status NetworkGroupConfigManager::add_network(const std::string &network_name, const GstElement *owner_element) +hailo_status NetworkGroupConfigManager::add_network_to_shared_network_group(const std::string &shared_device_id, const std::string &network_name, + const GstElement *owner_element) { std::unique_lock 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()); + if (shared_device_id.empty()) { + // the device is unique so we don't need to share anything + return HAILO_SUCCESS; + } - m_configured_networks[network_name] = GST_ELEMENT_NAME(owner_element); + auto found_by_device = m_configured_networks.find(shared_device_id); + if (found_by_device != m_configured_networks.end()) { + auto found_network = found_by_device->second.find(network_name); + GST_CHECK(found_network == found_by_device->second.end(), HAILO_INVALID_OPERATION, owner_element, RESOURCE, + "Network %s was already configured by %s by the same device!", network_name.c_str(), found_network->second.c_str()); + } + + m_configured_networks[shared_device_id][network_name] = GST_ELEMENT_NAME(owner_element); return HAILO_SUCCESS; } diff --git a/hailort/libhailort/bindings/gstreamer/gst-hailo/network_group_handle.hpp b/hailort/libhailort/bindings/gstreamer/gst-hailo/network_group_handle.hpp index b83dd24..f402678 100644 --- a/hailort/libhailort/bindings/gstreamer/gst-hailo/network_group_handle.hpp +++ b/hailort/libhailort/bindings/gstreamer/gst-hailo/network_group_handle.hpp @@ -31,23 +31,29 @@ class VDeviceManager final { public: - VDeviceManager() : m_shared_vdevices(), m_unique_vdevices() {} + VDeviceManager() : m_shared_vdevices(), m_shared_vdevices_scheduling_algorithm(), m_unique_vdevices() {} - Expected> create_vdevice(const void *element, const std::string &device_id, uint16_t device_count, uint32_t vdevice_key); + Expected> create_vdevice(const void *element, const std::string &device_id, uint16_t device_count, uint32_t vdevice_key, + hailo_scheduling_algorithm_t scheduling_algorithm); private: - Expected> create_shared_vdevice(const void *element, const std::string &device_id); - Expected> create_shared_vdevice(const void *element, uint16_t device_count, uint32_t vdevice_key); - Expected> create_unique_vdevice(const void *element, uint16_t device_count); - Expected> get_vdevice(const std::string &device_id); + Expected> create_shared_vdevice(const void *element, const std::string &device_id, + hailo_scheduling_algorithm_t scheduling_algorithm); + Expected> create_shared_vdevice(const void *element, uint16_t device_count, uint32_t vdevice_key, + hailo_scheduling_algorithm_t scheduling_algorithm); + Expected> create_unique_vdevice(const void *element, uint16_t device_count, + hailo_scheduling_algorithm_t scheduling_algorithm); + Expected> get_vdevice(const std::string &device_id, hailo_scheduling_algorithm_t scheduling_algorithm); /* Contains only the shared vdevices (either created by bdf, or with device-count && vdevice-key) Keys are either "" or "-" */ std::unordered_map> m_shared_vdevices; + std::unordered_map m_shared_vdevices_scheduling_algorithm; // Used to check that 2 shared vdevices gets the same scheduling-algorithm std::vector> m_unique_vdevices; std::mutex m_mutex; }; +using device_id_t = std::string; using network_name_t = std::string; using hailonet_name_t = std::string; @@ -58,7 +64,8 @@ public: Expected> configure_network_group(const void *element, const std::string &device_id, const char *network_group_name, uint16_t batch_size, std::shared_ptr &vdevice, std::shared_ptr hef, NetworkGroupsParamsMap &net_groups_params_map); - hailo_status add_network(const std::string &network_name, const GstElement *owner_element); + hailo_status add_network_to_shared_network_group(const std::string &shared_device_id, 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); @@ -69,7 +76,7 @@ private: // TODO: change this map to store only the shared network_groups (used by multiple hailonets with the same vdevices) std::unordered_map> m_configured_net_groups; - std::unordered_map m_configured_networks; + std::unordered_map> m_configured_networks; std::mutex m_mutex; }; @@ -96,7 +103,8 @@ 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 set_hef(const char *device_id, uint16_t device_count, uint32_t vdevice_key, hailo_scheduling_algorithm_t scheduling_algorithm, + const char *hef_path); hailo_status configure_network_group(const char *net_group_name, uint16_t batch_size); Expected, std::vector>> create_vstreams(const char *network_name, const std::vector &output_formats); @@ -104,6 +112,10 @@ public: hailo_status abort_streams(); Expected remove_network_group(); + hailo_status set_scheduler_timeout(const char *network_name, uint32_t timeout_ms); + hailo_status set_scheduler_threshold(const char *network_name, uint32_t threshold); + + std::shared_ptr hef() { return m_hef; @@ -111,13 +123,14 @@ public: private: Expected get_configure_params(Hef &hef, const char *net_group_name, uint16_t batch_size); - Expected> create_vdevice(const std::string &device_id, uint16_t device_count, uint32_t vdevice_key); + Expected> create_vdevice(const std::string &device_id, uint16_t device_count, uint32_t vdevice_key, + hailo_scheduling_algorithm_t scheduling_algorithm); 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_shared_device_id; // empty string when using unique device std::string m_net_group_name; std::string m_network_name; uint16_t m_batch_size; diff --git a/hailort/libhailort/bindings/python/CMakeLists.txt b/hailort/libhailort/bindings/python/CMakeLists.txt index febd4f0..d38c6e8 100644 --- a/hailort/libhailort/bindings/python/CMakeLists.txt +++ b/hailort/libhailort/bindings/python/CMakeLists.txt @@ -1 +1,9 @@ add_subdirectory(src) + +# copy files to venv +if(HAILO_BUILD_PYHAILORT_VENV) + add_custom_target(pyhailort_venv ALL + COMMAND ${CMAKE_COMMAND} -E copy $ ${CMAKE_CURRENT_LIST_DIR}/platform/hailo_platform/pyhailort/ + ) + add_dependencies(pyhailort_venv _pyhailort) +endif() \ No newline at end of file diff --git a/hailort/libhailort/bindings/python/examples/hef_infer_pipeline_vstream.py b/hailort/libhailort/bindings/python/examples/hef_infer_pipeline_vstream.py index b01c038..c1896df 100644 --- a/hailort/libhailort/bindings/python/examples/hef_infer_pipeline_vstream.py +++ b/hailort/libhailort/bindings/python/examples/hef_infer_pipeline_vstream.py @@ -1,6 +1,6 @@ from hailo_platform import (HEF, PcieDevice, ConfigureParams, InferVStreams, InputVStreamParams, OutputVStreamParams, FormatType) -from hailo_platform.drivers.hailort.pyhailort import HailoStreamInterface +from hailo_platform.pyhailort.pyhailort import HailoStreamInterface import numpy as np import argparse @@ -25,6 +25,8 @@ def main(): 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) + + print('Inference ran successfully') if __name__ == '__main__': main() \ No newline at end of file diff --git a/hailort/libhailort/bindings/python/platform/hailo_platform/__init__.py b/hailort/libhailort/bindings/python/platform/hailo_platform/__init__.py index 1c6ca2e..610f878 100644 --- a/hailort/libhailort/bindings/python/platform/hailo_platform/__init__.py +++ b/hailort/libhailort/bindings/python/platform/hailo_platform/__init__.py @@ -4,40 +4,10 @@ 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__))) @@ -45,11 +15,8 @@ def join_drivers_path(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, +from hailo_platform.pyhailort.hw_object import PcieDevice, EthernetDevice +from hailo_platform.pyhailort.pyhailort import (HEF, ConfigureParams, FormatType, FormatOrder, MipiDataTypeRx, MipiPixelsPerClock, MipiClockSelection, MipiIspImageInOrder, @@ -57,7 +24,8 @@ from hailo_platform.drivers.hailort.pyhailort import (HEF, ConfigureParams, Endianness, HailoStreamInterface, InputVStreamParams, OutputVStreamParams, InputVStreams, OutputVStreams, - InferVStreams, HailoStreamDirection, HailoFormatFlags, HailoCpuId, VDevice) + InferVStreams, HailoStreamDirection, HailoFormatFlags, HailoCpuId, VDevice, + DvmTypes, PowerMeasurementTypes, SamplingPeriod, AveragingFactor, MeasurementBufferIndex) def _verify_pyhailort_lib_exists(): python_version = "".join(str(i) for i in sys.version_info[:2]) @@ -66,16 +34,29 @@ def _verify_pyhailort_lib_exists(): "nt": "pyd", # Windows }[os.name] - path = f"{__path__[0]}/drivers/hailort/" + path = f"{__path__[0]}/pyhailort/" 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() +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' __version__ = get_version('hailo_platform') __all__ = ['EthernetDevice', 'DvmTypes', 'PowerMeasurementTypes', - 'SamplingPeriod', 'AveragingFactor', 'UDPRateLimiter', 'PcieDevice', 'HEF', + 'SamplingPeriod', 'AveragingFactor', 'MeasurementBufferIndex', 'UDPRateLimiter', 'PcieDevice', 'HEF', 'ConfigureParams', 'FormatType', 'FormatOrder', 'MipiDataTypeRx', 'MipiPixelsPerClock', 'MipiClockSelection', 'MipiIspImageInOrder', 'MipiIspImageOutDataType', 'join_drivers_path', 'IspLightFrequency', 'HailoPowerMode', 'Endianness', 'HailoStreamInterface', 'InputVStreamParams', 'OutputVStreamParams', 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 deleted file mode 100644 index 3dd945d..0000000 --- a/hailort/libhailort/bindings/python/platform/hailo_platform/common/compatibility/__init__.py +++ /dev/null @@ -1,27 +0,0 @@ -#!/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/paths_manager/config.py b/hailort/libhailort/bindings/python/platform/hailo_platform/common/paths_manager/config.py deleted file mode 100644 index 5e1d7fc..0000000 --- a/hailort/libhailort/bindings/python/platform/hailo_platform/common/paths_manager/config.py +++ /dev/null @@ -1,52 +0,0 @@ -#!/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 deleted file mode 100644 index 3dada52..0000000 --- a/hailort/libhailort/bindings/python/platform/hailo_platform/common/paths_manager/paths.py +++ /dev/null @@ -1,207 +0,0 @@ -#!/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 deleted file mode 100644 index 04d40ee..0000000 --- a/hailort/libhailort/bindings/python/platform/hailo_platform/common/paths_manager/version.py +++ /dev/null @@ -1,15 +0,0 @@ -#!/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/inference_targets.py b/hailort/libhailort/bindings/python/platform/hailo_platform/common/targets/inference_targets.py deleted file mode 100644 index 80335b4..0000000 --- a/hailort/libhailort/bindings/python/platform/hailo_platform/common/targets/inference_targets.py +++ /dev/null @@ -1,111 +0,0 @@ -#!/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 `. - """ - 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 deleted file mode 100644 index e69de29..0000000 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 deleted file mode 100644 index e69de29..0000000 diff --git a/hailort/libhailort/bindings/python/platform/hailo_platform/drivers/__init__.py b/hailort/libhailort/bindings/python/platform/hailo_platform/drivers/__init__.py index 22a799f..e7a12fb 100644 --- a/hailort/libhailort/bindings/python/platform/hailo_platform/drivers/__init__.py +++ b/hailort/libhailort/bindings/python/platform/hailo_platform/drivers/__init__.py @@ -1,6 +1,4 @@ #!/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. -""" +from warnings import warn +warn("Importing from 'hailo_platform.drivers' is deprecated, One should import from 'hailo_platform' module directly.", DeprecationWarning, stacklevel=2) \ No newline at end of file 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 index 0e7d4f3..ddf2de4 100644 --- a/hailort/libhailort/bindings/python/platform/hailo_platform/drivers/control_object.py +++ b/hailort/libhailort/bindings/python/platform/hailo_platform/drivers/control_object.py @@ -1,655 +1 @@ -#!/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) +from hailo_platform.pyhailort.control_object import * # noqa F401 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 index 0b0bb84..785a642 100644 --- a/hailort/libhailort/bindings/python/platform/hailo_platform/drivers/ethernet_utils.py +++ b/hailort/libhailort/bindings/python/platform/hailo_platform/drivers/ethernet_utils.py @@ -1,66 +1 @@ -#!/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)) +from hailo_platform.pyhailort.ethernet_utils import * # noqa F401 \ No newline at end of file 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 index bdfc8cf..af38579 100644 --- 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 @@ -1,331 +1,2 @@ #!/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 if the string field is empty - return string_field.rstrip('\x00') or "" - - 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 "" - 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 = '> 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)) +from hailo_platform.pyhailort.i2c_slaves import * # noqa F401 \ No newline at end of file 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 index b9da002..1732ad7 100644 --- 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 @@ -1,11 +1 @@ -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 +from hailo_platform.pyhailort.power_measurement import * # noqa F401 \ No newline at end of file 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 index ca2461e..a29dd95 100644 --- a/hailort/libhailort/bindings/python/platform/hailo_platform/drivers/hailort/pyhailort.py +++ b/hailort/libhailort/bindings/python/platform/hailo_platform/drivers/hailort/pyhailort.py @@ -1,2123 +1 @@ -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" ([]::.). 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 []::.""" - 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 []::.') - - -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()) +from hailo_platform.pyhailort.pyhailort import * # noqa F401 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 index c285caf..c70c019 100644 --- a/hailort/libhailort/bindings/python/platform/hailo_platform/drivers/hw_object.py +++ b/hailort/libhailort/bindings/python/platform/hailo_platform/drivers/hw_object.py @@ -1,465 +1 @@ -#!/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 `: 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() +from hailo_platform.pyhailort.hw_object import * # noqa F401 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 deleted file mode 100644 index e69de29..0000000 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 deleted file mode 100644 index 6855145..0000000 --- a/hailort/libhailort/bindings/python/platform/hailo_platform/paths_manager/paths.py +++ /dev/null @@ -1,10 +0,0 @@ -#!/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/common/paths_manager/__init__.py b/hailort/libhailort/bindings/python/platform/hailo_platform/pyhailort/__init__.py similarity index 100% rename from hailort/libhailort/bindings/python/platform/hailo_platform/common/paths_manager/__init__.py rename to hailort/libhailort/bindings/python/platform/hailo_platform/pyhailort/__init__.py diff --git a/hailort/libhailort/bindings/python/platform/hailo_platform/pyhailort/control_object.py b/hailort/libhailort/bindings/python/platform/hailo_platform/pyhailort/control_object.py new file mode 100644 index 0000000..f4b3e20 --- /dev/null +++ b/hailort/libhailort/bindings/python/platform/hailo_platform/pyhailort/control_object.py @@ -0,0 +1,745 @@ +#!/usr/bin/env python + +"""Control operations for the Hailo hardware device.""" +import struct +import sys +import signal + +from builtins import object +from abc import ABCMeta, abstractmethod +from future.utils import with_metaclass + +from hailo_platform.common.logger.logger import default_logger + +from hailo_platform.pyhailort.hailo_control_protocol import BoardInformation, CoreInformation, DeviceArchitectureTypes, ExtendedDeviceInformation, HealthInformation +from hailo_platform.pyhailort.power_measurement import SamplingPeriod, AveragingFactor, DvmTypes, PowerMeasurementTypes, MeasurementBufferIndex, _get_buffer_index_enum_member +from hailo_platform.pyhailort.pyhailort import InternalPcieDevice, ExceptionWrapper + +import hailo_platform.pyhailort._pyhailort as _pyhailort + + +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._device = None + + if sys.platform != "win32": + signal.pthread_sigmask(signal.SIG_BLOCK, [signal.SIGWINCH]) + + @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.pyhailort.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. + """ + with ExceptionWrapper(): + return self._device.configure(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, IP 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).""" + with ExceptionWrapper(): + return self._device.reset(_pyhailort.ResetDeviceMode.CHIP) + + def nn_core_reset(self): + """Resets the nn_core.""" + with ExceptionWrapper(): + return self._device.reset(_pyhailort.ResetDeviceMode.NN_CORE) + + def soft_reset(self): + """reloads the device firmware (soft reset)""" + with ExceptionWrapper(): + return self._device.reset(_pyhailort.ResetDeviceMode.SOFT) + + def forced_soft_reset(self): + """reloads the device firmware (forced soft reset)""" + with ExceptionWrapper(): + return self._device.reset(_pyhailort.ResetDeviceMode.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 + """ + with ExceptionWrapper(): + return self._device.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. + """ + with ExceptionWrapper(): + return self._device.write_memory(address, data_buffer, len(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.pyhailort.pyhailort.DvmTypes`): + Which DVM will be measured. Default (:class:`~hailo_platform.pyhailort.pyhailort.DvmTypes.AUTO`) will be different according to the board: \n + Default (:class:`~hailo_platform.pyhailort.pyhailort.DvmTypes.AUTO`) for EVB is an approximation to the total power consumption of the chip in PCIe setups. + It sums :class:`~hailo_platform.pyhailort.pyhailort.DvmTypes.VDD_CORE`, + :class:`~hailo_platform.pyhailort.pyhailort.DvmTypes.MIPI_AVDD` and :class:`~hailo_platform.pyhailort.pyhailort.DvmTypes.AVDD_H`. + Only :class:`~hailo_platform.pyhailort.pyhailort.PowerMeasurementTypes.POWER` can measured with this option. \n + Default (:class:`~hailo_platform.pyhailort.pyhailort.DvmTypes.AUTO`) for platforms supporting current monitoring (such as M.2 and mPCIe): :class:`~hailo_platform.pyhailort.pyhailort.DvmTypes.OVERCURRENT_PROTECTION` + measurement_type + (:class:`~hailo_platform.pyhailort.pyhailort.PowerMeasurementTypes`): + The type of the measurement. + + Returns: + float: The measured power. \n + For :class:`~hailo_platform.pyhailort.pyhailort.PowerMeasurementTypes`: \n + - :class:`~hailo_platform.pyhailort.pyhailort.PowerMeasurementTypes.SHUNT_VOLTAGE`: Unit is mV. \n + - :class:`~hailo_platform.pyhailort.pyhailort.PowerMeasurementTypes.BUS_VOLTAGE`: Unit is mV. \n + - :class:`~hailo_platform.pyhailort.pyhailort.PowerMeasurementTypes.POWER`: Unit is W. \n + - :class:`~hailo_platform.pyhailort.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.pyhailort.pyhailort.PowerMeasurementTypes`. + """ + if self.device_id.device_architecture != DeviceArchitectureTypes.HAILO8_B0: + raise ControlObjectException("Invalid device architecture: {}".format(self.device_id.device_architecture)) + with ExceptionWrapper(): + return self._device.power_measurement(dvm, measurement_type) + + def start_power_measurement(self, delay=None, averaging_factor=AveragingFactor.AVERAGE_256, sampling_period=SamplingPeriod.PERIOD_1100us): + """Start performing a long power measurement. + + Args: + delay: Unused parameter. Will be removed in future versions. + averaging_factor (:class:`~hailo_platform.pyhailort.pyhailort.AveragingFactor`): + Number of samples per time period, sensor configuration value. + sampling_period (:class:`~hailo_platform.pyhailort.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. + """ + # TODO: Remove deprecated arg + if delay is not None: + self._logger.warning("Passing 'delay' to 'start_power_measurement()' is deprecated and will be removed in future versions") + with ExceptionWrapper(): + return self._device.start_power_measurement(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. + """ + with ExceptionWrapper(): + return self._device.stop_power_measurement() + + def set_power_measurement(self, buffer_index=MeasurementBufferIndex.MEASUREMENT_BUFFER_INDEX_0, dvm=DvmTypes.AUTO, measurement_type=PowerMeasurementTypes.AUTO): + """Set parameters for long power measurement on an Hailo chip. + + Args: + buffer_index (:class:`~hailo_platform.pyhailort.pyhailort.MeasurementBufferIndex`): Index of the buffer on the firmware the data would be saved at. + Default is :class:`~hailo_platform.pyhailort.pyhailort.MeasurementBufferIndex.MEASUREMENT_BUFFER_INDEX_0` + dvm (:class:`~hailo_platform.pyhailort.pyhailort.DvmTypes`): + Which DVM will be measured. Default (:class:`~hailo_platform.pyhailort.pyhailort.DvmTypes.AUTO`) will be different according to the board: \n + Default (:class:`~hailo_platform.pyhailort.pyhailort.DvmTypes.AUTO`) for EVB is an approximation to the total power consumption of the chip in PCIe setups. + It sums :class:`~hailo_platform.pyhailort.pyhailort.DvmTypes.VDD_CORE`, + :class:`~hailo_platform.pyhailort.pyhailort.DvmTypes.MIPI_AVDD` and :class:`~hailo_platform.pyhailort.pyhailort.DvmTypes.AVDD_H`. + Only :class:`~hailo_platform.pyhailort.pyhailort.PowerMeasurementTypes.POWER` can measured with this option. \n + Default (:class:`~hailo_platform.pyhailort.pyhailort.DvmTypes.AUTO`) for platforms supporting current monitoring (such as M.2 and mPCIe): :class:`~hailo_platform.pyhailort.pyhailort.DvmTypes.OVERCURRENT_PROTECTION` + measurement_type + (:class:`~hailo_platform.pyhailort.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.pyhailort.pyhailort.PowerMeasurementTypes` + """ + # TODO: Remove deprecated arg + if isinstance(buffer_index, int): + self._logger.warning("Passing integer as 'buffer_index' to 'set_power_measurement()' is deprecated. One should pass " + ":class:`~hailo_platform.pyhailort.pyhailort.MeasurementBufferIndex` as 'buffer_index' instead.") + buffer_index = _get_buffer_index_enum_member(buffer_index) + with ExceptionWrapper(): + return self._device.set_power_measurement(buffer_index, dvm, measurement_type) + + def get_power_measurement(self, buffer_index=MeasurementBufferIndex.MEASUREMENT_BUFFER_INDEX_0, should_clear=True): + """Read measured power from a long power measurement + + Args: + buffer_index (:class:`~hailo_platform.pyhailort.pyhailort.MeasurementBufferIndex`): Index of the buffer on the firmware the data would be saved at. + Default is :class:`~hailo_platform.pyhailort.pyhailort.MeasurementBufferIndex.MEASUREMENT_BUFFER_INDEX_0` + should_clear (bool): Flag indicating if the results saved at the firmware will be deleted after reading. + + Returns: + :class:`~hailo_platform.pyhailort.pyhailort.PowerMeasurementData`: + Object containing measurement data \n + For :class:`~hailo_platform.pyhailort.pyhailort.PowerMeasurementTypes`: \n + - :class:`~hailo_platform.pyhailort.pyhailort.PowerMeasurementTypes.SHUNT_VOLTAGE`: Unit is mV. \n + - :class:`~hailo_platform.pyhailort.pyhailort.PowerMeasurementTypes.BUS_VOLTAGE`: Unit is mV. \n + - :class:`~hailo_platform.pyhailort.pyhailort.PowerMeasurementTypes.POWER`: Unit is W. \n + - :class:`~hailo_platform.pyhailort.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.pyhailort.pyhailort.PowerMeasurementTypes`. + """ + if self.device_id.device_architecture != DeviceArchitectureTypes.HAILO8_B0: + raise ControlObjectException("Invalid device architecture: {}".format(self.device_id.device_architecture)) + # TODO: Remove deprecated arg + if isinstance(buffer_index, int): + self._logger.warning("Passing integer as 'buffer_index' to 'get_power_measurement()' is deprecated. One should pass " + ":class:`~hailo_platform.pyhailort.pyhailort.MeasurementBufferIndex` as 'buffer_index' instead.") + buffer_index = _get_buffer_index_enum_member(buffer_index) + with ExceptionWrapper(): + return self._device.get_power_measurement(buffer_index, should_clear) + + def _examine_user_config(self): + with ExceptionWrapper(): + return self._device.examine_user_config() + + def read_user_config(self): + """Read the user configuration section as binary data. + + Returns: + str: User config as a binary buffer. + """ + with ExceptionWrapper(): + return self._device.read_user_config() + + def write_user_config(self, configuration): + """Write the user configuration. + + Args: + configuration (str): A binary representation of a Hailo device configuration. + """ + with ExceptionWrapper(): + return self._device.write_user_config(configuration) + + def _erase_user_config(self): + with ExceptionWrapper(): + return self._device.erase_user_config() + + def read_board_config(self): + """Read the board configuration section as binary data. + + Returns: + str: Board config as a binary buffer. + """ + with ExceptionWrapper(): + return self._device.read_board_config() + + def write_board_config(self, configuration): + """Write the static confuration. + + Args: + configuration (str): A binary representation of a Hailo device configuration. + """ + with ExceptionWrapper(): + return self._device.write_board_config(configuration) + + def identify(self): + """Gets the Hailo chip identification. + + Returns: + class HailoIdentifyResponse with Protocol version. + """ + with ExceptionWrapper(): + response = self._device.identify() + 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 + + def core_identify(self): + """Gets the Core Hailo chip identification. + + Returns: + class HailoIdentifyResponse with Protocol version. + """ + with ExceptionWrapper(): + response = self._device.core_identify() + core_information = CoreInformation(response.fw_version.major, response.fw_version.minor, + response.fw_version.revision, response.is_release) + return core_information + + 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). + """ + with ExceptionWrapper(): + return self._device.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. + """ + with ExceptionWrapper(): + return self._device.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. + """ + with ExceptionWrapper(): + return self._device.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. + """ + with ExceptionWrapper(): + return self._device._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. + """ + with ExceptionWrapper(): + return self._device._get_overcurrent_state() + + @staticmethod + def _create_c_i2c_slave(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 + + def i2c_write(self, slave, register_address, data): + """Write data to an I2C slave. + + Args: + slave (:class:`hailo_platform.pyhailort.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. + """ + c_slave = HcpControl._create_c_i2c_slave(slave) + with ExceptionWrapper(): + return self._device.i2c_write(c_slave, register_address, data, len(data)) + + def i2c_read(self, slave, register_address, data_length): + """Read data from an I2C slave. + + Args: + slave (:class:`hailo_platform.pyhailort.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. + """ + c_slave = HcpControl._create_c_i2c_slave(slave) + with ExceptionWrapper(): + return self._device.i2c_read(c_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) + """ + with ExceptionWrapper(): + return self._device.firmware_update(firmware_binary, len(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. + """ + with ExceptionWrapper(): + return self._device.second_stage_update(second_stage_binary, len(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.pyhailort.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" + + with ExceptionWrapper(): + return self._device.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" + + with ExceptionWrapper(): + return self._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) + + 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. + """ + with ExceptionWrapper(): + return self._device.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.pyhailort.pyhailort.Endianness`): + Big or little endian. + """ + with ExceptionWrapper(): + return self._device.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.pyhailort.pyhailort.SensorConfigTypes`): The sensor type. + i2c_bus_index (int): The I2C bus index of the sensor. + """ + with ExceptionWrapper(): + return self._device.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] + """ + with ExceptionWrapper(): + return self._device.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] + """ + with ExceptionWrapper(): + return self._device.sensor_reset(section_index) + + def wd_enable(self, cpu_id): + """Enable firmware watchdog. + + Args: + cpu_id (:class:`~hailo_platform.pyhailort.pyhailort.HailoCpuId`): 0 for App CPU, 1 for Core CPU. + """ + with ExceptionWrapper(): + return self._device.wd_enable(cpu_id) + + def wd_disable(self, cpu_id): + """Disable firmware watchdog. + + Args: + cpu_id (:class:`~hailo_platform.pyhailort.pyhailort.HailoCpuId`): 0 for App CPU, 1 for Core CPU. + """ + with ExceptionWrapper(): + return self._device.wd_disable(cpu_id) + + def wd_config(self, cpu_id, wd_cycles, wd_mode): + """Configure a firmware watchdog. + + Args: + cpu_id (:class:`~hailo_platform.pyhailort.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 + """ + with ExceptionWrapper(): + return self._device.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.pyhailort.pyhailort.HailoCpuId`): 0 for App CPU, 1 for Core CPU. + """ + with ExceptionWrapper(): + return self._device.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.pyhailort.pyhailort.TemperatureInfo`: + Temperature in celsius of the 2 internal temperature sensors (TS), and a sample + count (a running 16-bit counter) + """ + with ExceptionWrapper(): + return self._device.get_chip_temperature() + + def get_extended_device_information(self): + with ExceptionWrapper(): + response = self._device.get_extended_device_information() + 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 + + def _get_health_information(self): + with ExceptionWrapper(): + response = self._device._get_health_information() + 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 + + 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. + """ + with ExceptionWrapper(): + return self._device.set_pause_frames(rx_pause_frames_enable) + + def test_chip_memories(self): + """test all chip memories using smart BIST + + """ + with ExceptionWrapper(): + return self._device.test_chip_memories() + + def _get_device_handle(self): + return self._device + +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 + response_timeout_milliseconds = int(response_timeout_seconds * 1000) + if device is None: + with ExceptionWrapper(): + self.device = _pyhailort.Device.create_eth(remote_ip, remote_control_port, + response_timeout_milliseconds, max_number_of_attempts) + else: + self._device = device.device + self._device_id = self.identify() + + +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] + + if device is None: + with ExceptionWrapper(): + self._device = _pyhailort.Device.create_pcie(device_info) + else: + self._device = device.device + self._device_id = self.identify() + + 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. + """ + with ExceptionWrapper(): + return self._device.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. + """ + with ExceptionWrapper(): + return self._device.remove_notification_callback(notification_id) diff --git a/hailort/libhailort/bindings/python/platform/hailo_platform/pyhailort/ethernet_utils.py b/hailort/libhailort/bindings/python/platform/hailo_platform/pyhailort/ethernet_utils.py new file mode 100644 index 0000000..0b0bb84 --- /dev/null +++ b/hailort/libhailort/bindings/python/platform/hailo_platform/pyhailort/ethernet_utils.py @@ -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/pyhailort/hailo_control_protocol.py b/hailort/libhailort/bindings/python/platform/hailo_platform/pyhailort/hailo_control_protocol.py new file mode 100644 index 0000000..2754cc8 --- /dev/null +++ b/hailort/libhailort/bindings/python/platform/hailo_platform/pyhailort/hailo_control_protocol.py @@ -0,0 +1,323 @@ +#!/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 = 8 +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 if the string field is empty + return string_field.rstrip('\x00') or "" + + 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 "" + 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 HailoFirmwareVersion(object): + """Represents a Hailo chip firmware version.""" + DEV_BIT = 0x80000000 + CORE_BIT = 0x08000000 + FW_VERSION_FORMAT = '`: 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. + """ + if self._control_object is None: + raise HailoRTException( + "The device has been released and is not usable." + " Device is released when the function `release()` is called explicitly, or when created using a context manager and goes out of scope.") + 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.pyhailort._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.pyhailort._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.pyhailort._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.pyhailort._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.pyhailort._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.pyhailort._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.pyhailort.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.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(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.") + + def release(self): + """ + Release the allocated resources of the device. This function should be called when working with the device not as context-manager. + Note: After calling this function, the device will not be usable. + """ + if self._device is not None: + self._device.release() + self._device = None + self._control_object = None + + +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__() + + gc.collect() + + self._remote_ip = remote_ip + self._remote_control_port = remote_control_port + # EthernetDevice __del__ function tries to release self._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._device = None + self._control_object = None + + self._open_device() + + self._id = "{}".format(self._remote_ip) + 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._device = InternalEthernetDevice(self._remote_ip, self._remote_control_port) + self._control_object = UdpHcpControl(self._remote_ip, device=self._device, remote_control_port=self._remote_control_port) + + def __enter__(self): + return self + + def __exit__(self, *args): + self.release() + return False + + def __del__(self): + self.release() + + @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.pyhailort.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._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._device = None + self._device_info = None + self._control_object = None + + self._open_device(device_info) + + # 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.pyhailort.pyhailort.PcieDeviceInfo` + """ + return InternalPcieDevice.scan_devices() + + def _open_device(self, device_info): + self._device = InternalPcieDevice(device_info) + self._device_info = self._device._device_info # Handeling a case where device_info is None + self._control_object = PcieHcpControl(device=self._device, device_info=self._device_info) + + def __enter__(self): + return self + + def __exit__(self, *args): + self.release() + return False + + def __del__(self): + self.release() diff --git a/hailort/libhailort/bindings/python/platform/hailo_platform/pyhailort/i2c_slaves.py b/hailort/libhailort/bindings/python/platform/hailo_platform/pyhailort/i2c_slaves.py new file mode 100644 index 0000000..b70dde8 --- /dev/null +++ b/hailort/libhailort/bindings/python/platform/hailo_platform/pyhailort/i2c_slaves.py @@ -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.pyhailort.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.pyhailort.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.pyhailort.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.pyhailort.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.pyhailort.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/pyhailort/power_measurement.py b/hailort/libhailort/bindings/python/platform/hailo_platform/pyhailort/power_measurement.py new file mode 100644 index 0000000..b0612af --- /dev/null +++ b/hailort/libhailort/bindings/python/platform/hailo_platform/pyhailort/power_measurement.py @@ -0,0 +1,14 @@ +from hailo_platform.pyhailort.pyhailort import (DvmTypes, PowerMeasurementTypes, # noqa F401 + SamplingPeriod, AveragingFactor, + HailoPowerMeasurementUtils, MeasurementBufferIndex, HailoRTException) + +# https://github.com/pybind/pybind11/issues/253 +import re +def enum_to_dict(enum): + return {k: v for k, v in enum.__dict__.items() if not re.match("__(.*)", str(k)) and isinstance(v, enum)} + +def _get_buffer_index_enum_member(index): + for name, member in enum_to_dict(MeasurementBufferIndex).items(): + if int(member) == index: + return member + raise HailoRTException("Invalid index") diff --git a/hailort/libhailort/bindings/python/platform/hailo_platform/pyhailort/pyhailort.py b/hailort/libhailort/bindings/python/platform/hailo_platform/pyhailort/pyhailort.py new file mode 100644 index 0000000..8351041 --- /dev/null +++ b/hailort/libhailort/bindings/python/platform/hailo_platform/pyhailort/pyhailort.py @@ -0,0 +1,1805 @@ +import pkg_resources +# hailo_platform package has been renamed to hailort, but the import is still hailo_platform +__version__ = pkg_resources.get_distribution("hailort").version + +import sys + +from argparse import ArgumentTypeError +import numpy +import time +from hailo_platform.common.logger.logger import default_logger +import gc +import os + +import hailo_platform.pyhailort._pyhailort as _pyhailort +if _pyhailort.__version__ != __version__: + raise ImportError("_pyhailort version ({}) does not match pyhailort version ({})".format(_pyhailort.__version__, __version__)) + +from hailo_platform.pyhailort._pyhailort import (BootloaderVersion, TemperatureInfo, # noqa F401 + DvmTypes, PowerMeasurementTypes, # noqa F401 + PowerMeasurementData, NotificationId, # noqa F401 + OvercurrentAlertState, + FormatOrder, + AveragingFactor, SamplingPeriod, MeasurementBufferIndex, + 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 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.pyhailort.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.pyhailort.pyhailort.MipiPixelsPerClock`): Number of pixels transmitted at each + clock. + number_of_lanes (int): Number of lanes to use. + clock_selection (:class:`~hailo_platform.pyhailort.pyhailort.MipiClockSelection`): Selection of clock range that would be + used. Setting :class:`~hailo_platform.pyhailort.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.pyhailort.pyhailort.MipiIspImageInOrder`): + The ISP Rx bayer pixel order. Only relevant when the ISP is enabled. + isp_img_out_data_type (:class:`~hailo_platform.pyhailort.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.pyhailort.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.pyhailort._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.pyhailort._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.pyhailort._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.pyhailort._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.pyhailort._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.pyhailort._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.pyhailort._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.pyhailort._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.pyhailort._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.pyhailort._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.pyhailort._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.pyhailort._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.pyhailort._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.pyhailort._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].tf_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.pyhailort.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.pyhailort.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.pyhailort.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.Device.create_eth(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(): + self.device.release() + 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" ([]::.). 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 []::.""" + 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 []::.') + + +class InternalPcieDevice(object): + def __init__(self, device_info=None): + self.device = None + if device_info is None: + device_info = InternalPcieDevice.scan_devices()[0] + self._device_info = device_info + with ExceptionWrapper(): + self.device = _pyhailort.Device.create_pcie(self._device_info) + + def __del__(self): + self.release() + + def release(self): + if self.device is None: + return + with ExceptionWrapper(): + self.device.release() + 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(): + self.device.direct_write_memory(address, data) + + def read_memory(self, address, size): + with ExceptionWrapper(): + return self.device.direct_read_memory(address, size) + + +class PcieDebugLog(object): + def __init__(self, pci_device): + self._pcie_device = pci_device + + def read(self, count, cpu_id): + with ExceptionWrapper(): + return self._pcie_device.device.read_log(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.pyhailort.pyhailort.VDeviceParams`, optional): VDevice params, call + :func:`VDevice.create_params` to get default params. Excludes 'device_infos'. + device_infos (list of :obj:`hailo_platform.pyhailort.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 = [] + 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): + return self + + def release(self): + """Release the allocated resources of the device. This function should be called when working with the device not as context-manager.""" + 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.pyhailort.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.pyhailort.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.pyhailort.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.pyhailort.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.pyhailort.pyhailort.FormatType`): The + default format type of the data for all input virtual streams. If quantized is False, + the default is :attr:`~hailo_platform.pyhailort.pyhailort.FormatType.FLOAT32`. Otherwise, + the default is :attr:`~hailo_platform.pyhailort.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.pyhailort.pyhailort.FormatType`): The + default format type of the data for all input virtual streams. If quantized is False, + the default is :attr:`~hailo_platform.pyhailort.pyhailort.FormatType.FLOAT32`. Otherwise, + the default is :attr:`~hailo_platform.pyhailort.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.pyhailort.pyhailort.FormatType`): The + default format type of the data for all output virtual streams. If quantized is False, + the default is :attr:`~hailo_platform.pyhailort.pyhailort.FormatType.FLOAT32`. Otherwise, + the default is :attr:`~hailo_platform.pyhailort.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.pyhailort.pyhailort.FormatType`): The + default format type of the data for all output virtual streams. If quantized is False, + the default is :attr:`~hailo_platform.pyhailort.pyhailort.FormatType.FLOAT32`. Otherwise, + the default is :attr:`~hailo_platform.pyhailort.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.pyhailort.pyhailort.FormatType`): The + default format type of the data for all output virtual streams. If quantized is False, + the default is :attr:`~hailo_platform.pyhailort.pyhailort.FormatType.FLOAT32`. Otherwise, + the default is :attr:`~hailo_platform.pyhailort.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._vstream_info = self._get_vstream_info(net_group_name, vstream_name) + + if isinstance(pipeline, (_pyhailort.InferVStreams)): + self._user_buffer_format = pipeline.get_user_buffer_format(vstream_name) + self._output_shape = pipeline.get_shape(vstream_name) + else: + self._user_buffer_format = pipeline.get_user_buffer_format() + self._output_shape = pipeline.shape + + self._is_nms = (self._user_buffer_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._user_buffer_format.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 _pyhailort.get_dtype(self._user_buffer_format.type) + + @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): + if not self.is_nms: + raise HailoRTException("Requested NMS info for non-NMS layer") + return self._quantized_empty_bbox + + def _get_vstream_info(self, net_group_name, vstream_name): + output_vstream_infos = self._hef.get_output_vstream_infos(net_group_name) + for info in output_vstream_infos: + if info.name == vstream_name: + return info + raise HailoRTException("No vstream matches the given name {}".format(vstream_name)) + + @property + def tf_nms_fomrat_shape(self): + if not self.is_nms: + raise HailoRTException("Requested NMS info for non-NMS layer") + 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.tf_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()) \ No newline at end of file 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 deleted file mode 100644 index cc5eb6a..0000000 --- a/hailort/libhailort/bindings/python/platform/hailo_platform/tools/benchmark_command.py +++ /dev/null @@ -1,5 +0,0 @@ -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 deleted file mode 100644 index e69de29..0000000 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 deleted file mode 100644 index e69de29..0000000 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 deleted file mode 100644 index d1e6bf8..0000000 --- a/hailort/libhailort/bindings/python/platform/hailo_platform/tools/firmware/configure_firmware.py +++ /dev/null @@ -1,15 +0,0 @@ -#!/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/firmware_config.py b/hailort/libhailort/bindings/python/platform/hailo_platform/tools/firmware/firmware_config.py deleted file mode 100644 index 7b5738a..0000000 --- a/hailort/libhailort/bindings/python/platform/hailo_platform/tools/firmware/firmware_config.py +++ /dev/null @@ -1,286 +0,0 @@ -#!/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 deleted file mode 100644 index 3588e28..0000000 --- a/hailort/libhailort/bindings/python/platform/hailo_platform/tools/firmware/sensor_config.py +++ /dev/null @@ -1,9 +0,0 @@ -#!/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 deleted file mode 100644 index a15d218..0000000 --- a/hailort/libhailort/bindings/python/platform/hailo_platform/tools/firmware/update_firmware.py +++ /dev/null @@ -1,9 +0,0 @@ -#!/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 deleted file mode 100644 index 653d0d0..0000000 --- a/hailort/libhailort/bindings/python/platform/hailo_platform/tools/firmware/update_second_stage.py +++ /dev/null @@ -1,9 +0,0 @@ -#!/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 deleted file mode 100644 index d169039..0000000 --- a/hailort/libhailort/bindings/python/platform/hailo_platform/tools/fw_control.py +++ /dev/null @@ -1,20 +0,0 @@ -#!/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/common/targets/__init__.py b/hailort/libhailort/bindings/python/platform/hailo_platform/tools/hailocli/__init__.py similarity index 100% rename from hailort/libhailort/bindings/python/platform/hailo_platform/common/targets/__init__.py rename to hailort/libhailort/bindings/python/platform/hailo_platform/tools/hailocli/__init__.py 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/tools/hailocli/base_utils.py similarity index 100% rename from hailort/libhailort/bindings/python/platform/hailo_platform/common/tools/cmd_utils/base_utils.py rename to hailort/libhailort/bindings/python/platform/hailo_platform/tools/hailocli/base_utils.py diff --git a/hailort/libhailort/bindings/python/platform/hailo_platform/config_definitions.json b/hailort/libhailort/bindings/python/platform/hailo_platform/tools/hailocli/config_definitions.json similarity index 100% rename from hailort/libhailort/bindings/python/platform/hailo_platform/config_definitions.json rename to hailort/libhailort/bindings/python/platform/hailo_platform/tools/hailocli/config_definitions.json diff --git a/hailort/libhailort/bindings/python/platform/hailo_platform/tools/hailocli/example_config.json b/hailort/libhailort/bindings/python/platform/hailo_platform/tools/hailocli/example_config.json new file mode 100644 index 0000000..f1a08bf --- /dev/null +++ b/hailort/libhailort/bindings/python/platform/hailo_platform/tools/hailocli/example_config.json @@ -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/common/tools/cmd_utils/hailo_device_utils.py b/hailort/libhailort/bindings/python/platform/hailo_platform/tools/hailocli/hailo_device_utils.py similarity index 91% rename from hailort/libhailort/bindings/python/platform/hailo_platform/common/tools/cmd_utils/hailo_device_utils.py rename to hailort/libhailort/bindings/python/platform/hailo_platform/tools/hailocli/hailo_device_utils.py index fd33bb1..99be958 100644 --- a/hailort/libhailort/bindings/python/platform/hailo_platform/common/tools/cmd_utils/hailo_device_utils.py +++ b/hailort/libhailort/bindings/python/platform/hailo_platform/tools/hailocli/hailo_device_utils.py @@ -1,10 +1,10 @@ #!/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.tools.hailocli.base_utils import CmdUtilsBaseUtil +from hailo_platform.pyhailort.hw_object import EthernetDevice, PcieDevice from hailo_platform.common.logger.logger import default_logger -from hailo_platform.drivers.hailort.pyhailort import PcieDeviceInfo, InternalPcieDevice +from hailo_platform.pyhailort.pyhailort import PcieDeviceInfo, InternalPcieDevice logger = default_logger() diff --git a/hailort/libhailort/bindings/python/platform/hailo_platform/tools/hailocli/hailocli_commands.py b/hailort/libhailort/bindings/python/platform/hailo_platform/tools/hailocli/hailocli_commands.py new file mode 100644 index 0000000..d3a62fd --- /dev/null +++ b/hailort/libhailort/bindings/python/platform/hailo_platform/tools/hailocli/hailocli_commands.py @@ -0,0 +1,70 @@ + +from hailo_platform.tools.hailocli.base_utils import HailortCliUtil + +""" + HailoRTCLI matching commands in Hailo-CLI tool. +""" + +class BenchmarkCommandCLI(HailortCliUtil): + def __init__(self, parser): + super().__init__(parser, 'benchmark') + + +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') + + +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') + + +class RunCommandCLI(HailortCliUtil): + def __init__(self, parser): + super().__init__(parser, 'run') + + +class SensorConfigCommandCLI(HailortCliUtil): + def __init__(self, parser): + super().__init__(parser, 'sensor-config') + + +class FWUpdaterCLI(HailortCliUtil): + """Cli tool for firmware updates""" + def __init__(self, parser): + super().__init__(parser, 'fw-update') + + +class SSBUpdaterCLI(HailortCliUtil): + """Cli tool for second stage boot updates""" + def __init__(self, parser): + super().__init__(parser, 'ssb-update') + + +class UDPRateLimiterCLI(HailortCliUtil): + """CLI tool for UDP rate limitation.""" + def __init__(self, parser): + super().__init__(parser, 'udp-rate-limiter') \ No newline at end of file diff --git a/hailort/libhailort/bindings/python/platform/hailo_platform/tools/cmd_utils/main.py b/hailort/libhailort/bindings/python/platform/hailo_platform/tools/hailocli/main.py similarity index 79% rename from hailort/libhailort/bindings/python/platform/hailo_platform/tools/cmd_utils/main.py rename to hailort/libhailort/bindings/python/platform/hailo_platform/tools/hailocli/main.py index 8133f35..0e329c4 100644 --- a/hailort/libhailort/bindings/python/platform/hailo_platform/tools/cmd_utils/main.py +++ b/hailort/libhailort/bindings/python/platform/hailo_platform/tools/hailocli/main.py @@ -5,17 +5,10 @@ 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 - +from hailo_platform.tools.hailocli.base_utils import HailortCliUtil, Helper, HailortCliUtilError +from hailo_platform.tools.hailocli.hailocli_commands import (FWUpdaterCLI, SSBUpdaterCLI, ControlCommandCLI, ScanCommandCLI, + LoggerCommandCLI, MeasurePowerCommandCLI, RunCommandCLI, SensorConfigCommandCLI, + FWConfigCommandCLI, BenchmarkCommandCLI, UDPRateLimiterCLI) # Note: PlatformCommands are external dependencies in phase2-sdk/demos repo; don't change! class PlatformCommands: @@ -30,9 +23,7 @@ class PlatformCommands: '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), 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 deleted file mode 100644 index 153ea8d..0000000 --- a/hailort/libhailort/bindings/python/platform/hailo_platform/tools/infer_cli.py +++ /dev/null @@ -1,54 +0,0 @@ -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 deleted file mode 100644 index 1b16bbc..0000000 --- a/hailort/libhailort/bindings/python/platform/hailo_platform/tools/run_command.py +++ /dev/null @@ -1,5 +0,0 @@ -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 index 658edac..75967ef 100644 --- 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 @@ -6,8 +6,7 @@ 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 +from hailo_platform.pyhailort.pyhailort import ConfiguredNetwork, HEF, TrafficControl, INPUT_DATAFLOW_BASE_PORT DEFAULT_MAX_KBPS = 850e3 DEFAULT_MAX_KBPS_PAPRIKA_B0 = 160e3 @@ -41,7 +40,7 @@ class RateLimiterWrapper(object): """RateLimiterWrapper constructor. Args: - configured_network_group (:class:`~hailo_platform.drivers.hailort.pyhailort.ConfiguredNetwork`): The + configured_network_group (:class:`~hailo_platform.pyhailort.pyhailort.ConfiguredNetwork`): The target network_group. fps (int): Frame rate. fps_factor (float): Safety factor by which to multiply the calculated UDP rate. @@ -113,10 +112,4 @@ class UDPRateLimiter(object): 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') + return results \ No newline at end of file diff --git a/hailort/libhailort/bindings/python/platform/hailo_platform/tools/watchdog.py b/hailort/libhailort/bindings/python/platform/hailo_platform/tools/watchdog.py deleted file mode 100644 index acc485e..0000000 --- a/hailort/libhailort/bindings/python/platform/hailo_platform/tools/watchdog.py +++ /dev/null @@ -1,33 +0,0 @@ -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/tutorials/notebooks/Inference Tutorial.ipynb b/hailort/libhailort/bindings/python/platform/hailo_tutorials/notebooks/HRT_0_Inference_Tutorial.ipynb similarity index 100% rename from hailort/libhailort/bindings/python/platform/tutorials/notebooks/Inference Tutorial.ipynb rename to hailort/libhailort/bindings/python/platform/hailo_tutorials/notebooks/HRT_0_Inference_Tutorial.ipynb diff --git a/hailort/libhailort/bindings/python/platform/tutorials/notebooks/Power Measurement Tutorial.ipynb b/hailort/libhailort/bindings/python/platform/hailo_tutorials/notebooks/HRT_1_Power_Measurement_Tutorial.ipynb similarity index 90% rename from hailort/libhailort/bindings/python/platform/tutorials/notebooks/Power Measurement Tutorial.ipynb rename to hailort/libhailort/bindings/python/platform/hailo_tutorials/notebooks/HRT_1_Power_Measurement_Tutorial.ipynb index a180861..c55f7b1 100644 --- a/hailort/libhailort/bindings/python/platform/tutorials/notebooks/Power Measurement Tutorial.ipynb +++ b/hailort/libhailort/bindings/python/platform/hailo_tutorials/notebooks/HRT_1_Power_Measurement_Tutorial.ipynb @@ -35,8 +35,7 @@ "%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" + "from hailo_platform import PcieDevice, DvmTypes, PowerMeasurementTypes, SamplingPeriod, AveragingFactor, MeasurementBufferIndex # noqa F401\n" ] }, { @@ -88,7 +87,7 @@ "\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" + "A measurement index is selected. It is a member of the enum class MeasurementBufferIndex (between 0 and 3). The DVM is not given so the default DVM will be used, as explained above.\n" ] }, { @@ -97,8 +96,8 @@ "metadata": {}, "outputs": [], "source": [ - "index = 0\n", - "target.control.set_power_measurement(index)" + "buffer_index = MeasurementBufferIndex.MEASUREMENT_BUFFER_INDEX_0\n", + "target.control.set_power_measurement(buffer_index=buffer_index)" ] }, { @@ -153,7 +152,7 @@ "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", + " measurements = target.control.get_power_measurement(buffer_index=buffer_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", diff --git a/hailort/libhailort/bindings/python/platform/requirements.txt b/hailort/libhailort/bindings/python/platform/requirements.txt index 10782be..08c3adb 100644 --- a/hailort/libhailort/bindings/python/platform/requirements.txt +++ b/hailort/libhailort/bindings/python/platform/requirements.txt @@ -9,7 +9,6 @@ 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 diff --git a/hailort/libhailort/bindings/python/platform/setup.py b/hailort/libhailort/bindings/python/platform/setup.py index d269c63..04502e9 100644 --- a/hailort/libhailort/bindings/python/platform/setup.py +++ b/hailort/libhailort/bindings/python/platform/setup.py @@ -1,5 +1,5 @@ import os -import sys +import json from setuptools import setup, find_packages from wheel.bdist_wheel import bdist_wheel as orig_bdist_wheel @@ -12,14 +12,27 @@ class NonPurePythonBDistWheel(orig_bdist_wheel): self.root_is_pure = False -def _get_pyhailort_lib(): +def _get_pyhailort_lib_path(): + conf_file_path = os.path.join(os.path.abspath(os.path.dirname( __file__ )), "wheel_conf.json") extension = { "posix": "so", "nt": "pyd", # Windows }[os.name] - py = "".join(map(str, sys.version_info[:2])) + if not os.path.isfile(conf_file_path): + return None - return f"drivers/hailort/_pyhailort*{py}*.{extension}" + with open(conf_file_path, "r") as conf_file: + content = json.load(conf_file) + return f"../hailo_platform/pyhailort/_pyhailort*{content['py_version']}*{content['arch']}*.{extension}" + +def _get_package_paths(): + packages = [] + pyhailort_lib = _get_pyhailort_lib_path() + if pyhailort_lib: + packages.append(pyhailort_lib) + packages.append("../hailo_tutorials/notebooks/*") + packages.append("../hailo_tutorials/hefs/*") + return packages if __name__ == "__main__": @@ -32,7 +45,7 @@ if __name__ == "__main__": description="HailoRT", entry_points={ "console_scripts": [ - "hailo=hailo_platform.tools.cmd_utils.main:main", + "hailo=hailo_platform.tools.hailocli.main:main", ] }, install_requires=[ @@ -41,16 +54,13 @@ if __name__ == "__main__": "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 - ], + "hailo_platform": _get_package_paths(), }, packages=find_packages(), platforms=[ @@ -58,6 +68,6 @@ if __name__ == "__main__": "linux_aarch64", ], url="https://hailo.ai/", - version="4.6.0", + version="4.8.0", zip_safe=False, ) diff --git a/hailort/libhailort/bindings/python/src/CMakeLists.txt b/hailort/libhailort/bindings/python/src/CMakeLists.txt index 0df9525..e1147b4 100644 --- a/hailort/libhailort/bindings/python/src/CMakeLists.txt +++ b/hailort/libhailort/bindings/python/src/CMakeLists.txt @@ -1,5 +1,7 @@ cmake_minimum_required(VERSION 3.0.0) +option(HAILO_BUILD_PYHAILORT_INTERNAL OFF) + if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux") string(REPLACE "." "" dpython ${PYBIND11_PYTHON_VERSION}) # E.g "3.5" -> "35" if(${dpython} LESS "38") @@ -11,11 +13,20 @@ if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux") endif() set(PYHAILORT_DIR ${CMAKE_CURRENT_LIST_DIR}) -add_subdirectory(internal) + pybind11_add_module(_pyhailort pyhailort.cpp + device_api.cpp + hef_api.cpp + vstream_api.cpp ${HAILORT_COMMON_CPP_SOURCES} ) + +set_target_properties(_pyhailort PROPERTIES + CXX_STANDARD 14 + CXX_STANDARD_REQUIRED YES +) + target_include_directories(_pyhailort PRIVATE $ @@ -30,3 +41,14 @@ if(WIN32) endif() target_compile_options(_pyhailort PRIVATE ${HAILORT_COMPILE_OPTIONS}) exclude_archive_libs_symbols(_pyhailort) + +if (HAILO_BUILD_PYHAILORT_INTERNAL) + add_subdirectory(internal) + # copy files to venv + if(HAILO_BUILD_PYHAILORT_VENV) + add_custom_target(pyhailort_internal_venv ALL + COMMAND ${CMAKE_COMMAND} -E copy $ ${PROJECT_SOURCE_DIR}/platform_internals/hailo_platform_internals/pyhailort/ + ) + add_dependencies(pyhailort_internal_venv _pyhailort_internal) + endif() +endif() diff --git a/hailort/libhailort/bindings/python/src/bindings_common.hpp b/hailort/libhailort/bindings/python/src/bindings_common.hpp index ea31187..95b60a9 100644 --- a/hailort/libhailort/bindings/python/src/bindings_common.hpp +++ b/hailort/libhailort/bindings/python/src/bindings_common.hpp @@ -18,52 +18,46 @@ namespace hailort { - -int convert_format_type_to_int(const hailo_format_type_t& type) +class HailoRTBindingsCommon { - 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 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) +public: + static std::string convert_format_type_to_string(const hailo_format_type_t &type) { - 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}; + 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."); + } } -} + + static std::vector 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}; + } + } + + static py::dtype get_dtype(const hailo_format_type_t &type) + { + return py::dtype(HailoRTBindingsCommon::convert_format_type_to_string(type)); + } +}; } /* namespace hailort */ diff --git a/hailort/libhailort/bindings/python/src/device_api.cpp b/hailort/libhailort/bindings/python/src/device_api.cpp new file mode 100644 index 0000000..d363ed4 --- /dev/null +++ b/hailort/libhailort/bindings/python/src/device_api.cpp @@ -0,0 +1,568 @@ +/** + * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved. + * Distributed under the MIT license (https://opensource.org/licenses/MIT) + **/ +/** + * @file device_api.cpp + * @brief implementations of binding to hailo device + * + **/ + +#include "device_api.hpp" + + +namespace hailort +{ + +DeviceWrapper DeviceWrapper::create_pcie(hailo_pcie_device_info_t &device_info) +{ + auto device = Device::create_pcie(device_info); + VALIDATE_EXPECTED(device); + + return DeviceWrapper(device.release()); +} + +DeviceWrapper DeviceWrapper::create_eth(std::string &device_address, uint16_t port, + uint32_t timeout_milliseconds, uint8_t max_number_of_attempts) +{ + hailo_eth_device_info_t device_info = {}; + + /* Validate address length */ + if (INET_ADDRSTRLEN < device_address.size()) { + EXIT_WITH_ERROR("device_address is too long") + } + + device_info.host_address.sin_family = AF_INET; + device_info.host_address.sin_port = HAILO_ETH_PORT_ANY; + auto 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.c_str(), &(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; + + auto device = Device::create_eth(device_info); + VALIDATE_EXPECTED(device); + + return DeviceWrapper(device.release()); +} + +DeviceWrapper DeviceWrapper::create_core() +{ + auto device = Device::create_core_device(); + VALIDATE_EXPECTED(device); + + return DeviceWrapper(device.release()); +} + +void DeviceWrapper::release() +{ + m_device.reset(); +} + +/* Controls */ +hailo_device_identity_t DeviceWrapper::identify() +{ + auto board_info = device().identify(); + VALIDATE_EXPECTED(board_info); + + return board_info.release(); +} + +hailo_core_information_t DeviceWrapper::core_identify() +{ + auto core_info = device().core_identify(); + VALIDATE_EXPECTED(core_info); + + return core_info.release(); +} + +void DeviceWrapper::set_fw_logger(hailo_fw_logger_level_t level, uint32_t interface_mask) +{ + auto status = device().set_fw_logger(level, interface_mask); + VALIDATE_STATUS(status); +} + +void DeviceWrapper::set_throttling_state(bool should_activate) +{ + auto status = device().set_throttling_state(should_activate); + VALIDATE_STATUS(status); +} + +bool DeviceWrapper::get_throttling_state() +{ + + auto is_active_expected = device().get_throttling_state(); + VALIDATE_EXPECTED(is_active_expected); + + return is_active_expected.release(); +} + +void DeviceWrapper::set_overcurrent_state(bool should_activate) +{ + auto status = device().set_overcurrent_state(should_activate); + VALIDATE_STATUS(status); +} + +bool DeviceWrapper::get_overcurrent_state() +{ + auto is_required_expected = device().get_overcurrent_state(); + VALIDATE_EXPECTED(is_required_expected); + + return is_required_expected.release(); +} + +py::bytes DeviceWrapper::read_memory(uint32_t address, uint32_t length) +{ + std::unique_ptr response = make_unique_nothrow(length, '\x00'); + VALIDATE_NOT_NULL(response); + + MemoryView data_view(const_cast(reinterpret_cast(response->data())), length); + auto status = device().read_memory(address, data_view); + VALIDATE_STATUS(status); + + return *response; +} + +void DeviceWrapper::write_memory(uint32_t address, py::bytes data, uint32_t length) +{ + auto status = device().write_memory(address, MemoryView(const_cast(reinterpret_cast(std::string(data).c_str())), length)); + VALIDATE_STATUS(status); +} + +void DeviceWrapper::test_chip_memories() +{ + hailo_status status = device().test_chip_memories(); + VALIDATE_STATUS(status); +} + +void DeviceWrapper::i2c_write(hailo_i2c_slave_config_t *slave_config, uint32_t register_address, py::bytes data, + uint32_t length) +{ + VALIDATE_NOT_NULL(slave_config); + + std::string data_str(data); + MemoryView data_view = MemoryView::create_const(data_str.c_str(), length); + auto status = device().i2c_write(*slave_config, register_address, data_view); + VALIDATE_STATUS(status); +} + +py::bytes DeviceWrapper::i2c_read(hailo_i2c_slave_config_t *slave_config, uint32_t register_address, uint32_t length) +{ + VALIDATE_NOT_NULL(slave_config); + + std::unique_ptr response = make_unique_nothrow(length, '\x00'); + VALIDATE_NOT_NULL(response); + + MemoryView data_view(const_cast(reinterpret_cast(response->data())), length); + auto status = device().i2c_read(*slave_config, register_address, data_view); + VALIDATE_STATUS(status); + + return *response; +} + +float32_t DeviceWrapper::power_measurement(hailo_dvm_options_t dvm, + hailo_power_measurement_types_t measurement_type) +{ + auto measurement = device().power_measurement(dvm, measurement_type); + VALIDATE_EXPECTED(measurement); + + return measurement.release(); +} + +void DeviceWrapper::start_power_measurement(hailo_averaging_factor_t averaging_factor, hailo_sampling_period_t sampling_period) +{ + auto status = device().start_power_measurement(averaging_factor, sampling_period); + VALIDATE_STATUS(status); +} + +void DeviceWrapper::set_power_measurement(hailo_measurement_buffer_index_t buffer_index, hailo_dvm_options_t dvm, + hailo_power_measurement_types_t measurement_type) +{ + auto status = device().set_power_measurement(buffer_index, + dvm, measurement_type); + VALIDATE_STATUS(status); +} + +PowerMeasurementData DeviceWrapper::get_power_measurement(hailo_measurement_buffer_index_t buffer_index, bool should_clear) +{ + auto measurement_data = device().get_power_measurement(buffer_index, + should_clear); + VALIDATE_EXPECTED(measurement_data); + + return PowerMeasurementData(measurement_data.release()); +} + +void DeviceWrapper::stop_power_measurement() +{ + auto status = device().stop_power_measurement(); + VALIDATE_STATUS(status); +} + +void DeviceWrapper::reset(hailo_reset_device_mode_t mode) +{ + auto status = device().reset(mode); + VALIDATE_STATUS(status); +} + +hailo_fw_user_config_information_t DeviceWrapper::examine_user_config() +{ + auto user_config_info = device().examine_user_config(); + VALIDATE_EXPECTED(user_config_info); + + return user_config_info.release(); +} + +py::bytes DeviceWrapper::read_user_config() +{ + auto config_buffer = device().read_user_config(); + VALIDATE_EXPECTED(config_buffer); + + std::unique_ptr response = make_unique_nothrow( + const_cast(reinterpret_cast(config_buffer->data())), config_buffer->size()); + VALIDATE_NOT_NULL(response); + + return *response; +} + +void DeviceWrapper::write_user_config(py::bytes data) +{ + std::string data_str(data); + MemoryView data_view = MemoryView::create_const(data_str.c_str(), data_str.size()); + auto status = device().write_user_config(data_view); + VALIDATE_STATUS(status); +} + +void DeviceWrapper::erase_user_config() +{ + auto status = device().erase_user_config(); + VALIDATE_STATUS(status); +} + +py::bytes DeviceWrapper::read_board_config() +{ + auto config_buffer = device().read_board_config(); + VALIDATE_EXPECTED(config_buffer); + + std::unique_ptr response = make_unique_nothrow( + const_cast(reinterpret_cast(config_buffer->data())), config_buffer->size()); + VALIDATE_NOT_NULL(response); + + return *response; +} + +void DeviceWrapper::write_board_config(py::bytes data) +{ + std::string data_str(data); + MemoryView data_view = MemoryView::create_const(data_str.c_str(), data_str.size()); + auto status = device().write_board_config(data_view); + VALIDATE_STATUS(status); +} + +hailo_extended_device_information_t DeviceWrapper::get_extended_device_information() +{ + auto extended_device_info = device().get_extended_device_information(); + VALIDATE_EXPECTED(extended_device_info); + + return extended_device_info.release(); +} + +hailo_health_info_t DeviceWrapper::get_health_information() +{ + auto health_info = device().get_health_information(); + VALIDATE_EXPECTED(health_info); + + return health_info.release(); +} + +void DeviceWrapper::sensor_store_config(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) +{ + auto status = device().store_sensor_config(section_index, static_cast(sensor_type), reset_data_size, + config_height, config_width, config_fps, config_file_path, config_name); + VALIDATE_STATUS(status); +} + +void DeviceWrapper::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 status = 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); +} + +py::bytes DeviceWrapper::sensor_get_sections_info() +{ + auto buffer = device().sensor_get_sections_info(); + VALIDATE_EXPECTED(buffer); + + std::unique_ptr response = make_unique_nothrow( + const_cast(reinterpret_cast(buffer->data())), buffer->size()); + VALIDATE_NOT_NULL(response); + + return *response; +} + +void DeviceWrapper::sensor_set_i2c_bus_index(uint32_t sensor_type, uint32_t bus_index) +{ + hailo_status status = device().sensor_set_i2c_bus_index(static_cast(sensor_type), bus_index); + VALIDATE_STATUS(status); +} + +void DeviceWrapper::sensor_load_and_start_config(uint32_t section_index) +{ + auto status = device().sensor_load_and_start_config(section_index); + VALIDATE_STATUS(status); +} + +void DeviceWrapper::sensor_reset(uint32_t section_index) +{ + auto status = device().sensor_reset(section_index); + VALIDATE_STATUS(status); +} + +void DeviceWrapper::sensor_set_generic_i2c_slave(uint16_t slave_address, + uint8_t register_address_size, uint8_t bus_index, uint8_t should_hold_bus, uint8_t endianness) +{ + auto status = device().sensor_set_generic_i2c_slave(slave_address, register_address_size, + bus_index, should_hold_bus, endianness); + VALIDATE_STATUS(status); +} + +void DeviceWrapper::firmware_update(py::bytes fw_bin, uint32_t fw_bin_length, bool should_reset) +{ + auto status = device().firmware_update(MemoryView::create_const(std::string(fw_bin).c_str(), fw_bin_length), + should_reset); + VALIDATE_STATUS(status); +} + +void DeviceWrapper::second_stage_update(py::bytes second_stage_bin, uint32_t second_stage_bin_length) +{ + auto status = device().second_stage_update((uint8_t *)std::string(second_stage_bin).c_str(), + second_stage_bin_length); + VALIDATE_STATUS(status); +} + +py::list DeviceWrapper::configure(const HefWrapper &hef, + const NetworkGroupsParamsMap &configure_params) +{ + auto network_groups = 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; +} + +void DeviceWrapper::set_pause_frames(bool rx_pause_frames_enable) +{ + auto status = device().set_pause_frames(rx_pause_frames_enable); + VALIDATE_STATUS(status); +} + +void DeviceWrapper::wd_enable(hailo_cpu_id_t cpu_id) +{ + hailo_status status = device().wd_enable(cpu_id); + VALIDATE_STATUS(status); +} + +void DeviceWrapper::wd_disable(hailo_cpu_id_t cpu_id) +{ + hailo_status status = device().wd_disable(cpu_id); + VALIDATE_STATUS(status); +} + +void DeviceWrapper::wd_config(hailo_cpu_id_t cpu_id, uint32_t wd_cycles, hailo_watchdog_mode_t wd_mode) +{ + auto status = device().wd_config(cpu_id, wd_cycles, wd_mode); + VALIDATE_STATUS(status); +} + +uint32_t DeviceWrapper::previous_system_state(hailo_cpu_id_t cpu_id) +{ + auto system_state = device().previous_system_state(cpu_id); + VALIDATE_EXPECTED(system_state); + + return system_state.release(); +} + +hailo_chip_temperature_info_t DeviceWrapper::get_chip_temperature() +{ + auto temp_info = device().get_chip_temperature(); + VALIDATE_EXPECTED(temp_info); + + return temp_info.release(); +} + +void DeviceWrapper::set_notification_callback(const std::function &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().set_notification_callback( + [callback, op = std::move(opaque)] (Device &device, const hailo_notification_t ¬ification, void* opaque) { + (void)opaque; + callback((uintptr_t)(&device), notification, op); + }, notification_id, nullptr); + VALIDATE_STATUS(status); +} + +void DeviceWrapper::remove_notification_callback(hailo_notification_id_t notification_id) +{ + auto status = device().remove_notification_callback(notification_id); + VALIDATE_STATUS(status); +} + +py::bytes DeviceWrapper::read_log(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().read_log(response_view, cpu_id); + VALIDATE_EXPECTED(response_size_expected); + + response.resize(response_size_expected.release()); + return py::bytes(response); +} + +void DeviceWrapper::direct_write_memory(uint32_t address, py::bytes buffer) +{ + const auto buffer_str = static_cast(buffer); + hailo_status status = device().direct_write_memory(address, buffer_str.c_str(), + (uint32_t) (buffer_str.length())); + VALIDATE_STATUS(status); +} + +py::bytes DeviceWrapper::direct_read_memory(uint32_t address, uint32_t size) +{ + std::string buffer_str; + + buffer_str.reserve(size); + buffer_str.resize(size); + + hailo_status status = device().direct_read_memory(address, (char*)buffer_str.c_str(), size); + VALIDATE_STATUS(status); + + buffer_str.resize(size); + return py::bytes(buffer_str); +} + +void DeviceWrapper::add_to_python_module(py::module &m) +{ + py::class_(m, "Device") + // C'tors + .def("create_pcie", &DeviceWrapper::create_pcie) + .def("create_eth", &DeviceWrapper::create_eth) + .def("create_core", &DeviceWrapper::create_core) + .def("release", &DeviceWrapper::release) + + //HEF + .def("configure", &DeviceWrapper::configure) + + // Controls + .def("identify", &DeviceWrapper::identify) + .def("core_identify", &DeviceWrapper::core_identify) + .def("set_fw_logger", &DeviceWrapper::set_fw_logger) + .def("read_memory", &DeviceWrapper::read_memory) + .def("write_memory", &DeviceWrapper::write_memory) + .def("power_measurement", &DeviceWrapper::power_measurement) + .def("start_power_measurement", &DeviceWrapper::start_power_measurement) + .def("stop_power_measurement", &DeviceWrapper::stop_power_measurement) + .def("set_power_measurement", &DeviceWrapper::set_power_measurement) + .def("get_power_measurement", &DeviceWrapper::get_power_measurement) + .def("firmware_update", &DeviceWrapper::firmware_update) + .def("second_stage_update", &DeviceWrapper::second_stage_update) + .def("examine_user_config", &DeviceWrapper::examine_user_config) + .def("read_user_config", &DeviceWrapper::read_user_config) + .def("write_user_config", &DeviceWrapper::write_user_config) + .def("erase_user_config", &DeviceWrapper::erase_user_config) + .def("read_board_config", &DeviceWrapper::read_board_config) + .def("write_board_config", &DeviceWrapper::write_board_config) + .def("i2c_write", &DeviceWrapper::i2c_write) + .def("i2c_read", &DeviceWrapper::i2c_read) + .def("sensor_store_config", &DeviceWrapper::sensor_store_config) + .def("store_isp_config", &DeviceWrapper::store_isp_config) + .def("sensor_set_i2c_bus_index", &DeviceWrapper::sensor_set_i2c_bus_index) + .def("sensor_load_and_start_config", &DeviceWrapper::sensor_load_and_start_config) + .def("sensor_reset", &DeviceWrapper::sensor_reset) + .def("sensor_set_generic_i2c_slave", &DeviceWrapper::sensor_set_generic_i2c_slave) + .def("sensor_get_sections_info", &DeviceWrapper::sensor_get_sections_info) + .def("reset", &DeviceWrapper::reset) + .def("wd_enable", &DeviceWrapper::wd_enable) + .def("wd_disable", &DeviceWrapper::wd_disable) + .def("wd_config", &DeviceWrapper::wd_config) + .def("previous_system_state", &DeviceWrapper::previous_system_state) + .def("get_chip_temperature", &DeviceWrapper::get_chip_temperature) + .def("get_extended_device_information", &DeviceWrapper::get_extended_device_information) + .def("set_pause_frames", &DeviceWrapper::set_pause_frames) + .def("test_chip_memories", &DeviceWrapper::test_chip_memories) + .def("_get_health_information", &DeviceWrapper::get_health_information) + .def("set_throttling_state", &DeviceWrapper::set_throttling_state) + .def("get_throttling_state", &DeviceWrapper::get_throttling_state) + .def("_set_overcurrent_state", &DeviceWrapper::set_overcurrent_state) + .def("_get_overcurrent_state", &DeviceWrapper::get_overcurrent_state) + .def("direct_write_memory", &DeviceWrapper::direct_write_memory) + .def("direct_read_memory", &DeviceWrapper::direct_read_memory) + .def("read_log", &DeviceWrapper::read_log, py::return_value_policy::move) + + .def("set_notification_callback", &DeviceWrapper::set_notification_callback) + .def("remove_notification_callback", &DeviceWrapper::remove_notification_callback) + ; +} + +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(); + data.average_time_value_milliseconds = t[1].cast(); + data.min_value = t[2].cast(); + data.max_value = t[3].cast(); + data.total_number_of_samples = t[4].cast(); + 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)); +} + +} /* namespace hailort */ diff --git a/hailort/libhailort/bindings/python/src/device_api.hpp b/hailort/libhailort/bindings/python/src/device_api.hpp new file mode 100644 index 0000000..24f85e0 --- /dev/null +++ b/hailort/libhailort/bindings/python/src/device_api.hpp @@ -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 device_api.hpp + * @brief Defines binding to hailo device + * + **/ + +#ifndef _DEVICE_API_HPP_ +#define _DEVICE_API_HPP_ + +#include "utils.hpp" +#include "hailo/hailort.hpp" +#include "common/socket.hpp" +#include "hef_api.hpp" + +#include +#include + + +namespace hailort +{ + + +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; +}; + + +class DeviceWrapper final +{ +public: + + static DeviceWrapper create_pcie(hailo_pcie_device_info_t &device_info); + static DeviceWrapper create_eth(std::string &device_address, uint16_t port, + uint32_t timeout_milliseconds, uint8_t max_number_of_attempts); + static DeviceWrapper create_core(); + void release(); + + Device& device() + { + VALIDATE_NOT_NULL(m_device); + return *(m_device.get()); + } + + Device& operator*() // Used for control_internals + { + return device(); + } + + /* Controls */ + hailo_device_identity_t identify(); + hailo_core_information_t core_identify(); + void set_fw_logger(hailo_fw_logger_level_t level, uint32_t interface_mask); + void set_throttling_state(bool should_activate); + bool get_throttling_state(); + void set_overcurrent_state(bool should_activate); + bool get_overcurrent_state(); + py::bytes read_memory(uint32_t address, uint32_t length); + void write_memory(uint32_t address, py::bytes data, uint32_t length); + void test_chip_memories(); + void i2c_write(hailo_i2c_slave_config_t *slave_config, uint32_t register_address, py::bytes data, + uint32_t length); + py::bytes i2c_read(hailo_i2c_slave_config_t *slave_config, uint32_t register_address, uint32_t length); + float32_t power_measurement(hailo_dvm_options_t dvm, + hailo_power_measurement_types_t measurement_type); + void start_power_measurement(hailo_averaging_factor_t averaging_factor, hailo_sampling_period_t sampling_period); + void set_power_measurement(hailo_measurement_buffer_index_t buffer_index, hailo_dvm_options_t dvm, + hailo_power_measurement_types_t measurement_type); + PowerMeasurementData get_power_measurement(hailo_measurement_buffer_index_t buffer_index, bool should_clear); + void stop_power_measurement(); + void reset(hailo_reset_device_mode_t mode); + hailo_fw_user_config_information_t examine_user_config(); + py::bytes read_user_config(); + void write_user_config(py::bytes data); + void erase_user_config(); + py::bytes read_board_config(); + void write_board_config(py::bytes data); + hailo_extended_device_information_t get_extended_device_information(); + hailo_health_info_t get_health_information(); + void sensor_store_config(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); + void 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); + py::bytes sensor_get_sections_info(); + void sensor_set_i2c_bus_index(uint32_t sensor_type, uint32_t bus_index); + void sensor_load_and_start_config(uint32_t section_index); + void sensor_reset(uint32_t section_index); + void sensor_set_generic_i2c_slave(uint16_t slave_address, + uint8_t register_address_size, uint8_t bus_index, uint8_t should_hold_bus, uint8_t endianness); + void firmware_update(py::bytes fw_bin, uint32_t fw_bin_length, bool should_reset); + void second_stage_update(py::bytes second_stage_bin, uint32_t second_stage_bin_length); + py::list configure(const HefWrapper &hef, + const NetworkGroupsParamsMap &configure_params={}); + void set_pause_frames(bool rx_pause_frames_enable); + void wd_enable(hailo_cpu_id_t cpu_id); + void wd_disable(hailo_cpu_id_t cpu_id); + void wd_config(hailo_cpu_id_t cpu_id, uint32_t wd_cycles, hailo_watchdog_mode_t wd_mode); + uint32_t previous_system_state(hailo_cpu_id_t cpu_id); + hailo_chip_temperature_info_t get_chip_temperature(); + void set_notification_callback(const std::function &callback, + hailo_notification_id_t notification_id, py::object opaque); + void remove_notification_callback(hailo_notification_id_t notification_id); + py::bytes read_log(size_t byte_count, hailo_cpu_id_t cpu_id); + void direct_write_memory(uint32_t address, py::bytes buffer); + py::bytes direct_read_memory(uint32_t address, uint32_t size); + + static void add_to_python_module(py::module &m); + +private: + DeviceWrapper(std::unique_ptr &&device) + : m_device(std::move(device)) {} + + std::unique_ptr m_device; +}; + +} /* namespace hailort */ + +#endif /* _DEVICE_API_HPP_ */ diff --git a/hailort/libhailort/bindings/python/src/hef_api.cpp b/hailort/libhailort/bindings/python/src/hef_api.cpp new file mode 100644 index 0000000..d31c486 --- /dev/null +++ b/hailort/libhailort/bindings/python/src/hef_api.cpp @@ -0,0 +1,318 @@ +/** + * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved. + * Distributed under the MIT license (https://opensource.org/licenses/MIT) + **/ +/** + * @file hef_api.cpp + * @brief implementation of binding to an HEF class, and network_group usage over Python. + * + * TODO: doc + **/ + +#include "hef_api.hpp" + + +namespace hailort +{ + +HefWrapper::HefWrapper(const std::string &hef_path) +{ + auto hef_expected = Hef::create(hef_path); + VALIDATE_EXPECTED(hef_expected); + + hef = make_unique_nothrow(hef_expected.release()); + if (nullptr == hef) { + THROW_STATUS_ERROR(HAILO_OUT_OF_HOST_MEMORY); + } +} + +HefWrapper::HefWrapper(const MemoryView &hef_buffer) +{ + auto hef_expected = Hef::create(hef_buffer); + VALIDATE_EXPECTED(hef_expected); + + hef = make_unique_nothrow(hef_expected.release()); + if (nullptr == hef) { + THROW_STATUS_ERROR(HAILO_OUT_OF_HOST_MEMORY); + } +} + +HefWrapper HefWrapper::create_from_buffer(py::bytes data) +{ + return HefWrapper(MemoryView((uint8_t*)std::string(data).c_str(), std::string(data).size())); +} + +HefWrapper HefWrapper::create_from_file(const std::string &hef_path) +{ + return HefWrapper(hef_path); +} + +py::list HefWrapper::get_network_group_names() +{ + return py::cast(hef->get_network_groups_names()); +} + +py::list HefWrapper::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 HefWrapper::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 HefWrapper::get_bottleneck_fps(const std::string &net_group_name) +{ + Expected bottleneck_fps = hef->get_bottleneck_fps(net_group_name); + VALIDATE_EXPECTED(bottleneck_fps); + return bottleneck_fps.release(); +} + +py::dict HefWrapper::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 HefWrapper::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 HefWrapper::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 HefWrapper::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 HefWrapper::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 HefWrapper::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 HefWrapper::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 HefWrapper::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 HefWrapper::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 HefWrapper::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 HefWrapper::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 HefWrapper::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 HefWrapper::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 HefWrapper::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 HefWrapper::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 HefWrapper::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 res; + for (const auto &info : network_infos.value()) { + res.push_back(info.name); + } + + return py::cast(res); +} + +ActivatedAppContextManagerWrapper::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& ActivatedAppContextManagerWrapper::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 ActivatedAppContextManagerWrapper::exit() +{ + m_activated_net_group.reset(); +} + +void ActivatedAppContextManagerWrapper::add_to_python_module(py::module &m) +{ + py::class_(m, "ActivatedApp") + .def("__enter__", &ActivatedAppContextManagerWrapper::enter, py::return_value_policy::reference) + .def("__exit__", [&](ActivatedAppContextManagerWrapper &self, py::args) { self.exit(); }) + ; +} + +void HefWrapper::initialize_python_module(py::module &m) +{ + py::class_(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_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_(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 &input_vstreams_params) + { + return InputVStreamsWrapper::create(self, input_vstreams_params); + }) + .def("OutputVStreams", [](ConfiguredNetworkGroup &self, std::map &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_(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(buff->data()), buff->size()); + }) + .def("get_invalid_frames_count", [](ActivatedNetworkGroup& self) + { + return self.get_invalid_frames_count(); + }) + ; +} + +} /* namespace hailort */ diff --git a/hailort/libhailort/bindings/python/src/hef_api.hpp b/hailort/libhailort/bindings/python/src/hef_api.hpp index a8f7312..4de0a3c 100644 --- a/hailort/libhailort/bindings/python/src/hef_api.hpp +++ b/hailort/libhailort/bindings/python/src/hef_api.hpp @@ -14,8 +14,8 @@ #include "hailo/hef.hpp" #include "hailo/network_rate_calculator.hpp" +#include "hailo/network_group.hpp" -#include "bindings_common.hpp" #include "vstream_api.hpp" #include "utils.hpp" #include "common/logger_macros.hpp" @@ -34,210 +34,40 @@ 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_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_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); - } + HefWrapper(const std::string &hef_path); + HefWrapper(const MemoryView &hef_buffer); + static HefWrapper create_from_buffer(py::bytes data); + static HefWrapper create_from_file(const std::string &hef_path); + py::list get_network_group_names(); + py::list get_network_groups_infos(); + py::list get_sorted_output_names(std::string net_group_name); + float64_t get_bottleneck_fps(const std::string &net_group_name); + py::dict get_udp_rates_dict(const std::string &net_group_name, uint32_t fps, uint32_t max_supported_rate_bytes); + py::list get_original_names_from_vstream_name(const std::string &vstream_name, const std::string &net_group_name); + std::string get_vstream_name_from_original_name(const std::string &original_name, const std::string &net_group_name); + py::list get_stream_names_from_vstream_name(const std::string &vstream_name, const std::string &net_group_name); + py::list get_vstream_names_from_stream_name(const std::string &stream_name, const std::string &net_group_name); + 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); + 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); + py::list get_input_vstream_infos(const std::string &name); + py::list get_output_vstream_infos(const std::string &name); + py::list get_all_vstream_infos(const std::string &name); + py::list get_input_stream_infos(const std::string &name); + py::list get_output_stream_infos(const std::string &name); + py::list get_all_stream_infos(const std::string &name); + py::dict create_configure_params(hailo_stream_interface_t interface); const std::unique_ptr& 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 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 res; - for (const auto &info : network_infos.value()) { - res.push_back(info.name); - } - - return py::cast(res); - }; + const hailo_mipi_input_stream_params_t &mipi_params); + py::list get_networks_names(const std::string &net_group_name); + static void initialize_python_module(py::module &m); private: std::unique_ptr hef; @@ -247,129 +77,17 @@ 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_(m, "ActivatedApp") - .def("__enter__", &ActivatedAppContextManagerWrapper::enter, py::return_value_policy::reference) - .def("__exit__", [&](ActivatedAppContextManagerWrapper &self, py::args) { self.exit(); }) - ; - } - + const hailo_activate_network_group_params_t &network_group_params); + + const ActivatedNetworkGroup& enter(); + void exit(); + static void add_to_python_module(py::module &m); private: std::unique_ptr 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_(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_(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 &input_vstreams_params) - { - return InputVStreamsWrapper::create(self, input_vstreams_params); - }) - .def("OutputVStreams", [](ConfiguredNetworkGroup &self, std::map &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_(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(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 index e011653..c36310b 100644 --- a/hailort/libhailort/bindings/python/src/internal/CMakeLists.txt +++ b/hailort/libhailort/bindings/python/src/internal/CMakeLists.txt @@ -6,6 +6,11 @@ pybind11_add_module(_pyhailort_internal SHARED ${HAILORT_SRCS_ABS} ) +set_target_properties(_pyhailort_internal PROPERTIES + CXX_STANDARD 14 + CXX_STANDARD_REQUIRED YES +) + target_include_directories(_pyhailort_internal PRIVATE $ @@ -16,10 +21,10 @@ target_include_directories(_pyhailort_internal $ ) -target_link_libraries(_pyhailort_internal PRIVATE libhailort hef_proto spdlog::spdlog readerwriterqueue) +target_link_libraries(_pyhailort_internal PRIVATE libhailort hef_proto spdlog::spdlog readerwriterqueue microprofile) 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 +exclude_archive_libs_symbols(_pyhailort_internal) diff --git a/hailort/libhailort/bindings/python/src/internal/control_api.cpp b/hailort/libhailort/bindings/python/src/internal/control_api.cpp index 75a4f26..b53f4cc 100644 --- a/hailort/libhailort/bindings/python/src/internal/control_api.cpp +++ b/hailort/libhailort/bindings/python/src/internal/control_api.cpp @@ -16,180 +16,127 @@ namespace hailort { - -void ControlWrapper::set_clock_freq(uintptr_t device, uint32_t clock_freq) +void ControlWrapper::set_clock_freq(DeviceWrapper &device, uint32_t clock_freq) { - VALIDATE_NOT_NULL(reinterpret_cast(device)); - auto status = Control::set_clock_freq(*reinterpret_cast(device), clock_freq); + auto status = Control::set_clock_freq(*device, clock_freq); VALIDATE_STATUS(status); } -void ControlWrapper::close_all_streams(uintptr_t device) +void ControlWrapper::close_all_streams(DeviceWrapper &device) { - hailo_status status = HAILO_UNINITIALIZED; - - VALIDATE_NOT_NULL(reinterpret_cast(device)); - status = Control::close_all_streams(*reinterpret_cast(device)); + auto status = Control::close_all_streams(*device); VALIDATE_STATUS(status); } -void ControlWrapper::config_ahb_to_axi(uintptr_t device, bool use_64bit_data_only) +void ControlWrapper::config_ahb_to_axi(DeviceWrapper &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)); - status = Control::config_core_top(*reinterpret_cast(device), config_type, ¶ms); + auto status = Control::config_core_top(*device, config_type, ¶ms); VALIDATE_STATUS(status); - - return; } -void ControlWrapper::phy_operation(uintptr_t device, CONTROL_PROTOCOL__phy_operation_t operation_type) +void ControlWrapper::phy_operation(DeviceWrapper &device, CONTROL_PROTOCOL__phy_operation_t operation_type) { - hailo_status status = HAILO_UNINITIALIZED; - - VALIDATE_NOT_NULL(reinterpret_cast(device)); - status = Control::phy_operation(*reinterpret_cast(device), operation_type); + auto status = Control::phy_operation(*device, operation_type); VALIDATE_STATUS(status); - - return; } -uint32_t ControlWrapper::latency_measurement_read(uintptr_t device) +uint32_t ControlWrapper::latency_measurement_read(DeviceWrapper &device) { - hailo_status status = HAILO_UNINITIALIZED; uint32_t inbound_to_outbound_latency_nsec = 0; - VALIDATE_NOT_NULL(reinterpret_cast(device)); - status = Control::latency_measurement_read(*reinterpret_cast(device), &inbound_to_outbound_latency_nsec); + auto status = Control::latency_measurement_read(*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, +void ControlWrapper::latency_measurement_config(DeviceWrapper &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)); - status = Control::latency_measurement_config(*reinterpret_cast(device), latency_measurement_en, inbound_start_buffer_number, + auto status = Control::latency_measurement_config(*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) +void ControlWrapper::start_firmware_update(DeviceWrapper &device) { - hailo_status status = HAILO_UNINITIALIZED; - - VALIDATE_NOT_NULL(reinterpret_cast(device)); - status = Control::start_firmware_update(*reinterpret_cast(device)); + auto status = Control::start_firmware_update(*device); VALIDATE_STATUS(status); - - return; } -void ControlWrapper::finish_firmware_update(uintptr_t device) +void ControlWrapper::finish_firmware_update(DeviceWrapper &device) { - hailo_status status = HAILO_UNINITIALIZED; - - VALIDATE_NOT_NULL(reinterpret_cast(device)); - status = Control::finish_firmware_update(*reinterpret_cast(device)); + auto status = Control::finish_firmware_update(*device); VALIDATE_STATUS(status); - - return; } -void ControlWrapper::write_firmware_update(uintptr_t device, uint32_t offset, py::bytes data, uint32_t length) +void ControlWrapper::write_firmware_update(DeviceWrapper &device, uint32_t offset, py::bytes data, uint32_t length) { - hailo_status status = HAILO_UNINITIALIZED; - - VALIDATE_NOT_NULL(reinterpret_cast(device)); - status = Control::write_firmware_update(*reinterpret_cast(device), offset, (uint8_t*)std::string(data).c_str(), length); + auto status = Control::write_firmware_update(*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) +void ControlWrapper::validate_firmware_update(DeviceWrapper &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)); - status = Control::validate_firmware_update(*reinterpret_cast(device), &expected_md5, firmware_size); + auto status = Control::validate_firmware_update(*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) +py::bytes ControlWrapper::sensor_get_config(DeviceWrapper &device, uint32_t section_index, uint32_t offset, uint32_t data_length) { - hailo_status status = HAILO_UNINITIALIZED; std::unique_ptr response = make_unique_nothrow(data_length, '\x00'); VALIDATE_NOT_NULL(response); - VALIDATE_NOT_NULL(reinterpret_cast(device)); - status = Control::sensor_get_config(*reinterpret_cast(device), section_index, offset, data_length, - (uint8_t*)(response->data())); - + auto status = Control::sensor_get_config(*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) +void ControlWrapper::idle_time_set_measurement(DeviceWrapper &device, bool measurement_enable) { - hailo_status status = HAILO_UNINITIALIZED; - - VALIDATE_NOT_NULL(reinterpret_cast(device)); - status = Control::idle_time_set_measurement(*reinterpret_cast(device), measurement_enable); + auto status = Control::idle_time_set_measurement(*device, measurement_enable); VALIDATE_STATUS(status); } -uint64_t ControlWrapper::idle_time_get_measurement(uintptr_t device) +uint64_t ControlWrapper::idle_time_get_measurement(DeviceWrapper &device) { - hailo_status status = HAILO_UNINITIALIZED; uint64_t measurement = 0; - VALIDATE_NOT_NULL(reinterpret_cast(device)); - status = Control::idle_time_get_measurement(*reinterpret_cast(device), &measurement); + auto status = Control::idle_time_get_measurement(*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) +void ControlWrapper::d2h_notification_manager_set_host_info(DeviceWrapper &device, uint16_t host_port, uint32_t host_ip_address) { - VALIDATE_NOT_NULL(reinterpret_cast(device)); - hailo_status status = Control::d2h_notification_manager_set_host_info(*reinterpret_cast(device), host_port, host_ip_address); + auto status = Control::d2h_notification_manager_set_host_info(*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) +void ControlWrapper::d2h_notification_manager_send_host_info_notification(DeviceWrapper &device, uint8_t notification_priority) { - VALIDATE_NOT_NULL(reinterpret_cast(device)); - hailo_status status = Control::d2h_notification_manager_send_host_info_notification(*reinterpret_cast(device), notification_priority); + auto status = Control::d2h_notification_manager_send_host_info_notification(*device, notification_priority); VALIDATE_STATUS(status); } /* Context switch */ -void ControlWrapper::set_context_switch_breakpoint(uintptr_t device, +void ControlWrapper::set_context_switch_breakpoint(DeviceWrapper &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 = { @@ -202,72 +149,59 @@ void ControlWrapper::set_context_switch_breakpoint(uintptr_t device, break_at_any_action_index, action_index}; - VALIDATE_NOT_NULL(reinterpret_cast(device)); - status = Control::config_context_switch_breakpoint(*reinterpret_cast(device), breakpoint_id, - breakpoint_control, &breakpoint_data); + auto status = Control::config_context_switch_breakpoint(*device, breakpoint_id, breakpoint_control, &breakpoint_data); VALIDATE_STATUS(status); } -void ControlWrapper::continue_context_switch_breakpoint(uintptr_t device, uint8_t breakpoint_id) +void ControlWrapper::continue_context_switch_breakpoint(DeviceWrapper &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)); - status = Control::config_context_switch_breakpoint(*reinterpret_cast(device), breakpoint_id, + auto status = Control::config_context_switch_breakpoint(*device, breakpoint_id, breakpoint_control, &breakpoint_data); VALIDATE_STATUS(status); } -void ControlWrapper::clear_context_switch_breakpoint(uintptr_t device, uint8_t breakpoint_id) +void ControlWrapper::clear_context_switch_breakpoint(DeviceWrapper &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)); - status = Control::config_context_switch_breakpoint(*reinterpret_cast(device), breakpoint_id, + auto status = Control::config_context_switch_breakpoint(*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) +uint8_t ControlWrapper::get_context_switch_breakpoint_status(DeviceWrapper &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)); - status = Control::get_context_switch_breakpoint_status(*reinterpret_cast(device), breakpoint_id, + auto status = Control::get_context_switch_breakpoint_status(*device, breakpoint_id, &breakpoint_status); VALIDATE_STATUS(status); return static_cast(breakpoint_status); } -void ControlWrapper::config_context_switch_timestamp(uintptr_t device, uint16_t batch_index) +void ControlWrapper::config_context_switch_timestamp(DeviceWrapper &device, uint16_t batch_index) { - VALIDATE_NOT_NULL(reinterpret_cast(device)); - hailo_status status = Control::config_context_switch_timestamp(*reinterpret_cast(device), batch_index, true); + auto status = Control::config_context_switch_timestamp(*device, batch_index, true); VALIDATE_STATUS(status); } -void ControlWrapper::remove_context_switch_timestamp_configuration(uintptr_t device) +void ControlWrapper::remove_context_switch_timestamp_configuration(DeviceWrapper &device) { - VALIDATE_NOT_NULL(reinterpret_cast(device)); - hailo_status status = Control::config_context_switch_timestamp(*reinterpret_cast(device), 0, false); + auto status = Control::config_context_switch_timestamp(*device, 0, false); VALIDATE_STATUS(status); } -void ControlWrapper::enable_debugging(uintptr_t device, bool is_rma) +void ControlWrapper::enable_debugging(DeviceWrapper &device, bool is_rma) { - hailo_status status = HAILO_UNINITIALIZED; - - VALIDATE_NOT_NULL(reinterpret_cast(device)); - status = Control::enable_debugging(*reinterpret_cast(device), is_rma); + auto status = Control::enable_debugging(*device, is_rma); VALIDATE_STATUS(status); } diff --git a/hailort/libhailort/bindings/python/src/internal/control_api.hpp b/hailort/libhailort/bindings/python/src/internal/control_api.hpp index 47dce83..8106f34 100644 --- a/hailort/libhailort/bindings/python/src/internal/control_api.hpp +++ b/hailort/libhailort/bindings/python/src/internal/control_api.hpp @@ -14,6 +14,8 @@ #include "control.hpp" #include "utils.hpp" +#include "device_api.hpp" + #include #include #include @@ -29,36 +31,36 @@ 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, + static void set_clock_freq(DeviceWrapper &device, uint32_t clock_freq); + static void close_all_streams(DeviceWrapper &device); + static void config_ahb_to_axi(DeviceWrapper &device, bool use_64bit_data_only); + static void phy_operation(DeviceWrapper &device, CONTROL_PROTOCOL__phy_operation_t operation_type); + static uint32_t latency_measurement_read(DeviceWrapper &device); + static void latency_measurement_config(DeviceWrapper &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); + static void start_firmware_update(DeviceWrapper &device); + static void finish_firmware_update(DeviceWrapper &device); + static void write_firmware_update(DeviceWrapper &device, uint32_t offset, py::bytes data, uint32_t length); + static void validate_firmware_update(DeviceWrapper &device, py::bytes md5_raw_data, uint32_t firmware_size); + static py::bytes sensor_get_config(DeviceWrapper &device, uint32_t section_index, uint32_t offset, uint32_t data_length); + static void idle_time_set_measurement(DeviceWrapper &device, bool measurement_enable); + static uint64_t idle_time_get_measurement(DeviceWrapper &device); + static void d2h_notification_manager_set_host_info(DeviceWrapper &device, uint16_t host_port, uint32_t host_ip_address); + static void d2h_notification_manager_send_host_info_notification(DeviceWrapper &device, uint8_t notification_priority); + static void enable_debugging(DeviceWrapper &device, bool is_rma); /* Context switch */ - static void set_context_switch_breakpoint(uintptr_t device, uint8_t breakpoint_id, + static void set_context_switch_breakpoint(DeviceWrapper &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); + static void continue_context_switch_breakpoint(DeviceWrapper &device, uint8_t breakpoint_id); + static void clear_context_switch_breakpoint(DeviceWrapper &device, uint8_t breakpoint_id); + static uint8_t get_context_switch_breakpoint_status(DeviceWrapper &device, uint8_t breakpoint_id); + static void config_context_switch_timestamp(DeviceWrapper &device, uint16_t batch_index); + static void remove_context_switch_timestamp_configuration(DeviceWrapper &device); }; } /* namespace hailort */ diff --git a/hailort/libhailort/bindings/python/src/internal/pyhailort_internal.cpp b/hailort/libhailort/bindings/python/src/internal/pyhailort_internal.cpp index 0f4c360..620a89c 100644 --- a/hailort/libhailort/bindings/python/src/internal/pyhailort_internal.cpp +++ b/hailort/libhailort/bindings/python/src/internal/pyhailort_internal.cpp @@ -209,7 +209,7 @@ PYBIND11_MODULE(_pyhailort_internal, m) { .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) + .def_readonly("network_name", &LayerInfo::network_name) ; } diff --git a/hailort/libhailort/bindings/python/src/pyhailort.cpp b/hailort/libhailort/bindings/python/src/pyhailort.cpp index 20d15c1..b679830 100644 --- a/hailort/libhailort/bindings/python/src/pyhailort.cpp +++ b/hailort/libhailort/bindings/python/src/pyhailort.cpp @@ -5,22 +5,21 @@ #include #include #include +#include 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 "hailo/hailort.hpp" #include "hef_api.hpp" #include "vstream_api.hpp" #include "vdevice_api.hpp" +#include "device_api.hpp" + #include "utils.hpp" #include "utils.h" -#include "common/socket.hpp" +#include "bindings_common.hpp" + #include "sensor_config_exports.h" #include "hailort_defaults.hpp" #if defined(__GNUC__) @@ -32,62 +31,6 @@ 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(); - data.average_time_value_milliseconds = t[1].cast(); - data.min_value = t[2].cast(); - data.max_value = t[3].cast(); - data.total_number_of_samples = t[4].cast(); - 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) && @@ -101,799 +44,13 @@ bool hailo_format_equals(hailo_format_t &first, hailo_format_t &second){ } class UdpScan { public: - UdpScan(); + UdpScan() = default; std::list 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 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)); - - auto board_info = reinterpret_cast(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)); - - auto core_info = reinterpret_cast(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)); - - auto status = reinterpret_cast(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)); - - auto status = reinterpret_cast(device)->set_throttling_state(should_activate); - VALIDATE_STATUS(status); -} - -bool get_throttling_state(uintptr_t device) -{ - VALIDATE_NOT_NULL(reinterpret_cast(device)); - - auto is_active_expected = reinterpret_cast(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)); - - auto status = reinterpret_cast(device)->set_overcurrent_state(should_activate); - VALIDATE_STATUS(status); -} - -bool get_overcurrent_state(uintptr_t device) -{ - VALIDATE_NOT_NULL(reinterpret_cast(device)); - - auto is_required_expected = reinterpret_cast(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 response = make_unique_nothrow(length, '\x00'); - VALIDATE_NOT_NULL(response); - VALIDATE_NOT_NULL(reinterpret_cast(device)); - - MemoryView data_view(const_cast(reinterpret_cast(response->data())), length); - auto status = (reinterpret_cast(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)); - - auto status = (reinterpret_cast(device))->write_memory(address, MemoryView(const_cast(reinterpret_cast(std::string(data).c_str())), length)); - VALIDATE_STATUS(status); - - return; -} - -void test_chip_memories(uintptr_t device) -{ - VALIDATE_NOT_NULL(reinterpret_cast(device)); - - hailo_status status = reinterpret_cast(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)); - 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)->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)); - VALIDATE_NOT_NULL(slave_config); - - std::unique_ptr response = make_unique_nothrow(length, '\x00'); - VALIDATE_NOT_NULL(response); - - MemoryView data_view(const_cast(reinterpret_cast(response->data())), length); - auto status = reinterpret_cast(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)); - - auto measurement = reinterpret_cast(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)); - - auto status = reinterpret_cast(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)); - - auto status = reinterpret_cast(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)); - - auto measurement_data = reinterpret_cast(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)); - - auto user_config_info = reinterpret_cast(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)); - - auto config_buffer = reinterpret_cast(device)->read_user_config(); - VALIDATE_EXPECTED(config_buffer); - - std::unique_ptr response = make_unique_nothrow( - const_cast(reinterpret_cast(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)); - - std::string data_str(data); - MemoryView data_view = MemoryView::create_const(data_str.c_str(), data_str.size()); - auto status = reinterpret_cast(device)->write_user_config(data_view); - VALIDATE_STATUS(status); - - return; -} - -void erase_user_config(uintptr_t device) -{ - VALIDATE_NOT_NULL(reinterpret_cast(device)); - - auto status = reinterpret_cast(device)->erase_user_config(); - VALIDATE_STATUS(status); - - return; -} - -py::bytes read_board_config(uintptr_t device) -{ - auto config_buffer = reinterpret_cast(device)->read_board_config(); - VALIDATE_EXPECTED(config_buffer); - - std::unique_ptr response = make_unique_nothrow( - const_cast(reinterpret_cast(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)); - - std::string data_str(data); - MemoryView data_view = MemoryView::create_const(data_str.c_str(), data_str.size()); - auto status = reinterpret_cast(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)); - - auto extended_device_info = reinterpret_cast(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)); - auto health_info = reinterpret_cast(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)); - status = (reinterpret_cast(device))->store_sensor_config(section_index, static_cast(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)); - status = (reinterpret_cast(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)); - - auto buffer = (reinterpret_cast(device))->sensor_get_sections_info(); - VALIDATE_EXPECTED(buffer); - - std::unique_ptr response = make_unique_nothrow( - const_cast(reinterpret_cast(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)); - - hailo_status status = (reinterpret_cast(device))->sensor_set_i2c_bus_index( - static_cast(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)); - - auto status = (reinterpret_cast(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)); - - auto status = (reinterpret_cast(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)); - - auto status = (reinterpret_cast(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)); - hailo_status status = reinterpret_cast(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)); - hailo_status status = reinterpret_cast(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))->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(static_cast(src_buffer.mutable_data()), - static_cast(dst_buffer.mutable_data()), shape_size, quant_info); - break; - case HAILO_FORMAT_TYPE_UINT16: - Quantization::dequantize_output_buffer(static_cast(src_buffer.mutable_data()), - static_cast(dst_buffer.mutable_data()), shape_size, quant_info); - break; - case HAILO_FORMAT_TYPE_FLOAT32: - Quantization::dequantize_output_buffer(static_cast(src_buffer.mutable_data()), - static_cast(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(static_cast(src_buffer.mutable_data()), - static_cast(dst_buffer.mutable_data()), shape_size, quant_info); - break; - case HAILO_FORMAT_TYPE_FLOAT32: - Quantization::dequantize_output_buffer(static_cast(src_buffer.mutable_data()), - static_cast(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(static_cast(src_buffer.mutable_data()), - static_cast(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( - static_cast(dst_buffer.mutable_data()), shape_size, quant_info); - break; - case HAILO_FORMAT_TYPE_UINT16: - Quantization::dequantize_output_buffer_in_place( - static_cast(dst_buffer.mutable_data()), shape_size, quant_info); - break; - case HAILO_FORMAT_TYPE_FLOAT32: - Quantization::dequantize_output_buffer_in_place( - static_cast(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( - static_cast(dst_buffer.mutable_data()), shape_size, quant_info); - break; - case HAILO_FORMAT_TYPE_FLOAT32: - Quantization::dequantize_output_buffer_in_place( - static_cast(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( - static_cast(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(static_cast(src_buffer.mutable_data()), - static_cast(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(static_cast(src_buffer.mutable_data()), - static_cast(dst_buffer.mutable_data()), shape_size, quant_info); - break; - case HAILO_FORMAT_TYPE_UINT16: - Quantization::quantize_input_buffer(static_cast(src_buffer.mutable_data()), - static_cast(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(static_cast(src_buffer.mutable_data()), - static_cast(dst_buffer.mutable_data()), shape_size, quant_info); - break; - case HAILO_FORMAT_TYPE_UINT16: - Quantization::quantize_input_buffer(static_cast(src_buffer.mutable_data()), - static_cast(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)); - - auto status = reinterpret_cast(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)); - - hailo_status status = reinterpret_cast(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)); - - hailo_status status = reinterpret_cast(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)); - auto status = reinterpret_cast(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)); - - auto system_state = reinterpret_cast(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)); - - auto temp_info = reinterpret_cast(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 &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 ¬ification, 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(device), notification_id); - VALIDATE_STATUS(status); -} - -UdpScan::UdpScan() -{ -} - std::list UdpScan::scan_devices(char* interface_name, uint32_t timeout_milliseconds) { hailo_status status = HAILO_UNINITIALIZED; @@ -916,41 +73,248 @@ std::list UdpScan::scan_devices(char* interface_name, uint32_t time return device_addresses; } -py::bytes read_log(uintptr_t device, size_t byte_count, hailo_cpu_id_t cpu_id) +std::vector scan_pcie_devices(void) { - std::string response; + auto scan_result = Device::scan_pcie(); + VALIDATE_EXPECTED(scan_result); - 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); + return scan_result.release(); } -void direct_write_memory(uintptr_t device, uint32_t address, py::bytes buffer) +// 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) { - const auto buffer_str = static_cast(buffer); - hailo_status status = ((Device*)device)->direct_write_memory(address, buffer_str.c_str(), - (uint32_t) (buffer_str.length())); - VALIDATE_STATUS(status); + switch (dst_dtype) { + case HAILO_FORMAT_TYPE_UINT8: + Quantization::dequantize_output_buffer(static_cast(src_buffer.mutable_data()), + static_cast(dst_buffer.mutable_data()), shape_size, quant_info); + break; + case HAILO_FORMAT_TYPE_UINT16: + Quantization::dequantize_output_buffer(static_cast(src_buffer.mutable_data()), + static_cast(dst_buffer.mutable_data()), shape_size, quant_info); + break; + case HAILO_FORMAT_TYPE_FLOAT32: + Quantization::dequantize_output_buffer(static_cast(src_buffer.mutable_data()), + static_cast(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 = {}", + HailoRTBindingsCommon::convert_format_type_to_string(dst_dtype)); + THROW_STATUS_ERROR(HAILO_INVALID_ARGUMENT); + break; + } } -py::bytes direct_read_memory(uintptr_t device, uint32_t address, uint32_t size) +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) { - std::string buffer_str; + switch (dst_dtype) { + case HAILO_FORMAT_TYPE_UINT16: + Quantization::dequantize_output_buffer(static_cast(src_buffer.mutable_data()), + static_cast(dst_buffer.mutable_data()), shape_size, quant_info); + break; + case HAILO_FORMAT_TYPE_FLOAT32: + Quantization::dequantize_output_buffer(static_cast(src_buffer.mutable_data()), + static_cast(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 = {}", + HailoRTBindingsCommon::convert_format_type_to_string(dst_dtype)); + THROW_STATUS_ERROR(HAILO_INVALID_ARGUMENT); + break; + } +} - buffer_str.reserve(size); - buffer_str.resize(size); +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(static_cast(src_buffer.mutable_data()), + static_cast(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 = {}", + HailoRTBindingsCommon::convert_format_type_to_string(dst_dtype)); + THROW_STATUS_ERROR(HAILO_INVALID_ARGUMENT); + break; + } +} - hailo_status status = ((Device*)device)->direct_read_memory(address, (char*)buffer_str.c_str(), size); - VALIDATE_STATUS(status); +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( + static_cast(dst_buffer.mutable_data()), shape_size, quant_info); + break; + case HAILO_FORMAT_TYPE_UINT16: + Quantization::dequantize_output_buffer_in_place( + static_cast(dst_buffer.mutable_data()), shape_size, quant_info); + break; + case HAILO_FORMAT_TYPE_FLOAT32: + Quantization::dequantize_output_buffer_in_place( + static_cast(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 = {}", + HailoRTBindingsCommon::convert_format_type_to_string(dst_dtype)); + THROW_STATUS_ERROR(HAILO_INVALID_ARGUMENT); + break; + } +} - buffer_str.resize(size); - return py::bytes(buffer_str); +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( + static_cast(dst_buffer.mutable_data()), shape_size, quant_info); + break; + case HAILO_FORMAT_TYPE_FLOAT32: + Quantization::dequantize_output_buffer_in_place( + static_cast(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 = {}", + HailoRTBindingsCommon::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( + static_cast(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 = {}", + HailoRTBindingsCommon::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 = {}", HailoRTBindingsCommon::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 = {}", HailoRTBindingsCommon::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(static_cast(src_buffer.mutable_data()), + static_cast(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 = {}", HailoRTBindingsCommon::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(static_cast(src_buffer.mutable_data()), + static_cast(dst_buffer.mutable_data()), shape_size, quant_info); + break; + case HAILO_FORMAT_TYPE_UINT16: + Quantization::quantize_input_buffer(static_cast(src_buffer.mutable_data()), + static_cast(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 = {}", + HailoRTBindingsCommon::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(static_cast(src_buffer.mutable_data()), + static_cast(dst_buffer.mutable_data()), shape_size, quant_info); + break; + case HAILO_FORMAT_TYPE_UINT16: + Quantization::quantize_input_buffer(static_cast(src_buffer.mutable_data()), + static_cast(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 = {}", + HailoRTBindingsCommon::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 = {}", HailoRTBindingsCommon::convert_format_type_to_string(dst_dtype)); + THROW_STATUS_ERROR(HAILO_INVALID_ARGUMENT); + break; + } } std::string get_status_message(uint32_t status_in) @@ -1008,8 +372,7 @@ public: .def("reset_rate_limit", &TrafficControlUtilWrapper::reset_rate_limit) .def_static("get_interface_name", [](const std::string &ip) { return TrafficControlUtilWrapper::get_interface_name(ip); - }); - ; + }) ; } @@ -1025,67 +388,39 @@ private: // End of temp hack for hlpcie +static void validate_versions_match() +{ + hailo_version_t libhailort_version = {}; + auto status = hailo_get_library_version(&libhailort_version); + if (HAILO_SUCCESS != status) { + throw std::logic_error("Failed to get libhailort version"); + } + + bool versions_match = ((HAILORT_MAJOR_VERSION == libhailort_version.major) && + (HAILORT_MINOR_VERSION == libhailort_version.minor) && + (HAILORT_REVISION_VERSION == libhailort_version.revision)); + if (!versions_match) { + std::stringstream message; + message << "libhailort version (" << + libhailort_version.major << "." << libhailort_version.minor << "." << libhailort_version.revision << + ") does not match pyhailort version (" << + HAILORT_MAJOR_VERSION << "." << HAILORT_MINOR_VERSION << "." << HAILORT_REVISION_VERSION << ")"; + throw std::logic_error(message.str()); + } +} + PYBIND11_MODULE(_pyhailort, m) { + validate_versions_match(); + 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); + m.def("get_format_data_bytes", &HailoRTCommon::get_format_data_bytes); + m.def("get_dtype", &HailoRTBindingsCommon::get_dtype); + py::class_(m, "PcieDeviceInfo") .def(py::init<>()) .def_static("_parse", [](const std::string &device_info_str) { @@ -1170,6 +505,13 @@ PYBIND11_MODULE(_pyhailort, m) { .value("AVERAGE_1024", HAILO_AVERAGE_FACTOR_1024, "Each sample reflects a value of 1024 sub-samples.") ; + py::enum_(m, "MeasurementBufferIndex", "Enum-like class representing all FW buffers for power measurements storing.") + .value("MEASUREMENT_BUFFER_INDEX_0", HAILO_MEASUREMENT_BUFFER_INDEX_0) + .value("MEASUREMENT_BUFFER_INDEX_1", HAILO_MEASUREMENT_BUFFER_INDEX_1) + .value("MEASUREMENT_BUFFER_INDEX_2", HAILO_MEASUREMENT_BUFFER_INDEX_2) + .value("MEASUREMENT_BUFFER_INDEX_3", HAILO_MEASUREMENT_BUFFER_INDEX_3) + ; + py::class_(m, "Notification") .def_readonly("notification_id", &hailo_notification_t::id) .def_readonly("sequence", &hailo_notification_t::sequence) @@ -1353,12 +695,40 @@ PYBIND11_MODULE(_pyhailort, m) { .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) + .def(py::pickle( + [](const hailo_3d_image_shape_t &shape) { // __getstate__ + return py::make_tuple( + shape.height, + shape.width, + shape.features); + }, + [](py::tuple t) { // __setstate__ + hailo_3d_image_shape_t shape; + shape.height = t[0].cast(); + shape.width = t[1].cast(); + shape.features = t[2].cast(); + return shape; + } + )) ; py::class_(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) + .def(py::pickle( + [](const hailo_nms_shape_t &nms_shape) { // __getstate__ + return py::make_tuple( + nms_shape.number_of_classes, + nms_shape.max_bboxes_per_class); + }, + [](py::tuple t) { // __setstate__ + hailo_nms_shape_t nms_shape; + nms_shape.number_of_classes = t[0].cast(); + nms_shape.max_bboxes_per_class = t[1].cast(); + return nms_shape; + } + )) ; py::class_(m, "NmsInfo") @@ -1434,6 +804,23 @@ PYBIND11_MODULE(_pyhailort, m) { .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) + .def(py::pickle( + [](const hailo_quant_info_t &quant_info) { // __getstate__ + return py::make_tuple( + quant_info.qp_zp, + quant_info.qp_scale, + quant_info.limvals_min, + quant_info.limvals_max); + }, + [](py::tuple t) { // __setstate__ + hailo_quant_info_t quant_info; + quant_info.qp_zp = t[0].cast(); + quant_info.qp_scale = t[1].cast(); + quant_info.limvals_min = t[2].cast(); + quant_info.limvals_max = t[3].cast(); + return quant_info; + } + )) ; py::enum_(m, "MipiPixelsPerClock") @@ -1807,6 +1194,43 @@ PYBIND11_MODULE(_pyhailort, m) { .def("__repr__", [](const hailo_vstream_info_t &self) { return std::string("VStreamInfo(\"") + std::string(self.name) + std::string("\")"); }) + .def(py::pickle( + [](const hailo_vstream_info_t &vstream_info) { // __getstate__ + if (HAILO_FORMAT_ORDER_HAILO_NMS == vstream_info.format.order) { + return py::make_tuple( + vstream_info.name, + vstream_info.network_name, + vstream_info.direction, + vstream_info.format, + vstream_info.nms_shape, + vstream_info.quant_info); + } + else { + return py::make_tuple( + vstream_info.name, + vstream_info.network_name, + vstream_info.direction, + vstream_info.format, + vstream_info.shape, + vstream_info.quant_info); + } + }, + [](py::tuple t) { // __setstate__ + hailo_vstream_info_t vstream_info; + strcpy(vstream_info.name, t[0].cast().c_str()); + strcpy(vstream_info.network_name, t[1].cast().c_str()); + vstream_info.direction = t[2].cast(); + vstream_info.format = t[3].cast(); + if (HAILO_FORMAT_ORDER_HAILO_NMS == vstream_info.format.order) { + vstream_info.nms_shape = t[4].cast(); + } + else { + vstream_info.shape = t[4].cast(); + } + vstream_info.quant_info = t[5].cast(); + return vstream_info; + } + )) ; py::class_(m, "StreamInfo", py::module_local()) @@ -1847,23 +1271,18 @@ PYBIND11_MODULE(_pyhailort, m) { .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); + HefWrapper::initialize_python_module(m); VStream_api_initialize_python_module(m); VDevice_api_initialize_python_module(m); + DeviceWrapper::add_to_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 + std::stringstream version; + version << HAILORT_MAJOR_VERSION << "." << HAILORT_MINOR_VERSION << "." << HAILORT_REVISION_VERSION; + m.attr("__version__") = version.str(); } } /* namespace hailort */ diff --git a/hailort/libhailort/bindings/python/src/vdevice_api.hpp b/hailort/libhailort/bindings/python/src/vdevice_api.hpp index 857cfad..48e7d7a 100644 --- a/hailort/libhailort/bindings/python/src/vdevice_api.hpp +++ b/hailort/libhailort/bindings/python/src/vdevice_api.hpp @@ -15,7 +15,6 @@ #include "hailo/hef.hpp" #include "hailo/vdevice.hpp" -#include "bindings_common.hpp" #include "utils.hpp" #include "common/logger_macros.hpp" diff --git a/hailort/libhailort/bindings/python/src/vstream_api.cpp b/hailort/libhailort/bindings/python/src/vstream_api.cpp new file mode 100644 index 0000000..2ae7a98 --- /dev/null +++ b/hailort/libhailort/bindings/python/src/vstream_api.cpp @@ -0,0 +1,335 @@ +/** + * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved. + * Distributed under the MIT license (https://opensource.org/licenses/MIT) + **/ +/** + * @file vstream_api.cpp + * @brief Implementation of binding to virtual stream usage over Python. + **/ + +#include "common/logger_macros.hpp" +#include "common/utils.hpp" + +#include "vstream_api.hpp" +#include "bindings_common.hpp" +#include "utils.hpp" + + +namespace hailort +{ + +void InputVStreamWrapper::add_to_python_module(py::module &m) +{ + py::class_>(m, "InputVStream") + .def("send", [](InputVStream &self, py::array data) + { + hailo_status status = self.write( + MemoryView(const_cast(reinterpret_cast(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 HailoRTBindingsCommon::get_dtype(format_type); + }) + .def_property_readonly("shape", [](InputVStream &self) + { + return *py::array::ShapeContainer(HailoRTBindingsCommon::get_pybind_shape(self.get_info(), self.get_user_buffer_format())); + }) + ; +} + +InputVStreamsWrapper InputVStreamsWrapper::create(ConfiguredNetworkGroup &net_group, + const std::map &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> input_vstreams; + for (auto &input : input_vstreams_expected.value()) { + input_vstreams.emplace(input.name(), make_shared_nothrow(std::move(input))); + } + return InputVStreamsWrapper(input_vstreams); +} + +const InputVStreamsWrapper &InputVStreamsWrapper::enter() +{ + return std::ref(*this); +} + +void InputVStreamsWrapper::exit() +{ + m_input_vstreams.clear(); +} + +std::shared_ptr InputVStreamsWrapper::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 InputVStreamsWrapper::get_all_inputs() +{ + return py::cast(m_input_vstreams); +} + +void InputVStreamsWrapper::clear() +{ + std::vector> 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); +} + +void InputVStreamsWrapper::add_to_python_module(py::module &m) +{ + py::class_(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(); }) + ; +} + +InputVStreamsWrapper::InputVStreamsWrapper(std::unordered_map> &input_vstreams) + : m_input_vstreams(std::move(input_vstreams)) +{} + +py::dtype OutputVStreamWrapper::get_dtype(OutputVStream &self) +{ + const auto format_type = self.get_user_buffer_format().type; + return HailoRTBindingsCommon::get_dtype(format_type); +} + +hailo_format_t OutputVStreamWrapper::get_user_buffer_format(OutputVStream &self) +{ + const auto format = self.get_user_buffer_format(); + return format; +} + +auto OutputVStreamWrapper::get_shape(OutputVStream &self) +{ + return *py::array::ShapeContainer(HailoRTBindingsCommon::get_pybind_shape(self.get_info(), self.get_user_buffer_format())); +} + +void OutputVStreamWrapper::add_to_python_module(py::module &m) +{ + py::class_>(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(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) + ; +} + +OutputVStreamsWrapper OutputVStreamsWrapper::create(ConfiguredNetworkGroup &net_group, + const std::map &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> output_vstreams; + for (auto &output : output_vstreams_expected.value()) { + output_vstreams.emplace(output.name(), make_shared_nothrow(std::move(output))); + } + return OutputVStreamsWrapper(output_vstreams); +} + +std::shared_ptr OutputVStreamsWrapper::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 &OutputVStreamsWrapper::enter() +{ + return std::ref(*this); +} + +void OutputVStreamsWrapper::exit() +{ + m_output_vstreams.clear(); +} + +py::dict OutputVStreamsWrapper::get_all_outputs() +{ + return py::cast(m_output_vstreams); +} + +void OutputVStreamsWrapper::clear() +{ + std::vector> 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); +} + +void OutputVStreamsWrapper::add_to_python_module(py::module &m) +{ + py::class_(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(); }) + ; +} + +OutputVStreamsWrapper::OutputVStreamsWrapper(std::unordered_map> &output_vstreams) + : m_output_vstreams(std::move(output_vstreams)) +{} + +InferVStreamsWrapper InferVStreamsWrapper::create(ConfiguredNetworkGroup &network_group, + const std::map &input_vstreams_params, + const std::map &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(std::move(infer_pipeline.value())); + + return InferVStreamsWrapper(infer_vstream_ptr); +} + +void InferVStreamsWrapper::infer(std::map input_data, std::map output_data, + size_t batch_size) +{ + std::map input_data_c; + std::map output_data_c; + + for (auto& name_pair : input_data) { + input_data_c.emplace(name_pair.first, MemoryView(name_pair.second.mutable_data(), + static_cast(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(name_pair.second.nbytes()))); + } + + hailo_status status = m_infer_pipeline->infer(input_data_c, output_data_c, batch_size); + VALIDATE_STATUS(status); +} + +py::dtype InferVStreamsWrapper::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 HailoRTBindingsCommon::get_dtype(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 HailoRTBindingsCommon::get_dtype(output->get().get_user_buffer_format().type); +} + +hailo_format_t InferVStreamsWrapper::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 InferVStreamsWrapper::get_shape(const std::string &stream_name) +{ + auto input = m_infer_pipeline->get_input_by_name(stream_name); + if (HAILO_SUCCESS == input.status()) { + return HailoRTBindingsCommon::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 HailoRTBindingsCommon::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 InferVStreamsWrapper::release() +{ + m_infer_pipeline.reset(); +} + +void InferVStreamsWrapper::add_to_python_module(py::module &m) +{ + py::class_(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(); }) + ; +} + +InferVStreamsWrapper::InferVStreamsWrapper(std::shared_ptr &infer_pipeline) + : m_infer_pipeline(std::move(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 */ diff --git a/hailort/libhailort/bindings/python/src/vstream_api.hpp b/hailort/libhailort/bindings/python/src/vstream_api.hpp index 1c76256..2f38405 100644 --- a/hailort/libhailort/bindings/python/src/vstream_api.hpp +++ b/hailort/libhailort/bindings/python/src/vstream_api.hpp @@ -14,7 +14,6 @@ #include "common/utils.hpp" #include "hailo/vstream.hpp" #include "hailo/inference_pipeline.hpp" -#include "bindings_common.hpp" #include "utils.hpp" #include @@ -29,234 +28,49 @@ namespace hailort class InputVStreamWrapper final { public: - static void add_to_python_module(py::module &m) - { - py::class_>(m, "InputVStream") - .def("send", [](InputVStream &self, py::array data) - { - hailo_status status = self.write( - MemoryView(const_cast(reinterpret_cast(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())); - }) - ; - } + static void add_to_python_module(py::module &m); }; class InputVStreamsWrapper final { public: - static InputVStreamsWrapper create(ConfiguredNetworkGroup &net_group, - const std::map &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> input_vstreams; - for (auto &input : input_vstreams_expected.value()) { - input_vstreams.emplace(input.name(), make_shared_nothrow(std::move(input))); - } - return InputVStreamsWrapper(input_vstreams); - } - - const InputVStreamsWrapper &enter() - { - return std::ref(*this); - } - - void exit() - { - m_input_vstreams.clear(); - } - - std::shared_ptr 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> 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_(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(); }) - ; - } + const std::map &input_vstreams_params); + const InputVStreamsWrapper &enter(); + void exit(); + std::shared_ptr get_input_by_name(const std::string &name); + py::dict get_all_inputs(); + void clear(); + static void add_to_python_module(py::module &m); private: - InputVStreamsWrapper(std::unordered_map> &input_vstreams) : - m_input_vstreams(std::move(input_vstreams)) - {} - + InputVStreamsWrapper(std::unordered_map> &input_vstreams); std::unordered_map> 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_>(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(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) - ; - } + static py::dtype get_dtype(OutputVStream &self); + static hailo_format_t get_user_buffer_format(OutputVStream &self); + static auto get_shape(OutputVStream &self); + static void add_to_python_module(py::module &m); }; class OutputVStreamsWrapper final { public: - static OutputVStreamsWrapper create(ConfiguredNetworkGroup &net_group, - const std::map &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> output_vstreams; - for (auto &output : output_vstreams_expected.value()) { - output_vstreams.emplace(output.name(), make_shared_nothrow(std::move(output))); - } - return OutputVStreamsWrapper(output_vstreams); - } - - std::shared_ptr 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> 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_(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(); }) - ; - } + const std::map &output_vstreams_params); + std::shared_ptr get_output_by_name(const std::string &name); + const OutputVStreamsWrapper &enter(); + void exit(); + py::dict get_all_outputs(); + void clear(); + static void add_to_python_module(py::module &m); private: - OutputVStreamsWrapper(std::unordered_map> &output_vstreams) : - m_output_vstreams(std::move(output_vstreams)) - {} - + OutputVStreamsWrapper(std::unordered_map> &output_vstreams); std::unordered_map> m_output_vstreams; }; @@ -265,113 +79,22 @@ class InferVStreamsWrapper final public: static InferVStreamsWrapper create(ConfiguredNetworkGroup &network_group, const std::map &input_vstreams_params, - const std::map &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(std::move(infer_pipeline.value())); - - return InferVStreamsWrapper(infer_vstream_ptr); - } - + const std::map &output_vstreams_params); void infer(std::map input_data, std::map output_data, - size_t batch_size) - { - std::map input_data_c; - std::map output_data_c; - - for (auto& name_pair : input_data) { - input_data_c.emplace(name_pair.first, MemoryView(name_pair.second.mutable_data(), - static_cast(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(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 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_(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(); }) - ; - } + size_t batch_size); + py::dtype get_host_dtype(const std::string &stream_name); + hailo_format_t get_user_buffer_format(const std::string &stream_name); + std::vector get_shape(const std::string &stream_name); + void release(); + static void add_to_python_module(py::module &m); private: - InferVStreamsWrapper(std::shared_ptr &infer_pipeline) : - m_infer_pipeline(std::move(infer_pipeline)) - {} - - std::shared_ptr m_infer_pipeline; + InferVStreamsWrapper(std::shared_ptr &infer_pipeline); + + std::shared_ptr 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); -} - +void VStream_api_initialize_python_module(py::module &m); } /* namespace hailort */ #endif // _VSTREAM_API_HPP_ diff --git a/hailort/libhailort/cmake/toolchains/qnx.aarch64.cmake b/hailort/libhailort/cmake/toolchains/qnx.aarch64.cmake index c476c20..721da10 100644 --- a/hailort/libhailort/cmake/toolchains/qnx.aarch64.cmake +++ b/hailort/libhailort/cmake/toolchains/qnx.aarch64.cmake @@ -1,22 +1,50 @@ +# CMake added fix for QCC compiler in this version - will not compile in older versions +cmake_minimum_required(VERSION 3.14.0) + set(CMAKE_SYSTEM_NAME QNX) -set(arch ntoaarch64) -set(QNX_PROCESSOR aarch64) +set(QNX_PROCESSOR aarch64le) +SET(CMAKE_SYSTEM_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}) +SET(CMAKE_MAKE_PROGRAM "$ENV{QNX_HOST}/usr/bin/make" CACHE PATH "QNX Make Program") +SET(CMAKE_SH "$ENV{QNX_HOST}/usr/bin/sh " CACHE PATH "QNX shell Program") +SET(CMAKE_AR "$ENV{QNX_HOST}/usr/bin/nto${CMAKE_SYSTEM_PROCESSOR}-ar" CACHE PATH "QNX ar Program") +SET(CMAKE_RANLIB "$ENV{QNX_HOST}/usr/bin/nto${CMAKE_SYSTEM_PROCESSOR}-ranlib" CACHE PATH "QNX ranlib Program") +SET(CMAKE_NM "$ENV{QNX_HOST}/usr/bin/nto${CMAKE_SYSTEM_PROCESSOR}-nm" CACHE PATH "QNX nm Program") +SET(CMAKE_OBJCOPY "$ENV{QNX_HOST}/usr/bin/nto${CMAKE_SYSTEM_PROCESSOR}-objcopy" CACHE PATH "QNX objcopy Program") +SET(CMAKE_OBJDUMP "$ENV{QNX_HOST}/usr/bin/nto${CMAKE_SYSTEM_PROCESSOR}-objdump" CACHE PATH "QNX objdump Program") +SET(CMAKE_LINKER "$ENV{QNX_HOST}/usr/bin/nto${CMAKE_SYSTEM_PROCESSOR}-ld" CACHE PATH "QNX Linker Program") +SET(CMAKE_STRIP "$ENV{QNX_HOST}/usr/bin/nto${CMAKE_SYSTEM_PROCESSOR}-strip" CACHE PATH "QNX Strip Program") add_definitions("-D_QNX_SOURCE") -file(GLOB_RECURSE libgcc_a - "$ENV{QNX_HOST}/usr/lib/gcc/${QNX_PROCESSOR}*/*/pic/libgcc.a") +SET(CMAKE_SHARED_LIBRARY_PREFIX "lib") +SET(CMAKE_SHARED_LIBRARY_SUFFIX ".so") +SET(CMAKE_STATIC_LIBRARY_PREFIX "lib") +SET(CMAKE_STATIC_LIBRARY_SUFFIX ".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}") +SET(CMAKE_C_COMPILER $ENV{QNX_HOST}/usr/bin/qcc) +SET(CMAKE_C_FLAGS_DEBUG "-g") +SET(CMAKE_C_FLAGS_MINSIZEREL "-Os -DNDEBUG") +SET(CMAKE_C_FLAGS_RELEASE "-O3 -DNDEBUG") +SET(CMAKE_C_FLAGS_RELWITHDEBINFO "-O2 -g") + +SET(CMAKE_CXX_COMPILER $ENV{QNX_HOST}/usr/bin/q++) +SET(CMAKE_CXX_FLAGS_DEBUG "-g") +SET(CMAKE_CXX_FLAGS_MINSIZEREL "-Os -DNDEBUG") +SET(CMAKE_CXX_FLAGS_RELEASE "-O3 -DNDEBUG") +SET(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-O2 -g") + +LIST(APPEND CMAKE_FIND_ROOT_PATH $ENV{QNX_TARGET}) +set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) +set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) + +SET(CMAKE_C_FLAGS "-Vgcc_nto${QNX_PROCESSOR}" CACHE STRING "qcc c flags" FORCE) +SET(CMAKE_CXX_FLAGS "-Vgcc_nto${QNX_PROCESSOR} -lang-c++ -Y_cxx" CACHE STRING "qcc cxx flags" FORCE) + +set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--build-id=md5 -lang-c++ -lsocket ${EXTRA_CMAKE_LINKER_FLAGS}" CACHE STRING "exe_linker_flags") +set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--build-id=md5 -lang-c++ -lsocket ${EXTRA_CMAKE_LINKER_FLAGS}" CACHE STRING "so_linker_flags") # pybind is not supported in this platform set(HAILO_BUILD_PYBIND 0) diff --git a/hailort/libhailort/cmake/toolchains/qnx.x86_64.cmake b/hailort/libhailort/cmake/toolchains/qnx.x86_64.cmake index 2108b4f..1b885de 100644 --- a/hailort/libhailort/cmake/toolchains/qnx.x86_64.cmake +++ b/hailort/libhailort/cmake/toolchains/qnx.x86_64.cmake @@ -1,22 +1,50 @@ +# CMake added fix for QCC compiler in this version - will not compile in older versions +cmake_minimum_required(VERSION 3.14.0) + set(CMAKE_SYSTEM_NAME QNX) -set(arch ntox86_64) set(QNX_PROCESSOR x86_64) +SET(CMAKE_SYSTEM_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}) +SET(CMAKE_MAKE_PROGRAM "$ENV{QNX_HOST}/usr/bin/make" CACHE PATH "QNX Make Program") +SET(CMAKE_SH "$ENV{QNX_HOST}/usr/bin/sh " CACHE PATH "QNX shell Program") +SET(CMAKE_AR "$ENV{QNX_HOST}/usr/bin/nto${CMAKE_SYSTEM_PROCESSOR}-ar" CACHE PATH "QNX ar Program") +SET(CMAKE_RANLIB "$ENV{QNX_HOST}/usr/bin/nto${CMAKE_SYSTEM_PROCESSOR}-ranlib" CACHE PATH "QNX ranlib Program") +SET(CMAKE_NM "$ENV{QNX_HOST}/usr/bin/nto${CMAKE_SYSTEM_PROCESSOR}-nm" CACHE PATH "QNX nm Program") +SET(CMAKE_OBJCOPY "$ENV{QNX_HOST}/usr/bin/nto${CMAKE_SYSTEM_PROCESSOR}-objcopy" CACHE PATH "QNX objcopy Program") +SET(CMAKE_OBJDUMP "$ENV{QNX_HOST}/usr/bin/nto${CMAKE_SYSTEM_PROCESSOR}-objdump" CACHE PATH "QNX objdump Program") +SET(CMAKE_LINKER "$ENV{QNX_HOST}/usr/bin/nto${CMAKE_SYSTEM_PROCESSOR}-ld" CACHE PATH "QNX Linker Program") +SET(CMAKE_STRIP "$ENV{QNX_HOST}/usr/bin/nto${CMAKE_SYSTEM_PROCESSOR}-strip" CACHE PATH "QNX Strip Program") add_definitions("-D_QNX_SOURCE") -file(GLOB_RECURSE libgcc_a - "$ENV{QNX_HOST}/usr/lib/gcc/${QNX_PROCESSOR}*/*/pic/libgcc.a") +SET(CMAKE_SHARED_LIBRARY_PREFIX "lib") +SET(CMAKE_SHARED_LIBRARY_SUFFIX ".so") +SET(CMAKE_STATIC_LIBRARY_PREFIX "lib") +SET(CMAKE_STATIC_LIBRARY_SUFFIX ".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}") +SET(CMAKE_C_COMPILER $ENV{QNX_HOST}/usr/bin/qcc) +SET(CMAKE_C_FLAGS_DEBUG "-g") +SET(CMAKE_C_FLAGS_MINSIZEREL "-Os -DNDEBUG") +SET(CMAKE_C_FLAGS_RELEASE "-O3 -DNDEBUG") +SET(CMAKE_C_FLAGS_RELWITHDEBINFO "-O2 -g") + +SET(CMAKE_CXX_COMPILER $ENV{QNX_HOST}/usr/bin/q++) +SET(CMAKE_CXX_FLAGS_DEBUG "-g") +SET(CMAKE_CXX_FLAGS_MINSIZEREL "-Os -DNDEBUG") +SET(CMAKE_CXX_FLAGS_RELEASE "-O3 -DNDEBUG") +SET(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-O2 -g") + +LIST(APPEND CMAKE_FIND_ROOT_PATH $ENV{QNX_TARGET}) +set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) +set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) + +SET(CMAKE_C_FLAGS "-Vgcc_nto${QNX_PROCESSOR}" CACHE STRING "qcc c flags" FORCE) +SET(CMAKE_CXX_FLAGS "-Vgcc_nto${QNX_PROCESSOR} -lang-c++ -Y_cxx" CACHE STRING "qcc cxx flags" FORCE) + +set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--build-id=md5 -lang-c++ -lsocket ${EXTRA_CMAKE_LINKER_FLAGS}" CACHE STRING "exe_linker_flags") +set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--build-id=md5 -lang-c++ -lsocket ${EXTRA_CMAKE_LINKER_FLAGS}" CACHE STRING "so_linker_flags") # pybind is not supported in this platform set(HAILO_BUILD_PYBIND 0) diff --git a/hailort/libhailort/cmake/toolchains/toolchains.yaml b/hailort/libhailort/cmake/toolchains/toolchains.yaml index 460fa13..3c5abf6 100644 --- a/hailort/libhailort/cmake/toolchains/toolchains.yaml +++ b/hailort/libhailort/cmake/toolchains/toolchains.yaml @@ -12,6 +12,9 @@ - version: '3.8' installation: deb package_name: python3.8-dev + - version: '3.9' + installation: deb + package_name: python3.9-dev - name: linux.aarch64 required_packages: - gcc-aarch64-linux-gnu @@ -29,6 +32,10 @@ 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 + - version: '3.9' + installation: manual + package_name: https://launchpad.net/~deadsnakes/+archive/ubuntu/ppa/+build/23779329/+files/libpython3.9-dev_3.9.13-1+bionic1_arm64.deb + package_dest: /usr/include/aarch64-linux-gnu - name: linux.armv7l required_packages: - gcc-arm-linux-gnueabi diff --git a/hailort/libhailort/examples/CMakeLists.txt b/hailort/libhailort/examples/CMakeLists.txt index 401e511..06bc93b 100644 --- a/hailort/libhailort/examples/CMakeLists.txt +++ b/hailort/libhailort/examples/CMakeLists.txt @@ -2,11 +2,11 @@ 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) +find_package(HailoRT 4.8.0 EXACT REQUIRED) + add_library(example_base INTERFACE) target_link_libraries(example_base INTERFACE HailoRT::libhailort Threads::Threads) @@ -19,4 +19,6 @@ endif() add_subdirectory(cpp) add_subdirectory(c) +set_target_properties(${EXAMPLES_CPP_TARGETS} PROPERTIES CXX_STANDARD 14) + 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 index 21f4813..fd42924 100644 --- a/hailort/libhailort/examples/README.md +++ b/hailort/libhailort/examples/README.md @@ -5,28 +5,38 @@ The following examples are provided, demonstrating the HailoRT API: - 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_device_example` - Basic inference of a shortcut network (inputs are sent through the device and right back out, without any changes made to the data), using VDevice over multiple pcie devices: + - 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. - `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. + - `switch_network_groups_example` - Demonstrates how to work with multiple HEFs using virtual streams and HailoRT scheduler for automatic network group switching. + - This example uses pcie devices. + - `switch_single_io_network_groups_manually_example` - Demonstrates how to work with multiple single input single output HEFs, switching the created network groups manually, using virtual streams. + - `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. + - `infer_pipeline_example` - Basic inference of a shortcut network using inference pipeline (blocking) api. + - this example uses udp device. + - `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. + - 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_device_example` - Basic inference of a shortcut network over multiple devices, same as `multi_device_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. + - `switch_network_groups_example` - Demonstrates how to work with multiple HEFs using virtual streams and HailoRT scheduler, same as `switch_network_groups_example ` C example, uses HailoRT C++ api. + - `switch_network_groups_manually_example` -Demonstrates how to work with multiple HEFs, switching the running network_groups manually, with performance optimizations for I/O threads re-usage instead of re-creation at each network group activation. Uses C++ api. + - `infer_streams_example` - Basic inference of a shortcut network, same as `raw_streams_example` C example, uses HailoRT C++ api. + - `infer_pipeline_example` - Basic inference of a shortcut network using inference pipeline (blocking) api. + - same as `infer_pipeline_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. ## Compiling with CMake Examples are configured and compiled using the following commands: diff --git a/hailort/libhailort/examples/c/CMakeLists.txt b/hailort/libhailort/examples/c/CMakeLists.txt index e001b9e..ec29225 100644 --- a/hailort/libhailort/examples/c/CMakeLists.txt +++ b/hailort/libhailort/examples/c/CMakeLists.txt @@ -18,14 +18,17 @@ 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_network_groups_example switch_network_groups_example.c) +target_link_libraries(c_switch_network_groups_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(c_switch_single_io_network_groups_manually_example switch_single_io_network_groups_manually_example.c) +target_link_libraries(c_switch_single_io_network_groups_manually_example PRIVATE example_base) -add_executable(multi_device_example_c multi_device_example.c) -target_link_libraries(multi_device_example_c PRIVATE example_base) +add_executable(c_multi_device_example multi_device_example.c) +target_link_libraries(c_multi_device_example PRIVATE example_base) + +add_executable(c_power_measurement_example power_measurement_example.c) +target_link_libraries(c_power_measurement_example PRIVATE example_base) set(EXAMPLES_C_TARGETS c_data_quantization_example @@ -33,7 +36,8 @@ set(EXAMPLES_C_TARGETS 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 + c_switch_network_groups_example + c_switch_single_io_network_groups_manually_example + c_multi_device_example + c_power_measurement_example PARENT_SCOPE) diff --git a/hailort/libhailort/examples/c/common.h b/hailort/libhailort/examples/c/common.h index 85e6e89..aafb298 100644 --- a/hailort/libhailort/examples/c/common.h +++ b/hailort/libhailort/examples/c/common.h @@ -3,7 +3,7 @@ * Distributed under the MIT license (https://opensource.org/licenses/MIT) **/ /** - * @ file example_common.h + * @file example_common.h * Common macros and defines used by Hailort Examples **/ diff --git a/hailort/libhailort/examples/c/data_quantization_example.c b/hailort/libhailort/examples/c/data_quantization_example.c index 831d3c1..4275bfe 100644 --- a/hailort/libhailort/examples/c/data_quantization_example.c +++ b/hailort/libhailort/examples/c/data_quantization_example.c @@ -3,7 +3,7 @@ * Distributed under the MIT license (https://opensource.org/licenses/MIT) **/ /** - * @ file data_quantization_example.c + * @file data_quantization_example.c * This example demonstrates using quantization on an HEF network with multiple inputs and multiple outputs **/ diff --git a/hailort/libhailort/examples/c/hailo_thread.h b/hailort/libhailort/examples/c/hailo_thread.h index 8bd6778..2c0d3be 100644 --- a/hailort/libhailort/examples/c/hailo_thread.h +++ b/hailort/libhailort/examples/c/hailo_thread.h @@ -3,7 +3,7 @@ * Distributed under the MIT license (https://opensource.org/licenses/MIT) **/ /** - * @ file hailo_thread.h + * @file hailo_thread.h * Common threads related functions, for linux and windows **/ diff --git a/hailort/libhailort/examples/c/infer_pipeline_example.c b/hailort/libhailort/examples/c/infer_pipeline_example.c index fc8f7fa..b5d16be 100644 --- a/hailort/libhailort/examples/c/infer_pipeline_example.c +++ b/hailort/libhailort/examples/c/infer_pipeline_example.c @@ -3,7 +3,7 @@ * Distributed under the MIT license (https://opensource.org/licenses/MIT) **/ /** - * @ file infer_pipeline_example.c + * @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. diff --git a/hailort/libhailort/examples/c/multi_device_example.c b/hailort/libhailort/examples/c/multi_device_example.c index 6dbfa27..a631e5f 100644 --- a/hailort/libhailort/examples/c/multi_device_example.c +++ b/hailort/libhailort/examples/c/multi_device_example.c @@ -11,7 +11,7 @@ * You shall not reproduce, modify or distribute this software without prior written permission. **/ /** - * @ file multi_device_example.c + * @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. diff --git a/hailort/libhailort/examples/c/power_measurement_example.c b/hailort/libhailort/examples/c/power_measurement_example.c new file mode 100644 index 0000000..b4c75ea --- /dev/null +++ b/hailort/libhailort/examples/c/power_measurement_example.c @@ -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 power_measurement_example.c + * This example demonstrates power and current measurements. + **/ + +#include "common.h" +#include "hailo/hailort.h" + +#include + +#define SAMPLING_PERIOD (HAILO_SAMPLING_PERIOD_1100US) +#define AVERAGE_FACTOR (HAILO_AVERAGE_FACTOR_256) +#define DVM_OPTION (HAILO_DVM_OPTIONS_AUTO) // For current measurement over EVB - pass DVM explicitly (see hailo_dvm_options_t) +#define MEASUREMENT_BUFFER_INDEX (HAILO_MEASUREMENT_BUFFER_INDEX_0) + +#define MEASUREMENTS_DURATION_SECS (5) +#define MAX_PCIE_DEVICES (16) + +#define MEASUREMENT_UNITS(__type) \ + ((HAILO_POWER_MEASUREMENT_TYPES__POWER == __type) ? ("W") : ("mA")) + +#define USAGE_ERROR_MSG ("Args parsing error.\nUsage: power_measurement_example [power / current]\n" \ + "* power - measure power consumption in W\n" \ + "* current - measure current in mA\n") + +#define POWER_ARG "power" +#define CURRENT_ARG "current" + + +void sleep_seconds(uint32_t duration_seconds) +{ +#if defined(__unix__) || defined(__QNX__) + sleep(duration_seconds); +#else + Sleep(duration_seconds); +#endif +} + +void parse_arguments(int argc, char **argv, hailo_power_measurement_types_t *measurement_type) +{ + if (2 != argc) { + printf(USAGE_ERROR_MSG); + exit(1); + } + + if (0 == strncmp(POWER_ARG, argv[1], ARRAY_LENGTH(POWER_ARG))) { + *measurement_type = HAILO_POWER_MEASUREMENT_TYPES__POWER; + } else if (0 == strncmp(CURRENT_ARG, argv[1], ARRAY_LENGTH(CURRENT_ARG))) { + *measurement_type = HAILO_POWER_MEASUREMENT_TYPES__CURRENT; + } else { + printf(USAGE_ERROR_MSG); + exit(1); + } +} + +hailo_status print_measurements_results(hailo_device device, hailo_power_measurement_data_t *result, hailo_power_measurement_types_t type) +{ + hailo_status status = HAILO_UNINITIALIZED; + hailo_device_id_t id = {0}; + status = hailo_get_device_id(device, &id); + REQUIRE_SUCCESS(status, l_exit, "Failed to get device id"); + const char* type_str = (type == HAILO_POWER_MEASUREMENT_TYPES__POWER) ? "Power measurement" : + "Current measurement"; + + printf("Device %s:\n", id.id); + printf(" %s\n", type_str); + printf(" Minimum value: %f %s\n", result->min_value, MEASUREMENT_UNITS(type)); + printf(" Average value: %f %s\n", result->average_value, MEASUREMENT_UNITS(type)); + printf(" Maximum value: %f %s\n", result->max_value, MEASUREMENT_UNITS(type)); + +l_exit: + return status; +} + +int main(int argc, char **argv) +{ + hailo_status status = HAILO_UNINITIALIZED; + hailo_vdevice vdevice = NULL; + hailo_pcie_device_info_t device_infos[MAX_PCIE_DEVICES]; + size_t actual_device_count = 0; + hailo_vdevice_params_t params = {0}; + hailo_device physical_devices[MAX_PCIE_DEVICES]; + hailo_power_measurement_data_t measurement_result[MAX_PCIE_DEVICES] = {0}; + hailo_power_measurement_types_t measurement_type = {0}; + + parse_arguments(argc, argv, &measurement_type); + + status = hailo_scan_pcie_devices(device_infos, MAX_PCIE_DEVICES, &actual_device_count); + REQUIRE_SUCCESS(status, l_exit, "Failed to scan pcie_device"); + + status = hailo_init_vdevice_params(¶ms); + REQUIRE_SUCCESS(status, l_exit, "Failed to init vdevice_params"); + + params.device_count = (uint32_t)actual_device_count; + status = hailo_create_vdevice(¶ms, &vdevice); + REQUIRE_SUCCESS(status, l_exit, "Failed to create vdevice"); + + status = hailo_get_physical_devices(vdevice, physical_devices, &actual_device_count); + REQUIRE_SUCCESS(status, l_release_vdevice, "Failed to get physical devices"); + + for (size_t i = 0; i < actual_device_count; i++) { + status = hailo_stop_power_measurement(physical_devices[i]); + REQUIRE_SUCCESS(status, l_exit, "Failed stopping former measurements"); + + status = hailo_set_power_measurement(physical_devices[i], MEASUREMENT_BUFFER_INDEX, DVM_OPTION, measurement_type); + REQUIRE_SUCCESS(status, l_exit, "Failed setting measurement params"); + + status = hailo_start_power_measurement(physical_devices[i], AVERAGE_FACTOR, SAMPLING_PERIOD); + REQUIRE_SUCCESS(status, l_exit, "Failed to start measurement"); + } + + sleep_seconds(MEASUREMENTS_DURATION_SECS); + + for (size_t i = 0; i < actual_device_count; i++) { + status = hailo_stop_power_measurement(physical_devices[i]); + REQUIRE_SUCCESS(status, l_exit, "Failed to stop measurement"); + + status = hailo_get_power_measurement(physical_devices[i], MEASUREMENT_BUFFER_INDEX, true, &(measurement_result[i])); + REQUIRE_SUCCESS(status, l_exit, "Failed to get measurement results"); + + status = print_measurements_results(physical_devices[i], &(measurement_result[i]), measurement_type); + REQUIRE_SUCCESS(status, l_release_vdevice, "Failed to print measurement results"); + } + + status = HAILO_SUCCESS; + +l_release_vdevice: + (void) hailo_release_vdevice(vdevice); +l_exit: + return status; +} \ No newline at end of file diff --git a/hailort/libhailort/examples/c/raw_streams_example.c b/hailort/libhailort/examples/c/raw_streams_example.c index 84f48b6..33592bf 100644 --- a/hailort/libhailort/examples/c/raw_streams_example.c +++ b/hailort/libhailort/examples/c/raw_streams_example.c @@ -3,7 +3,7 @@ * Distributed under the MIT license (https://opensource.org/licenses/MIT) **/ /** - * @ file raw_streams_example.c + * @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. @@ -185,8 +185,13 @@ int main() size_t index = 0; status = hailo_create_pcie_device(NULL, &device); + /* + For simplicity, passing NULL as `device_info` - This function will fail in case more than one PCIe device is present. + See `hailo_scan_pcie_devices` and `hailo_create_pcie_device` functions documentation. + */ 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); diff --git a/hailort/libhailort/examples/c/switch_hefs_example.c b/hailort/libhailort/examples/c/switch_network_groups_example.c similarity index 78% rename from hailort/libhailort/examples/c/switch_hefs_example.c rename to hailort/libhailort/examples/c/switch_network_groups_example.c index 336d3a3..e3f258c 100644 --- a/hailort/libhailort/examples/c/switch_hefs_example.c +++ b/hailort/libhailort/examples/c/switch_network_groups_example.c @@ -3,11 +3,10 @@ * 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. + * @file switch_network_groups_example.c + * This example demonstrates basic usage of HailoRT streaming api over multiple network groups, using VStreams. + * It loads several network_groups (via several HEFs) into a Hailo PCIe VDevice and performs a inferences on all of them in parallel. + * The network_groups switching is performed automatically by the HailoRT scheduler. **/ #include "common.h" @@ -20,7 +19,6 @@ #define INFER_FRAME_COUNT (100) #define HEF_COUNT (2) -#define RUN_COUNT (10) #define DEVICE_COUNT (1) typedef struct write_thread_args_t { @@ -172,7 +170,6 @@ int main() 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]; @@ -183,21 +180,19 @@ int main() 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]; + hailo_thread input_vstream_threads[HEF_COUNT][MAX_EDGE_LAYERS]; + hailo_thread output_vstream_threads[HEF_COUNT][MAX_EDGE_LAYERS]; + write_thread_args_t write_args[HEF_COUNT][MAX_EDGE_LAYERS]; + read_thread_args_t read_args[HEF_COUNT][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"}; + char HEF_FILES[HEF_COUNT][MAX_HEF_PATH_LEN] = {"hefs/multi_network_shortcut_net.hef", "hefs/shortcut_net.hef"}; status = hailo_init_vdevice_params(¶ms); REQUIRE_SUCCESS(status, l_exit, "Failed init vdevice_params"); params.device_count = DEVICE_COUNT; + params.scheduling_algorithm = HAILO_SCHEDULING_ALGORITHM_ROUND_ROBIN; status = hailo_create_vdevice(¶ms, &vdevice); REQUIRE_SUCCESS(status, l_exit, "Failed to create vdevice"); @@ -221,43 +216,32 @@ int main() 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 (hef_index = 0; hef_index < HEF_COUNT; hef_index++) { + 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[hef_index][i], &write_args[hef_index][i]); + } + REQUIRE_SUCCESS(status, l_release_vstreams, "Failed creating write threads"); - 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[hef_index][i], &read_args[hef_index][i]); + } + REQUIRE_SUCCESS(status, l_release_vstreams, "Failed creating read threads"); + } + + for (hef_index = 0; hef_index < HEF_COUNT; hef_index++) { + for (size_t i = 0; i < num_input_vstreams[hef_index]; i++) { + status = hailo_join_thread(&input_vstream_threads[hef_index][i]); + if (HAILO_SUCCESS != status) { + printf("write_thread failed \n"); } + } - 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; + for (size_t i = 0; i < num_output_vstreams[hef_index]; i++) { + status = hailo_join_thread(&output_vstream_threads[hef_index][i]); + if (HAILO_SUCCESS != status) { + printf("read_thread failed \n"); } } } @@ -266,8 +250,6 @@ int main() 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]); diff --git a/hailort/libhailort/examples/c/switch_single_io_hefs_example.c b/hailort/libhailort/examples/c/switch_single_io_network_groups_manually_example.c similarity index 99% rename from hailort/libhailort/examples/c/switch_single_io_hefs_example.c rename to hailort/libhailort/examples/c/switch_single_io_network_groups_manually_example.c index bb3c4d2..3d09f95 100644 --- a/hailort/libhailort/examples/c/switch_single_io_hefs_example.c +++ b/hailort/libhailort/examples/c/switch_single_io_network_groups_manually_example.c @@ -3,7 +3,7 @@ * Distributed under the MIT license (https://opensource.org/licenses/MIT) **/ /** - * @ file switch_single_io_hefs_example.c + * @file switch_single_io_network_groups_manually_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. diff --git a/hailort/libhailort/examples/c/vstreams_example.c b/hailort/libhailort/examples/c/vstreams_example.c index 31854d4..e75ef41 100644 --- a/hailort/libhailort/examples/c/vstreams_example.c +++ b/hailort/libhailort/examples/c/vstreams_example.c @@ -3,7 +3,7 @@ * Distributed under the MIT license (https://opensource.org/licenses/MIT) **/ /** - * @ file vstreams_example.c + * @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. diff --git a/hailort/libhailort/examples/cmake/FindHailoRT.cmake b/hailort/libhailort/examples/cmake/FindHailoRT.cmake deleted file mode 100644 index 173726c..0000000 --- a/hailort/libhailort/examples/cmake/FindHailoRT.cmake +++ /dev/null @@ -1,35 +0,0 @@ -# - 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 index e3f17b4..47d2913 100644 --- a/hailort/libhailort/examples/cpp/CMakeLists.txt +++ b/hailort/libhailort/examples/cpp/CMakeLists.txt @@ -12,21 +12,25 @@ 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_network_groups_example switch_network_groups_example.cpp) +target_link_libraries(cpp_switch_network_groups_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(cpp_switch_network_groups_manually_example switch_network_groups_manually_example.cpp) +target_link_libraries(cpp_switch_network_groups_manually_example PRIVATE example_base) -add_executable(multi_device_example_cpp multi_device_example.cpp) -target_link_libraries(multi_device_example_cpp PRIVATE example_base) +add_executable(cpp_multi_device_example multi_device_example.cpp) +target_link_libraries(cpp_multi_device_example PRIVATE example_base) + +add_executable(cpp_power_measurement_example power_measurement_example.cpp) +target_link_libraries(cpp_power_measurement_example 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 + cpp_switch_network_groups_example + cpp_switch_network_groups_manually_example + cpp_multi_device_example + cpp_power_measurement_example PARENT_SCOPE) diff --git a/hailort/libhailort/examples/cpp/infer_pipeline_example.cpp b/hailort/libhailort/examples/cpp/infer_pipeline_example.cpp index 10113aa..0b03790 100644 --- a/hailort/libhailort/examples/cpp/infer_pipeline_example.cpp +++ b/hailort/libhailort/examples/cpp/infer_pipeline_example.cpp @@ -3,7 +3,7 @@ * Distributed under the MIT license (https://opensource.org/licenses/MIT) **/ /** - * @ file vstreams_example + * @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. @@ -19,15 +19,7 @@ constexpr hailo_format_type_t FORMAT_TYPE = HAILO_FORMAT_TYPE_AUTO; #define USAGE_ERROR_MSG ("Args parsing error.\nUsage: infer_pipeline_example \n") - -using hailort::Device; -using hailort::Hef; -using hailort::Expected; -using hailort::make_unexpected; -using hailort::ConfiguredNetworkGroup; -using hailort::MemoryView; -using hailort::InferVStreams; - +using namespace hailort; Expected> configure_network_group(Device &device) { diff --git a/hailort/libhailort/examples/cpp/multi_device_example.cpp b/hailort/libhailort/examples/cpp/multi_device_example.cpp index 2d55c21..2b1e2df 100644 --- a/hailort/libhailort/examples/cpp/multi_device_example.cpp +++ b/hailort/libhailort/examples/cpp/multi_device_example.cpp @@ -11,7 +11,7 @@ * You shall not reproduce, modify or distribute this software without prior written permission. **/ /** - * @ file multi_device_example.cpp + * @file multi_device_example.cpp * This example demonstrates how to work with multiple devices using virtual device. **/ @@ -25,17 +25,7 @@ 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; - +using namespace hailort; Expected> configure_network_group(VDevice &vdevice) { diff --git a/hailort/libhailort/examples/cpp/multi_network_vstream_example.cpp b/hailort/libhailort/examples/cpp/multi_network_vstream_example.cpp index 1deed94..ac6aa12 100644 --- a/hailort/libhailort/examples/cpp/multi_network_vstream_example.cpp +++ b/hailort/libhailort/examples/cpp/multi_network_vstream_example.cpp @@ -22,18 +22,9 @@ 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 namespace hailort; using InOutVStreams = std::pair, std::vector>; - Expected> configure_network_group(VDevice &vdevice, Hef &hef, uint16_t batch_size[NET_COUNT]) { auto configure_params = hef.create_configure_params(HAILO_STREAM_INTERFACE_PCIE); @@ -225,7 +216,7 @@ int main() 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(); + return network_infos.status(); } auto vstreams = create_vstreams_per_network(*network_group.value(), network_infos.value()); diff --git a/hailort/libhailort/examples/cpp/power_measurement_example.cpp b/hailort/libhailort/examples/cpp/power_measurement_example.cpp new file mode 100644 index 0000000..21c5ce2 --- /dev/null +++ b/hailort/libhailort/examples/cpp/power_measurement_example.cpp @@ -0,0 +1,138 @@ +/** + * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved. + * Distributed under the MIT license (https://opensource.org/licenses/MIT) + **/ +/** + * @file power_measurement_example.cpp + * This example demonstrates power and current measurements. + **/ + +#include "hailo/hailort.hpp" + +#include +#include +#include + +#define SAMPLING_PERIOD (HAILO_SAMPLING_PERIOD_1100US) +#define AVERAGE_FACTOR (HAILO_AVERAGE_FACTOR_256) +#define DVM_OPTION (HAILO_DVM_OPTIONS_AUTO) // For current measurement over EVB - pass DVM explicitly (see hailo_dvm_options_t) +#define MEASUREMENT_BUFFER_INDEX (HAILO_MEASUREMENT_BUFFER_INDEX_0) + +#define MEASUREMENT_UNITS(__type) \ + ((HAILO_POWER_MEASUREMENT_TYPES__POWER == __type) ? ("W") : ("mA")) + +#define USAGE_ERROR_MSG ("Args parsing error.\nUsage: power_measurement_example [power / current]\n" \ + "* power - measure power consumption in W\n" \ + "* current - measure current in mA\n") + +const std::string POWER_ARG = "power"; +const std::string CURRENT_ARG = "current"; + +const std::chrono::seconds MEASUREMENTS_DURATION_SECS(5); + +using namespace hailort; + +Expected parse_arguments(int argc, char **argv) +{ + if (2 != argc) { + std::cerr << USAGE_ERROR_MSG << std::endl; + return make_unexpected(HAILO_INVALID_ARGUMENT); + } + + if (POWER_ARG == std::string(argv[1])) { + return HAILO_POWER_MEASUREMENT_TYPES__POWER; + } else if (CURRENT_ARG == std::string(argv[1])) { + return HAILO_POWER_MEASUREMENT_TYPES__CURRENT; + } else { + std::cerr << USAGE_ERROR_MSG << std::endl; + return make_unexpected(HAILO_INVALID_ARGUMENT); + } +} + +void print_measurements_results(Device &device, const hailo_power_measurement_data_t &result, hailo_power_measurement_types_t type) +{ + auto id = device.get_dev_id(); + + auto type_str = (type == HAILO_POWER_MEASUREMENT_TYPES__POWER) ? "Power measurement" : + "Current measurement"; + + std::cout << "Device" << std::string(id) << ":" << std::endl; + std::cout << " " << type_str << std::endl; + std::cout << " Minimum value: " << result.min_value << MEASUREMENT_UNITS(type) << std::endl; + std::cout << " Average value: " << result.average_value << MEASUREMENT_UNITS(type) << std::endl; + std::cout << " Maximum value: " << result.max_value << MEASUREMENT_UNITS(type) << std::endl; +} + +int main(int argc, char **argv) +{ + auto measurement_type = parse_arguments(argc, argv); + if (!measurement_type) { + return measurement_type.status(); + } + + auto scan_res = Device::scan_pcie(); + if (!scan_res) { + std::cerr << "Failed to scan pcie_device" << std::endl; + return scan_res.status(); + } + + hailo_vdevice_params_t params; + auto status = hailo_init_vdevice_params(¶ms); + if (HAILO_SUCCESS != status) { + std::cerr << "Failed init vdevice_params, status = " << status << std::endl; + return status; + } + + params.device_count = static_cast(scan_res->size()); + auto vdevice = VDevice::create(params); + if (!vdevice) { + std::cerr << "Failed create vdevice, status = " << vdevice.status() << std::endl; + return vdevice.status(); + } + + auto physical_devices = vdevice.value()->get_physical_devices(); + if (!physical_devices) { + std::cerr << "Failed to get physical devices" << std::endl; + return physical_devices.status(); + } + + for (auto &physical_device : physical_devices.value()) { + status = physical_device.get().stop_power_measurement(); + if (HAILO_SUCCESS != status) { + std::cerr << "Failed stopping former measurements" << std::endl; + return status; + } + + status = physical_device.get().set_power_measurement(MEASUREMENT_BUFFER_INDEX, DVM_OPTION, measurement_type.value()); + if (HAILO_SUCCESS != status) { + std::cerr << "Failed setting measurement params" << std::endl; + return status; + } + + status = physical_device.get().start_power_measurement(AVERAGE_FACTOR, SAMPLING_PERIOD); + if (HAILO_SUCCESS != status) { + std::cerr << "Failed to start measurement" << std::endl; + return status; + } + } + + std::this_thread::sleep_for(MEASUREMENTS_DURATION_SECS); + + for (auto &physical_device : physical_devices.value()) { + status = physical_device.get().stop_power_measurement(); + if (HAILO_SUCCESS != status) { + std::cerr << "Failed to stop measurement" << std::endl; + return status; + } + + auto measurement_result = physical_device.get().get_power_measurement(MEASUREMENT_BUFFER_INDEX, true); + if (!measurement_result) { + std::cerr << "Failed to get measurement results" << std::endl; + return measurement_result.status(); + } + + print_measurements_results(physical_device.get(), measurement_result.value(), measurement_type.value()); + } + + return HAILO_SUCCESS; +} \ No newline at end of file diff --git a/hailort/libhailort/examples/cpp/raw_streams_example.cpp b/hailort/libhailort/examples/cpp/raw_streams_example.cpp index c336bf3..df2c37e 100644 --- a/hailort/libhailort/examples/cpp/raw_streams_example.cpp +++ b/hailort/libhailort/examples/cpp/raw_streams_example.cpp @@ -3,7 +3,7 @@ * Distributed under the MIT license (https://opensource.org/licenses/MIT) **/ /** - * @ file raw_streams_example + * @file raw_streams_example * This example demonstrates using low level streams over c++ **/ @@ -17,20 +17,7 @@ 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; - +using namespace hailort; Expected> configure_network_group(Device &device) { @@ -157,6 +144,10 @@ hailo_status infer(InputStreamRefVector &input_streams, OutputStreamRefVector &o int main() { auto device = Device::create_pcie(); + /* + For simplicity, not passing `device_info` - This function will fail in case more than one PCIe device is present. + See `hailort::Device::scan_pcie` and `hailort::Device::create_pcie` functions documentation. + */ if (!device) { std::cerr << "Failed create_pcie " << device.status() << std::endl; return device.status(); diff --git a/hailort/libhailort/examples/cpp/switch_hefs_example.cpp b/hailort/libhailort/examples/cpp/switch_hefs_example.cpp deleted file mode 100644 index aab82c8..0000000 --- a/hailort/libhailort/examples/cpp/switch_hefs_example.cpp +++ /dev/null @@ -1,196 +0,0 @@ -/** - * 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 -#include - -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 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 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>>> build_vstreams( - const std::vector> &configured_network_groups) -{ - std::vector, std::vector>> 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> create_read_threads(std::vector &vstreams, - std::vector &read_results) -{ - std::vector> 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(read_all, - std::ref(vstream), std::ref(read_results.back()))); - } - return read_threads; -} - -std::vector> create_write_threads(std::vector &vstreams, - std::vector &write_results) -{ - std::vector> 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(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(¶ms); - 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 hef_paths = {"hefs/shortcut_net.hef", "hefs/shortcut_net.hef"}; - std::vector> 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 read_results; - auto read_threads = create_read_threads(vstreams_per_network_group[network_group_idx].second, read_results); - - std::vector 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_network_groups_example.cpp b/hailort/libhailort/examples/cpp/switch_network_groups_example.cpp new file mode 100644 index 0000000..a0a067d --- /dev/null +++ b/hailort/libhailort/examples/cpp/switch_network_groups_example.cpp @@ -0,0 +1,170 @@ +/** + * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved. + * Distributed under the MIT license (https://opensource.org/licenses/MIT) + **/ +/** + * @file switch_network_groups_example.cpp + * This example demonstrates basic usage of HailoRT streaming api over multiple network groups, using VStreams. + * It loads several network_groups (via several HEFs) into a Hailo PCIe VDevice and performs a inferences on all of them in parallel. + * The network_groups switching is performed automatically by the HailoRT scheduler. + **/ + +#include "hailo/hailort.hpp" + +#include +#include + +constexpr bool QUANTIZED = true; +constexpr hailo_format_type_t FORMAT_TYPE = HAILO_FORMAT_TYPE_AUTO; +constexpr size_t INFER_FRAME_COUNT = 100; +constexpr uint32_t DEVICE_COUNT = 1; + +using namespace hailort; +using ThreadsVector = std::vector>; +using StatusVector = std::vector>; + +void write_all(InputVStream &input_vstream, std::shared_ptr status_out) +{ + std::vector 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; + } + } + *status_out = HAILO_SUCCESS; + return; +} + +void read_all(OutputVStream &output_vstream, std::shared_ptr status_out) +{ + std::vector 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; + } + } + *status_out = HAILO_SUCCESS; + return; +} + +Expected, std::vector>>> build_vstreams( + const std::vector> &configured_network_groups) +{ + std::vector, std::vector>> 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; +} + +void create_read_threads(std::vector &vstreams, StatusVector &read_results, ThreadsVector &threads_vector) +{ + for (auto &vstream : vstreams) { + read_results.push_back(std::make_shared(HAILO_UNINITIALIZED)); + threads_vector.emplace_back(std::make_unique(read_all, std::ref(vstream), read_results.back())); + } +} + +void create_write_threads(std::vector &vstreams, StatusVector &write_results, ThreadsVector &threads_vector) +{ + for (auto &vstream : vstreams) { + write_results.push_back(std::make_shared(HAILO_UNINITIALIZED)); + threads_vector.emplace_back(std::make_unique(write_all, std::ref(vstream), write_results.back())); + } +} + +Expected> create_vdevice() +{ + hailo_vdevice_params_t params; + auto status = hailo_init_vdevice_params(¶ms); + if (HAILO_SUCCESS != status) { + std::cerr << "Failed init vdevice_params, status = " << status << std::endl; + return make_unexpected(status); + } + params.scheduling_algorithm = HAILO_SCHEDULING_ALGORITHM_ROUND_ROBIN; + params.device_count = DEVICE_COUNT; + + return VDevice::create(params); +} + +Expected>> configure_hefs(VDevice &vdevice, std::vector &hef_paths) +{ + std::vector> results; + + for (const auto &path : hef_paths) { + auto hef_exp = Hef::create(path); + if (!hef_exp) { + return make_unexpected(hef_exp.status()); + } + auto hef = hef_exp.release(); + + auto added_network_groups = vdevice.configure(hef); + if (!added_network_groups) { + return make_unexpected(added_network_groups.status()); + } + results.insert(results.end(), added_network_groups->begin(), + added_network_groups->end()); + } + return results; +} + +int main() +{ + auto vdevice_exp = create_vdevice(); + 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 hef_paths = {"hefs/multi_network_shortcut_net.hef", "hefs/shortcut_net.hef"}; + auto configured_network_groups_exp = configure_hefs(*vdevice, hef_paths); + if (!configured_network_groups_exp) { + std::cerr << "Failed to configure HEFs, status = " << configured_network_groups_exp.status() << std::endl; + return configured_network_groups_exp.status(); + } + auto configured_network_groups = configured_network_groups_exp.release(); + + 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(); + + ThreadsVector threads; + StatusVector results; + + for (auto &vstreams_pair : vstreams_per_network_group) { + // Create send/recv threads + create_read_threads(vstreams_pair.second, results, threads); + create_write_threads(vstreams_pair.first, results, threads); + } + + // Join threads and validate results + for (auto &thread : threads) { + if (thread->joinable()) { + thread->join(); + } + } + for (auto &status : results) { + if (HAILO_SUCCESS != *status) { + std::cerr << "Inference failed, status = " << *status << std::endl; + return *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_network_groups_manually_example.cpp similarity index 96% rename from hailort/libhailort/examples/cpp/switch_hefs_example_threads_reuse.cpp rename to hailort/libhailort/examples/cpp/switch_network_groups_manually_example.cpp index 735a5f8..aabfe67 100644 --- a/hailort/libhailort/examples/cpp/switch_hefs_example_threads_reuse.cpp +++ b/hailort/libhailort/examples/cpp/switch_network_groups_manually_example.cpp @@ -2,11 +2,11 @@ * 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 +/** + * @file switch_network_groups_manually_example.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. + * short inference on each one. * After inference is finished, the example switches to the next HEF and start inference again. **/ @@ -23,15 +23,7 @@ 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; - +using namespace hailort; #include #include diff --git a/hailort/libhailort/examples/cpp/vstreams_example.cpp b/hailort/libhailort/examples/cpp/vstreams_example.cpp index d87f800..5488e14 100644 --- a/hailort/libhailort/examples/cpp/vstreams_example.cpp +++ b/hailort/libhailort/examples/cpp/vstreams_example.cpp @@ -3,7 +3,7 @@ * Distributed under the MIT license (https://opensource.org/licenses/MIT) **/ /** - * @ file vstreams_example + * @file vstreams_example * This example demonstrates using virtual streams over c++ **/ @@ -17,17 +17,7 @@ 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; - +using namespace hailort; Expected> configure_network_group(VDevice &vdevice) { diff --git a/hailort/libhailort/hef.proto b/hailort/libhailort/hef.proto index d8d5cdf..f629ed7 100644 --- a/hailort/libhailort/hef.proto +++ b/hailort/libhailort/hef.proto @@ -32,6 +32,8 @@ enum ProtoHEFExtensionType { MULTI_NETWORK_VARIABLE_BATCH_SIZE = 8; IS_NMS_MULTI_CONTEXT = 9; OFFLOAD_ARGMAX = 10; + HW_PADDING = 11; + PRELIMINARY_RUN_ASAP = 12; UNUSED = 0XFFFF; } @@ -461,6 +463,7 @@ message ProtoHEFEdgeLayerBase { repeated ProtoHEFResourceIndices buffer_indices = 13; bool host_argmax = 14; + uint32 max_shmifo_size = 15; } // Additional information for specific layer types diff --git a/hailort/libhailort/include/hailo/device.hpp b/hailort/libhailort/include/hailo/device.hpp index 9527df3..90eba66 100644 --- a/hailort/libhailort/include/hailo/device.hpp +++ b/hailort/libhailort/include/hailo/device.hpp @@ -312,7 +312,7 @@ public: /** * Start performing a long power measurement. * - * @param[in] delay_milliseconds Amount of time between each measurement interval. + * @param[in] unused Unused parameter. * 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. @@ -323,8 +323,9 @@ public: * 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. + * @note This function is deprecated. One should use 'Device::start_power_measurement(hailo_averaging_factor_t averaging_factor, hailo_sampling_period_t sampling_period)' */ - hailo_status start_power_measurement(uint32_t delay_milliseconds, hailo_averaging_factor_t averaging_factor, hailo_sampling_period_t sampling_period); + hailo_status start_power_measurement(uint32_t unused, hailo_averaging_factor_t averaging_factor, hailo_sampling_period_t sampling_period); /** * Set parameters for long power measurement. @@ -338,6 +339,7 @@ public: * @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. + * @note This function is deprecated. One should use 'Device::set_power_measurement(hailo_measurement_buffer_index_t buffer_index, hailo_dvm_options_t dvm, hailo_power_measurement_types_t measurement_type)' */ hailo_status set_power_measurement(uint32_t index, hailo_dvm_options_t dvm, hailo_power_measurement_types_t measurement_type); @@ -347,10 +349,54 @@ public: * @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. + * passed to 'Device::set_power_measurement'. Otherwise, returns a ::hailo_status error. + * @note This function is deprecated. One should use "Device::get_power_measurement(hailo_measurement_buffer_index_t buffer_index, bool should_clear)' */ Expected get_power_measurement(uint32_t index, bool should_clear); + /** + * Start performing a long power measurement. + * + * @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(hailo_averaging_factor_t averaging_factor, hailo_sampling_period_t sampling_period); + + + /** + * Set parameters for long power measurement. + * + * @param[in] buffer_index A ::hailo_measurement_buffer_index_t represents the buffer on the firmware the data would be saved at. + * Should match the one passed to 'Device::get_power_measurement'. + * @param[in] dvm Which DVM will be measured. Default (::HAILO_DVM_OPTIONS_AUTO) will be different according to the board:
+ * - 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(hailo_measurement_buffer_index_t buffer_index, hailo_dvm_options_t dvm, hailo_power_measurement_types_t measurement_type); + + /** + * Read measured power from a long power measurement + * + * @param[in] buffer_index A ::hailo_measurement_buffer_index_t represents the buffer on the firmware the data would be saved at. + * Should match the one passed to 'Device::set_power_measurement'. + * @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 'Device::set_power_measurement'. Otherwise, returns a ::hailo_status error. + */ + Expected get_power_measurement(hailo_measurement_buffer_index_t buffer_index, bool should_clear); + /** * Stop performing a long power measurement. * @@ -387,7 +433,7 @@ public: * @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, + virtual hailo_status set_notification_callback(const NotificationCallback &func, hailo_notification_id_t notification_id, void *opaque) = 0; /** @@ -614,6 +660,8 @@ public: Expected> get_number_of_contexts_per_network_group(); Expected download_context_action_list(uint8_t context_index, uint32_t *base_address, uint32_t *batch_counter, uint16_t max_size = 10000); + // The batch configured is reset between network groups + hailo_status set_context_action_list_timestamp_batch(uint16_t batch_index); virtual ~Device() = default; Device(const Device &) = delete; diff --git a/hailort/libhailort/include/hailo/event.hpp b/hailort/libhailort/include/hailo/event.hpp index d3da128..81190d3 100644 --- a/hailort/libhailort/include/hailo/event.hpp +++ b/hailort/libhailort/include/hailo/event.hpp @@ -21,9 +21,29 @@ #include #endif +#if defined(__QNX__) +#include +#include + +// Forward declare neosmart::neosmart_event_t_ +namespace neosmart { + struct neosmart_event_t_; +} +#endif // defined (__QNX__) + namespace hailort { +// underlying_waitable_handle_t +#if defined(_MSC_VER) || defined(__linux__) + typedef underlying_handle_t underlying_waitable_handle_t; +#elif defined(__QNX__) + typedef neosmart::neosmart_event_t_* underlying_waitable_handle_t; +#else + #error "Unsupported Platform" +#endif + + class Waitable; using WaitablePtr = std::shared_ptr; using WaitablePtrList = std::vector; @@ -31,7 +51,7 @@ using WaitablePtrList = std::vector; class HAILORTAPI Waitable { public: - explicit Waitable(underlying_handle_t handle); + explicit Waitable(underlying_waitable_handle_t handle); virtual ~Waitable(); Waitable(Waitable&& other); @@ -45,22 +65,25 @@ public: 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(); + underlying_waitable_handle_t get_underlying_handle(); +#if defined(__QNX__) + virtual void post_wait() = 0; +#endif // defined (__QNX__) 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); + #if defined(_MSC_VER) || defined(__QNX__) + static hailo_status wait_for_single_object(underlying_waitable_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); + static hailo_status eventfd_poll(underlying_waitable_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); + static hailo_status eventfd_read(underlying_waitable_handle_t fd); + static hailo_status eventfd_write(underlying_waitable_handle_t fd); #endif - underlying_handle_t m_handle; + underlying_waitable_handle_t m_handle; }; class Event; @@ -86,9 +109,12 @@ public: virtual hailo_status signal() override; virtual bool is_auto_reset() override; hailo_status reset(); +#if defined(__QNX__) + virtual void post_wait() override; +#endif // defined (__QNX__) private: - static underlying_handle_t open_event_handle(const State& initial_state); + static underlying_waitable_handle_t open_event_handle(const State& initial_state); }; class Semaphore; @@ -106,9 +132,19 @@ public: virtual hailo_status wait(std::chrono::milliseconds timeout) override; virtual hailo_status signal() override; virtual bool is_auto_reset() override; +#if defined(__QNX__) + Semaphore(underlying_waitable_handle_t handle, uint32_t initial_count); + Semaphore(Semaphore&& other); + + virtual void post_wait() override; +#endif // defined (__QNX__) private: - static underlying_handle_t open_semaphore_handle(uint32_t initial_count); + static underlying_waitable_handle_t open_semaphore_handle(uint32_t initial_count); +#if defined (__QNX__) + std::atomic m_count; + std::mutex m_sem_mutex; +#endif // defined(__QNX__) }; } /* namespace hailort */ diff --git a/hailort/libhailort/include/hailo/expected.hpp b/hailort/libhailort/include/hailo/expected.hpp index d6b8c3c..cae0ea9 100644 --- a/hailort/libhailort/include/hailo/expected.hpp +++ b/hailort/libhailort/include/hailo/expected.hpp @@ -209,7 +209,7 @@ class Expected final { public: /** - * Expected can access Expected's private members (needed for implicit upcasting) + * Expected can access Expected\'s private members (needed for implicit upcasting) */ template friend class Expected; @@ -231,30 +231,42 @@ public: * Copy constructor */ explicit Expected(const Expected &other) : - m_value(other.m_value), m_status(other.m_status) - {} + { + if (other.has_value()) { + construct(&m_value, other.m_value); + } + } /** * Copy constructor for implicit upcasting */ template Expected(const Expected& other) : - m_value(other.m_value), m_status(other.m_status) - {} + { + if (other.has_value()) { + construct(&m_value, other.m_value); + } + } /** * Move constructor * * Construct a new Expected 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. + * - other.m_status moved to this.m_status. + * - other.m_value moved to this.m_value if other.m_value exists. + * + * If other had value before the move, it will still have the value that was moved (so the value object is valid but + * in an unspecified state). */ Expected(Expected &&other) : - m_value(std::move(other.m_value)), - m_status(std::exchange(other.m_status, HAILO_UNINITIALIZED)) - {} + m_status(other.m_status) + { + if (other.has_value()) { + construct(&m_value, std::move(other.m_value)); + } + } /** * Construct a new Expected from T& where: @@ -448,6 +460,12 @@ public: } private: + template + static void construct(T *value, Args &&...args) + { + new ((void*)value) T(std::forward(args)...); + } + union { T m_value; }; diff --git a/hailort/libhailort/include/hailo/hailort.h b/hailort/libhailort/include/hailo/hailort.h index c59f2fb..3519e7c 100644 --- a/hailort/libhailort/include/hailo/hailort.h +++ b/hailort/libhailort/include/hailo/hailort.h @@ -300,6 +300,17 @@ typedef enum hailo_averaging_factor_e { HAILO_AVERAGE_FACTOR_MAX_ENUM = HAILO_MAX_ENUM } hailo_averaging_factor_t; +/** Enum that represents buffers on the device for power measurements storing */ +typedef enum hailo_measurement_buffer_index_e { + HAILO_MEASUREMENT_BUFFER_INDEX_0 = 0, + HAILO_MEASUREMENT_BUFFER_INDEX_1, + HAILO_MEASUREMENT_BUFFER_INDEX_2, + HAILO_MEASUREMENT_BUFFER_INDEX_3, + + /** Max enum value to maintain ABI Integrity */ + HAILO_MEASUREMENT_BUFFER_INDEX_MAX_ENUM = HAILO_MAX_ENUM +} hailo_measurement_buffer_index_t; + /** Data of the power measurement samples */ typedef struct { float32_t average_value; @@ -326,12 +337,25 @@ typedef struct { uint32_t func; } hailo_pcie_device_info_t; +/** Scheduler algorithm */ +typedef enum hailo_scheduling_algorithm_e { + /** Scheduling disabled */ + HAILO_SCHEDULING_ALGORITHM_NONE = 0, + /** Round Robin */ + HAILO_SCHEDULING_ALGORITHM_ROUND_ROBIN, + + /** Max enum value to maintain ABI Integrity */ + HAILO_SCHEDULING_ALGORITHM_MAX_ENUM = HAILO_MAX_ENUM +} hailo_scheduling_algorithm_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; + /** The scheduling algorithm to use for network group scheduling */ + hailo_scheduling_algorithm_t scheduling_algorithm; } hailo_vdevice_params_t; /** Device architecture */ @@ -940,14 +964,7 @@ typedef enum { /** 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_interface_t stream_interface; hailo_stream_direction_t direction; union { hailo_pcie_input_stream_params_t pcie_input_params; @@ -1193,7 +1210,7 @@ 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. **/ +/** Hailo device ID string - BDF for PCIe devices, IP address for Ethernet devices, "Core" for core devices. **/ typedef struct { char id[HAILO_MAX_DEVICE_ID_LENGTH]; } hailo_device_id_t; @@ -1729,7 +1746,7 @@ HAILORTAPI hailo_status hailo_set_pause_frames(hailo_device device, bool rx_paus /** * Get device id which is the identification string of the device. BDF for PCIe devices, - * MAC address for Ethernet devices, "Core" for core devices. + * IP address for Ethernet devices, "Core" for core devices. * * @param[in] device A ::hailo_device object. * @param[out] id The returned device id. @@ -2004,8 +2021,6 @@ HAILORTAPI hailo_status hailo_power_measurement(hailo_device device, hailo_dvm_o * 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 @@ -2016,14 +2031,15 @@ HAILORTAPI hailo_status hailo_power_measurement(hailo_device device, hailo_dvm_o * 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, +HAILORTAPI hailo_status hailo_start_power_measurement(hailo_device device, 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] buffer_index A ::hailo_measurement_buffer_index_t represents the buffer on the firmware the data would be saved at. + * Should match the one passed to ::hailo_get_power_measurement. * @param[in] dvm Which DVM will be measured. Default (::HAILO_DVM_OPTIONS_AUTO) will be different according to the board:
* - 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. @@ -2033,20 +2049,21 @@ HAILORTAPI hailo_status hailo_start_power_measurement(hailo_device device, uint3 * 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, +HAILORTAPI hailo_status hailo_set_power_measurement(hailo_device device, hailo_measurement_buffer_index_t buffer_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] buffer_index A ::hailo_measurement_buffer_index_t represents the buffer on the firmware the data would be saved at. + * Should match the one passed to ::hailo_set_power_measurement. * @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, +HAILORTAPI hailo_status hailo_get_power_measurement(hailo_device device, hailo_measurement_buffer_index_t buffer_index, bool should_clear, hailo_power_measurement_data_t *measurement_data); /** @@ -2425,6 +2442,41 @@ HAILORTAPI hailo_status hailo_get_output_stream( HAILORTAPI hailo_status hailo_get_latency_measurement(hailo_configured_network_group configured_network_group, const char *network_name, hailo_latency_measurement_result_t *result); +/** + * Sets the maximum time period that may pass before getting run time from the scheduler, + * even without reaching the minimum required send requests (e.g. threshold - see hailo_set_scheduler_threshold()), + * as long as at least one send request has been sent. + * This time period is measured since the last time the scheduler gave this network group run time. + * + * @param[in] configured_network_group NetworkGroup for which to set the scheduler timeout. + * @param[in] timeout_ms Timeout in milliseconds. + * @param[in] network_name Network name for which to set the timeout. + * If NULL is passed, the timeout will be set for all the networks in the network group. + * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns a ::hailo_status error. + * @note Using this function is only allowed when scheduling_algorithm is not ::HAILO_SCHEDULING_ALGORITHM_NONE, and before the creation of any vstreams. + * @note The default timeout is 0ms. + * @note Currently, setting the timeout for a specific network is not supported. + */ +HAILORTAPI hailo_status hailo_set_scheduler_timeout(hailo_configured_network_group configured_network_group, + uint32_t timeout_ms, const char *network_name); + +/** + * Sets the minimum number of send requests required before the network is considered ready to get run time from the scheduler. + * If at least one send request has been sent, but the threshold is not reached within a set time period (e.g. timeout - see hailo_set_scheduler_timeout()), + * the scheduler will consider the network ready regardless. + * + * @param[in] configured_network_group NetworkGroup for which to set the scheduler threshold. + * @param[in] threshold Threshold in number of frames. + * @param[in] network_name Network name for which to set the threshold. + * If NULL is passed, the threshold will be set for all the networks in the network group. + * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns a ::hailo_status error. + * @note Using this function is only allowed when scheduling_algorithm is not ::HAILO_SCHEDULING_ALGORITHM_NONE, and before the creation of any vstreams. + * @note The default threshold is 1. + * @note Currently, setting the threshold for a specific network is not supported. + */ +HAILORTAPI hailo_status hailo_set_scheduler_threshold(hailo_configured_network_group configured_network_group, + uint32_t threshold, const char *network_name); + /** @} */ // end of group_network_group_functions /** @defgroup group_stream_functions Stream functions @@ -3051,110 +3103,6 @@ HAILORTAPI hailo_status hailo_get_network_infos(hailo_configured_network_group n * @{ */ -/** - * 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 diff --git a/hailort/libhailort/include/hailo/hailort_common.hpp b/hailort/libhailort/include/hailo/hailort_common.hpp index f161de1..a3b9fae 100644 --- a/hailort/libhailort/include/hailo/hailort_common.hpp +++ b/hailort/libhailort/include/hailo/hailort_common.hpp @@ -30,6 +30,7 @@ public: 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 uint64_t NMS_DUMMY_DELIMITER = 0xFFFFFFFFFFFFFFFE; 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; diff --git a/hailort/libhailort/include/hailo/network_group.hpp b/hailort/libhailort/include/hailo/network_group.hpp index 9c192e0..3de2c47 100644 --- a/hailort/libhailort/include/hailo/network_group.hpp +++ b/hailort/libhailort/include/hailo/network_group.hpp @@ -57,6 +57,11 @@ public: ActivatedNetworkGroup &operator=(ActivatedNetworkGroup &&other) = delete; ActivatedNetworkGroup(ActivatedNetworkGroup &&other) noexcept = default; + /** + * @return The network group name. + */ + virtual const std::string &get_network_group_name() const = 0; + virtual Expected get_intermediate_buffer(const IntermediateBufferKey &key) = 0; /** @@ -191,8 +196,7 @@ public: * @return Upon success, returns Expected of a pointer to ActivatedNetworkGroup object. * Otherwise, returns Unexpected of ::hailo_status error. */ - virtual Expected> activate( - const hailo_activate_network_group_params_t &network_group_params) = 0; + virtual Expected> activate(const hailo_activate_network_group_params_t &network_group_params) = 0; /** * Block until network group is activated, or until timeout is passed. @@ -303,12 +307,46 @@ public: */ virtual Expected> get_all_vstream_infos(const std::string &network_name="") const = 0; + /** + * Sets the maximum time period that may pass before getting run time from the scheduler, + * even without reaching the minimum required send requests (e.g. threshold - see set_scheduler_threshold()), + * as long as at least one send request has been sent. + * This time period is measured since the last time the scheduler gave this network group run time. + * + * @param[in] timeout Timeout in milliseconds. + * @param[in] network_name Network name for which to set the timeout. + * If not passed, the timeout will be set for all the networks in the network group. + * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns a ::hailo_status error. + * @note Using this function is only allowed when scheduling_algorithm is not ::HAILO_SCHEDULING_ALGORITHM_NONE, and before the creation of any vstreams. + * @note The default timeout is 0ms. + * @note Currently, setting the timeout for a specific network is not supported. + */ + virtual hailo_status set_scheduler_timeout(const std::chrono::milliseconds &timeout, const std::string &network_name="") = 0; + + /** + * Sets the minimum number of send requests required before the network is considered ready to get run time from the scheduler. + * If at least one send request has been sent, but the threshold is not reached within a set time period (e.g. timeout - see hailo_set_scheduler_timeout()), + * the scheduler will consider the network ready regardless. + * + * @param[in] threshold Threshold in number of frames. + * @param[in] network_name Network name for which to set the threshold. + * If not passed, the threshold will be set for all the networks in the network group. + * @return Upon success, returns ::HAILO_SUCCESS. Otherwise, returns a ::hailo_status error. + * @note Using this function is only allowed when scheduling_algorithm is not ::HAILO_SCHEDULING_ALGORITHM_NONE, and before the creation of any vstreams. + * @note The default threshold is 1. + * @note Currently, setting the threshold for a specific network is not supported. + */ + virtual hailo_status set_scheduler_threshold(uint32_t threshold, const std::string &network_name="") = 0; + virtual AccumulatorPtr get_activation_time_accumulator() const = 0; virtual AccumulatorPtr get_deactivation_time_accumulator() const = 0; protected: ConfiguredNetworkGroup() = default; + virtual Expected> activate_internal( + const hailo_activate_network_group_params_t &network_group_params, uint16_t dynamic_batch_size) = 0; + private: friend class ActivatedNetworkGroup; }; diff --git a/hailort/libhailort/include/hailo/platform.h b/hailort/libhailort/include/hailo/platform.h index 8fa3468..7820d37 100644 --- a/hailort/libhailort/include/hailo/platform.h +++ b/hailort/libhailort/include/hailo/platform.h @@ -50,9 +50,11 @@ // underlying_handle_t #ifndef underlying_handle_t #if defined(_MSC_VER) -typedef HANDLE underlying_handle_t; +typedef HANDLE underlying_handle_t; +#elif defined(__linux__) || defined(__QNX__) +typedef int underlying_handle_t; #else -typedef int underlying_handle_t; +#error "Unsupported Platform" #endif #endif diff --git a/hailort/libhailort/include/hailo/quantization.hpp b/hailort/libhailort/include/hailo/quantization.hpp index fa80905..51bb26c 100644 --- a/hailort/libhailort/include/hailo/quantization.hpp +++ b/hailort/libhailort/include/hailo/quantization.hpp @@ -11,6 +11,7 @@ #define _HAILO_QUANTIZATION_HPP_ #include "hailo/hailort.h" +#include "hailo/hailort_common.hpp" #include #include diff --git a/hailort/libhailort/include/hailo/stream.hpp b/hailort/libhailort/include/hailo/stream.hpp index 4269a3f..41a7bc6 100644 --- a/hailort/libhailort/include/hailo/stream.hpp +++ b/hailort/libhailort/include/hailo/stream.hpp @@ -78,6 +78,11 @@ public: */ virtual EventPtr &get_network_group_activated_event() = 0; + /** + * @returns whether the stream is managed by a network group scheduler. + */ + virtual bool is_scheduled() = 0; + /** * Writes the entire buffer to the stream without transformations * @@ -124,7 +129,7 @@ protected: // 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 activate_stream(uint16_t dynamic_batch_size) = 0; virtual hailo_status deactivate_stream() = 0; virtual Expected sync_write_raw_buffer(const MemoryView &buffer) = 0; @@ -182,6 +187,11 @@ public: * @returns a pointer for network group activated event. */ virtual EventPtr &get_network_group_activated_event() = 0; + + /** + * @returns whether the stream is managed by a network group scheduler. + */ + virtual bool is_scheduled() = 0; /** * @returns the stream's info. @@ -231,7 +241,7 @@ protected: OutputStream() = default; OutputStream(OutputStream&&); - virtual hailo_status activate_stream() = 0; + virtual hailo_status activate_stream(uint16_t dynamic_batch_size) = 0; virtual hailo_status deactivate_stream() = 0; virtual hailo_status read_all(MemoryView &buffer) = 0; diff --git a/hailort/libhailort/include/hailo/transform.hpp b/hailort/libhailort/include/hailo/transform.hpp index 4d059f4..54abd0d 100644 --- a/hailort/libhailort/include/hailo/transform.hpp +++ b/hailort/libhailort/include/hailo/transform.hpp @@ -141,8 +141,6 @@ private: Buffer m_transpose_buffer; }; -typedef InputTransformContext InputTransformer DEPRECATED("InputTransformer is deprecated. One should use InputTransformContext"); - /*! Object used for output stream transformation*/ class HAILORTAPI OutputTransformContext { @@ -247,8 +245,6 @@ protected: 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: diff --git a/hailort/libhailort/src/CMakeLists.txt b/hailort/libhailort/src/CMakeLists.txt index 3a59d9c..b60e1a6 100644 --- a/hailort/libhailort/src/CMakeLists.txt +++ b/hailort/libhailort/src/CMakeLists.txt @@ -2,6 +2,8 @@ cmake_minimum_required(VERSION 3.0.0) find_package(Threads REQUIRED) +include(GNUInstallDirs) +include(CMakePackageConfigHelpers) include(${CMAKE_CURRENT_SOURCE_DIR}/../cmake/common_compiler_options.cmake) FUNCTION(relative_to_absolute_paths output) @@ -43,18 +45,24 @@ set(HAILORT_CPP_SOURCES context_switch/vdma_config_network_group.cpp context_switch/vdma_config_activated_network_group.cpp context_switch/network_group.cpp + context_switch/network_group_wrapper.cpp context_switch/resource_manager.cpp + context_switch/pipeline_multiplexer.cpp intermediate_buffer.cpp + config_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 + vdma/mapped_buffer.cpp + vdma/sg_buffer.cpp + vdma/continuous_buffer.cpp + pcie_device.cpp pcie_stream.cpp @@ -68,6 +76,8 @@ set(HAILORT_CPP_SOURCES vstream.cpp inference_pipeline.cpp + + network_group_scheduler.cpp ) set(common_dir "${PROJECT_SOURCE_DIR}/common/src") @@ -116,6 +126,10 @@ 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) +target_link_libraries(libhailort PRIVATE microprofile) +if(CMAKE_SYSTEM_NAME STREQUAL QNX) + target_link_libraries(libhailort PRIVATE pevents pci) +endif() set(HAILORT_PUBLIC_HEADERS ${HAILORT_INC_DIR}/hailo/hailort.h @@ -174,7 +188,44 @@ target_compile_definitions(libhailort PUBLIC ) install(TARGETS libhailort - LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} - CONFIGURATIONS Release - PUBLIC_HEADER DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/hailo" - CONFIGURATIONS Release) + EXPORT HailoRTTargets + CONFIGURATIONS Release + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + PUBLIC_HEADER DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/hailo" + INCLUDES DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}" +) + +if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux") + install(CODE "execute_process(COMMAND ldconfig)") +endif() + +# Export libhailort +set(CMAKE_SCRIPTS_DIR ${CMAKE_CURRENT_BINARY_DIR}) +configure_package_config_file(${CMAKE_CURRENT_SOURCE_DIR}/Config.cmake.in + "${CMAKE_SCRIPTS_DIR}/HailoRTConfig.cmake" + INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/HailoRT +) +write_basic_package_version_file( + "${CMAKE_SCRIPTS_DIR}/HailoRTConfigVersion.cmake" + VERSION "${HAILORT_MAJOR_VERSION}.${HAILORT_MINOR_VERSION}.${HAILORT_REVISION_VERSION}" + COMPATIBILITY ExactVersion +) + +# Support builds without installation +set(HailoRT_DIR "${CMAKE_SCRIPTS_DIR}" PARENT_SCOPE) + +# Package installation +install(FILES + "${CMAKE_SCRIPTS_DIR}/HailoRTConfig.cmake" + "${CMAKE_SCRIPTS_DIR}/HailoRTConfigVersion.cmake" + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/HailoRT + COMPONENT libhailort +) +install(EXPORT HailoRTTargets + FILE HailoRTTargets.cmake + NAMESPACE HailoRT:: + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/HailoRT + COMPONENT libhailort +) diff --git a/hailort/libhailort/src/Config.cmake.in b/hailort/libhailort/src/Config.cmake.in new file mode 100644 index 0000000..b69f0e2 --- /dev/null +++ b/hailort/libhailort/src/Config.cmake.in @@ -0,0 +1,10 @@ +@PACKAGE_INIT@ + +if(TARGET libhailort) + if(${HailoRT_FIND_VERSION} VERSION_EQUAL "${HAILORT_MAJOR_VERSION}.${HAILORT_MINOR_VERSION}.${HAILORT_REVISION_VERSION}") + add_library(HailoRT::libhailort ALIAS libhailort) + endif() +else() + include("${CMAKE_CURRENT_LIST_DIR}/HailoRTTargets.cmake") + check_required_components(HailoRT) +endif() \ No newline at end of file diff --git a/hailort/libhailort/src/config_buffer.cpp b/hailort/libhailort/src/config_buffer.cpp new file mode 100644 index 0000000..96cae2a --- /dev/null +++ b/hailort/libhailort/src/config_buffer.cpp @@ -0,0 +1,143 @@ +/** + * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved. + * Distributed under the MIT license (https://opensource.org/licenses/MIT) + **/ +/** + * @file config_buffer.cpp + * @brief Manages configuration vdma buffer. The configuration buffer contains nn-configurations in a specific + * hw format (ccw). + */ + +#include "config_buffer.hpp" +#include "vdma/sg_buffer.hpp" +#include "vdma/continuous_buffer.hpp" + +#include + +namespace hailort { + +Expected ConfigBuffer::create(HailoRTDriver &driver, uint8_t channel_index, + const std::vector &cfg_sizes) +{ + const auto buffer_size = std::accumulate(cfg_sizes.begin(), cfg_sizes.end(), 0); + + auto buffer_ptr = should_use_ccb(driver) ? + create_ccb_buffer(driver, buffer_size) : + create_sg_buffer(driver, channel_index, cfg_sizes); + CHECK_EXPECTED(buffer_ptr); + + return ConfigBuffer(buffer_ptr.release(), buffer_size); +} + +ConfigBuffer::ConfigBuffer(std::unique_ptr &&buffer, + size_t total_buffer_size) + : m_buffer(std::move(buffer)), + m_total_buffer_size(total_buffer_size), m_acc_buffer_offset(0), m_acc_desc_count(0), + m_current_buffer_size(0) +{} + +Expected ConfigBuffer::program_descriptors() +{ + auto descriptors_count = + m_buffer->program_descriptors(m_acc_buffer_offset, VdmaInterruptsDomain::NONE, VdmaInterruptsDomain::DEVICE, + m_acc_desc_count, false); + CHECK_EXPECTED(descriptors_count); + + m_acc_desc_count += descriptors_count.value(); + m_acc_buffer_offset = 0; + + return descriptors_count; +} + +hailo_status ConfigBuffer::write(const void *data, size_t data_size) +{ + size_t total_offset = (m_acc_desc_count * m_buffer->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; +} + +size_t ConfigBuffer::get_total_cfg_size() +{ + return m_total_buffer_size; +} + +vdma::VdmaBuffer::Type ConfigBuffer::buffer_type() const +{ + return m_buffer->type(); +} + +size_t ConfigBuffer::get_current_buffer_size() +{ + return m_current_buffer_size; +} + +uint16_t ConfigBuffer::desc_page_size() const +{ + return m_buffer->desc_page_size(); +} + +uint64_t ConfigBuffer::dma_address() const +{ + return m_buffer->dma_address(); +} + +uint32_t ConfigBuffer::total_desc_count() const +{ + return m_buffer->descs_count(); +} + +uint32_t ConfigBuffer::acc_desc_count() const +{ + return m_acc_desc_count; +} + +Expected> ConfigBuffer::create_sg_buffer(HailoRTDriver &driver, + uint8_t channel_index, const std::vector &cfg_sizes) +{ + 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 = vdma::SgBuffer::create(driver, descs_count, page_size, HailoRTDriver::DmaDirection::H2D, + channel_index); + CHECK_EXPECTED(buffer); + + auto buffer_ptr = make_unique_nothrow(buffer.release()); + CHECK_NOT_NULL_AS_EXPECTED(buffer_ptr, HAILO_OUT_OF_HOST_MEMORY); + + return std::unique_ptr(std::move(buffer_ptr)); +} + +Expected> ConfigBuffer::create_ccb_buffer(HailoRTDriver &driver, + uint32_t buffer_size) +{ + buffer_size = vdma::ContinuousBuffer::get_buffer_size(buffer_size); + auto buffer = vdma::ContinuousBuffer::create(buffer_size, driver); + CHECK_EXPECTED(buffer); + + auto buffer_ptr = make_unique_nothrow(buffer.release()); + CHECK_NOT_NULL_AS_EXPECTED(buffer_ptr, HAILO_OUT_OF_HOST_MEMORY); + + return std::unique_ptr(std::move(buffer_ptr)); +} + +bool ConfigBuffer::should_use_ccb(HailoRTDriver &driver) +{ + switch (driver.dma_type()) { + case HailoRTDriver::DmaType::PCIE: + return false; + case HailoRTDriver::DmaType::DRAM: + return true; + default: + assert(true); + return false; + } +} + +} /* hailort */ \ No newline at end of file diff --git a/hailort/libhailort/src/config_buffer.hpp b/hailort/libhailort/src/config_buffer.hpp new file mode 100644 index 0000000..4a92e89 --- /dev/null +++ b/hailort/libhailort/src/config_buffer.hpp @@ -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 config_buffer.hpp + * @brief Manages configuration vdma buffer. The configuration buffer contains nn-configurations in a specific + * hw format (ccw). + */ + +#ifndef _HAILO_CONFIG_BUFFER_HPP_ +#define _HAILO_CONFIG_BUFFER_HPP_ + +#include "vdma/vdma_buffer.hpp" + +namespace hailort { + + +class ConfigBuffer final +{ +public: + static Expected create(HailoRTDriver &driver, uint8_t channel_index, + const std::vector &cfg_sizes); + + // 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 program_descriptors(); + + 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(); + + vdma::VdmaBuffer::Type buffer_type() const; + uint16_t desc_page_size() const; + uint64_t dma_address() const; + uint32_t total_desc_count() const; + + uint32_t acc_desc_count() const; + +private: + ConfigBuffer(std::unique_ptr &&buffer, size_t total_buffer_size); + + static Expected> create_sg_buffer(HailoRTDriver &driver, + uint8_t channel_index, const std::vector &cfg_sizes); + static Expected> create_ccb_buffer(HailoRTDriver &driver, + uint32_t buffer_size); + + static bool should_use_ccb(HailoRTDriver &driver); + + std::unique_ptr m_buffer; + const size_t m_total_buffer_size; + size_t m_acc_buffer_offset; + uint32_t m_acc_desc_count; + size_t m_current_buffer_size; +}; + +} /* hailort */ + +#endif /* _HAILO_CONFIG_BUFFER_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 index f224700..0911f66 100644 --- a/hailort/libhailort/src/context_switch/config_manager.hpp +++ b/hailort/libhailort/src/context_switch/config_manager.hpp @@ -36,7 +36,7 @@ class ConfigManager virtual ~ConfigManager() {} virtual ConfigManagerType get_manager_type() = 0; virtual Expected add_hef(Hef &hef, const std::map &configure_params={}) = 0; + ConfigureNetworkParams> &configure_params, bool is_scheduler_used = false) = 0; protected: hailo_status validate_boundary_streams_were_created(Hef &hef, const std::string &network_group_name, ConfiguredNetworkGroup &network_group) 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 index 7fe37b8..80b4f96 100644 --- a/hailort/libhailort/src/context_switch/hcp_config_activated_network_group.cpp +++ b/hailort/libhailort/src/context_switch/hcp_config_activated_network_group.cpp @@ -14,6 +14,7 @@ namespace hailort { Expected HcpConfigActivatedNetworkGroup::create(Device &device, std::vector &config, + const std::string &network_group_name, const hailo_activate_network_group_params_t &network_group_params, std::map> &input_streams, std::map> &output_streams, @@ -36,20 +37,29 @@ Expected HcpConfigActivatedNetworkGroup::create( CHECK_SUCCESS_AS_EXPECTED(status); } - HcpConfigActivatedNetworkGroup object(device, active_net_group_holder, network_group_params, input_streams, output_streams, + HcpConfigActivatedNetworkGroup object(device, active_net_group_holder, network_group_name, 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> &input_streams, - std::map> &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) +HcpConfigActivatedNetworkGroup::HcpConfigActivatedNetworkGroup( + Device &device, + HcpConfigActiveAppHolder &active_net_group_holder, + const std::string &network_group_name, + const hailo_activate_network_group_params_t &network_group_params, + std::map> &input_streams, + std::map> &output_streams, + hailo_power_mode_t power_mode, + EventPtr &&network_group_activated_event, + hailo_status &status) : + ActivatedNetworkGroupBase(network_group_params, CONTROL_PROTOCOL__IGNORE_DYNAMIC_BATCH_SIZE, + 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), + m_network_group_name(network_group_name) { // Validate ActivatedNetworkGroup status if (HAILO_SUCCESS != status) { @@ -66,4 +76,9 @@ HcpConfigActivatedNetworkGroup::~HcpConfigActivatedNetworkGroup() } } +const std::string &HcpConfigActivatedNetworkGroup::get_network_group_name() const +{ + return m_network_group_name; +} + } /* namespace hailort */ diff --git a/hailort/libhailort/src/context_switch/hcp_config_manager.cpp b/hailort/libhailort/src/context_switch/hcp_config_manager.cpp index d0e67ff..d314347 100644 --- a/hailort/libhailort/src/context_switch/hcp_config_manager.cpp +++ b/hailort/libhailort/src/context_switch/hcp_config_manager.cpp @@ -40,7 +40,7 @@ namespace hailort { Expected HcpConfigManager::add_hef(Hef &hef, - const NetworkGroupsParamsMap &configure_params) + const NetworkGroupsParamsMap &configure_params, bool /*is_scheduler_used*/) { auto &hef_network_groups = hef.pimpl->network_groups(); auto current_net_group_index = static_cast(m_net_groups.size()); @@ -95,7 +95,15 @@ Expected HcpConfigManager::add_hef(Hef &hef, auto net_group_ptr = make_shared_nothrow(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(net_group_ptr)); + + auto net_group_wrapper = ConfiguredNetworkGroupWrapper::create(net_group_ptr); + CHECK_EXPECTED(net_group_wrapper); + + auto net_group_wrapper_ptr = make_shared_nothrow(net_group_wrapper.release()); + CHECK_AS_EXPECTED(nullptr != net_group_wrapper_ptr, HAILO_OUT_OF_HOST_MEMORY); + m_net_group_wrappers.emplace_back(net_group_wrapper_ptr); + + added_network_groups.emplace_back(std::static_pointer_cast(net_group_wrapper_ptr)); current_net_group_index++; diff --git a/hailort/libhailort/src/context_switch/hcp_config_network_group.cpp b/hailort/libhailort/src/context_switch/hcp_config_network_group.cpp index 735597c..f48c993 100644 --- a/hailort/libhailort/src/context_switch/hcp_config_network_group.cpp +++ b/hailort/libhailort/src/context_switch/hcp_config_network_group.cpp @@ -15,12 +15,12 @@ HcpConfigNetworkGroup::HcpConfigNetworkGroup(Device &device, HcpConfigActiveAppH m_config(std::move(config)), m_active_net_group_holder(active_net_group_holder), m_device(device) {} -Expected> HcpConfigNetworkGroup::activate( - const hailo_activate_network_group_params_t &network_group_params) +Expected> HcpConfigNetworkGroup::activate_impl( + const hailo_activate_network_group_params_t &network_group_params, uint16_t /* dynamic_batch_size */) { auto start_time = std::chrono::steady_clock::now(); - auto activated_net_group = HcpConfigActivatedNetworkGroup::create(m_device, m_config, network_group_params, + auto activated_net_group = HcpConfigActivatedNetworkGroup::create(m_device, m_config, get_network_group_name(), 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); @@ -46,4 +46,33 @@ Expected HcpConfigNetworkGroup::get_boundary_channel_index(uint8_t stre static_cast(stream_index + OUTPUT_CHANNEL_INDEX_OFFSET); } +hailo_status HcpConfigNetworkGroup::set_scheduler_timeout(const std::chrono::milliseconds &timeout, const std::string &network_name) +{ + (void) timeout; + (void) network_name; + return HAILO_INVALID_OPERATION; +} + +hailo_status HcpConfigNetworkGroup::set_scheduler_threshold(uint32_t threshold, const std::string &network_name) +{ + (void) threshold; + (void) network_name; + return HAILO_INVALID_OPERATION; +} + +Expected> HcpConfigNetworkGroup::get_latnecy_meters() +{ + /* hcp does not support latnecy. return empty map */ + LatencyMetersMap empty_map; + return make_shared_nothrow(empty_map); +} + +Expected> HcpConfigNetworkGroup::get_boundary_vdma_channel_by_stream_name( + const std::string &stream_name) +{ + LOGGER__ERROR("get_boundary_vdma_channel_by_stream_name function for stream name {} is not supported in hcp config manager", + stream_name); + return make_unexpected(HAILO_INVALID_OPERATION); +} + } /* namespace hailort */ diff --git a/hailort/libhailort/src/context_switch/hef_metadata.cpp b/hailort/libhailort/src/context_switch/hef_metadata.cpp index c749eb8..0a13864 100644 --- a/hailort/libhailort/src/context_switch/hef_metadata.cpp +++ b/hailort/libhailort/src/context_switch/hef_metadata.cpp @@ -453,6 +453,7 @@ hailo_status HEF_METADATA__add_enable_lcu_default_action( uint8_t **action_data_current_offset, uint8_t cluster_index, uint8_t lcu_index, + uint8_t network_index, bool is_repeated) { hailo_status status = HAILO_UNINITIALIZED; @@ -465,6 +466,7 @@ hailo_status HEF_METADATA__add_enable_lcu_default_action( 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.network_index = network_index; status = hef_metadata__update_slicing_info(context_info, action_data_current_offset, sizeof(enable_lcu_action), true); @@ -609,17 +611,13 @@ hailo_status HEF_METADATA__add_network_boundary_output_edge_layer( } 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) + 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, + const CONTROL_PROTOCOL__host_buffer_info_t &host_buffer_info) { hailo_status status = HAILO_UNINITIALIZED; CONTROL_PROTOCOL__inter_context_output_t *edge_layer_info = nullptr; @@ -647,11 +645,7 @@ hailo_status HEF_METADATA__add_inter_context_output_edge_layer( 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_info->host_buffer_info = host_buffer_info; *(edge_layer_current_offset) += sizeof(*edge_layer_info); @@ -667,10 +661,9 @@ hailo_status HEF_METADATA__add_ddr_buffer_output_edge_layer( 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) + uint32_t buffered_rows_count) { hailo_status status = HAILO_UNINITIALIZED; CONTROL_PROTOCOL__ddr_buffer_output_t *edge_layer_info = nullptr; @@ -702,22 +695,22 @@ hailo_status HEF_METADATA__add_ddr_buffer_output_edge_layer( 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; + edge_layer_info->buffered_rows_count = buffered_rows_count; 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) + 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, + uint32_t initial_credit_size) { hailo_status status = HAILO_UNINITIALIZED; CONTROL_PROTOCOL__network_boundary_input_t *edge_layer_info = nullptr; @@ -746,6 +739,7 @@ hailo_status HEF_METADATA__add_network_boundary_input_edge_layer( nn_stream_config); edge_layer_info->desc_page_size = desc_page_size; + edge_layer_info->initial_credit_size = initial_credit_size; *(edge_layer_current_offset) += sizeof(*edge_layer_info); @@ -753,16 +747,14 @@ hailo_status HEF_METADATA__add_network_boundary_input_edge_layer( } 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) + 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, + const CONTROL_PROTOCOL__host_buffer_info_t &host_buffer_info, + uint32_t initial_credit_size) { hailo_status status = HAILO_UNINITIALIZED; CONTROL_PROTOCOL__inter_context_input_t *edge_layer_info = nullptr; @@ -790,11 +782,8 @@ hailo_status HEF_METADATA__add_inter_context_input_edge_layer( 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_info->host_buffer_info = host_buffer_info; + edge_layer_info->initial_credit_size = initial_credit_size; *(edge_layer_current_offset) += sizeof(*edge_layer_info); @@ -802,15 +791,15 @@ hailo_status HEF_METADATA__add_inter_context_input_edge_layer( } 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) + 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, + uint32_t initial_credit_size) { hailo_status status = HAILO_UNINITIALIZED; CONTROL_PROTOCOL__ddr_buffer_input_t *edge_layer_info = nullptr; @@ -839,9 +828,8 @@ hailo_status HEF_METADATA__add_ddr_buffer_input_edge_layer( 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_info->initial_credit_size = initial_credit_size; *(edge_layer_current_offset) += sizeof(*edge_layer_info); @@ -907,6 +895,32 @@ hailo_status HEF_METADATA__add_ddr_buffering_start( return HAILO_SUCCESS; } + +hailo_status HEF_METADATA__burst_credits_task_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__BURST_CREDITS_TASK_START_ACTION_T burst_credits_task_start{}; + + CHECK_ARG_NOT_NULL(action_data_current_offset); + CHECK_ARG_NOT_NULL(*action_data_current_offset); + + burst_credits_task_start.header.action_type = CONTROL_PROTOCOL__CONTEXT_SWITCH_ACTION_BURST_CREDITS_TASK_START; + burst_credits_task_start.header.is_repeated = is_repeated; + + status = hef_metadata__update_slicing_info(context_info, + action_data_current_offset, + sizeof(burst_credits_task_start), + true); + CHECK_SUCCESS(status); + + memcpy((*action_data_current_offset), &burst_credits_task_start, sizeof(burst_credits_task_start)); + *(action_data_current_offset) += sizeof(burst_credits_task_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 index ae39ecf..d57f150 100644 --- a/hailort/libhailort/src/context_switch/hef_metadata.hpp +++ b/hailort/libhailort/src/context_switch/hef_metadata.hpp @@ -224,6 +224,7 @@ hailo_status HEF_METADATA__add_enable_lcu_non_default_action( * @param[out] action - pointer to the action * @param[in] cluster_index - cluster index * @param[in] lcu_index - lcu_index + * @param[in] network_index - network index * @param[in] is_repeated - 'true' if the action is part of a "repeated sequence" (a group of consecutive actions * with the same type) * @@ -233,6 +234,7 @@ hailo_status HEF_METADATA__add_enable_lcu_default_action( uint8_t **action_data_current_offset, uint8_t cluster_index, uint8_t lcu_index, + uint8_t network_index, bool is_repeated); /** @@ -301,13 +303,7 @@ hailo_status HEF_METADATA__add_network_boundary_output_edge_layer( * @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] host_buffer_info - info about host buffer */ hailo_status HEF_METADATA__add_inter_context_output_edge_layer( CONTROL_PROTOCOL__context_switch_context_info_t *context_info, @@ -316,11 +312,7 @@ hailo_status HEF_METADATA__add_inter_context_output_edge_layer( 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); + const CONTROL_PROTOCOL__host_buffer_info_t &host_buffer_info); /** * build edge layer - vdma DDR buffer output @@ -333,11 +325,9 @@ hailo_status HEF_METADATA__add_inter_context_output_edge_layer( * @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 + * @param[in] buffered_rows_count - amount of rows to buffer. * */ hailo_status HEF_METADATA__add_ddr_buffer_output_edge_layer( @@ -349,10 +339,9 @@ hailo_status HEF_METADATA__add_ddr_buffer_output_edge_layer( 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); + uint32_t buffered_rows_count); /** * build edge layer - vdma network boundary input @@ -364,6 +353,7 @@ hailo_status HEF_METADATA__add_ddr_buffer_output_edge_layer( * @param[in] network_index - network index * @param[in] nn_stream_config * @param[in] desc_page_size - desc page size in bytes + * @param[in] initial_credit_size - initial credit size, if 0 is set the firmware takes its default value. * */ hailo_status HEF_METADATA__add_network_boundary_input_edge_layer( @@ -373,7 +363,8 @@ hailo_status HEF_METADATA__add_network_boundary_input_edge_layer( uint8_t vdma_channel_index, uint8_t network_index, const CONTROL_PROTOCOL__nn_stream_config_t &nn_stream_config, - uint16_t desc_page_size); + uint16_t desc_page_size, + uint32_t initial_credit_size); /** * build edge layer - vdma intermediate buffer input @@ -384,9 +375,8 @@ hailo_status HEF_METADATA__add_network_boundary_input_edge_layer( * @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 + * @param[in] host_buffer_info - info about host buffer + * @param[in] initial_credit_size - initial credit size, if 0 is set the firmware takes its default value. * */ hailo_status HEF_METADATA__add_inter_context_input_edge_layer( @@ -396,10 +386,8 @@ hailo_status HEF_METADATA__add_inter_context_input_edge_layer( 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); + const CONTROL_PROTOCOL__host_buffer_info_t &host_buffer_info, + uint32_t initial_credit_size); /** * build edge layer - vdma ddr buffer input @@ -410,11 +398,9 @@ hailo_status HEF_METADATA__add_inter_context_input_edge_layer( * @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 - * + * @param[in] initial_credit_size - initial credit size, if 0 is set the firmware takes its default value. */ hailo_status HEF_METADATA__add_ddr_buffer_input_edge_layer( CONTROL_PROTOCOL__context_switch_context_info_t *context_info, @@ -425,7 +411,7 @@ hailo_status HEF_METADATA__add_ddr_buffer_input_edge_layer( 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); + uint32_t initial_credit_size); /** * Build add ddr pair info action @@ -448,6 +434,7 @@ hailo_status HEF_METADATA__add_ddr_pair_info( const uint32_t descriptors_per_frame, const uint16_t programmed_descriptors_count, bool is_repeated); + /** * Build add ddr buffering start * @@ -462,6 +449,20 @@ hailo_status HEF_METADATA__add_ddr_buffering_start( uint8_t **action_data_current_offset, bool is_repeated); +/** + * Build add burst credits task 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__burst_credits_task_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 index 081ceb7..9ae43b7 100644 --- a/hailort/libhailort/src/context_switch/multi_context/resource_manager.hpp +++ b/hailort/libhailort/src/context_switch/multi_context/resource_manager.hpp @@ -28,9 +28,8 @@ #include "hailo/hailort.h" #include "intermediate_buffer.hpp" -#include "vdma_buffer.hpp" +#include "config_buffer.hpp" #include "vdma_channel.hpp" -#include "vdma_descriptor_list.hpp" #include "control_protocol.hpp" #include "pcie_device.hpp" @@ -124,40 +123,6 @@ private: 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 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: @@ -180,16 +145,20 @@ public: 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)) {} + m_network_index_map(std::move(other.m_network_index_map)), + m_latency_meters(std::move(other.m_latency_meters)), + m_boundary_channels(std::move(other.m_boundary_channels)) {} ExpectedRef create_inter_context_buffer(uint32_t transfer_size, uint8_t src_stream_index, - uint8_t src_context_index, const std::string &partial_network_name); + uint8_t src_context_index, const std::string &network_name); ExpectedRef get_intermediate_buffer(const IntermediateBufferKey &key); - Expected> get_desc_buffer_sizes_for_boundary_channel(uint32_t transfer_size, - const std::string &partial_network_name); + Expected> create_boundary_vdma_channel(uint8_t channel_index, uint32_t transfer_size, + const std::string &network_name, const std::string &stream_name, + VdmaChannel::Direction channel_direction); + ExpectedRef create_ddr_buffer(DdrChannelsInfo &ddr_info, uint8_t context_index); - Expected get_control_network_group_header(); + Expected get_control_network_group_header(bool is_scheduler_used); using context_info_t = CONTROL_PROTOCOL__context_switch_context_info_t; @@ -203,12 +172,12 @@ public: return m_contexts; } - std::vector &preliminary_config() + std::vector &preliminary_config() { return m_preliminary_config; } - std::vector &dynamic_config(uint8_t context_index) + std::vector &dynamic_config(uint8_t context_index) { assert(context_index < m_dynamic_config.size()); return m_dynamic_config[context_index]; @@ -253,6 +222,11 @@ public: return m_vdma_device.get_dev_id(); } + LatencyMetersMap &get_latnecy_meters() + { + return m_latency_meters; + } + Expected get_boundary_channel_index(uint8_t stream_index, hailo_stream_direction_t direction, const std::string &layer_name); Expected get_default_streams_interface(); @@ -260,24 +234,28 @@ public: Expected read_intermediate_buffer(const IntermediateBufferKey &key); hailo_status set_number_of_cfg_channels(const uint8_t number_of_cfg_channels); - static Expected create_config_resources(uint8_t channel_index, - const std::vector &cfg_sizes, HailoRTDriver &driver); void update_preliminary_config_buffer_info(); void update_dynamic_contexts_buffer_info(); - hailo_status create_vdma_channels(); + hailo_status create_internal_vdma_channels(); hailo_status register_fw_managed_vdma_channels(); hailo_status unregister_fw_managed_vdma_channels(); + hailo_status set_inter_context_channels_dynamic_batch_size(uint16_t dynamic_batch_size); hailo_status open_ddr_channels(); void abort_ddr_channels(); void close_ddr_channels(); - hailo_status enable_state_machine(); + hailo_status enable_state_machine(uint16_t dynamic_batch_size); hailo_status reset_state_machine(); - Expected 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); + Expected get_network_batch_size(const std::string &network_name) const; + Expected> get_boundary_vdma_channel_by_stream_name(const std::string &stream_name); + private: - ExpectedRef create_intermediate_buffer(uint32_t transfer_size, uint16_t batch_size, - const IntermediateBufferKey &key); + ExpectedRef create_intermediate_buffer(IntermediateBuffer::ChannelType channel_type, + uint32_t transfer_size, uint16_t batch_size, const IntermediateBufferKey &key); + void update_config_buffer_info(std::vector &config_buffers, + CONTROL_PROTOCOL__context_switch_context_info_t &context); + hailo_status fill_infer_features(CONTROL_PROTOCOL__application_header_t &app_header); + hailo_status fill_network_batch_size(CONTROL_PROTOCOL__application_header_t &app_header, bool is_scheduler_used); std::vector m_ddr_infos; std::vector m_contexts; @@ -285,24 +263,27 @@ private: VdmaDevice &m_vdma_device; HailoRTDriver &m_driver; const ConfigureNetworkParams m_config_params; - std::vector m_preliminary_config; + std::vector m_preliminary_config; // m_dynamic_config[context_index][config_index] - std::vector> m_dynamic_config; - std::map> m_intermediate_buffers; + std::vector> m_dynamic_config; + std::map m_intermediate_buffers; std::vector m_inter_context_channels; std::vector m_config_channels; std::vector m_ddr_buffer_channels; std::shared_ptr m_network_group_metadata; uint8_t m_net_group_index; const std::vector m_network_index_map; + LatencyMetersMap m_latency_meters; // Latency meter per network + std::map> m_boundary_channels; //map of string name and connected vDMA channel ResourcesManager(VdmaDevice &vdma_device, HailoRTDriver &driver, - const ConfigureNetworkParams config_params, std::vector &&preliminary_config, - std::vector> &&dynamic_config, std::shared_ptr &&network_group_metadata, uint8_t net_group_index, - const std::vector &&network_index_map) : + const ConfigureNetworkParams config_params, std::vector &&preliminary_config, + std::vector> &&dynamic_config, std::shared_ptr &&network_group_metadata, uint8_t net_group_index, + const std::vector &&network_index_map, LatencyMetersMap &&latency_meters) : 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)) {}; + 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)), + m_latency_meters(std::move(latency_meters)) {}; }; 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 index 980bee9..e1ff082 100644 --- 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 @@ -30,10 +30,12 @@ public: static Expected create( VdmaConfigActiveAppHolder &active_net_group_holder, + const std::string &network_group_name, std::vector> resources_managers, const hailo_activate_network_group_params_t &network_group_params, + uint16_t dynamic_batch_size, std::map> &input_streams, - std::map> &output_streams, + std::map> &output_streams, EventPtr network_group_activated_event, AccumulatorPtr deactivation_time_accumulator); @@ -44,11 +46,14 @@ public: VdmaConfigActivatedNetworkGroup &operator=(VdmaConfigActivatedNetworkGroup &&other) = delete; VdmaConfigActivatedNetworkGroup(VdmaConfigActivatedNetworkGroup &&other) noexcept; + virtual const std::string &get_network_group_name() const override; virtual Expected get_intermediate_buffer(const IntermediateBufferKey &key) override; private: VdmaConfigActivatedNetworkGroup( + const std::string &network_group_name, const hailo_activate_network_group_params_t &network_group_params, + uint16_t dynamic_batch_size, std::map> &input_streams, std::map> &output_streams, std::vector> &&resources_managers, @@ -64,6 +69,7 @@ private: static void ddr_send_thread_main(DdrChannelsInfo ddr_info, std::shared_ptr> desc_list_num_ready); + std::string m_network_group_name; bool m_should_reset_state_machine; VdmaConfigActiveAppHolder &m_active_net_group_holder; // One ResourcesManager per connected physical device. Currently only one device is supported. 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 index 4dd8d99..1aa9506 100644 --- a/hailort/libhailort/src/context_switch/multi_context/vdma_config_manager.hpp +++ b/hailort/libhailort/src/context_switch/multi_context/vdma_config_manager.hpp @@ -12,6 +12,7 @@ #define HAILO_VDMA_CONFIG_MANAGER_HPP_ #include "context_switch/config_manager.hpp" +#include "context_switch/network_group_wrapper.hpp" #include "context_switch/multi_context/vdma_config_network_group.hpp" #include "hailo/hailort.h" #include "hailo/device.hpp" @@ -20,8 +21,7 @@ #include "common/utils.hpp" #include "hlpcie.hpp" #include "vdma_channel.hpp" -#include "vdma_buffer.hpp" -#include "vdma_descriptor_list.hpp" +#include "network_group_scheduler.hpp" #include #include @@ -38,24 +38,26 @@ public: static Expected create(VDevice &vdevice); virtual ConfigManagerType get_manager_type(); virtual Expected add_hef(Hef &hef, - const NetworkGroupsParamsMap &configure_params={}); + const NetworkGroupsParamsMap &configure_params, bool is_scheduler_used=false); static hailo_status update_network_batch_size(ConfigureNetworkParams &configure_params); - virtual ~VdmaConfigManager() {} + virtual ~VdmaConfigManager() = default; 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> &&devices, bool is_vdevice); + VdmaConfigManager(std::vector> &&devices, bool is_vdevice, NetworkGroupSchedulerWeakPtr network_group_scheduler); // TODO: (SDK-16665) Dont need is_active flag for dtor? std::vector> m_devices; std::vector> m_net_groups; + std::vector> m_net_group_wrappers; VdmaConfigActiveAppHolder m_active_net_group_holder; bool m_is_vdevice; + NetworkGroupSchedulerWeakPtr m_network_group_scheduler; }; } /* namespace hailort */ 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 index f57b130..477b298 100644 --- 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 @@ -13,9 +13,7 @@ #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" @@ -23,6 +21,7 @@ #include "hailort_defaults.hpp" #include "context_switch/network_group_internal.hpp" #include "context_switch/multi_context/resource_manager.hpp" +#include "network_group_scheduler.hpp" #include #include @@ -41,26 +40,33 @@ public: static Expected create(VdmaConfigActiveAppHolder &active_net_group_holder, const ConfigureNetworkParams &config_params, std::vector> resources_managers, - std::shared_ptr network_group_metadata); + std::shared_ptr network_group_metadata, NetworkGroupSchedulerWeakPtr network_group_scheduler); std::vector> &get_resources_managers() { return m_resources_managers; } - hailo_status create_vdevice_streams_from_config_params(); + hailo_status create_vdevice_streams_from_config_params(network_group_handle_t network_group_handle); hailo_status create_output_vdevice_stream_from_config_params( - const hailo_stream_parameters_t &stream_params, const std::string &stream_name); + const hailo_stream_parameters_t &stream_params, const std::string &stream_name, network_group_handle_t network_group_handle); hailo_status create_input_vdevice_stream_from_config_params( - const hailo_stream_parameters_t &stream_params, const std::string &stream_name); + const hailo_stream_parameters_t &stream_params, const std::string &stream_name, network_group_handle_t network_group_handle); - virtual Expected> activate( - const hailo_activate_network_group_params_t &network_group_params = HailoRTDefaults::get_network_group_params()) override; + virtual Expected> activate_impl( + const hailo_activate_network_group_params_t &network_group_params, uint16_t dynamic_batch_size) override; virtual Expected get_default_streams_interface() override; virtual Expected get_boundary_channel_index(uint8_t stream_index, hailo_stream_direction_t direction, const std::string &layer_name) override; + virtual Expected> get_latnecy_meters() override; + virtual Expected> get_boundary_vdma_channel_by_stream_name( + const std::string &stream_name) override; + + void set_network_group_handle(network_group_handle_t handle); + virtual hailo_status set_scheduler_timeout(const std::chrono::milliseconds &timeout, const std::string &network_name) override; + virtual hailo_status set_scheduler_threshold(uint32_t threshold, const std::string &network_name) override; virtual ~VdmaConfigNetworkGroup() = default; VdmaConfigNetworkGroup(const VdmaConfigNetworkGroup &other) = delete; @@ -68,16 +74,19 @@ public: 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)) {} + m_resources_managers(std::move(other.m_resources_managers)), m_network_group_scheduler(std::move(other.m_network_group_scheduler)) {} private: VdmaConfigNetworkGroup(VdmaConfigActiveAppHolder &active_net_group_holder, const ConfigureNetworkParams &config_params, std::vector> &&resources_managers, - const NetworkGroupMetadata &network_group_metadata, hailo_status &status); + const NetworkGroupMetadata &network_group_metadata, NetworkGroupSchedulerWeakPtr network_group_scheduler, hailo_status &status); VdmaConfigActiveAppHolder &m_active_net_group_holder; std::vector> m_resources_managers; + NetworkGroupSchedulerWeakPtr m_network_group_scheduler; + network_group_handle_t m_network_group_handle; + }; } /* namespace hailort */ diff --git a/hailort/libhailort/src/context_switch/network_group.cpp b/hailort/libhailort/src/context_switch/network_group.cpp index 41ce33f..d364754 100644 --- a/hailort/libhailort/src/context_switch/network_group.cpp +++ b/hailort/libhailort/src/context_switch/network_group.cpp @@ -1,3 +1,12 @@ +/** + * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved. + * Distributed under the MIT license (https://opensource.org/licenses/MIT) + **/ +/** + * @file network_group.cpp + * @brief: Configured Network Group and Activated Network Group + **/ + #include "hailo/transform.hpp" #include "network_group_internal.hpp" #include "hef_internal.hpp" @@ -14,7 +23,9 @@ namespace hailort { ActivatedNetworkGroupBase::ActivatedNetworkGroupBase(const hailo_activate_network_group_params_t &network_group_params, - std::map> &input_streams, std::map> &output_streams, + uint16_t dynamic_batch_size, + std::map> &input_streams, + std::map> &output_streams, EventPtr &&network_group_activated_event, hailo_status &status) : m_network_group_params(network_group_params), m_input_streams(input_streams), @@ -27,7 +38,7 @@ ActivatedNetworkGroupBase::ActivatedNetworkGroupBase(const hailo_activate_networ return; } - status = activate_low_level_streams(); + status = activate_low_level_streams(dynamic_batch_size); if (HAILO_SUCCESS != status) { LOGGER__ERROR("Failed to activate low level streams"); return; @@ -40,49 +51,14 @@ ActivatedNetworkGroupBase::ActivatedNetworkGroupBase(const hailo_activate_networ } } -Expected ConfiguredNetworkGroupBase::create_hw_latency_meter(Device &device, - const std::vector &layers) -{ - std::set 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(d2h_channel_indexes, MAX_IRQ_TIMESTAMPS_SIZE); -} - -hailo_status ActivatedNetworkGroupBase::activate_low_level_streams() +hailo_status ActivatedNetworkGroupBase::activate_low_level_streams(uint16_t dynamic_batch_size) { for (auto &name_pair : m_input_streams) { - auto status = name_pair.second->activate_stream(); + auto status = name_pair.second->activate_stream(dynamic_batch_size); CHECK_SUCCESS(status); } for (auto &name_pair : m_output_streams) { - auto status = name_pair.second->activate_stream(); + auto status = name_pair.second->activate_stream(dynamic_batch_size); CHECK_SUCCESS(status); } @@ -132,6 +108,14 @@ Expected> ConfiguredNetworkGroup::activat return activate(network_group_params); } +Expected> ConfiguredNetworkGroupBase::activate( + const hailo_activate_network_group_params_t &network_group_params) +{ + CHECK_AS_EXPECTED(!m_is_scheduling, HAILO_INVALID_OPERATION, + "Manually activating a network group is not allowed when the network group scheduler is active!"); + return activate_internal(network_group_params, CONTROL_PROTOCOL__IGNORE_DYNAMIC_BATCH_SIZE); +} + Expected get_latency(LatencyMeterPtr &latency_meter, bool clear) { auto hw_latency = latency_meter->get_latency(clear); @@ -148,10 +132,14 @@ Expected ConfiguredNetworkGroupBase::get_latency_measu bool clear = ((m_config_params.latency & HAILO_LATENCY_CLEAR_AFTER_GET) == HAILO_LATENCY_CLEAR_AFTER_GET); LatencyMeasurementResult result = {}; + auto latency_meters_exp = get_latnecy_meters(); + CHECK_EXPECTED(latency_meters_exp); + auto latency_meters = latency_meters_exp.release(); + if (network_name.empty()) { std::chrono::nanoseconds latency_sum(0); uint32_t measurements_count = 0; - for (auto &latency_meter_pair : m_latency_meter) { + for (auto &latency_meter_pair : *latency_meters.get()) { auto hw_latency = get_latency(latency_meter_pair.second, clear); if (HAILO_NOT_AVAILABLE == hw_latency.status()) { continue; @@ -166,13 +154,11 @@ Expected ConfiguredNetworkGroupBase::get_latency_measu } 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); + if(!contains(*latency_meters, network_name)) { + LOGGER__DEBUG("No latency measurements was found for network {}", network_name); + return make_unexpected(HAILO_NOT_FOUND); } - auto hw_latency = get_latency(m_latency_meter.at(partial_network_name.value()), clear); + auto hw_latency = get_latency(latency_meters->at(network_name), clear); if (HAILO_NOT_AVAILABLE == hw_latency.status()) { return make_unexpected(HAILO_NOT_AVAILABLE); } @@ -182,8 +168,6 @@ Expected ConfiguredNetworkGroupBase::get_latency_measu return result; } - - Expected ConfiguredNetworkGroupBase::get_output_streams_from_vstream_names( const std::map &outputs_params) { @@ -282,13 +266,19 @@ Expected ConfiguredNetworkGroupBase::get_layer_info(const std::string ConfiguredNetworkGroupBase::ConfiguredNetworkGroupBase( const ConfigureNetworkParams &config_params, const uint8_t net_group_index, const NetworkGroupMetadata &network_group_metadata, hailo_status &status) : - ConfiguredNetworkGroup::ConfiguredNetworkGroup(), + ConfiguredNetworkGroupBase(config_params, net_group_index, network_group_metadata, false, status) +{} + +ConfiguredNetworkGroupBase::ConfiguredNetworkGroupBase( + const ConfigureNetworkParams &config_params, const uint8_t net_group_index, + const NetworkGroupMetadata &network_group_metadata, bool is_scheduling, hailo_status &status) : m_config_params(config_params), + m_min_configured_batch_size(get_smallest_configured_batch_size(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() + m_deactivation_time_accumulator(), + m_is_scheduling(is_scheduling) { auto event = Event::create_shared(Event::State::not_signalled); if (nullptr == event) { @@ -315,6 +305,33 @@ ConfiguredNetworkGroupBase::ConfiguredNetworkGroupBase( status = HAILO_SUCCESS; } +uint16_t ConfiguredNetworkGroupBase::get_smallest_configured_batch_size(const ConfigureNetworkParams &config_params) +{ + // There are two possible situations: + // 1) All networks in the network group have the same configured (and hence smallest) batch_size => + // We return that batch size. + // 2) Not all of the networks have the same configured (and hence smallest) batch_size. Currently, when + // using dynamic_batch_sizes, all networks will use the same dynamic_batch_size (until HRT-6535 is done). + // Hence, we must not set a dynamic_batch_size to a value greater than the smallest configured network + // batch_size (e.g. all the resources allocated are for at most the configured network batch_size). + return std::min_element(config_params.network_params_by_name.begin(), config_params.network_params_by_name.end(), + [](const auto& lhs, const auto& rhs) { return lhs.second.batch_size < rhs.second.batch_size; })->second.batch_size; +} + +Expected> ConfiguredNetworkGroupBase::activate_internal( + const hailo_activate_network_group_params_t &network_group_params, uint16_t dynamic_batch_size) +{ + CHECK_AS_EXPECTED(dynamic_batch_size <= m_min_configured_batch_size, HAILO_INVALID_ARGUMENT, + "Dynamic batch size ({}) must be less than/equal to the smallest configured batch size ({})", + dynamic_batch_size, m_min_configured_batch_size); + return activate_impl(network_group_params, dynamic_batch_size); +} + +Expected> ConfiguredNetworkGroupBase::force_activate(uint16_t dynamic_batch_size) +{ + return activate_internal(HailoRTDefaults::get_network_group_params(), dynamic_batch_size); +} + const std::string &ConfiguredNetworkGroupBase::get_network_group_name() const { return m_network_group_metadata.network_group_name(); @@ -326,11 +343,8 @@ Expected ConfiguredNetworkGroupBase::get_stream_batch_size(const std:: 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) { + if (network_params_pair.first == layer_info.network_name) { auto batch_size = network_params_pair.second.batch_size; return batch_size; } @@ -341,15 +355,17 @@ Expected ConfiguredNetworkGroupBase::get_stream_batch_size(const std:: return make_unexpected(HAILO_NOT_FOUND); } +const ConfigureNetworkParams ConfiguredNetworkGroupBase::get_config_params() const +{ + return m_config_params; +} + 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); @@ -360,11 +376,11 @@ hailo_status ConfiguredNetworkGroupBase::create_input_stream_from_config_params( 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 vdma_channel_ptr = get_boundary_vdma_channel_by_stream_name(stream_name); + CHECK_EXPECTED_AS_STATUS(vdma_channel_ptr, "Failed to get vdma channel for output 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); + auto input_stream = PcieInputStream::create(device, vdma_channel_ptr.release(), + edge_layer.value(), batch_size_exp.value(), m_network_group_activated_event); CHECK_EXPECTED_AS_STATUS(input_stream); m_input_streams.insert(make_pair(stream_name, input_stream.release())); } @@ -374,11 +390,11 @@ hailo_status ConfiguredNetworkGroupBase::create_input_stream_from_config_params( 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 vdma_channel_ptr = get_boundary_vdma_channel_by_stream_name(stream_name); + CHECK_EXPECTED_AS_STATUS(vdma_channel_ptr, "Failed to get vdma channel for output 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); + auto input_stream = CoreInputStream::create(device, vdma_channel_ptr.release(), + edge_layer.value(), batch_size_exp.value(), m_network_group_activated_event); CHECK_EXPECTED_AS_STATUS(input_stream); m_input_streams.insert(make_pair(stream_name, input_stream.release())); } @@ -415,9 +431,6 @@ hailo_status ConfiguredNetworkGroupBase::create_output_stream_from_config_params 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); @@ -428,11 +441,11 @@ hailo_status ConfiguredNetworkGroupBase::create_output_stream_from_config_params 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 vdma_channel_ptr = get_boundary_vdma_channel_by_stream_name(stream_name); + CHECK_EXPECTED_AS_STATUS(vdma_channel_ptr, "Failed to get vdma channel 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); + auto output_stream = PcieOutputStream::create(device, vdma_channel_ptr.release(), + edge_layer.value(), batch_size_exp.value(), m_network_group_activated_event); CHECK_EXPECTED_AS_STATUS(output_stream); m_output_streams.insert(make_pair(stream_name, output_stream.release())); } @@ -442,12 +455,11 @@ hailo_status ConfiguredNetworkGroupBase::create_output_stream_from_config_params 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 vdma_channel_ptr = get_boundary_vdma_channel_by_stream_name(stream_name); + CHECK_EXPECTED_AS_STATUS(vdma_channel_ptr, "Failed to get vdma channel 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); + auto output_stream = CoreOutputStream::create(device, vdma_channel_ptr.release(), + edge_layer.value(), batch_size_exp.value(), m_network_group_activated_event); CHECK_EXPECTED_AS_STATUS(output_stream); m_output_streams.insert(make_pair(stream_name, output_stream.release())); } @@ -473,20 +485,6 @@ hailo_status ConfiguredNetworkGroupBase::create_output_stream_from_config_params 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: @@ -516,10 +514,7 @@ hailo_status ConfiguredNetworkGroupBase::create_streams_from_config_params(Devic Expected 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()); + auto input_stream_infos = m_network_group_metadata.get_input_stream_infos(network_name); CHECK_EXPECTED(input_stream_infos); InputStreamRefVector result; @@ -533,10 +528,7 @@ Expected ConfiguredNetworkGroupBase::get_input_streams_by_ Expected 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()); + auto output_stream_infos = m_network_group_metadata.get_output_stream_infos(network_name); CHECK_EXPECTED(output_stream_infos); OutputStreamRefVector result; @@ -658,10 +650,7 @@ Expected> ConfiguredNetworkGroupBa 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()); + auto input_vstream_infos = m_network_group_metadata.get_input_vstream_infos(network_name); CHECK_EXPECTED(input_vstream_infos); std::map res; @@ -675,10 +664,7 @@ Expected> ConfiguredNetworkGroupBa 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()); + auto output_vstream_infos = m_network_group_metadata.get_output_vstream_infos(network_name); CHECK_EXPECTED(output_vstream_infos); std::map res; @@ -696,37 +682,25 @@ Expected> ConfiguredNetworkGroupBase::get_netw Expected> 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()); + return m_network_group_metadata.get_all_stream_infos(network_name); } Expected> 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()); + return m_network_group_metadata.get_input_vstream_infos(network_name); } Expected> 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()); + return m_network_group_metadata.get_output_vstream_infos(network_name); } Expected> 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()); + return m_network_group_metadata.get_all_vstream_infos(network_name); } AccumulatorPtr ConfiguredNetworkGroupBase::get_activation_time_accumulator() const diff --git a/hailort/libhailort/src/context_switch/network_group_internal.hpp b/hailort/libhailort/src/context_switch/network_group_internal.hpp index bab75ce..d863990 100644 --- a/hailort/libhailort/src/context_switch/network_group_internal.hpp +++ b/hailort/libhailort/src/context_switch/network_group_internal.hpp @@ -5,15 +5,16 @@ /** * @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 + * and ActivatedNetworkGroup interfaces. All internal classes that are relevant should inherit from the * ConfiguredNetworkGroupBase and ActivatedNetworkGroupBase classes. - * Hence, the hiearchy is as follows: + * Hence, the hierarchy is as follows: * ----------------------------------------------------------------------------- - * | ConfiguredNetworkGroup | (External "interface") - * | | | - * | ConfiguredNetworkGroupBase | (Base classes) - * | / \ | - * | VdmaConfigNetworkGroup HcpConfigNetworkGroup | (Actual implementations) + * | ConfiguredNetworkGroup | (External "interface") + * | ________________|___________________ | + * | / \ | + * | ConfiguredNetworkGroupBase ConfiguredNetworkGroupWrapper | (Base classes) + * | / \ | + * | VdmaConfigNetworkGroup HcpConfigNetworkGroup | (Actual implementations) * ----------------------------------------------------------------------------- * | ActivatedNetworkGroup | (External "interface") * | | | @@ -31,6 +32,8 @@ #include "hailo/network_group.hpp" #include "hef_internal.hpp" #include "common/latency_meter.hpp" +#include "control_protocol.h" +#include "vdma_channel.hpp" namespace hailort { @@ -52,12 +55,13 @@ protected: hailo_activate_network_group_params_t m_network_group_params; ActivatedNetworkGroupBase(const hailo_activate_network_group_params_t &network_group_params, + uint16_t dynamic_batch_size, std::map> &input_streams, std::map> &output_streams, EventPtr &&network_group_activated_event, hailo_status &status); private: - hailo_status activate_low_level_streams(); + hailo_status activate_low_level_streams(uint16_t dynamic_batch_size); hailo_status validate_network_group_params(const hailo_activate_network_group_params_t &network_group_params); std::map> &m_input_streams; @@ -74,6 +78,9 @@ public: ConfiguredNetworkGroupBase &operator=(ConfiguredNetworkGroupBase &&other) = delete; ConfiguredNetworkGroupBase(ConfiguredNetworkGroupBase &&other) = default; + Expected> force_activate( + uint16_t dynamic_batch_size = CONTROL_PROTOCOL__IGNORE_DYNAMIC_BATCH_SIZE); + virtual Expected> activate(const hailo_activate_network_group_params_t &network_group_params) override; virtual hailo_status wait_for_activation(const std::chrono::milliseconds &timeout) override; virtual const std::string &get_network_group_name() const override; @@ -126,9 +133,18 @@ public: Expected get_stream_batch_size(const std::string &stream_name); + const ConfigureNetworkParams get_config_params() const; + protected: ConfiguredNetworkGroupBase(const ConfigureNetworkParams &config_params, const uint8_t m_net_group_index, const NetworkGroupMetadata &network_group_metadata, hailo_status &status); + ConfiguredNetworkGroupBase(const ConfigureNetworkParams &config_params, const uint8_t m_net_group_index, + const NetworkGroupMetadata &network_group_metadata, bool is_scheduling, hailo_status &status); + + virtual Expected> activate_internal( + const hailo_activate_network_group_params_t &network_group_params, uint16_t dynamic_batch_size) override; + virtual Expected> activate_impl( + const hailo_activate_network_group_params_t &network_group_params, uint16_t dynamic_batch_size) = 0; hailo_status create_output_stream_from_config_params(Device &device, const hailo_stream_parameters_t &stream_params, const std::string &stream_name); @@ -142,16 +158,25 @@ protected: virtual Expected get_boundary_channel_index(uint8_t stream_index, hailo_stream_direction_t direction, const std::string &layer_name) = 0; + virtual Expected> get_latnecy_meters() = 0; + virtual Expected> get_boundary_vdma_channel_by_stream_name(const std::string &stream_name) = 0; const ConfigureNetworkParams m_config_params; + const uint16_t m_min_configured_batch_size; // TODO: remove after HRT-6535 uint8_t m_net_group_index; - std::map m_latency_meter; // Latency meter per network std::map> m_input_streams; std::map> 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; + +private: + friend class ConfiguredNetworkGroupWrapper; + + static uint16_t get_smallest_configured_batch_size(const ConfigureNetworkParams &config_params); + + bool m_is_scheduling; }; } /* namespace hailort */ diff --git a/hailort/libhailort/src/context_switch/network_group_wrapper.cpp b/hailort/libhailort/src/context_switch/network_group_wrapper.cpp new file mode 100644 index 0000000..3181e5c --- /dev/null +++ b/hailort/libhailort/src/context_switch/network_group_wrapper.cpp @@ -0,0 +1,180 @@ +/** + * Copyright (c) 2022 Hailo Technologies Ltd. All rights reserved. + * Distributed under the MIT license (https://opensource.org/licenses/MIT) + **/ +/** + * @file network_group_wrapper.cpp + * @brief: Network Group Wrapper + **/ + +#include "network_group_wrapper.hpp" + +namespace hailort +{ + +const std::string &ConfiguredNetworkGroupWrapper::get_network_group_name() const +{ + return m_configured_network_group->get_network_group_name(); +} + +Expected ConfiguredNetworkGroupWrapper::get_default_streams_interface() +{ + return m_configured_network_group->get_default_streams_interface(); +} + +std::vector> ConfiguredNetworkGroupWrapper::get_input_streams_by_interface(hailo_stream_interface_t stream_interface) +{ + return m_configured_network_group->get_input_streams_by_interface(stream_interface); +} + +std::vector> ConfiguredNetworkGroupWrapper::get_output_streams_by_interface(hailo_stream_interface_t stream_interface) +{ + return m_configured_network_group->get_output_streams_by_interface(stream_interface); +} + +ExpectedRef ConfiguredNetworkGroupWrapper::get_input_stream_by_name(const std::string& name) +{ + return m_configured_network_group->get_input_stream_by_name(name); +} +ExpectedRef ConfiguredNetworkGroupWrapper::get_output_stream_by_name(const std::string& name) +{ + return m_configured_network_group->get_output_stream_by_name(name); +} + +Expected ConfiguredNetworkGroupWrapper::get_input_streams_by_network(const std::string &network_name) +{ + return m_configured_network_group->get_input_streams_by_network(network_name); +} + +Expected ConfiguredNetworkGroupWrapper::get_output_streams_by_network(const std::string &network_name) +{ + return m_configured_network_group->get_output_streams_by_network(network_name); +} + +InputStreamRefVector ConfiguredNetworkGroupWrapper::get_input_streams() +{ + return m_configured_network_group->get_input_streams(); +} + +OutputStreamRefVector ConfiguredNetworkGroupWrapper::get_output_streams() +{ + return m_configured_network_group->get_output_streams(); +} + +Expected ConfiguredNetworkGroupWrapper::get_latency_measurement(const std::string &network_name) +{ + return m_configured_network_group->get_latency_measurement(network_name); +} + +Expected ConfiguredNetworkGroupWrapper::get_output_streams_from_vstream_names( + const std::map &outputs_params) +{ + return m_configured_network_group->get_output_streams_from_vstream_names(outputs_params); +} + +Expected> ConfiguredNetworkGroupWrapper::activate_internal(const hailo_activate_network_group_params_t &network_group_params, uint16_t dynamic_batch_size) +{ + return m_configured_network_group->activate_internal(network_group_params, dynamic_batch_size); +} + +hailo_status ConfiguredNetworkGroupWrapper::wait_for_activation(const std::chrono::milliseconds &timeout) +{ + return m_configured_network_group->wait_for_activation(timeout); +} + +Expected> ConfiguredNetworkGroupWrapper::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) +{ + return m_configured_network_group->make_input_vstream_params(quantized, format_type, timeout_ms, queue_size, network_name); +} +Expected> ConfiguredNetworkGroupWrapper::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) +{ + return m_configured_network_group->make_output_vstream_params(quantized, format_type, timeout_ms, queue_size, network_name); +} + +Expected>> ConfiguredNetworkGroupWrapper::make_output_vstream_params_groups( + bool quantized, hailo_format_type_t format_type, uint32_t timeout_ms, uint32_t queue_size) +{ + return m_configured_network_group->make_output_vstream_params_groups(quantized, format_type, timeout_ms, queue_size); +} + +Expected>> ConfiguredNetworkGroupWrapper::get_output_vstream_groups() +{ + return m_configured_network_group->get_output_vstream_groups(); +} + +Expected> ConfiguredNetworkGroupWrapper::get_network_infos() const +{ + return m_configured_network_group->get_network_infos(); +} + +Expected> ConfiguredNetworkGroupWrapper::get_all_stream_infos(const std::string &network_name) const +{ + return m_configured_network_group->get_all_stream_infos(network_name); +} + +Expected> ConfiguredNetworkGroupWrapper::get_input_vstream_infos(const std::string &network_name) const +{ + return m_configured_network_group->get_input_vstream_infos(network_name); +} + +Expected> ConfiguredNetworkGroupWrapper::get_output_vstream_infos(const std::string &network_name) const +{ + return m_configured_network_group->get_output_vstream_infos(network_name); +} + +Expected> ConfiguredNetworkGroupWrapper::get_all_vstream_infos(const std::string &network_name) const +{ + return m_configured_network_group->get_all_vstream_infos(network_name); +} + +hailo_status ConfiguredNetworkGroupWrapper::set_scheduler_timeout(const std::chrono::milliseconds &timeout, const std::string &network_name) +{ + return m_configured_network_group->set_scheduler_timeout(timeout, network_name); +} +hailo_status ConfiguredNetworkGroupWrapper::set_scheduler_threshold(uint32_t threshold, const std::string &network_name) +{ + return m_configured_network_group->set_scheduler_threshold(threshold, network_name); +} + +AccumulatorPtr ConfiguredNetworkGroupWrapper::get_activation_time_accumulator() const +{ + return m_configured_network_group->get_activation_time_accumulator(); +} + +AccumulatorPtr ConfiguredNetworkGroupWrapper::get_deactivation_time_accumulator() const +{ + return m_configured_network_group->get_deactivation_time_accumulator(); +} + +std::shared_ptr ConfiguredNetworkGroupWrapper::get_configured_network() const +{ + return m_configured_network_group; +} + +ConfiguredNetworkGroupWrapper::ConfiguredNetworkGroupWrapper(std::shared_ptr configured_network_group) : + m_configured_network_group(configured_network_group) +{} + +Expected ConfiguredNetworkGroupWrapper::create(std::shared_ptr configured_network_group) +{ + return ConfiguredNetworkGroupWrapper(configured_network_group); +} + +Expected ConfiguredNetworkGroupWrapper::clone() +{ + auto wrapper = create(m_configured_network_group); + CHECK_EXPECTED(wrapper); + + return wrapper; +} + +Expected> ConfiguredNetworkGroupWrapper::activate(const hailo_activate_network_group_params_t &network_group_params) +{ + return m_configured_network_group->activate(network_group_params); +} + +} /* namespace hailort */ diff --git a/hailort/libhailort/src/context_switch/network_group_wrapper.hpp b/hailort/libhailort/src/context_switch/network_group_wrapper.hpp new file mode 100644 index 0000000..a23f6ac --- /dev/null +++ b/hailort/libhailort/src/context_switch/network_group_wrapper.hpp @@ -0,0 +1,87 @@ +/** + * Copyright (c) 2022 Hailo Technologies Ltd. All rights reserved. + * Distributed under the MIT license (https://opensource.org/licenses/MIT) + **/ +/** + * @file network_group_wrapper.hpp + * @brief Class declaration for ConfiguredNetworkGroupWrapper, a wrapper around ConfiguredNetworkGroupBase which is used + * to support multiple ConfiguredNetworkGroup objects that encapsulate the same actual configured network group. + **/ + +#ifndef _HAILO_NETWORK_GROUP_WRAPPER_HPP_ +#define _HAILO_NETWORK_GROUP_WRAPPER_HPP_ + +#include "hailo/hailort.h" +#include "hailo/network_group.hpp" +#include "network_group_internal.hpp" + +namespace hailort +{ + +class ConfiguredNetworkGroupWrapper : public ConfiguredNetworkGroup +{ +public: + virtual ~ConfiguredNetworkGroupWrapper() = default; + ConfiguredNetworkGroupWrapper(const ConfiguredNetworkGroupWrapper &other) = delete; + ConfiguredNetworkGroupWrapper &operator=(const ConfiguredNetworkGroupWrapper &other) = delete; + ConfiguredNetworkGroupWrapper &operator=(ConfiguredNetworkGroupWrapper &&other) = delete; + ConfiguredNetworkGroupWrapper(ConfiguredNetworkGroupWrapper &&other) = default; + + static Expected create(std::shared_ptr configured_network_group); + Expected clone(); + + virtual const std::string &get_network_group_name() const override; + virtual Expected get_default_streams_interface() override; + + virtual std::vector> get_input_streams_by_interface(hailo_stream_interface_t stream_interface) override; + virtual std::vector> get_output_streams_by_interface(hailo_stream_interface_t stream_interface) override; + virtual ExpectedRef get_input_stream_by_name(const std::string& name) override; + virtual ExpectedRef get_output_stream_by_name(const std::string& name) override; + virtual Expected get_input_streams_by_network(const std::string &network_name="") override; + virtual Expected get_output_streams_by_network(const std::string &network_name="") override; + virtual InputStreamRefVector get_input_streams() override; + virtual OutputStreamRefVector get_output_streams() override; + virtual Expected get_latency_measurement(const std::string &network_name="") override; + virtual Expected get_output_streams_from_vstream_names( + const std::map &outputs_params) override; + + virtual Expected> activate(const hailo_activate_network_group_params_t &network_group_params) override; + virtual hailo_status wait_for_activation(const std::chrono::milliseconds &timeout) override; + + virtual Expected> 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> 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>> make_output_vstream_params_groups( + bool quantized, hailo_format_type_t format_type, uint32_t timeout_ms, uint32_t queue_size) override; + virtual Expected>> get_output_vstream_groups() override; + + virtual Expected> get_network_infos() const override; + virtual Expected> get_all_stream_infos(const std::string &network_name="") const override; + virtual Expected> get_input_vstream_infos(const std::string &network_name="") const override; + virtual Expected> get_output_vstream_infos(const std::string &network_name="") const override; + virtual Expected> get_all_vstream_infos(const std::string &network_name="") const override; + + virtual hailo_status set_scheduler_timeout(const std::chrono::milliseconds &timeout, const std::string &network_name) override; + virtual hailo_status set_scheduler_threshold(uint32_t threshold, const std::string &network_name) override; + + virtual AccumulatorPtr get_activation_time_accumulator() const override; + virtual AccumulatorPtr get_deactivation_time_accumulator() const override; + + std::shared_ptr get_configured_network() const; + +protected: + virtual Expected> activate_internal( + const hailo_activate_network_group_params_t &network_group_params, uint16_t dynamic_batch_size) override; + +private: + ConfiguredNetworkGroupWrapper(std::shared_ptr configured_network_group); + + std::shared_ptr m_configured_network_group; +}; + +} + +#endif /* _HAILO_NETWORK_GROUP_WRAPPER_HPP_ */ \ No newline at end of file diff --git a/hailort/libhailort/src/context_switch/pipeline_multiplexer.cpp b/hailort/libhailort/src/context_switch/pipeline_multiplexer.cpp new file mode 100644 index 0000000..c39d88c --- /dev/null +++ b/hailort/libhailort/src/context_switch/pipeline_multiplexer.cpp @@ -0,0 +1,74 @@ +/** + * Copyright (c) 2022 Hailo Technologies Ltd. All rights reserved. + * Distributed under the MIT license (https://opensource.org/licenses/MIT) + **/ +/** + * @file pipeline_multiplexer.cpp + * @brief: Pipeline Multiplexer + **/ + +#include "pipeline_multiplexer.hpp" +#include "common/utils.hpp" + +namespace hailort +{ + +PipelineMultiplexer::PipelineMultiplexer() : + m_is_enabled(false), + m_mutex(), + m_readers(), + m_write_mutex() +{} + +hailo_status PipelineMultiplexer::add_reader(EventPtr &reader_event) +{ + std::unique_lock lock(m_mutex); + + auto was_empty = m_readers.empty(); + m_readers.push(reader_event); + if (was_empty) { + auto status = reader_event->signal(); + CHECK_SUCCESS(status); + } + + return HAILO_SUCCESS; +} + +hailo_status PipelineMultiplexer::signal_done_reading() +{ + std::unique_lock lock(m_mutex); + + if (m_readers.empty()) { + return HAILO_INTERNAL_FAILURE; + } + m_readers.front()->reset(); + m_readers.pop(); + + if (!m_readers.empty()) { + m_readers.front()->signal(); + } + + return HAILO_SUCCESS; +} + +void PipelineMultiplexer::enable() +{ + m_is_enabled = true; +} + +bool PipelineMultiplexer::is_enabled() const +{ + return m_is_enabled; +} + +void PipelineMultiplexer::acquire_write_lock() +{ + m_write_mutex.lock(); +} + +void PipelineMultiplexer::release_write_lock() +{ + m_write_mutex.unlock(); +} + +} /* namespace hailort */ diff --git a/hailort/libhailort/src/context_switch/pipeline_multiplexer.hpp b/hailort/libhailort/src/context_switch/pipeline_multiplexer.hpp new file mode 100644 index 0000000..74a5fb8 --- /dev/null +++ b/hailort/libhailort/src/context_switch/pipeline_multiplexer.hpp @@ -0,0 +1,52 @@ +/** + * Copyright (c) 2022 Hailo Technologies Ltd. All rights reserved. + * Distributed under the MIT license (https://opensource.org/licenses/MIT) + **/ +/** + * @file pipeline_multiplexer.hpp + * @brief The pipeline multiplexer is a synchronization mechanism that allows communication + * between different pipelines that use the same low-level streams. + **/ + +#ifndef _HAILO_PIPELINE_MULTIPLEXER_HPP_ +#define _HAILO_PIPELINE_MULTIPLEXER_HPP_ + +#include "hailo/event.hpp" + +#include +#include + +namespace hailort +{ + +class PipelineMultiplexer +{ +public: + virtual ~PipelineMultiplexer() = default; + PipelineMultiplexer(const PipelineMultiplexer &other) = delete; + PipelineMultiplexer &operator=(const PipelineMultiplexer &other) = delete; + PipelineMultiplexer &operator=(PipelineMultiplexer &&other) = delete; + PipelineMultiplexer(PipelineMultiplexer &&other) = delete; + + PipelineMultiplexer(); + + hailo_status add_reader(EventPtr &reader_event); + hailo_status signal_done_reading(); + + void enable(); + bool is_enabled() const; + + void acquire_write_lock(); + void release_write_lock(); + +private: + bool m_is_enabled; + std::mutex m_mutex; + std::queue m_readers; + + std::mutex m_write_mutex; +}; + +} /* namespace hailort */ + +#endif /* _HAILO_PIPELINE_MULTIPLEXER_HPP_ */ diff --git a/hailort/libhailort/src/context_switch/resource_manager.cpp b/hailort/libhailort/src/context_switch/resource_manager.cpp index 35b6c18..141be48 100644 --- a/hailort/libhailort/src/context_switch/resource_manager.cpp +++ b/hailort/libhailort/src/context_switch/resource_manager.cpp @@ -6,100 +6,77 @@ 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 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(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 ResourcesManager::create_config_resources(uint8_t channel_index, - const std::vector &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> build_network_index_map(ProtoHEFNetworkGroupPtr network_group_proto, const NetworkGroupSupportedFeatures &supported_features) { - std::vector partial_network_name_vector; + std::vector network_names_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); + network_names_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); + auto network_name = HefUtils::get_network_name(*network_group_proto, partial_network_name); + network_names_vector.push_back(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); + network_names_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); + network_names_vector.push_back(HailoRTDefaults::get_network_name(net_group_name)); } - return partial_network_name_vector; + return network_names_vector; +} + +static Expected create_hw_latency_meter(const std::vector &layers) +{ + std::set d2h_channel_indexes; + + 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(d2h_channel_indexes, MAX_IRQ_TIMESTAMPS_SIZE); +} + +static Expected create_latency_meters_from_config_params( + const ConfigureNetworkParams &config_params, std::shared_ptr network_group_metadata) +{ + LatencyMetersMap latency_meters_map; + + if ((config_params.latency & HAILO_LATENCY_MEASURE) == HAILO_LATENCY_MEASURE) { + // Best affort for starting latency meter. + auto networks_names = network_group_metadata->get_network_names(); + for (auto &network_name : networks_names) { + auto layer_infos = network_group_metadata->get_all_layer_infos(network_name); + CHECK_EXPECTED(layer_infos); + auto latency_meter = create_hw_latency_meter(layer_infos.value()); + if (latency_meter) { + latency_meters_map.emplace(network_name, latency_meter.release()); + LOGGER__DEBUG("Starting hw latency measurement for network {}", network_name); + } + } + } + + return latency_meters_map; } Expected ResourcesManager::create(VdmaDevice &vdma_device, HailoRTDriver &driver, @@ -115,7 +92,7 @@ Expected ResourcesManager::create(VdmaDevice &vdma_device, Hai uint8_t cfg_channels_count = (0 == network_group_proto->network_group_metadata().cfg_channels_count()) ? 1u : static_cast(network_group_proto->network_group_metadata().cfg_channels_count()); - std::vector preliminary_configs_vector; + std::vector 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 ({})", @@ -124,18 +101,18 @@ Expected ResourcesManager::create(VdmaDevice &vdma_device, Hai 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); + auto buffer_resource = ConfigBuffer::create(driver, cfg_index, + parsing_info.cfg_infos_preliminary_config.at(cfg_index)); CHECK_EXPECTED(buffer_resource); preliminary_configs_vector.emplace_back(buffer_resource.release()); } - std::vector> dynamic_cfg_vectors; + std::vector> 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 dynamic_cfg_vector_per_context; + std::vector 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, @@ -146,8 +123,8 @@ Expected ResourcesManager::create(VdmaDevice &vdma_device, Hai 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); + auto buffer_resource = ConfigBuffer::create(driver, cfg_index, + parsing_info.cfg_infos_per_context[ctxt_index].at(cfg_index)); CHECK_EXPECTED(buffer_resource); dynamic_cfg_vector_per_context.emplace_back(buffer_resource.release()); @@ -157,9 +134,12 @@ Expected ResourcesManager::create(VdmaDevice &vdma_device, Hai auto network_index_map = build_network_index_map(network_group_proto, network_group_metadata->supported_features()); CHECK_EXPECTED(network_index_map); + + auto latency_meters = create_latency_meters_from_config_params(config_params, network_group_metadata); + CHECK_EXPECTED(latency_meters); 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())); + std::move(network_index_map.release()), latency_meters.release()); auto status = resources_manager.set_number_of_cfg_channels(cfg_channels_count); CHECK_SUCCESS_AS_EXPECTED(status); @@ -167,22 +147,27 @@ Expected ResourcesManager::create(VdmaDevice &vdma_device, Hai return resources_manager; } -hailo_status ResourcesManager::fill_network_batch_size(CONTROL_PROTOCOL__application_header_t &app_header) +hailo_status ResourcesManager::fill_infer_features(CONTROL_PROTOCOL__application_header_t &app_header) +{ + app_header.infer_features.preliminary_run_asap = m_network_group_metadata->supported_features().preliminary_run_asap; + return HAILO_SUCCESS; +} + +hailo_status ResourcesManager::fill_network_batch_size(CONTROL_PROTOCOL__application_header_t &app_header, bool is_scheduler_used) { app_header.networks_count = static_cast(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; + auto network_name_from_params = 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; + auto const network_name_from_map = m_network_index_map[network_index]; + if (network_name_from_map == network_name_from_params) { + app_header.batch_size[network_index] = (is_scheduler_used) ? HAILO_DEFAULT_BATCH_SIZE : 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); + LOGGER__ERROR("Failed to find network with network name {}", network_name_from_params); return HAILO_NOT_FOUND; } } @@ -190,7 +175,7 @@ hailo_status ResourcesManager::fill_network_batch_size(CONTROL_PROTOCOL__applica return HAILO_SUCCESS; } -hailo_status ResourcesManager::create_vdma_channels() +hailo_status ResourcesManager::create_internal_vdma_channels() { std::vector intermediate_channels_idx; std::vector cfg_channels_idx; @@ -234,15 +219,67 @@ hailo_status ResourcesManager::create_vdma_channels() return HAILO_SUCCESS; } -ExpectedRef 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) +Expected> ResourcesManager::create_boundary_vdma_channel( + uint8_t channel_index, uint32_t transfer_size, const std::string &network_name, const std::string &stream_name, + VdmaChannel::Direction channel_direction) { - auto network_batch_size_exp = get_network_batch_size_from_partial_name(partial_network_name); + auto network_batch_size = get_network_batch_size(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"); + + auto edge_layer = m_network_group_metadata->get_layer_info_by_stream_name(stream_name); + CHECK_EXPECTED(edge_layer); + auto latency_meter = (contains(m_latency_meters, edge_layer->network_name)) ? m_latency_meters.at(edge_layer->network_name) : nullptr; + auto stream_index = edge_layer.value().index; + + /* TODO - HRT-6829- page_size should be calculated inside the vDMA channel class create function */ + auto desc_sizes_pair = VdmaDescriptorList::get_desc_buffer_sizes_for_single_transfer(m_driver, + static_cast(min_active_trans), static_cast(max_active_trans), transfer_size); + CHECK_EXPECTED(desc_sizes_pair); + + const auto page_size = desc_sizes_pair->first; + const auto descs_count = desc_sizes_pair->second; + auto channel = VdmaChannel::create(channel_index, channel_direction, m_driver, page_size, + stream_index, latency_meter, network_batch_size.value()); + CHECK_EXPECTED(channel); + const auto status = channel->allocate_resources(descs_count); + CHECK_SUCCESS_AS_EXPECTED(status); + + auto channel_ptr = make_shared_nothrow(channel.release()); + CHECK_NOT_NULL_AS_EXPECTED(channel_ptr, HAILO_OUT_OF_HOST_MEMORY); + + m_boundary_channels.emplace(stream_name, channel_ptr); + + return channel_ptr; +} + +Expected> ResourcesManager::get_boundary_vdma_channel_by_stream_name(const std::string &stream_name) +{ + auto boundary_channel_it = m_boundary_channels.find(stream_name); + if (std::end(m_boundary_channels) == boundary_channel_it) { + return make_unexpected(HAILO_NOT_FOUND); + } + + return std::shared_ptr(m_boundary_channels[stream_name]); +} + +ExpectedRef ResourcesManager::create_inter_context_buffer(uint32_t transfer_size, + uint8_t src_stream_index, uint8_t src_context_index, const std::string &network_name) +{ + auto network_batch_size_exp = get_network_batch_size(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); + auto intermediate_buffer = create_intermediate_buffer(IntermediateBuffer::ChannelType::INTER_CONTEXT, transfer_size, + network_batch_size, intermediate_buffer_key); CHECK_EXPECTED(intermediate_buffer); auto intermediate_buffer_ref = intermediate_buffer.release(); @@ -259,51 +296,25 @@ ExpectedRef ResourcesManager::get_intermediate_buffer(const return make_unexpected(HAILO_NOT_FOUND); } - return std::ref(*intermediate_buffer_it->second); + return std::ref(intermediate_buffer_it->second); } -Expected> 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(min_active_trans), static_cast(max_active_trans), transfer_size); -} - -Expected ResourcesManager::get_control_network_group_header() +Expected ResourcesManager::get_control_network_group_header(bool is_scheduler_used) { CONTROL_PROTOCOL__application_header_t app_header = {}; app_header.dynamic_contexts_count = static_cast(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(host_boundary_channels_bitmap_local); - app_header.host_ddr_channels_bitmap = static_cast(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++) { @@ -314,9 +325,9 @@ Expected ResourcesManager::get_control_n cfg_handle_idx++; } } - app_header.cfg_channels_count = cfg_handle_idx; app_header.power_mode = static_cast(m_config_params.power_mode); - fill_network_batch_size(app_header); + CHECK_SUCCESS_AS_EXPECTED(fill_infer_features(app_header), "Invalid infer features"); + CHECK_SUCCESS_AS_EXPECTED(fill_network_batch_size(app_header, is_scheduler_used), "Invalid network batch sizes"); return app_header; } @@ -334,6 +345,18 @@ Expected ResourcesManager::get_available_channel_index(std::set ResourcesManager::get_available_channel_index(std::set ResourcesManager::get_boundary_channel_index(uint8_t stream_in 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++; - } + update_config_buffer_info(m_preliminary_config, m_contexts[0]); } void ResourcesManager::update_dynamic_contexts_buffer_info() @@ -399,14 +406,8 @@ 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++; - } + for (auto &cfg_context : m_dynamic_config) { + update_config_buffer_info(cfg_context, m_contexts[ctxt_index]); ctxt_index++; } } @@ -420,7 +421,7 @@ ExpectedRef ResourcesManager::create_ddr_buffer(DdrChannelsI 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(number_of_transfers), + return create_intermediate_buffer(IntermediateBuffer::ChannelType::DDR, transfer_size, static_cast(number_of_transfers), intermediate_buffer_key); } @@ -462,12 +463,9 @@ hailo_status ResourcesManager::register_fw_managed_vdma_channels() 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); - } + for (auto &ch : m_ddr_buffer_channels) { + status = ch.register_fw_controlled_channel(); + CHECK_SUCCESS(status); } return HAILO_SUCCESS; @@ -488,28 +486,34 @@ hailo_status ResourcesManager::unregister_fw_managed_vdma_channels() 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); - } + for (auto &ch : m_ddr_buffer_channels) { + status = ch.unregister_fw_controlled_channel(); + CHECK_SUCCESS(status); } return HAILO_SUCCESS; } -Expected ResourcesManager::get_network_batch_size_from_partial_name(const std::string &partial_network_name) const +hailo_status ResourcesManager::set_inter_context_channels_dynamic_batch_size(uint16_t dynamic_batch_size) +{ + for (auto &key_buff_pair : m_intermediate_buffers) { + const auto status = key_buff_pair.second.reprogram_inter_context(dynamic_batch_size); + CHECK_SUCCESS(status); + } + + return HAILO_SUCCESS; +} + +Expected ResourcesManager::get_network_batch_size(const std::string &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) { + auto const network_name_from_params = network_map.first; + if (network_name_from_params == network_name) { return Expected(network_map.second.batch_size); } } - LOGGER__ERROR("Failed to find network with network name {}", partial_network_name); + LOGGER__ERROR("Failed to find network with network name {}", network_name); return make_unexpected(HAILO_NOT_FOUND); } @@ -523,11 +527,11 @@ Expected ResourcesManager::read_intermediate_buffer(const IntermediateBu return make_unexpected(HAILO_NOT_FOUND); } - auto &intermediate_buffer = *intermediate_buffer_it->second; + auto &intermediate_buffer = intermediate_buffer_it->second; return intermediate_buffer.read(); } -hailo_status ResourcesManager::enable_state_machine() +hailo_status ResourcesManager::enable_state_machine(uint16_t dynamic_batch_size) { if (Device::Type::CORE == m_vdma_device.get_type()) { // On core device, the nn_manager is not responsible to reset the nn-core so @@ -536,7 +540,7 @@ hailo_status ResourcesManager::enable_state_machine() CHECK_SUCCESS(status); } - return Control::enable_network_group(m_vdma_device, m_net_group_index); + return Control::enable_network_group(m_vdma_device, m_net_group_index, dynamic_batch_size); } hailo_status ResourcesManager::reset_state_machine() @@ -544,49 +548,35 @@ 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 ResourcesManager::create_intermediate_buffer(uint32_t transfer_size, uint16_t batch_size, +ExpectedRef ResourcesManager::create_intermediate_buffer( + IntermediateBuffer::ChannelType channel_type, 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); + auto intermediate_buffer = IntermediateBuffer::create(m_driver, channel_type, 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); + return std::ref(emplace_res.first->second); +} + +void ResourcesManager::update_config_buffer_info(std::vector &config_buffers, + CONTROL_PROTOCOL__context_switch_context_info_t &context) +{ + assert(CONTROL_PROTOCOL__MAX_CFG_CHANNELS >= config_buffers.size()); + context.cfg_channels_count = static_cast(config_buffers.size()); + + auto i = 0; + for (const auto &config : config_buffers) { + context.config_buffer_infos[i].buffer_type = static_cast( + (config.buffer_type() == vdma::VdmaBuffer::Type::SCATTER_GATHER) ? + CONTROL_PROTOCOL__HOST_BUFFER_TYPE_EXTERNAL_DESC : CONTROL_PROTOCOL__HOST_BUFFER_TYPE_CCB); + context.config_buffer_infos[i].dma_address = config.dma_address(); + context.config_buffer_infos[i].desc_page_size = config.desc_page_size(); + context.config_buffer_infos[i].total_desc_count = config.total_desc_count(); + context.config_buffer_infos[i].bytes_in_pattern = config.acc_desc_count() * config.desc_page_size(); + + i++; + } } } /* 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 index 05a90b0..a81871d 100644 --- 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 @@ -36,6 +36,7 @@ class HcpConfigActivatedNetworkGroup : public ActivatedNetworkGroupBase { public: static Expected create(Device &device, std::vector &config, + const std::string &network_group_name, const hailo_activate_network_group_params_t &network_group_params, std::map> &input_streams, std::map> &output_streams, @@ -49,7 +50,9 @@ class HcpConfigActivatedNetworkGroup : public ActivatedNetworkGroupBase 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) {}; + m_device(other.m_device), m_network_group_name(std::move(other.m_network_group_name)) {}; + + virtual const std::string &get_network_group_name() const override; virtual Expected get_intermediate_buffer(const IntermediateBufferKey &/*key*/) override { @@ -60,6 +63,7 @@ class HcpConfigActivatedNetworkGroup : public ActivatedNetworkGroupBase private: HcpConfigActivatedNetworkGroup(Device &device, HcpConfigActiveAppHolder &active_net_group_holder, + const std::string &network_group_name, const hailo_activate_network_group_params_t &network_group_params, std::map> &input_streams, std::map> &output_streams, @@ -69,6 +73,7 @@ class HcpConfigActivatedNetworkGroup : public ActivatedNetworkGroupBase bool m_is_active; hailo_power_mode_t m_power_mode; Device &m_device; + std::string m_network_group_name; }; } /* namespace hailort */ 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 index 5ccc4cb..e463edb 100644 --- a/hailort/libhailort/src/context_switch/single_context/hcp_config_manager.hpp +++ b/hailort/libhailort/src/context_switch/single_context/hcp_config_manager.hpp @@ -13,6 +13,7 @@ #define HAILO_HCP_CONFIG_MANAGER_HPP_ #include "context_switch/config_manager.hpp" +#include "context_switch/network_group_wrapper.hpp" #include "context_switch/single_context/hcp_config_network_group.hpp" #include "hailo/hailort.h" #include "hailo/device.hpp" @@ -33,7 +34,7 @@ public: virtual ConfigManagerType get_manager_type(); virtual Expected add_hef(Hef &hef, - const NetworkGroupsParamsMap &configure_params={}); + const NetworkGroupsParamsMap &configure_params, bool is_scheduler_used = false); HcpConfigManager(const HcpConfigManager &other) = delete; HcpConfigManager &operator=(const HcpConfigManager &other) = delete; @@ -43,6 +44,7 @@ public: private: // TODO: (SDK-16665) Dont need is_active flag for dtor? std::vector> m_net_groups; + std::vector> m_net_group_wrappers; Device &m_device; HcpConfigActiveAppHolder m_active_net_group_holder; }; 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 index 378d67e..c346381 100644 --- 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 @@ -35,12 +35,17 @@ public: const ConfigureNetworkParams &config_params, uint8_t net_group_index, NetworkGroupMetadata &&network_group_metadata, hailo_status &status); - virtual Expected> activate( - const hailo_activate_network_group_params_t &network_group_params = HailoRTDefaults::get_network_group_params()) override; + virtual Expected> activate_impl( + const hailo_activate_network_group_params_t &network_group_params, uint16_t dynamic_batch_size) override; virtual Expected get_default_streams_interface() override; virtual Expected get_boundary_channel_index(uint8_t stream_index, hailo_stream_direction_t direction, const std::string &layer_name) override; + virtual Expected> get_latnecy_meters() override; + virtual Expected> get_boundary_vdma_channel_by_stream_name( + const std::string &stream_name) override; + virtual hailo_status set_scheduler_timeout(const std::chrono::milliseconds &timeout, const std::string &network_name) override; + virtual hailo_status set_scheduler_threshold(uint32_t threshold, const std::string &network_name) override; virtual ~HcpConfigNetworkGroup() = default; HcpConfigNetworkGroup(const HcpConfigNetworkGroup &other) = delete; 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 index bbf47a8..20dac3c 100644 --- a/hailort/libhailort/src/context_switch/vdma_config_activated_network_group.cpp +++ b/hailort/libhailort/src/context_switch/vdma_config_activated_network_group.cpp @@ -16,8 +16,10 @@ namespace hailort Expected VdmaConfigActivatedNetworkGroup::create( VdmaConfigActiveAppHolder &active_net_group_holder, + const std::string &network_group_name, std::vector> resources_managers, const hailo_activate_network_group_params_t &network_group_params, + uint16_t dynamic_batch_size, std::map> &input_streams, std::map> &output_streams, EventPtr network_group_activated_event, @@ -29,7 +31,7 @@ Expected VdmaConfigActivatedNetworkGroup::creat CHECK_ARG_NOT_NULL_AS_EXPECTED(deactivation_time_accumulator); auto status = HAILO_UNINITIALIZED; - VdmaConfigActivatedNetworkGroup object(network_group_params, input_streams, output_streams, + VdmaConfigActivatedNetworkGroup object(network_group_name, network_group_params, dynamic_batch_size, 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); @@ -38,22 +40,25 @@ Expected VdmaConfigActivatedNetworkGroup::creat } VdmaConfigActivatedNetworkGroup::VdmaConfigActivatedNetworkGroup( - const hailo_activate_network_group_params_t &network_group_params, - std::map> &input_streams, - std::map> &output_streams, - std::vector> &&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) + const std::string &network_group_name, + const hailo_activate_network_group_params_t &network_group_params, + uint16_t dynamic_batch_size, + std::map> &input_streams, + std::map> &output_streams, + std::vector> &&resources_managers, + VdmaConfigActiveAppHolder &active_net_group_holder, + EventPtr &&network_group_activated_event, + AccumulatorPtr deactivation_time_accumulator, + hailo_status &status) : + ActivatedNetworkGroupBase(network_group_params, dynamic_batch_size, input_streams, + output_streams, std::move(network_group_activated_event), status), + m_network_group_name(network_group_name), + 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) { @@ -67,19 +72,16 @@ VdmaConfigActivatedNetworkGroup::VdmaConfigActivatedNetworkGroup( 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(); + status = resources_manager->set_inter_context_channels_dynamic_batch_size(dynamic_batch_size); if (HAILO_SUCCESS != status) { - LOGGER__ERROR("Failed to initialize DDR resources."); + LOGGER__ERROR("Failed to set inter-context channels dynamic batch size."); return; } } for (auto &resources_manager : m_resources_managers) { - status = resources_manager->enable_state_machine(); + status = resources_manager->enable_state_machine(dynamic_batch_size); if (HAILO_SUCCESS != status) { LOGGER__ERROR("Failed to activate state-machine"); return; @@ -89,6 +91,7 @@ VdmaConfigActivatedNetworkGroup::VdmaConfigActivatedNetworkGroup( VdmaConfigActivatedNetworkGroup::VdmaConfigActivatedNetworkGroup(VdmaConfigActivatedNetworkGroup &&other) noexcept : ActivatedNetworkGroupBase(std::move(other)), + m_network_group_name(std::move(other.m_network_group_name)), 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)), @@ -106,15 +109,6 @@ VdmaConfigActivatedNetworkGroup::~VdmaConfigActivatedNetworkGroup() 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(); @@ -138,6 +132,11 @@ VdmaConfigActivatedNetworkGroup::~VdmaConfigActivatedNetworkGroup() m_deactivation_time_accumulator->add_data_point(elapsed_time_ms); } +const std::string &VdmaConfigActivatedNetworkGroup::get_network_group_name() const +{ + return m_network_group_name; +} + Expected VdmaConfigActivatedNetworkGroup::get_intermediate_buffer(const IntermediateBufferKey &key) { CHECK_AS_EXPECTED(1 == m_resources_managers.size(), HAILO_INVALID_OPERATION, @@ -146,184 +145,4 @@ Expected VdmaConfigActivatedNetworkGroup::get_intermediate_buffer(const 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>(static_cast(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> 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(hw_num_proc.value() - last_num_proc) : - static_cast(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((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> 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(hw_num_proc.value() - last_num_proc) : - static_cast(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 index 0a3bc7e..dd87f5c 100644 --- a/hailort/libhailort/src/context_switch/vdma_config_manager.cpp +++ b/hailort/libhailort/src/context_switch/vdma_config_manager.cpp @@ -23,6 +23,7 @@ #include "hailo/hef.hpp" #include "control.hpp" #include "hailort_defaults.hpp" +#include "vdevice_internal.hpp" #include "pcie_device.hpp" #include "hlpcie.hpp" @@ -38,7 +39,9 @@ Expected VdmaConfigManager::create(VdmaDevice &device) const bool is_vdevice = false; std::vector> devices; devices.push_back(device); - VdmaConfigManager manager(std::move(devices), is_vdevice); + + auto empty_weak_ptr = NetworkGroupSchedulerWeakPtr(); + VdmaConfigManager manager(std::move(devices), is_vdevice, empty_weak_ptr); return manager; } @@ -56,16 +59,15 @@ Expected VdmaConfigManager::create(VDevice &vdevice) pcie_devices.emplace_back(static_cast(dev.get())); } - - VdmaConfigManager manager(std::move(pcie_devices), is_vdevice); + VdmaConfigManager manager(std::move(pcie_devices), is_vdevice, static_cast(vdevice).network_group_scheduler()); return manager; } -VdmaConfigManager::VdmaConfigManager(std::vector> &&devices, bool is_vdevice) - : m_devices(std::move(devices)), m_net_groups(), m_is_vdevice(is_vdevice) {} +VdmaConfigManager::VdmaConfigManager(std::vector> &&devices, bool is_vdevice, NetworkGroupSchedulerWeakPtr network_group_scheduler) + : m_devices(std::move(devices)), m_net_groups(), m_net_group_wrappers(), m_is_vdevice(is_vdevice), m_network_group_scheduler(network_group_scheduler) {} Expected VdmaConfigManager::add_hef(Hef &hef, - const NetworkGroupsParamsMap &configure_params) + const NetworkGroupsParamsMap &configure_params, bool scheduler_is_used) { auto &hef_network_groups = hef.pimpl->network_groups(); auto current_net_group_index = static_cast(m_net_groups.size()); @@ -128,7 +130,7 @@ Expected VdmaConfigManager::add_hef(Hef &hef, } auto net_group = VdmaConfigNetworkGroup::create(m_active_net_group_holder, config_params, - resources_managers, network_group_metadata_ptr); + resources_managers, network_group_metadata_ptr, m_network_group_scheduler); current_net_group_index++; auto net_group_ptr = make_shared_nothrow(net_group.release()); @@ -137,7 +139,16 @@ Expected VdmaConfigManager::add_hef(Hef &hef, // TODO: move this func into VdmaConfigNetworkGroup c'tor if (m_is_vdevice) { - status = net_group_ptr->create_vdevice_streams_from_config_params(); + auto network_group_handle = INVALID_NETWORK_GROUP_HANDLE; + auto network_group_scheduler = m_network_group_scheduler.lock(); + if (network_group_scheduler) { + auto network_group_handle_exp = network_group_scheduler->add_network_group(net_group_ptr); + CHECK_EXPECTED(network_group_handle_exp); + network_group_handle = network_group_handle_exp.value(); + net_group_ptr->set_network_group_handle(network_group_handle); + } + + status = net_group_ptr->create_vdevice_streams_from_config_params(network_group_handle); CHECK_SUCCESS_AS_EXPECTED(status); } else { status = net_group_ptr->create_streams_from_config_params(net_group_ptr->get_resources_managers()[0]->get_device()); @@ -148,7 +159,14 @@ Expected VdmaConfigManager::add_hef(Hef &hef, 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(net_group_ptr)); + auto net_group_wrapper = ConfiguredNetworkGroupWrapper::create(net_group_ptr); + CHECK_EXPECTED(net_group_wrapper); + + auto net_group_wrapper_ptr = make_shared_nothrow(net_group_wrapper.release()); + CHECK_AS_EXPECTED(nullptr != net_group_wrapper_ptr, HAILO_OUT_OF_HOST_MEMORY); + m_net_group_wrappers.emplace_back(net_group_wrapper_ptr); + + added_network_groups.emplace_back(std::static_pointer_cast(net_group_wrapper_ptr)); } std::string unmatched_keys = ""; for (const auto &pair : configure_params_copy) { @@ -175,7 +193,7 @@ Expected VdmaConfigManager::add_hef(Hef &hef, 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(); + auto net_group_header_exp = resource_manager->get_control_network_group_header(scheduler_is_used); 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(); @@ -190,13 +208,25 @@ Expected VdmaConfigManager::add_hef(Hef &hef, } } - // Reset context_switch status - auto status = Control::reset_context_switch_state_machine(device.get()); - CHECK_SUCCESS_AS_EXPECTED(status); + { + // Add instance that guards scheduler to deactivate network_group temporary + auto scheduler_idle_guard = NetworkGroupScheduler::create_scheduler_idle_guard(); + if (m_is_vdevice) { + auto network_group_scheduler = m_network_group_scheduler.lock(); + if (network_group_scheduler) { + auto status = scheduler_idle_guard->set_scheduler(network_group_scheduler); + 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); + // 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; @@ -218,8 +248,8 @@ hailo_status VdmaConfigManager::update_network_batch_size(ConfigureNetworkParams 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) { + /* In case user works with network group, overide the network batch size.*/ for (auto &network_params : config_params.network_params_by_name) { network_params.second.batch_size = config_params.batch_size; } diff --git a/hailort/libhailort/src/context_switch/vdma_config_network_group.cpp b/hailort/libhailort/src/context_switch/vdma_config_network_group.cpp index fd5610d..c0187b4 100644 --- a/hailort/libhailort/src/context_switch/vdma_config_network_group.cpp +++ b/hailort/libhailort/src/context_switch/vdma_config_network_group.cpp @@ -11,12 +11,12 @@ namespace hailort Expected VdmaConfigNetworkGroup::create(VdmaConfigActiveAppHolder &active_net_group_holder, const ConfigureNetworkParams &config_params, std::vector> resources_managers, - std::shared_ptr network_group_metadata) + std::shared_ptr network_group_metadata, NetworkGroupSchedulerWeakPtr network_group_scheduler) { auto status = HAILO_UNINITIALIZED; VdmaConfigNetworkGroup object(active_net_group_holder, config_params, - std::move(resources_managers), *network_group_metadata, status); + std::move(resources_managers), *network_group_metadata, network_group_scheduler, status); CHECK_SUCCESS_AS_EXPECTED(status); return object; @@ -25,19 +25,21 @@ Expected VdmaConfigNetworkGroup::create(VdmaConfigActive VdmaConfigNetworkGroup::VdmaConfigNetworkGroup(VdmaConfigActiveAppHolder &active_net_group_holder, const ConfigureNetworkParams &config_params, std::vector> &&resources_managers, - const NetworkGroupMetadata &network_group_metadata, hailo_status &status) : + const NetworkGroupMetadata &network_group_metadata, NetworkGroupSchedulerWeakPtr network_group_scheduler, 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 + resources_managers[0]->get_network_group_index(), // All ResourceManagers shares the same net_group_index + network_group_metadata, !network_group_scheduler.expired(), status), m_active_net_group_holder(active_net_group_holder), - m_resources_managers(std::move(resources_managers)) {} + m_resources_managers(std::move(resources_managers)), + m_network_group_scheduler(network_group_scheduler), + m_network_group_handle(INVALID_NETWORK_GROUP_HANDLE) {} -Expected> VdmaConfigNetworkGroup::activate( - const hailo_activate_network_group_params_t &network_group_params) +Expected> VdmaConfigNetworkGroup::activate_impl( + const hailo_activate_network_group_params_t &network_group_params, uint16_t dynamic_batch_size) { 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_active_net_group_holder, get_network_group_name(), m_resources_managers, network_group_params, dynamic_batch_size, m_input_streams, m_output_streams, m_network_group_activated_event, m_deactivation_time_accumulator); const auto elapsed_time_ms = std::chrono::duration( std::chrono::steady_clock::now() - start_time).count(); @@ -77,25 +79,11 @@ Expected VdmaConfigNetworkGroup::get_boundary_channel_index(uint8_t str return m_resources_managers[0]->get_boundary_channel_index(stream_index, direction, layer_name); } -hailo_status VdmaConfigNetworkGroup::create_vdevice_streams_from_config_params() +hailo_status VdmaConfigNetworkGroup::create_vdevice_streams_from_config_params(network_group_handle_t network_group_handle) { - 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."); - } + // TODO - HRT-6931 - raise error on this case + if (((m_config_params.latency & HAILO_LATENCY_MEASURE) == HAILO_LATENCY_MEASURE) && (1 < m_resources_managers.size())) { + 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) { @@ -103,14 +91,14 @@ hailo_status VdmaConfigNetworkGroup::create_vdevice_streams_from_config_params() case HAILO_H2D_STREAM: { auto status = create_input_vdevice_stream_from_config_params(stream_parameters_pair.second, - stream_parameters_pair.first); + stream_parameters_pair.first, network_group_handle); 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); + stream_parameters_pair.first, network_group_handle); CHECK_SUCCESS(status); } break; @@ -124,18 +112,16 @@ hailo_status VdmaConfigNetworkGroup::create_vdevice_streams_from_config_params() } hailo_status VdmaConfigNetworkGroup::create_input_vdevice_stream_from_config_params(const hailo_stream_parameters_t &stream_params, - const std::string &stream_name) + const std::string &stream_name, network_group_handle_t network_group_handle) { 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); + stream_name, network_group_handle, m_network_group_activated_event, + m_network_group_scheduler); CHECK_EXPECTED_AS_STATUS(input_stream); m_input_streams.insert(make_pair(stream_name, input_stream.release())); @@ -143,22 +129,67 @@ hailo_status VdmaConfigNetworkGroup::create_input_vdevice_stream_from_config_par } hailo_status VdmaConfigNetworkGroup::create_output_vdevice_stream_from_config_params(const hailo_stream_parameters_t &stream_params, - const std::string &stream_name) + const std::string &stream_name, network_group_handle_t network_group_handle) { 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); + stream_name, network_group_handle, m_network_group_activated_event, + m_network_group_scheduler); CHECK_EXPECTED_AS_STATUS(output_stream); m_output_streams.insert(make_pair(stream_name, output_stream.release())); return HAILO_SUCCESS; } +void VdmaConfigNetworkGroup::set_network_group_handle(network_group_handle_t handle) +{ + m_network_group_handle = handle; +} + +hailo_status VdmaConfigNetworkGroup::set_scheduler_timeout(const std::chrono::milliseconds &timeout, const std::string &network_name) +{ + auto network_group_scheduler = m_network_group_scheduler.lock(); + CHECK(network_group_scheduler, HAILO_INVALID_OPERATION, + "Cannot set scheduler timeout for network group {}, as it is configured on a vdevice which does not have scheduling enabled", get_network_group_name()); + if (network_name != HailoRTDefaults::get_network_name(get_network_group_name())) { + CHECK(network_name.empty(), HAILO_NOT_IMPLEMENTED, "Setting scheduler timeout for a specific network is currently not supported"); + } + auto status = network_group_scheduler->set_timeout(m_network_group_handle, timeout, network_name); + CHECK_SUCCESS(status); + return HAILO_SUCCESS; +} + +hailo_status VdmaConfigNetworkGroup::set_scheduler_threshold(uint32_t threshold, const std::string &network_name) +{ + auto network_group_scheduler = m_network_group_scheduler.lock(); + CHECK(network_group_scheduler, HAILO_INVALID_OPERATION, + "Cannot set scheduler threshold for network group {}, as it is configured on a vdevice which does not have scheduling enabled", get_network_group_name()); + if (network_name != HailoRTDefaults::get_network_name(get_network_group_name())) { + CHECK(network_name.empty(), HAILO_NOT_IMPLEMENTED, "Setting scheduler threshold for a specific network is currently not supported"); + } + auto status = network_group_scheduler->set_threshold(m_network_group_handle, threshold, network_name); + CHECK_SUCCESS(status); + return HAILO_SUCCESS; +} + +Expected> VdmaConfigNetworkGroup::get_latnecy_meters() +{ + auto latency_meters = m_resources_managers[0]->get_latnecy_meters(); + return make_shared_nothrow(latency_meters); +} + +Expected> VdmaConfigNetworkGroup::get_boundary_vdma_channel_by_stream_name(const std::string &stream_name) +{ + if (1 < m_resources_managers.size()) { + LOGGER__ERROR("get_boundary_vdma_channel_by_stream_name function is not supported on more than 1 physical device."); + return make_unexpected(HAILO_INVALID_OPERATION); + } + + return m_resources_managers[0]->get_boundary_vdma_channel_by_stream_name(stream_name); +} + } /* namespace hailort */ diff --git a/hailort/libhailort/src/control.cpp b/hailort/libhailort/src/control.cpp index 12cb00f..b20b4cc 100644 --- a/hailort/libhailort/src/control.cpp +++ b/hailort/libhailort/src/control.cpp @@ -25,6 +25,9 @@ namespace hailort #define MIN(x, y) (((x) < (y)) ? (x) : (y)) #endif +#define POWER_MEASUREMENT_DELAY_MS(__sample_period, __average_factor) \ + (static_cast((__sample_period) / 1000.0 * (__average_factor) * 2 * 1.2)) + #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" \ @@ -1097,7 +1100,7 @@ exit: return status; } -hailo_status Control::set_power_measurement(Device &device, uint32_t index, CONTROL_PROTOCOL__dvm_options_t dvm, +hailo_status Control::set_power_measurement(Device &device, hailo_measurement_buffer_index_t buffer_index, CONTROL_PROTOCOL__dvm_options_t dvm, CONTROL_PROTOCOL__power_measurement_types_t measurement_type) { hailo_status status = HAILO_UNINITIALIZED; @@ -1110,11 +1113,11 @@ hailo_status Control::set_power_measurement(Device &device, uint32_t index, CONT 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); + CHECK(CONTROL_PROTOCOL__MAX_NUMBER_OF_POWER_MEASUREMETS > buffer_index, + HAILO_INVALID_ARGUMENT, "Invalid power measurement index {}", buffer_index); common_status = CONTROL_PROTOCOL__pack_set_power_measurement_request(&request, &request_size, device.get_control_sequence(), - index, dvm, measurement_type); + buffer_index, dvm, measurement_type); status = (HAILO_COMMON_STATUS__SUCCESS == common_status) ? HAILO_SUCCESS : HAILO_INTERNAL_FAILURE; if (HAILO_SUCCESS != status) { goto exit; @@ -1144,7 +1147,7 @@ exit: return status; } -hailo_status Control::get_power_measurement(Device &device, uint32_t index, bool should_clear, +hailo_status Control::get_power_measurement(Device &device, hailo_measurement_buffer_index_t buffer_index, bool should_clear, hailo_power_measurement_data_t *measurement_data) { hailo_status status = HAILO_UNINITIALIZED; @@ -1158,11 +1161,11 @@ hailo_status Control::get_power_measurement(Device &device, uint32_t index, bool 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(CONTROL_PROTOCOL__MAX_NUMBER_OF_POWER_MEASUREMETS > buffer_index, + HAILO_INVALID_ARGUMENT, "Invalid power measurement index {}", buffer_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); + buffer_index, should_clear); status = (HAILO_COMMON_STATUS__SUCCESS == common_status) ? HAILO_SUCCESS : HAILO_INTERNAL_FAILURE; if (HAILO_SUCCESS != status) { goto exit; @@ -1194,7 +1197,7 @@ exit: return status; } -hailo_status Control::start_power_measurement(Device &device, uint32_t delay_milliseconds, +hailo_status Control::start_power_measurement(Device &device, CONTROL_PROTOCOL__averaging_factor_t averaging_factor , CONTROL_PROTOCOL__sampling_period_t sampling_period) { hailo_status status = HAILO_UNINITIALIZED; @@ -1206,6 +1209,13 @@ hailo_status Control::start_power_measurement(Device &device, uint32_t delay_mil CONTROL_PROTOCOL__response_header_t *header = NULL; CONTROL_PROTOCOL__payload_t *payload = NULL; + uint32_t delay_milliseconds = 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 == delay_milliseconds) { + delay_milliseconds = 1; + } + 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; @@ -2392,18 +2402,14 @@ hailo_status Control::context_switch_set_context_info(Device &device, 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.config_buffer_infos) == sizeof(context_info->config_buffer_infos), + "mismatch in sizes of config_buffer_infos"); 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)); + context_info_single_control.cfg_channels_count = context_info->cfg_channels_count; + memcpy(context_info_single_control.config_buffer_infos, + context_info->config_buffer_infos, + sizeof(context_info_single_control.config_buffer_infos)); memcpy(&(context_info_single_control.context_stream_remap_data), &(context_info->context_stream_remap_data), @@ -2717,7 +2723,7 @@ hailo_status Control::download_context_action_list(Device &device, uint8_t conte hailo_status Control::change_context_switch_status(Device &device, CONTROL_PROTOCOL__CONTEXT_SWITCH_STATUS_t state_machine_status, - uint8_t network_group_index) + uint8_t network_group_index, uint16_t dynamic_batch_size) { hailo_status status = HAILO_UNINITIALIZED; HAILO_COMMON_STATUS_t common_status = HAILO_COMMON_STATUS__UNINITIALIZED; @@ -2729,7 +2735,7 @@ hailo_status Control::change_context_switch_status(Device &device, 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); + device.get_control_sequence(), state_machine_status, network_group_index, dynamic_batch_size); status = (HAILO_COMMON_STATUS__SUCCESS == common_status) ? HAILO_SUCCESS : HAILO_INTERNAL_FAILURE; if (HAILO_SUCCESS != status) { goto exit; @@ -2752,14 +2758,18 @@ exit: return status; } -hailo_status Control::enable_network_group(Device &device, uint8_t network_group_index) +hailo_status Control::enable_network_group(Device &device, uint8_t network_group_index, uint16_t dynamic_batch_size) { - return Control::change_context_switch_status(device, CONTROL_PROTOCOL__CONTEXT_SWITCH_STATUS_ENABLED, network_group_index); + return Control::change_context_switch_status(device, CONTROL_PROTOCOL__CONTEXT_SWITCH_STATUS_ENABLED, + network_group_index, dynamic_batch_size); } hailo_status Control::reset_context_switch_state_machine(Device &device) { - return Control::change_context_switch_status(device, CONTROL_PROTOCOL__CONTEXT_SWITCH_STATUS_RESET, 0); + static const auto IGNORE_NETWORK_GROUP_INDEX = 0; + static const auto IGNORE_DYNAMIC_BATCH_SIZE = 0; + return Control::change_context_switch_status(device, CONTROL_PROTOCOL__CONTEXT_SWITCH_STATUS_RESET, + IGNORE_NETWORK_GROUP_INDEX, IGNORE_DYNAMIC_BATCH_SIZE); } hailo_status Control::wd_enable(Device &device, uint8_t cpu_id, bool should_enable) @@ -2993,7 +3003,7 @@ exit: return status; } -hailo_status Control::switch_network_group(Device &device, uint8_t network_group_index) +hailo_status Control::switch_network_group(Device &device, uint8_t network_group_index, uint16_t dynamic_batch_size) { hailo_status status = HAILO_UNINITIALIZED; HAILO_COMMON_STATUS_t common_status = HAILO_COMMON_STATUS__UNINITIALIZED; @@ -3004,11 +3014,10 @@ hailo_status Control::switch_network_group(Device &device, uint8_t network_group 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); + network_group_index, dynamic_batch_size); status = (HAILO_COMMON_STATUS__SUCCESS == common_status) ? HAILO_SUCCESS : HAILO_INTERNAL_FAILURE; if (HAILO_SUCCESS != status) { goto exit; diff --git a/hailort/libhailort/src/control.hpp b/hailort/libhailort/src/control.hpp index 3702993..57f303c 100644 --- a/hailort/libhailort/src/control.hpp +++ b/hailort/libhailort/src/control.hpp @@ -293,8 +293,7 @@ public: * * @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); + static hailo_status enable_network_group(Device &device, uint8_t network_group_index, uint16_t dynamic_batch_size); /** * reset context switch state machine * @@ -335,10 +334,12 @@ public: * * @param[in] device - The Hailo device. * @param[in] network_group_index - network_group index + * @param[in] dynamic_batch_size - dynamic_batch size (or CONTROL_PROTOCOL__IGNORE_DYNAMIC_BATCH_SIZE if it's + * to be ignored) * * @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); + static hailo_status switch_network_group(Device &device, uint8_t network_group_index, uint16_t dynamic_batch_size); /** * Enable/disable halt transmition following Rx pause frame @@ -383,11 +384,11 @@ public: // 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, + static hailo_status set_power_measurement(Device &device, hailo_measurement_buffer_index_t buffer_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, + static hailo_status get_power_measurement(Device &device, hailo_measurement_buffer_index_t buffer_index, bool should_clear, hailo_power_measurement_data_t *measurement_data); - static hailo_status start_power_measurement(Device &device, uint32_t delay_milliseconds, + static hailo_status start_power_measurement(Device &device, CONTROL_PROTOCOL__averaging_factor_t averaging_factor, CONTROL_PROTOCOL__sampling_period_t sampling_period); static hailo_status stop_power_measurement(Device &device); @@ -404,7 +405,7 @@ private: 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); + uint8_t network_group_index, uint16_t dynamic_batch_size); }; } /* namespace hailort */ diff --git a/hailort/libhailort/src/control_protocol.cpp b/hailort/libhailort/src/control_protocol.cpp index e400560..ef17df8 100644 --- a/hailort/libhailort/src/control_protocol.cpp +++ b/hailort/libhailort/src/control_protocol.cpp @@ -1696,19 +1696,17 @@ HAILO_COMMON_STATUS_t CONTROL_PROTOCOL__pack_context_switch_set_context_info_req 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)); + /* cfg_channels_count */ + request->parameters.context_switch_set_context_info_request.cfg_channels_count_length = + BYTE_ORDER__htonl(sizeof(request->parameters.context_switch_set_context_info_request.cfg_channels_count)); + request->parameters.context_switch_set_context_info_request.cfg_channels_count = context_info->cfg_channels_count; - /* 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 cfg buffer info */ + request->parameters.context_switch_set_context_info_request.config_buffer_infos_length = + BYTE_ORDER__htonl(sizeof(request->parameters.context_switch_set_context_info_request.config_buffer_infos)); + memcpy(&(request->parameters.context_switch_set_context_info_request.config_buffer_infos), + &(context_info->config_buffer_infos), + sizeof(request->parameters.context_switch_set_context_info_request.config_buffer_infos)); /* context unused stream index */ request->parameters.context_switch_set_context_info_request.context_stream_remap_data_length = @@ -1834,7 +1832,8 @@ exit: 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) + CONTROL_PROTOCOL__CONTEXT_SWITCH_STATUS_t state_machine_status, uint8_t application_index, + uint16_t dynamic_batch_size) { HAILO_COMMON_STATUS_t status = HAILO_COMMON_STATUS__UNINITIALIZED; size_t local_request_size = 0; @@ -1847,7 +1846,7 @@ HAILO_COMMON_STATUS_t CONTROL_PROTOCOL__pack_change_context_switch_status_reques /* 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); + control_protocol__pack_request_header(request, sequence, HAILO_CONTROL_OPCODE_CHANGE_CONTEXT_SWITCH_STATUS, 3); /* state_machine_status */ request->parameters.change_context_switch_status_request.state_machine_status_length = @@ -1860,6 +1859,11 @@ HAILO_COMMON_STATUS_t CONTROL_PROTOCOL__pack_change_context_switch_status_reques 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; + + /* dynamic_batch_size */ + request->parameters.change_context_switch_status_request.dynamic_batch_size_length = + BYTE_ORDER__htonl(sizeof(request->parameters.change_context_switch_status_request.dynamic_batch_size)); + request->parameters.change_context_switch_status_request.dynamic_batch_size = dynamic_batch_size; *request_size = local_request_size; status = HAILO_COMMON_STATUS__SUCCESS; @@ -2092,7 +2096,7 @@ exit: } 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) + uint8_t application_index, uint16_t dynamic_batch_size) { HAILO_COMMON_STATUS_t status = HAILO_COMMON_STATUS__UNINITIALIZED; size_t local_request_size = 0; @@ -2110,6 +2114,11 @@ HAILO_COMMON_STATUS_t CONTROL_PROTOCOL__pack_switch_application_request( 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; + + /* dynamic_batch_size */ + request->parameters.switch_application_request.dynamic_batch_size_length = + BYTE_ORDER__htonl(sizeof(request->parameters.switch_application_request.dynamic_batch_size)); + request->parameters.switch_application_request.dynamic_batch_size = dynamic_batch_size; *request_size = local_request_size; status = HAILO_COMMON_STATUS__SUCCESS; diff --git a/hailort/libhailort/src/control_protocol.hpp b/hailort/libhailort/src/control_protocol.hpp index c59129a..0337f7b 100644 --- a/hailort/libhailort/src/control_protocol.hpp +++ b/hailort/libhailort/src/control_protocol.hpp @@ -46,8 +46,8 @@ typedef struct { } 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]; + uint8_t cfg_channels_count; + CONTROL_PROTOCOL__host_buffer_info_t config_buffer_infos[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]; @@ -130,7 +130,8 @@ HAILO_COMMON_STATUS_t CONTROL_PROTOCOL__pack_download_context_action_list_reques 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); + CONTROL_PROTOCOL__CONTEXT_SWITCH_STATUS_t state_machine_status, uint8_t application_index, + uint16_t dynamic_batch_size); HAILO_COMMON_STATUS_t CONTROL_PROTOCOL__pack_wd_enable( CONTROL_PROTOCOL__request_t *request, size_t *request_size, @@ -153,7 +154,7 @@ HAILO_COMMON_STATUS_t CONTROL_PROTOCOL__pack_d2h_event_manager_set_host_info_req 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); + uint8_t application_index, uint16_t dynamic_batch_size); 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); diff --git a/hailort/libhailort/src/core_stream.cpp b/hailort/libhailort/src/core_stream.cpp index 27d9897..6ba66fe 100644 --- a/hailort/libhailort/src/core_stream.cpp +++ b/hailort/libhailort/src/core_stream.cpp @@ -13,8 +13,8 @@ namespace hailort { Expected> CoreInputStream::create(Device &device, - uint8_t channel_index, const LayerInfo &edge_layer, - uint16_t batch_size, EventPtr network_group_activated_event, LatencyMeterPtr latency_meter) + std::shared_ptr channel, const LayerInfo &edge_layer, + uint16_t batch_size, EventPtr network_group_activated_event) { hailo_status status = HAILO_UNINITIALIZED; @@ -23,8 +23,8 @@ Expected> CoreInputStream::create(Device &devic CoreDevice *core_device = reinterpret_cast(&device); std::unique_ptr 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)); + std::move(channel), edge_layer, std::move(network_group_activated_event), batch_size, + DEFAULT_TRANSFER_TIMEOUT, status)); CHECK((nullptr != local_stream), make_unexpected(HAILO_OUT_OF_HOST_MEMORY)); CHECK_SUCCESS_AS_EXPECTED(status); @@ -33,20 +33,19 @@ Expected> CoreInputStream::create(Device &devic CoreInputStream::CoreInputStream( CoreDevice &device, - uint8_t channel_index, + std::shared_ptr channel, 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) + VdmaInputStream(device, std::move(channel), edge_layer, network_group_activated_event, + batch_size, transfer_timeout, HAILO_STREAM_INTERFACE_CORE, status) {} Expected> CoreOutputStream::create(Device &device, - uint8_t channel_index, const LayerInfo &edge_layer, uint16_t batch_size, - EventPtr network_group_activated_event, LatencyMeterPtr latency_meter) + std::shared_ptr channel, const LayerInfo &edge_layer, + uint16_t batch_size, EventPtr network_group_activated_event) { hailo_status status = HAILO_UNINITIALIZED; CHECK_AS_EXPECTED(device.get_type() == Device::Type::CORE, HAILO_INTERNAL_FAILURE, @@ -54,8 +53,8 @@ Expected> CoreOutputStream::create(Device &dev CoreDevice *core_device = reinterpret_cast(&device); std::unique_ptr 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)); + std::move(channel), edge_layer, std::move(network_group_activated_event), + batch_size, DEFAULT_TRANSFER_TIMEOUT, status)); CHECK((nullptr != local_stream), make_unexpected(HAILO_OUT_OF_HOST_MEMORY)); CHECK_SUCCESS_AS_EXPECTED(status); @@ -64,15 +63,14 @@ Expected> CoreOutputStream::create(Device &dev CoreOutputStream::CoreOutputStream( CoreDevice &device, - uint8_t channel_index, + std::shared_ptr channel, 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) + VdmaOutputStream(device, std::move(channel), edge_layer, + network_group_activated_event, batch_size, transfer_timeout, status) {} } /* namespace hailort */ diff --git a/hailort/libhailort/src/core_stream.hpp b/hailort/libhailort/src/core_stream.hpp index c0b95a8..dceef1c 100644 --- a/hailort/libhailort/src/core_stream.hpp +++ b/hailort/libhailort/src/core_stream.hpp @@ -22,20 +22,19 @@ public: CoreInputStream(CoreInputStream &&other) = default; virtual ~CoreInputStream() = default; - static Expected> create(Device &device, uint8_t channel_index, - const LayerInfo &edge_layer, uint16_t batch_size, EventPtr network_group_activated_event, - LatencyMeterPtr latency_meter = nullptr); + static Expected> create(Device &device, + std::shared_ptr channel, const LayerInfo &edge_layer, uint16_t batch_size, + EventPtr network_group_activated_event); virtual hailo_stream_interface_t get_interface() const override { return HAILO_STREAM_INTERFACE_CORE; } private: CoreInputStream( CoreDevice &device, - uint8_t channel_index, + std::shared_ptr channel, 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); }; @@ -45,20 +44,19 @@ public: CoreOutputStream(CoreOutputStream &&other) = default; virtual ~CoreOutputStream() = default; - static Expected> create(Device &device, uint8_t channel_index, - const LayerInfo &edge_layer, uint16_t batch_size, EventPtr network_group_activated_event, - LatencyMeterPtr latency_meter); + static Expected> create(Device &device, + std::shared_ptr channel, const LayerInfo &edge_layer, uint16_t batch_size, + EventPtr network_group_activated_event); virtual hailo_stream_interface_t get_interface() const override { return HAILO_STREAM_INTERFACE_CORE; } private: explicit CoreOutputStream( CoreDevice &device, - uint8_t channel_index, + std::shared_ptr channel, 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); }; diff --git a/hailort/libhailort/src/device.cpp b/hailort/libhailort/src/device.cpp index aef126b..7c70aed 100644 --- a/hailort/libhailort/src/device.cpp +++ b/hailort/libhailort/src/device.cpp @@ -239,21 +239,45 @@ Expected Device::power_measurement(hailo_dvm_options_t dvm, hailo_pow return res; } -hailo_status Device::start_power_measurement(uint32_t delay_milliseconds, hailo_averaging_factor_t averaging_factor, hailo_sampling_period_t sampling_period) +hailo_status Device::start_power_measurement(uint32_t /*unused*/, hailo_averaging_factor_t averaging_factor, hailo_sampling_period_t sampling_period) { - return Control::start_power_measurement(*this, delay_milliseconds, static_cast(averaging_factor), - static_cast(sampling_period)); + // TODO: Remove deprecated function + LOGGER__WARNING("'Device::start_power_measurement(uint32_t unused, hailo_averaging_factor_t averaging_factor, hailo_sampling_period_t sampling_period)' is deprecated. "\ + "One should use ''Device::start_power_measurement(hailo_averaging_factor_t averaging_factor, hailo_sampling_period_t sampling_period)"); + return start_power_measurement(averaging_factor, 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(dvm), static_cast(measurement_type)); + // TODO: Remove deprecated function + LOGGER__WARNING("'Device::set_power_measurement(uint32_t index, hailo_dvm_options_t dvm, hailo_power_measurement_types_t measurement_type)' is deprecated. "\ + "One should use ''Device::set_power_measurement(hailo_measurement_buffer_index_t buffer_index, hailo_dvm_options_t dvm, hailo_power_measurement_types_t measurement_type)"); + return set_power_measurement(static_cast(index), dvm, measurement_type); } Expected Device::get_power_measurement(uint32_t index, bool should_clear) +{ + // TODO: Remove deprecated function + LOGGER__WARNING("'Device::get_power_measurement(uint32_t index, bool should_clear)' is deprecated. "\ + "One should use ''Device::set_power_measurement(hailo_measurement_buffer_index_t buffer_index, bool should_clear)"); + return get_power_measurement(static_cast(index), should_clear); +} + +hailo_status Device::start_power_measurement(hailo_averaging_factor_t averaging_factor, hailo_sampling_period_t sampling_period) +{ + return Control::start_power_measurement(*this, static_cast(averaging_factor), + static_cast(sampling_period)); +} + +hailo_status Device::set_power_measurement(hailo_measurement_buffer_index_t buffer_index, hailo_dvm_options_t dvm, hailo_power_measurement_types_t measurement_type) +{ + return Control::set_power_measurement(*this, buffer_index, static_cast(dvm), static_cast(measurement_type)); +} + +Expected Device::get_power_measurement(hailo_measurement_buffer_index_t buffer_index, bool should_clear) { hailo_power_measurement_data_t measurement_data = {}; - auto status = Control::get_power_measurement(*this, index, should_clear, &measurement_data); + auto status = Control::get_power_measurement(*this, buffer_index, should_clear, &measurement_data); CHECK_SUCCESS_AS_EXPECTED(status); return measurement_data; } @@ -439,4 +463,10 @@ Expected Device::download_context_action_list(uint8_t context_index, uin return final_action_list.release(); } +hailo_status Device::set_context_action_list_timestamp_batch(uint16_t batch_index) +{ + static const bool ENABLE_USER_CONFIG = true; + return Control::config_context_switch_timestamp(*this, batch_index, ENABLE_USER_CONFIG); +} + } /* namespace hailort */ diff --git a/hailort/libhailort/src/device_internal.cpp b/hailort/libhailort/src/device_internal.cpp index c1b1e37..4a6d87f 100644 --- a/hailort/libhailort/src/device_internal.cpp +++ b/hailort/libhailort/src/device_internal.cpp @@ -79,14 +79,17 @@ hailo_status DeviceBase::reset(hailo_reset_device_mode_t mode) return reset_impl(reset_type); } -hailo_status DeviceBase::set_notification_callback(NotificationCallback func, hailo_notification_id_t notification_id, void *opaque) +hailo_status DeviceBase::set_notification_callback(const 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); + auto func_ptr = make_shared_nothrow(func); + CHECK_NOT_NULL(func_ptr, HAILO_OUT_OF_HOST_MEMORY); + const std::lock_guard lock(m_callbacks_lock); - m_d2h_callbacks[notification_id].func = func; + m_d2h_callbacks[notification_id].func = func_ptr; m_d2h_callbacks[notification_id].opaque = opaque; return HAILO_SUCCESS; } @@ -558,7 +561,7 @@ void DeviceBase::d2h_notification_thread_main(const std::string &device_id) LOGGER__INFO("[{}] Got notification from fw with id: {}", device_id, hailo_notification_id); - NotificationCallback callback_func = nullptr; + std::shared_ptr callback_func = nullptr; void *callback_opaque = nullptr; { const std::lock_guard lock(m_callbacks_lock); @@ -574,7 +577,7 @@ void DeviceBase::d2h_notification_thread_main(const std::string &device_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, ¬ification.message_parameters, sizeof(notification.message_parameters)); - callback_func(*this, callback_notification, callback_opaque); + (*callback_func)(*this, callback_notification, callback_opaque); } } } diff --git a/hailort/libhailort/src/device_internal.hpp b/hailort/libhailort/src/device_internal.hpp index cc0a7d5..344f8eb 100644 --- a/hailort/libhailort/src/device_internal.hpp +++ b/hailort/libhailort/src/device_internal.hpp @@ -55,7 +55,7 @@ public: virtual Expected 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 set_notification_callback(const 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); @@ -122,7 +122,7 @@ private: Expected get_fw_type(); typedef struct { - NotificationCallback func; + std::shared_ptr func; void *opaque; } d2h_notification_callback_t; diff --git a/hailort/libhailort/src/eth_stream.cpp b/hailort/libhailort/src/eth_stream.cpp index e82c48d..8b7bd10 100644 --- a/hailort/libhailort/src/eth_stream.cpp +++ b/hailort/libhailort/src/eth_stream.cpp @@ -78,7 +78,8 @@ hailo_status EthernetInputStream::deactivate_stream() return HAILO_SUCCESS; } -hailo_status EthernetInputStream::activate_stream() +// Note: Ethernet streams don't work with dynamic batch sizes +hailo_status EthernetInputStream::activate_stream(uint16_t /* dynamic_batch_size */) { hailo_status status = HAILO_UNINITIALIZED; CONTROL_PROTOCOL__config_stream_params_t params = {}; @@ -460,7 +461,8 @@ hailo_status EthernetOutputStream::deactivate_stream() return HAILO_SUCCESS; } -hailo_status EthernetOutputStream::activate_stream() +// Note: Ethernet streams don't work with dynamic batch sizes +hailo_status EthernetOutputStream::activate_stream(uint16_t /* dynamic_batch_size */) { hailo_status status = HAILO_UNINITIALIZED; CONTROL_PROTOCOL__config_stream_params_t params = {}; diff --git a/hailort/libhailort/src/eth_stream.hpp b/hailort/libhailort/src/eth_stream.hpp index 241c495..1cc7153 100644 --- a/hailort/libhailort/src/eth_stream.hpp +++ b/hailort/libhailort/src/eth_stream.hpp @@ -79,7 +79,7 @@ public: const LayerInfo &edge_layer, const hailo_eth_input_stream_params_t ¶ms, EventPtr network_group_activated_event); uint16_t get_remote_port(); - virtual hailo_status activate_stream() override; + virtual hailo_status activate_stream(uint16_t dynamic_batch_size) 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; @@ -194,7 +194,7 @@ public: static Expected> create(Device &device, const LayerInfo &edge_layer, const hailo_eth_output_stream_params_t ¶ms, EventPtr network_group_activated_event); - virtual hailo_status activate_stream() override; + virtual hailo_status activate_stream(uint16_t dynamic_batch_size) 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; diff --git a/hailort/libhailort/src/event_internal.hpp b/hailort/libhailort/src/event_internal.hpp index 605e176..04559e8 100644 --- a/hailort/libhailort/src/event_internal.hpp +++ b/hailort/libhailort/src/event_internal.hpp @@ -67,8 +67,8 @@ private: // * 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; + #if defined(_MSC_VER) || defined(__QNX__) + using WaitHandleArray = std::array; #else using WaitHandleArray = std::array; #endif diff --git a/hailort/libhailort/src/hailort.cpp b/hailort/libhailort/src/hailort.cpp index c771928..26aff59 100644 --- a/hailort/libhailort/src/hailort.cpp +++ b/hailort/libhailort/src/hailort.cpp @@ -295,30 +295,30 @@ hailo_status hailo_power_measurement(hailo_device device, hailo_dvm_options_t dv return HAILO_SUCCESS; } -hailo_status hailo_start_power_measurement(hailo_device device, uint32_t delay_milliseconds, +hailo_status hailo_start_power_measurement(hailo_device device, 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), delay_milliseconds, static_cast(averaging_factor), static_cast(sampling_period)); + auto status = Control::start_power_measurement(*reinterpret_cast(device), static_cast(averaging_factor), static_cast(sampling_period)); CHECK_SUCCESS(status); return HAILO_SUCCESS; } -hailo_status hailo_set_power_measurement(hailo_device device, uint32_t index, +hailo_status hailo_set_power_measurement(hailo_device device, hailo_measurement_buffer_index_t buffer_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), index, static_cast(dvm), static_cast(measurement_type)); + auto status = Control::set_power_measurement(*reinterpret_cast(device), buffer_index, static_cast(dvm), static_cast(measurement_type)); CHECK_SUCCESS(status); return HAILO_SUCCESS; } -hailo_status hailo_get_power_measurement(hailo_device device, uint32_t index, bool should_clear, +hailo_status hailo_get_power_measurement(hailo_device device, hailo_measurement_buffer_index_t buffer_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), index, should_clear, measurement_data); + auto status = Control::get_power_measurement(*reinterpret_cast(device), buffer_index, should_clear, measurement_data); CHECK_SUCCESS(status); return HAILO_SUCCESS; } @@ -847,23 +847,6 @@ hailo_status hailo_hef_get_all_vstream_infos(hailo_hef hef, const char *name, 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(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) { @@ -882,6 +865,24 @@ HAILORTAPI hailo_status hailo_get_latency_measurement(hailo_configured_network_g return HAILO_SUCCESS; } +hailo_status hailo_set_scheduler_timeout(hailo_configured_network_group configured_network_group, + uint32_t timeout_ms, const char *network_name) +{ + CHECK_ARG_NOT_NULL(configured_network_group); + + std::string network_name_str = (nullptr == network_name) ? "" : network_name; + return ((ConfiguredNetworkGroup*)configured_network_group)->set_scheduler_timeout(std::chrono::milliseconds(timeout_ms), network_name_str); +} + +hailo_status hailo_set_scheduler_threshold(hailo_configured_network_group configured_network_group, + uint32_t threshold, const char *network_name) +{ + CHECK_ARG_NOT_NULL(configured_network_group); + + std::string network_name_str = (nullptr == network_name) ? "" : network_name; + return ((ConfiguredNetworkGroup*)configured_network_group)->set_scheduler_threshold(threshold, network_name_str); +} + 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) { @@ -1153,96 +1154,6 @@ hailo_status hailo_transform_frame_by_output_transform_context(hailo_output_tran 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(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(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(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(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(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(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) { diff --git a/hailort/libhailort/src/hailort_common.cpp b/hailort/libhailort/src/hailort_common.cpp index 6b6fcbc..f6bf80d 100644 --- a/hailort/libhailort/src/hailort_common.cpp +++ b/hailort/libhailort/src/hailort_common.cpp @@ -17,5 +17,6 @@ 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; +const uint64_t HailoRTCommon::NMS_DUMMY_DELIMITER; } /* namespace hailort */ diff --git a/hailort/libhailort/src/hailort_defaults.hpp b/hailort/libhailort/src/hailort_defaults.hpp index 73704fe..ad428b7 100644 --- a/hailort/libhailort/src/hailort_defaults.hpp +++ b/hailort/libhailort/src/hailort_defaults.hpp @@ -52,9 +52,9 @@ constexpr hailo_format_order_t DEFAULT_FORMAT_ARGMAX_ORDER_MAP[] = { }; -#define HAILO_DEFAULT_PARTIAL_NETWORK_NAME (std::string("default_network_name")) #define HAILO_DEFAULT_NETWORK_NAME_QUALIFIER (std::string("/")) + class HailoRTDefaults { public: @@ -323,16 +323,11 @@ public: { std::string default_network_name = net_group_name + HAILO_DEFAULT_NETWORK_NAME_QUALIFIER + - HAILO_DEFAULT_PARTIAL_NETWORK_NAME; + net_group_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; diff --git a/hailort/libhailort/src/hef.cpp b/hailort/libhailort/src/hef.cpp index ba0d276..3c5f624 100644 --- a/hailort/libhailort/src/hef.cpp +++ b/hailort/libhailort/src/hef.cpp @@ -404,18 +404,19 @@ hailo_status Hef::Impl::fill_networks_metadata() auto sorted_output_names = HefUtils::get_sorted_output_names(*network_group); CHECK_EXPECTED_AS_STATUS(sorted_output_names); - std::vector sorted_partial_network_names; + std::vector sorted_network_names; if (supported_features.multi_network_support) { - sorted_partial_network_names.reserve(network_group->networks_names().size()); + sorted_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); + auto network_name = HefUtils::get_network_name(*network_group, partial_network_name); + sorted_network_names.push_back(network_name); } } else { - sorted_partial_network_names.push_back(HAILO_DEFAULT_PARTIAL_NETWORK_NAME); + sorted_network_names.push_back(HailoRTDefaults::get_network_name(network_group->network_group_metadata().network_group_name())); } NetworkGroupMetadata metadata(network_group->network_group_metadata().network_group_name(), - layer_infos.release(), sorted_output_names.release(), supported_features, sorted_partial_network_names); + layer_infos.release(), sorted_output_names.release(), supported_features, sorted_network_names); m_network_group_metadata.emplace( network_group->network_group_metadata().network_group_name(), metadata); } @@ -489,13 +490,15 @@ NetworkGroupSupportedFeatures Hef::Impl::get_supported_features(const ProtoHEFHe const std::vector &hef_extensions, const ProtoHEFIncludedFeatures &included_features, const std::vector &hef_optional_extensions) { - NetworkGroupSupportedFeatures supported_features = {}; + 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, + supported_features.multi_context = check_hef_extension(ProtoHEFExtensionType::IS_MULTI_CONTEXTS, header, hef_extensions, included_features); + supported_features.preliminary_run_asap = check_hef_extension(ProtoHEFExtensionType::PRELIMINARY_RUN_ASAP, + header, hef_extensions, included_features); return supported_features; } @@ -672,27 +675,27 @@ bool HefConfigurator::is_hw_padding_supported(const ProtoHEFEdgeLayer &edge_laye } Expected> Hef::Impl::get_input_stream_infos(const std::string &net_group_name, - const std::string &partial_network_name) + const std::string &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); + return network_group_metadata->get_input_stream_infos(network_name); } Expected> Hef::Impl::get_output_stream_infos(const std::string &net_group_name, - const std::string &partial_network_name) + const std::string &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); + return network_group_metadata->get_output_stream_infos(network_name); } Expected> Hef::Impl::get_all_stream_infos(const std::string &net_group_name, - const std::string &partial_network_name) + const std::string &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); + return network_group_metadata->get_all_stream_infos(network_name); } Expected> Hef::Impl::get_network_infos(const std::string &net_group_name) @@ -730,27 +733,27 @@ Expected Hef::Impl::get_stream_info_by_name(const std::stri } Expected> Hef::Impl::get_input_vstream_infos(const std::string &net_group_name, - const std::string &partial_network_name) + const std::string &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); + return network_group_metadata->get_input_vstream_infos(network_name); } Expected> Hef::Impl::get_output_vstream_infos(const std::string &net_group_name, - const std::string &partial_network_name) + const std::string &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); + return network_group_metadata->get_output_vstream_infos(network_name); } Expected> Hef::Impl::get_all_vstream_infos(const std::string &net_group_name, - const std::string &partial_network_name) + const std::string &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); + return network_group_metadata->get_all_vstream_infos(network_name); } const std::vector& Hef::Impl::network_groups() const @@ -808,29 +811,26 @@ Expected> Hef::Impl::get_network_group_and_n 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); + auto network_name = HailoRTDefaults::get_network_name(network_group_name); + return std::make_pair(network_group_name, 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); + auto network_name = HailoRTDefaults::get_network_name(network_group_name); + return std::make_pair(network_group_name, 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; + auto full_network_name = HefUtils::get_network_name(network_group_name, partial_network_name); if (name == full_network_name) { - return std::make_pair(network_group_name, partial_network_name); + return std::make_pair(network_group_name, full_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); + if (name == HailoRTDefaults::get_network_name(network_group_name)) { + return std::make_pair(network_group_name, name); } } } @@ -968,7 +968,7 @@ hailo_status HefUtils::fill_layer_info(const ProtoHEFEdgeLayerInfo &info, } layer_info.name = info.name(); - layer_info.partial_network_name = partial_network_name; + layer_info.network_name = HefUtils::get_network_name(net_group, partial_network_name); layer_info.is_mux = false; layer_info.direction = direction; layer_info.quant_info.limvals_max = info.numeric_info().limvals_max(); @@ -989,7 +989,7 @@ hailo_status HefUtils::fill_layer_info(const ProtoHEFEdgeLayerInfo &info, // 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); + status = fill_fused_nms_info(fused_layer, fused_layer_info, layer_info.quant_info, layer_info.network_name); CHECK_SUCCESS(status); layer_info.fused_nms_layer.push_back(fused_layer_info); break; @@ -1002,7 +1002,7 @@ hailo_status HefUtils::fill_layer_info(const ProtoHEFEdgeLayerInfo &info, } 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) + hailo_quant_info_t &defuse_quant_info, const std::string &network_name) { auto base_info = info.layer_info().edge_layer_base(); auto format_order_exp = HailoRTDefaults::get_device_format_order(base_info.format()); @@ -1030,7 +1030,7 @@ hailo_status HefUtils::fill_fused_nms_info(const ProtoHEFEdgeLayerFused &info, L return HAILO_INTERNAL_FAILURE; } layer_info.name = info.layer_info().name(); - layer_info.partial_network_name = partial_network_name; + layer_info.network_name = 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 @@ -1064,7 +1064,7 @@ hailo_status HefUtils::fill_mux_info(const ProtoHEFEdgeLayerMux &info, } layer_info.name = info.name(); - layer_info.partial_network_name = partial_network_name; + layer_info.network_name = HefUtils::get_network_name(net_group, 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(); @@ -1232,10 +1232,18 @@ inline std::pair old_hef_parse_initial_l3(uint32_t initial_l3 return std::make_pair(initial_l3_cut, initial_l3_offset); } +static uint32_t get_initial_credit_size(const ProtoHEFEdgeLayer &edge_layer_info) +{ + // On old HEFs the max shmifo is not defined, so 0 wil be returned and the firmware will choose the default value. + return edge_layer_info.layer_info().edge_layer_base().max_shmifo_size(); +} + 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) + uint8_t **context_meta_data_head_pointer, uint8_t stream_index, const ProtoHEFEdgeLayer &edge_layer_info, + const CONTROL_PROTOCOL__nn_stream_config_t &nn_stream_config, + ContextSwitchChannelsParsingInfo &channels_parsing_info, ResourcesManager &resources_manager, + const std::string &layer_name, const std::string &network_name, uint8_t network_index, + uint32_t frame_credits_in_bytes) { auto channel_index = resources_manager.get_available_channel_index( channels_parsing_info.H2D_channels_in_use, ChannelInfo::Type::BOUNDARY, VdmaChannel::Direction::H2D, layer_name); @@ -1246,15 +1254,20 @@ static hailo_status fill_boundary_input_layer(CONTROL_PROTOCOL__context_switch_c CHECK_EXPECTED_AS_STATUS(channel_info); channel_info->get().set_pcie_stream_index(stream_index); + auto vdma_channel = resources_manager.create_boundary_vdma_channel(channel_index.value(), frame_credits_in_bytes, + network_name, layer_name, VdmaChannel::Direction::H2D); + CHECK_EXPECTED_AS_STATUS(vdma_channel); + // 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 */ + const uint32_t initial_credit_size = get_initial_credit_size(edge_layer_info); 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); + vdma_channel.value()->get_page_size(), initial_credit_size); CHECK_SUCCESS(status); return HAILO_SUCCESS; @@ -1306,17 +1319,17 @@ static hailo_status fill_inter_context_input_layer(CONTROL_PROTOCOL__context_swi stream_index, src_context, dst_context, h2d_channel_index.value()); /* Update metadata */ + const uint32_t initial_credit_size = get_initial_credit_size(*edge_layer_info); 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()); + intermediate_buffer.get_host_buffer_info(), initial_credit_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) + const std::string &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); @@ -1327,19 +1340,21 @@ static hailo_status fill_boundary_output_layer(CONTROL_PROTOCOL__context_switch_ CHECK_EXPECTED_AS_STATUS(channel_info); channel_info->get().set_pcie_stream_index(stream_index); + + auto vdma_channel = resources_manager.create_boundary_vdma_channel(channel_index.value(), frame_credits_in_bytes, + network_name, layer_name, VdmaChannel::Direction::D2H); + CHECK_EXPECTED_AS_STATUS(vdma_channel); + auto page_size = vdma_channel.value()->get_page_size(); + // 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); + nn_stream_config, frame_credits_in_bytes, page_size); CHECK_SUCCESS(status); return HAILO_SUCCESS; @@ -1349,7 +1364,7 @@ static hailo_status fill_inter_context_output_layer(CONTROL_PROTOCOL__context_sw 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 &channels_to_unlock, uint8_t network_index, const std::string &partial_network_name) + std::set &channels_to_unlock, uint8_t network_index, const std::string &network_name) { std::vector connected_h2d_channels; @@ -1366,7 +1381,7 @@ static hailo_status fill_inter_context_output_layer(CONTROL_PROTOCOL__context_sw 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); + stream_index, src_context, network_name); CHECK_EXPECTED_AS_STATUS(intermediate_buffer_exp); auto &intermediate_buffer = intermediate_buffer_exp->get(); @@ -1375,9 +1390,7 @@ static hailo_status fill_inter_context_output_layer(CONTROL_PROTOCOL__context_sw /* 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()); + stream_index, d2h_channel_index.value(), network_index, nn_stream_config, intermediate_buffer.get_host_buffer_info()); CHECK_SUCCESS(status); return HAILO_SUCCESS; @@ -1391,32 +1404,30 @@ static hailo_status fill_ddr_layer_multi_context(CONTROL_PROTOCOL__context_switc { /* Find out if the connected layer has already been parsed */ uint8_t channel_index = 0; + + CHECK(resources_manager.get_supported_features().padded_ddr_buffers, HAILO_INVALID_HEF, + "Failed opening non-compatible HEF that uses the following deprecated features: host-managed DDR buffers." + "Please re-compile the HEF using a newer Dataflow Compiler version (v3.11.0 or newer)"); + 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); - } + 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); - } + 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! */ @@ -1427,9 +1438,11 @@ static hailo_status fill_ddr_layer_multi_context(CONTROL_PROTOCOL__context_switc 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); + const uint32_t initial_credit_size = get_initial_credit_size(edge_layer_proto); 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); + nn_stream_config, ddr_info.intermediate_buffer->dma_address(), ddr_info.intermediate_buffer->depth(), + initial_credit_size); } } else if (HAILO_D2H_STREAM == direction) { /* we have the info already, just validate saved info, add ch_index, and return success! */ @@ -1443,8 +1456,8 @@ static hailo_status fill_ddr_layer_multi_context(CONTROL_PROTOCOL__context_switc 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(ddr_info.intermediate_buffer->descs_count() - 1), ddr_info.intermediate_buffer->desc_page_size(), - ddr_info.intermediate_buffer->depth(), fw_managed_channel); + ddr_info.intermediate_buffer->desc_page_size(), ddr_info.intermediate_buffer->depth(), + ddr_info.min_buffered_rows); } } else { LOGGER__ERROR("Invalid layer direction"); @@ -1486,40 +1499,32 @@ static hailo_status fill_ddr_layer_multi_context(CONTROL_PROTOCOL__context_switc 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(); + 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 - } + auto programed_descs = ddr_buffer->get().program_ddr(); + CHECK_EXPECTED_AS_STATUS(programed_descs); + local_info.initial_programed_descs = programed_descs.release(); local_info.desc_list_size_mask = static_cast(local_info.intermediate_buffer->descs_count() - 1); // Add layer to metadata if (HAILO_H2D_STREAM == direction) { + const uint32_t initial_credit_size = get_initial_credit_size(edge_layer_proto); 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); + initial_credit_size); 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(local_info.intermediate_buffer->descs_count() - 1), local_info.intermediate_buffer->desc_page_size(), - local_info.intermediate_buffer->depth(), fw_managed_channel); + local_info.intermediate_buffer->desc_page_size(), local_info.intermediate_buffer->depth(), + local_info.min_buffered_rows); CHECK_SUCCESS(status); } else { LOGGER__ERROR("Invalid layer direction"); @@ -1539,15 +1544,27 @@ Expected HefUtils::get_partial_network_name_by_index(const ProtoHEF 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(); + auto partial_network_name = network_group_proto.network_group_metadata().network_group_name(); + return partial_network_name; } } +std::string HefUtils::get_network_name(const std::string &net_group_name, const std::string &partial_network_name) +{ + return net_group_name + HAILO_DEFAULT_NETWORK_NAME_QUALIFIER + partial_network_name; +} + +std::string HefUtils::get_network_name(const ProtoHEFNetworkGroup &net_group, const std::string &partial_network_name) +{ + return HefUtils::get_network_name(net_group.network_group_metadata().network_group_name(), 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 &channels_to_unlock) + uint8_t context_index, std::set &channels_to_unlock, const std::string &network_name, + uint8_t network_index) { uint8_t stream_index = 0; uint32_t frame_credits_in_bytes = 0; @@ -1565,8 +1582,6 @@ static hailo_status parse_and_fill_h2d_layer_multi_context( 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((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); @@ -1574,7 +1589,8 @@ static hailo_status parse_and_fill_h2d_layer_multi_context( 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); + edge_layer_proto, *nn_stream_config, channels_parsing_info, resources_manager, layer_name, + network_name, network_index, frame_credits_in_bytes); case ProtoHEFEdgeConnectionType::PROTO__EDGE_CONNECTION_TYPE__INTERMEDIATE: return fill_inter_context_input_layer(context_info, context_meta_data_head_pointer, resources_manager, @@ -1592,11 +1608,12 @@ static hailo_status parse_and_fill_h2d_layer_multi_context( } } -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 &channels_to_unlock) +static hailo_status parse_and_fill_d2h_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 &channels_to_unlock, const std::string &network_name, + uint8_t network_index) { uint8_t stream_index = 0; uint32_t frame_credits_in_bytes = 0; @@ -1633,23 +1650,17 @@ static hailo_status parse_and_fill_d2h_layer_multi_context(ProtoHEFNetworkGroupP return HAILO_INVALID_HEF; } - auto support_multi_networks = resources_manager.get_supported_features().multi_network_support; - auto network_index = static_cast((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()); + network_index, network_name); 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()); + &edge_layer_proto, channels_parsing_info, channels_to_unlock, network_index, network_name); case ProtoHEFEdgeConnectionType::PROTO__EDGE_CONNECTION_TYPE__DDR: return fill_ddr_layer_multi_context(context_info, context_meta_data_head_pointer, resources_manager, context_index, @@ -1662,49 +1673,6 @@ static hailo_status parse_and_fill_d2h_layer_multi_context(ProtoHEFNetworkGroupP } } -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 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) { @@ -1737,6 +1705,64 @@ static hailo_status fill_ddr_buffers_info(CONTROL_PROTOCOL__context_switch_conte return HAILO_SUCCESS; } +static hailo_status parse_and_fill_edge_layers_mapping(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 channels_to_unlock = {}; + + const auto number_of_edge_layers = context_metadata->edge_layers_size(); + CHECK(0 < number_of_edge_layers, HAILO_INVALID_HEF, "No edge layers in this context"); + 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 (const auto &edge_layer_proto : context_metadata->edge_layers()) { + auto support_multi_networks = resources_manager.get_supported_features().multi_network_support; + auto network_index = static_cast((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); + auto network_name = HefUtils::get_network_name(*network_group_proto, partial_network_name.value()); + + 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, + network_name, network_index); + 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(context_info, context_meta_data_head_pointer, + edge_layer_proto, channels_parsing_info, resources_manager, context_index, channels_to_unlock, + network_name, network_index); + 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; + } + } + + status = fill_ddr_buffers_info(context_info, context_meta_data_head_pointer, resources_manager, context_index); + CHECK_SUCCESS(status); + + return HAILO_SUCCESS; +} + // Returns pairs of form [start, end] (inclusive) of repeated 'ContextSwitchConfigAction's in the given vector static std::vector> get_repreated_actions_boundary_indices( const std::vector &actions) @@ -1819,14 +1845,15 @@ static std::map get_start_indexes_of_repeated_actions( return result; } -static std::set get_end_indexes_of_write_ccw_actions( +static std::set get_end_indexes_of_action_type( const std::vector &actions, - const std::vector> &repeated_indexes) + const std::vector> &repeated_indexes, + const ContextSwitchConfigAction::Type &required_action_type) { std::set 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) { + if (required_action_type != curr_action_type) { continue; } @@ -1837,7 +1864,7 @@ static std::set get_end_indexes_of_write_ccw_actions( } static hailo_status proccess_write_ccw_action(const ContextSwitchConfigActionPtr &configuration_action, - std::vector &config_resources, std::set &pending_cfg_ch_buffer, + std::vector &config_resources, std::set &pending_cfg_ch_buffer, std::vector &total_ccw_bursts, const std::set &end_indexes_of_write_ccw_actions, const uint32_t &action_index, const bool support_pre_fetch, std::vector &processed_configuration_actions) @@ -1862,11 +1889,16 @@ static hailo_status proccess_write_ccw_action(const ContextSwitchConfigActionPtr /* 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()); + CHECK(total_ccw_bursts.size() == config_resources.size(), HAILO_INTERNAL_FAILURE, "Invalid cfg channels count"); + for (const auto cfg_channel : pending_cfg_ch_buffer) { + CHECK(cfg_channel < config_resources.size(), HAILO_INTERNAL_FAILURE, "Invalid cfg channel index"); + + auto fetch_config_action = support_pre_fetch ? + AddCcwBurstAction::create(cfg_channel, total_ccw_bursts[cfg_channel]) : + CreateConfigDescAndFetchAction::create(cfg_channel, config_resources[cfg_channel]); + CHECK_EXPECTED_AS_STATUS(fetch_config_action); + processed_configuration_actions.emplace_back(fetch_config_action.release()); + } /* Cleanups */ pending_cfg_ch_buffer.clear(); @@ -1880,15 +1912,31 @@ static hailo_status proccess_write_ccw_action(const ContextSwitchConfigActionPtr return HAILO_SUCCESS; } +static hailo_status proccess_trigger_new_data_input_action(const ContextSwitchConfigActionPtr &configuration_action, + const std::set &end_indexes_of_trigger_new_data_input_actions, + const uint32_t &action_index, std::vector &processed_configuration_actions) +{ + // At the end of a consecutive group of TriggerNewDataFromDataInput actions, we can trigger the BurstCreditsTask + // in the FW, via StartBurstCreditsTaskAction. + processed_configuration_actions.emplace_back(configuration_action); + if (contains(end_indexes_of_trigger_new_data_input_actions, action_index)) { + auto start_burst_credits_task_action = StartBurstCreditsTaskAction::create(); + CHECK_EXPECTED_AS_STATUS(start_burst_credits_task_action); + processed_configuration_actions.emplace_back(start_burst_credits_task_action.release()); + } + + 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. +// * At the end of each consecutive group of TriggerNewDataFromDataInput, a StartBurstCreditsTaskAction is added. static Expected> process_configuration_actions( std::vector &input_configuration_actions, - std::vector &config_resources, const bool support_pre_fetch) + std::vector &config_resources, const bool support_pre_fetch) { std::vector processed_configuration_actions; @@ -1896,8 +1944,12 @@ static Expected> process_configuration std::vector 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); + const auto start_indexes_of_repeated_actions = get_start_indexes_of_repeated_actions( + input_configuration_actions, repeated_indexes); + const auto end_indexes_of_write_ccws = get_end_indexes_of_action_type(input_configuration_actions, + repeated_indexes, ContextSwitchConfigAction::Type::WriteDataCcw); + const auto end_indexes_of_trigger_new_data_from_inputs = get_end_indexes_of_action_type( + input_configuration_actions, repeated_indexes, ContextSwitchConfigAction::Type::TriggerNewDataFromDataInput); 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" @@ -1914,11 +1966,13 @@ static Expected> process_configuration // 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); + total_ccw_bursts, end_indexes_of_write_ccws, action_index, support_pre_fetch, processed_configuration_actions); + CHECK_SUCCESS_AS_EXPECTED(status); + } else if (ContextSwitchConfigAction::Type::TriggerNewDataFromDataInput == configuration_action->get_type()) { + auto status = proccess_trigger_new_data_input_action(configuration_action, end_indexes_of_trigger_new_data_from_inputs, + action_index, processed_configuration_actions); CHECK_SUCCESS_AS_EXPECTED(status); } else { // Add the current action @@ -1937,7 +1991,7 @@ static bool is_mercury_device_type(const ProtoHEFHwArch &hw_arch) } static hailo_status parse_actions_in_operation(const ProtoHEFOperation &operation, - Device &device, const ProtoHEFHwArch &hw_arch, std::vector &config_resources, + Device &device, const ProtoHEFHwArch &hw_arch, std::vector &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) { @@ -1977,26 +2031,16 @@ static hailo_status fill_context_recepies_for_multi_context(ProtoHEFNetworkGroup 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, + status = parse_and_fill_edge_layers_mapping(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(proto_context.metadata().shmiglue_info().should_use_shmiglue()); - /* Parse context */ + // Parse context for (const auto &operation : proto_context.operations()) { const auto operation_trigger = ContextSwitchTrigger::create(operation.trigger()); CHECK_EXPECTED_AS_STATUS(operation_trigger); @@ -2017,14 +2061,24 @@ static hailo_status fill_context_recepies_for_multi_context(ProtoHEFNetworkGroup 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) + ContextSwitchChannelsParsingInfo &channels_parsing_info, 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 */ + if (resources_manager.get_supported_features().preliminary_run_asap) { + // Add edge layers mapping (only preliminary_run_asap networks have edge layers in the preliminary context) + static const auto PRELIMINARY_CONTEXT_INDEX = 0; + auto status = parse_and_fill_edge_layers_mapping(network_group_proto, &context_info, &context_meta_data_head_pointer, + &(network_group_proto->contexts(PRELIMINARY_CONTEXT_INDEX).metadata()), channels_parsing_info, resources_manager, + PRELIMINARY_CONTEXT_INDEX); + CHECK_SUCCESS(status); + } + + // 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); @@ -2036,7 +2090,7 @@ static hailo_status fill_preliminary_config_recepies_for_multi_context(const Pro CHECK_SUCCESS(status); } - // update context_network_data_length per context, and preliminary_context_descriptors count in main header + // Update context_network_data_length per context, and preliminary_context_descriptors count in main header context_info.context_network_data_length = static_cast(context_meta_data_head_pointer - context_info.context_network_data); @@ -2071,24 +2125,32 @@ Expected> Hef::Impl::create_resources_manager( auto preliminary_context = resources_manager->add_new_context(); CHECK_EXPECTED(preliminary_context); + // TODO: Support sharing of ContextSwitchChannelsParsingInfo between fill_preliminary_config_recepies_for_multi_context + // and fill_context_recepies_for_multi_context (HRT-6683). + // When running in preliminary_run_asap mode the channels used by the first dynamic context and by the preliminary + // context are the same channels. We need to add the sane edge_layers for both contexts (look for calls to + // hef_metadata__add_edge_layer_header) so that the needed actions will be added to the action list. However, + // due to the logic in ResourceManager::get_available_channel_index's blacklist channels that should be the same + // will get different indexes. Need to refactor the logic of that function and the entire channel index allocation logic. + ContextSwitchChannelsParsingInfo channels_parsing_info_preliminary{}; 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); + resources_manager.value(), channels_parsing_info_preliminary, 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 = {}; - + ContextSwitchChannelsParsingInfo channels_parsing_info_dynamic{}; 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); + context_index, network_group_proto->contexts(context_index), device, channels_parsing_info_dynamic); CHECK_SUCCESS_AS_EXPECTED(status); } resources_manager->update_dynamic_contexts_buffer_info(); - status = resources_manager->create_vdma_channels(); + status = resources_manager->create_internal_vdma_channels(); CHECK_SUCCESS_AS_EXPECTED(status); auto resources_manager_ptr = make_shared_nothrow(resources_manager.release()); @@ -2383,7 +2445,7 @@ hailo_status ContextSwitchTrigger::add_to_trigger_group(CONTROL_PROTOCOL__contex } Expected ContextSwitchConfigAction::create(const ProtoHEFAction &proto_action, Device &device, - std::vector &config, const ResourcesManager &resources_manager, const ProtoHEFNetworkGroup &net_group, + std::vector &config, const ResourcesManager &resources_manager, const ProtoHEFNetworkGroup &net_group, bool support_pre_fetch) { switch (proto_action.action_case()) { @@ -2516,7 +2578,7 @@ bool WriteDataAction::supports_repeated_block() const } Expected WriteDataCcwAction::create(const ProtoHEFAction& proto_action, - std::vector &config, bool support_pre_fetch) + std::vector &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, @@ -2525,39 +2587,34 @@ Expected WriteDataCcwAction::create(const ProtoHEF 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)); + auto result = ContextSwitchConfigActionPtr(new (std::nothrow) WriteDataCcwAction(proto_action, + config[proto_action.write_data_ccw().cfg_channel_index()], support_pre_fetch)); CHECK_AS_EXPECTED((nullptr != result), HAILO_OUT_OF_HOST_MEMORY); return result; } -WriteDataCcwAction::WriteDataCcwAction(const ProtoHEFAction& proto_action, std::vector &config, +WriteDataCcwAction::WriteDataCcwAction(const ProtoHEFAction& proto_action, ConfigBuffer &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() +bool WriteDataCcwAction::is_last_ccw_write() { - 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()) { + if ((write_size + m_config.get_current_buffer_size()) != m_config.get_total_cfg_size()) { return false; } return true; } -hailo_status WriteDataCcwAction::pad_with_nops(uint8_t cfg_channel_index) +hailo_status WriteDataCcwAction::pad_with_nops() { - auto page_size = m_config[cfg_channel_index].get_page_size(); - auto buffer_size = m_config[cfg_channel_index].get_total_cfg_size(); + auto page_size = m_config.desc_page_size(); + auto buffer_size = m_config.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); @@ -2570,7 +2627,7 @@ hailo_status WriteDataCcwAction::pad_with_nops(uint8_t cfg_channel_index) 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(&CCW_NOP), sizeof(CCW_NOP)); + m_config.write(reinterpret_cast(&CCW_NOP), sizeof(CCW_NOP)); } return HAILO_SUCCESS; @@ -2579,13 +2636,22 @@ hailo_status WriteDataCcwAction::pad_with_nops(uint8_t cfg_channel_index) 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(m_proto_action.write_data_ccw().cfg_channel_index())); + const bool is_last_write = is_last_ccw_write(); + if (m_support_pre_fetch && is_last_write) { + auto status = pad_with_nops(); 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()); + auto status = m_config.write(m_proto_action.write_data_ccw().data().data(), + m_proto_action.write_data_ccw().data().length()); + CHECK_SUCCESS(status); + + if (m_support_pre_fetch && is_last_write) { + auto desc_count = m_config.program_descriptors(); + CHECK_EXPECTED_AS_STATUS(desc_count); + } + + return HAILO_SUCCESS; } bool WriteDataCcwAction::supports_repeated_block() const @@ -2594,53 +2660,54 @@ bool WriteDataCcwAction::supports_repeated_block() const return false; } -Expected CreateConfigDescAndFetchAction::create( - std::vector &config_resources, const std::set &pending_cfg_ch_buffer, - const std::vector &ccw_bursts, bool support_pre_fetch) +Expected AddCcwBurstAction::create(uint8_t channel_index, uint16_t ccw_bursts) { - 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)); + auto result = ContextSwitchConfigActionPtr(new (std::nothrow) AddCcwBurstAction(channel_index, ccw_bursts)); CHECK_AS_EXPECTED((nullptr != result), HAILO_OUT_OF_HOST_MEMORY); return result; } -CreateConfigDescAndFetchAction::CreateConfigDescAndFetchAction(std::vector &config, const std::set &pending_cfg_ch_buffer, - const std::vector &ccw_bursts, bool support_pre_fetch) : +AddCcwBurstAction::AddCcwBurstAction(uint8_t channel_index, uint16_t ccw_bursts) : + ContextSwitchConfigAction(Type::AddCcwBurst), + m_channel_index(channel_index), + m_ccw_bursts(ccw_bursts) +{} + +hailo_status AddCcwBurstAction::execute(CONTROL_PROTOCOL__context_switch_context_info_t *context_info, + uint8_t **context_meta_data_head_pointer) +{ + return HEF_METADATA__add_ccw_bursts_action(context_info, context_meta_data_head_pointer, + m_ccw_bursts, m_channel_index, m_is_in_repeated_block); +} + +bool AddCcwBurstAction::supports_repeated_block() const +{ + return false; +} + +Expected CreateConfigDescAndFetchAction::create(uint8_t channel_index, ConfigBuffer &config_buffer) +{ + auto result = ContextSwitchConfigActionPtr(new (std::nothrow) CreateConfigDescAndFetchAction(channel_index, config_buffer)); + CHECK_AS_EXPECTED((nullptr != result), HAILO_OUT_OF_HOST_MEMORY); + return result; +} + +CreateConfigDescAndFetchAction::CreateConfigDescAndFetchAction(uint8_t channel_index, ConfigBuffer &config_buffer) : 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) + m_channel_index(channel_index), + m_config_buffer(config_buffer) {} 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) { + const auto desc_count = m_config_buffer.program_descriptors(); + CHECK_EXPECTED_AS_STATUS(desc_count); - 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(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; + CHECK(IS_FIT_IN_UINT16(desc_count.value()), HAILO_INVALID_OPERATION, + "On cfg with continuous mode, max descriptors size must fit in uint16_t"); + return HEF_METADATA__add_read_vdma_action(context_info, context_meta_data_head_pointer, + static_cast(desc_count.value()), m_channel_index, m_is_in_repeated_block); } bool CreateConfigDescAndFetchAction::supports_repeated_block() const @@ -2652,6 +2719,31 @@ bool CreateConfigDescAndFetchAction::supports_repeated_block() const return false; } +Expected StartBurstCreditsTaskAction::create() +{ + auto result = ContextSwitchConfigActionPtr(new (std::nothrow) StartBurstCreditsTaskAction()); + CHECK_AS_EXPECTED((nullptr != result), HAILO_OUT_OF_HOST_MEMORY); + return result; +} + +StartBurstCreditsTaskAction::StartBurstCreditsTaskAction() : + ContextSwitchConfigAction(Type::StartBurstCreditsTask) +{} + +hailo_status StartBurstCreditsTaskAction::execute(CONTROL_PROTOCOL__context_switch_context_info_t *context_info, + uint8_t **context_meta_data_head_pointer) +{ + return HEF_METADATA__burst_credits_task_start(context_info, context_meta_data_head_pointer, + m_is_in_repeated_block); +} + +bool StartBurstCreditsTaskAction::supports_repeated_block() const +{ + // We don't support repeated blocks for this action, since only one is added per group of consecutive + // TriggerNewDataFromDataInput actions. + return false; +} + Expected RepeatedHeaderAction::create( CONTROL_PROTOCOL__ACTION_TYPE_t sub_action_type, uint8_t num_actions) { @@ -2731,14 +2823,15 @@ Expected EnableLcuAction::create(const ProtoHEFAct 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); + auto network_name = HefUtils::get_network_name(net_group, partial_network_name.value()); + + auto batch_size = resources_manager.get_network_batch_size(network_name); + CHECK_EXPECTED(batch_size); const auto kernel_done_address = static_cast(proto_action.enable_lcu().lcu_kernel_done_address()); const auto kernel_done_count = static_cast(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()); + (CONTEXT_SWITCH_DEFS__ENABLE_LCU_DEFAULT_KERNEL_COUNT == kernel_done_count); auto result = ContextSwitchConfigActionPtr(new (std::nothrow) EnableLcuAction(proto_action, is_default, network_index)); CHECK_AS_EXPECTED((nullptr != result), HAILO_OUT_OF_HOST_MEMORY); @@ -2769,7 +2862,7 @@ hailo_status EnableLcuAction::execute(CONTROL_PROTOCOL__context_switch_context_i const auto lcu_index = static_cast(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); + cluster_index, lcu_index, m_network_index, m_is_in_repeated_block); } else { const auto kernel_done_address = static_cast(m_proto_action.enable_lcu().lcu_kernel_done_address()); const auto kernel_done_count = static_cast(m_proto_action.enable_lcu().lcu_kernel_done_count()); @@ -3234,12 +3327,12 @@ Expected> Hef::make_input_vstream_ } Expected> Hef::Impl::make_input_vstream_params( - const std::string &net_group_name, const std::string &partial_network_name, bool quantized, + const std::string &net_group_name, const std::string &network_name, bool quantized, hailo_format_type_t format_type, uint32_t timeout_ms, uint32_t queue_size) { std::map 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); + network_name, input_vstreams_params, quantized, format_type, timeout_ms, queue_size); CHECK_SUCCESS_AS_EXPECTED(status); return input_vstreams_params; @@ -3257,24 +3350,24 @@ Expected> Hef::make_output_vstream } Expected> Hef::Impl::make_output_vstream_params( - const std::string &net_group_name, const std::string &partial_network_name, bool quantized, + const std::string &net_group_name, const std::string &network_name, bool quantized, hailo_format_type_t format_type, uint32_t timeout_ms, uint32_t queue_size) { std::map 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); + 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 &input_vstreams_params, + const std::string &network_name, std::map &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); + auto input_vstream_infos = network_group_metadata->get_input_vstream_infos(network_name); CHECK_EXPECTED_AS_STATUS(input_vstream_infos); return fill_missing_vstream_params_with_default(input_vstreams_params, input_vstream_infos.value(), @@ -3282,12 +3375,12 @@ hailo_status Hef::Impl::fill_missing_input_vstream_params_with_default(const std } 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 &output_vstream_params, + const std::string &network_name, std::map &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); + auto output_vstream_infos = network_group_metadata->get_output_vstream_infos(network_name); CHECK_EXPECTED_AS_STATUS(output_vstream_infos); return fill_missing_vstream_params_with_default(output_vstream_params, output_vstream_infos.value(), @@ -3400,16 +3493,16 @@ Expected> Hef::Impl::create_ne 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; + for (const auto &partial_network_name : net_group.value()->networks_names()) { + auto network_name = HefUtils::get_network_name(net_group_name, partial_network_name); 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)); + auto network_name = HailoRTDefaults::get_network_name(net_group_name); + results.emplace(std::make_pair(network_name, params)); } return results; @@ -3456,51 +3549,64 @@ Expected> Hef::Impl::create_str NetworkGroupMetadata::NetworkGroupMetadata(const std::string &network_group_name, std::vector &&layer_infos, std::vector &&sorted_output_names, NetworkGroupSupportedFeatures &supported_features, - const std::vector &sorted_partial_network_names) + const std::vector &sorted_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) + m_sorted_network_names(sorted_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); + m_input_layer_infos[layer_info.network_name].push_back(layer_info); } else { - m_output_layer_infos[layer_info.partial_network_name].push_back(layer_info); + m_output_layer_infos[layer_info.network_name].push_back(layer_info); } } } -Expected> NetworkGroupMetadata::get_input_layer_infos(const std::string &partial_network_name) const +Expected NetworkGroupMetadata::get_layer_info_by_stream_name(const std::string &stream_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); + auto layer_infos = 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); +} + +Expected> NetworkGroupMetadata::get_input_layer_infos(const std::string &network_name) const +{ + CHECK_AS_EXPECTED((network_name.empty()) || (network_name == default_network_name()) || contains(m_input_layer_infos, network_name), + HAILO_NOT_FOUND, "Network name {} is not found in networks metadata", network_name); std::vector 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)) { + if ((network_name == layer_infos_pair.first) || (network_name.empty()) || (network_name == default_network_name())) { res.insert(res.end(), layer_infos_pair.second.begin(), layer_infos_pair.second.end()); } } return res; } -Expected> NetworkGroupMetadata::get_output_layer_infos(const std::string &partial_network_name) const +Expected> NetworkGroupMetadata::get_output_layer_infos(const std::string &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); + CHECK_AS_EXPECTED((network_name.empty()) || (network_name == default_network_name()) || contains(m_output_layer_infos, network_name), + HAILO_NOT_FOUND, "Network name {} is not found in networks metadata", network_name); std::vector 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)) { + if ((network_name == layer_infos_pair.first) || (network_name.empty()) || (network_name == default_network_name())) { res.insert(res.end(), layer_infos_pair.second.begin(), layer_infos_pair.second.end()); } } return res; } -Expected> NetworkGroupMetadata::get_all_layer_infos(const std::string &partial_network_name) const +Expected> NetworkGroupMetadata::get_all_layer_infos(const std::string &network_name) const { - auto input_layer_infos = get_input_layer_infos(partial_network_name); + auto input_layer_infos = get_input_layer_infos(network_name); CHECK_EXPECTED(input_layer_infos); - auto output_layer_infos = get_output_layer_infos(partial_network_name); + auto output_layer_infos = get_output_layer_infos(network_name); CHECK_EXPECTED(output_layer_infos); std::vector res; @@ -3511,28 +3617,28 @@ Expected> NetworkGroupMetadata::get_all_layer_infos(const return res; } -Expected> NetworkGroupMetadata::get_input_stream_infos(const std::string &partial_network_name) const +Expected> NetworkGroupMetadata::get_input_stream_infos(const std::string &network_name) const { - auto input_layer_infos = get_input_layer_infos(partial_network_name); + auto input_layer_infos = get_input_layer_infos(network_name); CHECK_EXPECTED(input_layer_infos); return convert_layer_infos_to_stream_infos(input_layer_infos.value()); } -Expected> NetworkGroupMetadata::get_output_stream_infos(const std::string &partial_network_name) const +Expected> NetworkGroupMetadata::get_output_stream_infos(const std::string &network_name) const { - auto output_layer_infos = get_output_layer_infos(partial_network_name); + auto output_layer_infos = get_output_layer_infos(network_name); CHECK_EXPECTED(output_layer_infos); return convert_layer_infos_to_stream_infos(output_layer_infos.value()); } -Expected> NetworkGroupMetadata::get_all_stream_infos(const std::string &partial_network_name) const +Expected> NetworkGroupMetadata::get_all_stream_infos(const std::string &network_name) const { - auto input_stream_infos = get_input_stream_infos(partial_network_name); + auto input_stream_infos = get_input_stream_infos(network_name); CHECK_EXPECTED(input_stream_infos); - auto output_stream_infos = get_output_stream_infos(partial_network_name); + auto output_stream_infos = get_output_stream_infos(network_name); CHECK_EXPECTED(output_stream_infos); std::vector res; @@ -3543,17 +3649,17 @@ Expected> NetworkGroupMetadata::get_all_stream_ return res; } -Expected> NetworkGroupMetadata::get_input_vstream_infos(const std::string &partial_network_name) const +Expected> NetworkGroupMetadata::get_input_vstream_infos(const std::string &network_name) const { - auto input_layer_infos = get_input_layer_infos(partial_network_name); + auto input_layer_infos = get_input_layer_infos(network_name); CHECK_EXPECTED(input_layer_infos); return convert_layer_infos_to_vstream_infos(input_layer_infos.value()); } -Expected> NetworkGroupMetadata::get_output_vstream_infos(const std::string &partial_network_name) const +Expected> NetworkGroupMetadata::get_output_vstream_infos(const std::string &network_name) const { - auto output_layer_infos = get_output_layer_infos(partial_network_name); + auto output_layer_infos = get_output_layer_infos(network_name); CHECK_EXPECTED(output_layer_infos); auto res = convert_layer_infos_to_vstream_infos(output_layer_infos.value()); @@ -3584,12 +3690,12 @@ Expected> NetworkGroupMetadata::get_output_vst return res; } -Expected> NetworkGroupMetadata::get_all_vstream_infos(const std::string &partial_network_name) const +Expected> NetworkGroupMetadata::get_all_vstream_infos(const std::string &network_name) const { - auto input_vstream_infos = get_input_vstream_infos(partial_network_name); + auto input_vstream_infos = get_input_vstream_infos(network_name); CHECK_EXPECTED(input_vstream_infos); - auto output_vstream_infos = get_output_vstream_infos(partial_network_name); + auto output_vstream_infos = get_output_vstream_infos(network_name); CHECK_EXPECTED(output_vstream_infos); std::vector res; @@ -3659,7 +3765,7 @@ std::vector NetworkGroupMetadata::convert_layer_infos_to_v { std::vector res; for (auto &layer_info : layer_infos) { - auto vstream_infos = LayerInfoUtils::get_vstream_infos_from_layer_info(layer_info, m_network_group_name); + auto vstream_infos = LayerInfoUtils::get_vstream_infos_from_layer_info(layer_info); 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)) { @@ -3670,31 +3776,13 @@ std::vector NetworkGroupMetadata::convert_layer_infos_to_v return res; } -Expected 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(partial_network_name); - } - } - - LOGGER__ERROR("Found no partial network name for network name {}", network_name); - return make_unexpected(HAILO_NOT_FOUND); -} - Expected> NetworkGroupMetadata::get_network_infos() const { std::vector 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) { + network_infos.reserve(m_sorted_network_names.size()); + for (auto const &network_name : m_sorted_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); diff --git a/hailort/libhailort/src/hef_internal.hpp b/hailort/libhailort/src/hef_internal.hpp index a0a0ca4..31b93c2 100644 --- a/hailort/libhailort/src/hef_internal.hpp +++ b/hailort/libhailort/src/hef_internal.hpp @@ -48,7 +48,7 @@ namespace hailort { class ResourcesManager; -class ConfigResources; +class ConfigBuffer; class VdmaConfigActivatedNetworkGroup; using VdmaConfigActiveAppHolder = ActiveNetworkGroupHolder; using ProtoHEFNetworkGroupPtr = std::shared_ptr; @@ -86,7 +86,8 @@ static const std::vector SUPPORTED_EXTENSIONS = { COMPRESSED_PARAMS, TRANSPOSE_COMPONENT, IS_NMS_MULTI_CONTEXT, - OFFLOAD_ARGMAX + OFFLOAD_ARGMAX, + PRELIMINARY_RUN_ASAP // Extention added in platform 4.8 release }; struct HefParsingInfo @@ -102,6 +103,7 @@ struct NetworkGroupSupportedFeatures { bool padded_ddr_buffers; bool multi_network_support; bool multi_context; + bool preliminary_run_asap; }; static inline bool is_h2d_boundary_info_layer(const ProtoHEFEdgeLayer& layer) @@ -150,28 +152,24 @@ class HailoRTDriver; class NetworkGroupMetadata { public: NetworkGroupMetadata(const std::string &network_group_name, std::vector &&layer_infos, std::vector &&sorted_output_names, - NetworkGroupSupportedFeatures &supported_features, const std::vector &sorted_partial_network_names); + NetworkGroupSupportedFeatures &supported_features, const std::vector &sorted_network_names); - Expected> get_input_layer_infos(const std::string &partial_network_name = HAILO_DEFAULT_PARTIAL_NETWORK_NAME) const; - Expected> get_output_layer_infos(const std::string &partial_network_name = HAILO_DEFAULT_PARTIAL_NETWORK_NAME) const; - Expected> get_all_layer_infos(const std::string &partial_network_name = HAILO_DEFAULT_PARTIAL_NETWORK_NAME) const; + Expected> get_input_layer_infos(const std::string &network_name = "") const; + Expected> get_output_layer_infos(const std::string &network_name = "") const; + Expected> get_all_layer_infos(const std::string &network_name = "") const; + Expected get_layer_info_by_stream_name(const std::string &stream_name) const; - Expected> get_input_stream_infos(const std::string &partial_network_name = HAILO_DEFAULT_PARTIAL_NETWORK_NAME) const; - Expected> get_output_stream_infos(const std::string &partial_network_name = HAILO_DEFAULT_PARTIAL_NETWORK_NAME) const; - Expected> get_all_stream_infos(const std::string &partial_network_name = HAILO_DEFAULT_PARTIAL_NETWORK_NAME) const; + Expected> get_input_stream_infos(const std::string &network_name = "") const; + Expected> get_output_stream_infos(const std::string &network_name = "") const; + Expected> get_all_stream_infos(const std::string &network_name = "") const; - Expected> get_input_vstream_infos(const std::string &partial_network_name = HAILO_DEFAULT_PARTIAL_NETWORK_NAME) const; - Expected> get_output_vstream_infos(const std::string &partial_network_name = HAILO_DEFAULT_PARTIAL_NETWORK_NAME) const; - Expected> get_all_vstream_infos(const std::string &partial_network_name = HAILO_DEFAULT_PARTIAL_NETWORK_NAME) const; + Expected> get_input_vstream_infos(const std::string &network_name = "") const; + Expected> get_output_vstream_infos(const std::string &network_name = "") const; + Expected> get_all_vstream_infos(const std::string &network_name = "") const; Expected> get_vstream_names_from_stream_name(const std::string &stream_name) const; Expected> 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 get_partial_network_name(const std::string &network_name) const; - Expected> get_network_infos() const; const std::string &network_group_name() const @@ -179,6 +177,11 @@ public: return m_network_group_name; } + const std::string default_network_name() const + { + return HailoRTDefaults::get_network_name(m_network_group_name); + } + const std::vector get_sorted_output_names() const { return m_sorted_output_names; @@ -189,9 +192,9 @@ public: return m_supported_features; } - const std::vector get_partial_network_names() const + const std::vector get_network_names() const { - return m_sorted_partial_network_names; + return m_sorted_network_names; } private: @@ -205,7 +208,7 @@ private: std::string m_network_group_name; std::vector m_sorted_output_names; NetworkGroupSupportedFeatures m_supported_features; - std::vector m_sorted_partial_network_names; + std::vector m_sorted_network_names; }; class Hef::Impl final @@ -225,20 +228,20 @@ public: Expected> get_network_infos(const std::string &net_group_name=""); Expected> get_input_stream_infos(const std::string &net_group_name="", - const std::string &partial_network_name=""); + const std::string &network_name=""); Expected> get_output_stream_infos(const std::string &net_group_name="", - const std::string &partial_network_name=""); + const std::string &network_name=""); Expected> get_all_stream_infos(const std::string &net_group_name="", - const std::string &partial_network_name=""); + const std::string &network_name=""); Expected get_stream_info_by_name(const std::string &stream_name, hailo_stream_direction_t stream_direction, const std::string &net_group_name=""); Expected> get_input_vstream_infos(const std::string &net_group_name="", - const std::string &partial_network_name=""); + const std::string &network_name=""); Expected> get_output_vstream_infos(const std::string &net_group_name="", - const std::string &partial_network_name=""); + const std::string &network_name=""); Expected> get_all_vstream_infos(const std::string &net_group_name="", - const std::string &partial_network_name=""); + const std::string &network_name=""); Expected> get_sorted_output_names(const std::string &net_group_name=""); Expected get_number_of_input_streams(const std::string &net_group_name=""); Expected get_number_of_output_streams(const std::string &net_group_name=""); @@ -247,7 +250,7 @@ public: static bool contains_ddr_layers(const ProtoHEFNetworkGroup& net_group); static hailo_status validate_net_group_unique_layer_names(ProtoHEFNetworkGroupPtr net_group); Expected> get_network_input_vstream_infos(const std::string &net_group_name="", - const std::string &partial_network_name=""); + const std::string &network_name=""); Expected> get_stream_names_from_vstream_name(const std::string &vstream_name, const std::string &net_group_name=""); @@ -287,16 +290,16 @@ public: const hailo_mipi_input_stream_params_t &mipi_params); Expected> make_input_vstream_params( - const std::string &net_group_name, const std::string &partial_network_name, bool quantized, + const std::string &net_group_name, const std::string &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 &input_vstreams_params, + const std::string &network_name, std::map &input_vstreams_params, bool quantized, hailo_format_type_t format_type, uint32_t timeout_ms, uint32_t queue_size); Expected> make_output_vstream_params( - const std::string &net_group_name, const std::string &partial_network_name, bool quantized, + const std::string &net_group_name, const std::string &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 &output_vstream_params, + const std::string &network_name, std::map &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 &vstream_params, std::vector &name_to_format_info, bool quantized, hailo_format_type_t format_type, uint32_t timeout_ms, @@ -334,9 +337,9 @@ private: static Expected get_parsing_info(ProtoHEFNetworkGroupPtr net_group); Expected> get_inputs_vstream_names_and_format_info( - const std::string &net_group_name, const std::string &partial_network_name); + const std::string &net_group_name, const std::string &network_name); Expected> get_outputs_vstream_names_and_format_info( - const std::string &net_group_name, const std::string &partial_network_name); + const std::string &net_group_name, const std::string &network_name); static Expected get_vstream_name_from_original_name_mux(const std::string &original_name, const ProtoHefEdge &layer); static Expected> get_original_names_from_vstream_name_mux(const std::string &vstream_name, const ProtoHefEdge &layer); @@ -394,6 +397,10 @@ public: static Expected> get_network_infos(const ProtoHEFNetworkGroup &net_group, const std::string &net_group_name, const NetworkGroupSupportedFeatures &supported_features); + + static std::string get_network_name(const ProtoHEFNetworkGroup &net_group, const std::string &partial_network_name); + static std::string get_network_name(const std::string &net_group_name, const std::string &partial_network_name); + private: static hailo_status fill_layer_info_with_base_info(const ProtoHEFEdgeLayerBase &base_info, const ProtoHEFEdgeConnectionType &edge_connection_type, @@ -403,7 +410,7 @@ private: 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); + LayerInfo &layer_info, hailo_quant_info_t &defuse_quant_info, const std::string &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, @@ -441,6 +448,7 @@ public: None, WriteData, WriteDataCcw, + AddCcwBurst, CreateDescForCcw, ReadVdma, TriggerSequencer, @@ -453,11 +461,12 @@ public: WaitForModuleConfigDone, AddDdrPairInfo, AddDdrBufferingStart, - AddRrepeated + AddRrepeated, + StartBurstCreditsTask }; static Expected create(const ProtoHEFAction &proto_action, Device &device, - std::vector &config, const ResourcesManager &resources_manager, const ProtoHEFNetworkGroup &net_group, + std::vector &config, const ResourcesManager &resources_manager, const ProtoHEFNetworkGroup &net_group, bool support_pre_fetch); ContextSwitchConfigAction(ContextSwitchConfigAction &&) = default; @@ -529,7 +538,7 @@ class WriteDataCcwAction : public ContextSwitchConfigAction { public: static Expected create(const ProtoHEFAction& proto_action, - std::vector &config, bool support_pre_fetch); + std::vector &config, bool support_pre_fetch); WriteDataCcwAction(WriteDataCcwAction &&) = default; WriteDataCcwAction(const WriteDataCcwAction &) = delete; WriteDataCcwAction &operator=(WriteDataCcwAction &&) = delete; @@ -541,21 +550,37 @@ public: virtual bool supports_repeated_block() const override; private: - WriteDataCcwAction(const ProtoHEFAction& proto_action, std::vector &config, + WriteDataCcwAction(const ProtoHEFAction& proto_action, ConfigBuffer &config, bool support_pre_fetch); - bool should_pad_with_nops(); - hailo_status pad_with_nops(uint8_t cfg_channel_index); + bool is_last_ccw_write(); + hailo_status pad_with_nops(); - std::vector &m_config; + ConfigBuffer &m_config; const bool m_support_pre_fetch; }; +class AddCcwBurstAction : public ContextSwitchConfigAction +{ +public: + static Expected create(uint8_t channel_index, uint16_t ccw_bursts); + + 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: + AddCcwBurstAction(uint8_t channel_index, uint16_t ccw_bursts); + + const uint8_t m_channel_index; + const uint16_t m_ccw_bursts; +}; + class CreateConfigDescAndFetchAction : public ContextSwitchConfigAction { public: - static Expected create(std::vector &config_resources, - const std::set &pending_cfg_ch_buffer, const std::vector &ccw_burst, bool support_pre_fetch); + static Expected create(uint8_t channel_index, ConfigBuffer &config_buffer); + CreateConfigDescAndFetchAction(CreateConfigDescAndFetchAction &&) = default; CreateConfigDescAndFetchAction(const CreateConfigDescAndFetchAction &) = delete; CreateConfigDescAndFetchAction &operator=(CreateConfigDescAndFetchAction &&) = delete; @@ -567,13 +592,29 @@ public: virtual bool supports_repeated_block() const override; private: - CreateConfigDescAndFetchAction(std::vector &config, const std::set &pending_cfg_ch_buffer, - const std::vector &ccw_burst, bool support_pre_fetch); - - std::vector &m_config; - const std::set m_pending_cfg_ch_buffer; - const std::vector m_ccw_bursts; - const bool m_support_pre_fetch; + CreateConfigDescAndFetchAction(uint8_t channel_index, ConfigBuffer &config_buffer); + + const uint8_t m_channel_index; + ConfigBuffer &m_config_buffer; +}; + +class StartBurstCreditsTaskAction : public ContextSwitchConfigAction +{ +public: + static Expected create(); + + StartBurstCreditsTaskAction(StartBurstCreditsTaskAction &&) = default; + StartBurstCreditsTaskAction(const StartBurstCreditsTaskAction &) = delete; + StartBurstCreditsTaskAction &operator=(StartBurstCreditsTaskAction &&) = delete; + StartBurstCreditsTaskAction &operator=(const StartBurstCreditsTaskAction &) = delete; + virtual ~StartBurstCreditsTaskAction() = 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: + StartBurstCreditsTaskAction(); }; class RepeatedHeaderAction : public ContextSwitchConfigAction diff --git a/hailort/libhailort/src/inference_pipeline.cpp b/hailort/libhailort/src/inference_pipeline.cpp index 897cbf7..45d1ce6 100644 --- a/hailort/libhailort/src/inference_pipeline.cpp +++ b/hailort/libhailort/src/inference_pipeline.cpp @@ -12,6 +12,7 @@ #include "vstream_internal.hpp" #include "hailort_defaults.hpp" #include "context_switch/network_group_internal.hpp" +#include "context_switch/network_group_wrapper.hpp" #include @@ -99,7 +100,7 @@ Expected InferVStreams::create(ConfiguredNetworkGroup &net_group, { auto network_infos = net_group.get_network_infos(); CHECK_EXPECTED(network_infos); - auto is_multi_context = (dynamic_cast(net_group)).get_supported_features().multi_context; + auto is_multi_context = (dynamic_cast(net_group)).get_configured_network()->get_supported_features().multi_context; std::map> input_param_count_per_network; size_t total_inputs_found = 0; size_t total_outputs_found = 0; diff --git a/hailort/libhailort/src/intermediate_buffer.cpp b/hailort/libhailort/src/intermediate_buffer.cpp index e34c1e5..540ee6a 100644 --- a/hailort/libhailort/src/intermediate_buffer.cpp +++ b/hailort/libhailort/src/intermediate_buffer.cpp @@ -7,62 +7,36 @@ * @brief Manages intermediate buffer for inter-context or ddr channels. */ -#include "intermediate_buffer.hpp" #include "context_switch/multi_context/resource_manager.hpp" +#include "intermediate_buffer.hpp" +#include "vdma/sg_buffer.hpp" +#include "vdma/continuous_buffer.hpp" + namespace hailort { -Expected> IntermediateBuffer::create(Type type, HailoRTDriver &driver, - const uint32_t transfer_size, const uint16_t batch_size) +Expected IntermediateBuffer::create(HailoRTDriver &driver, + ChannelType channel_type, 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(type)); - return make_unexpected(HAILO_INVALID_OPERATION); - } + auto buffer_ptr = should_use_ccb(driver, channel_type) ? + create_ccb_buffer(driver, transfer_size, batch_size) : + create_sg_buffer(driver, transfer_size, batch_size); + CHECK_EXPECTED(buffer_ptr); + + return IntermediateBuffer(buffer_ptr.release(), transfer_size, batch_size); } -Expected> 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(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(intermediate_buffer.release()); -} - -hailo_status ExternalDescIntermediateBuffer::program_inter_context() +hailo_status IntermediateBuffer::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); + for (uint16_t i = 0; i < m_max_batch_size; i++) { + const auto first_desc_interrupts_domain = VdmaInterruptsDomain::NONE; + const auto last_desc_interrupts_domain = ((m_max_batch_size - 1) == i) ? + VdmaInterruptsDomain::DEVICE : VdmaInterruptsDomain::NONE; + static const auto BUFFER_NOT_CIRCULAR = false; + auto desc_count_local = m_buffer->program_descriptors(m_transfer_size, first_desc_interrupts_domain, + last_desc_interrupts_domain, acc_offset, BUFFER_NOT_CIRCULAR); 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(); @@ -70,31 +44,182 @@ hailo_status ExternalDescIntermediateBuffer::program_inter_context() return HAILO_SUCCESS; } -Expected ExternalDescIntermediateBuffer::program_ddr() +hailo_status IntermediateBuffer::reprogram_inter_context(uint16_t batch_size) { - return m_desc_list.program_descs_for_ddr_transfers(m_desc_list.desc_page_size(), false, 1, - static_cast(m_desc_list.count()), 0, true); + const auto prev_batch_size = m_dynamic_batch_size; + auto status = set_dynamic_batch_size(batch_size); + CHECK_SUCCESS(status); + + if (prev_batch_size == m_dynamic_batch_size) { + LOGGER__TRACE("Batch size hasn't changed ({}); nothing to be done.", batch_size); + return HAILO_SUCCESS; + } + + status = m_buffer->reprogram_device_interrupts_for_end_of_batch(m_transfer_size, prev_batch_size, + VdmaInterruptsDomain::NONE); + CHECK_SUCCESS(status, "Failed reprogramming device interrupts for the end of the previous batch (size {})", + prev_batch_size); + status = m_buffer->reprogram_device_interrupts_for_end_of_batch(m_transfer_size, m_dynamic_batch_size, + VdmaInterruptsDomain::DEVICE); + CHECK_SUCCESS(status, "Failed reprogramming device interrupts for the end of the current batch (size {})", + m_dynamic_batch_size); + + return HAILO_SUCCESS; } -Expected ExternalDescIntermediateBuffer::program_host_managed_ddr(uint16_t row_size, uint32_t buffered_rows, - uint16_t initial_desc_offset) +Expected IntermediateBuffer::program_ddr() { - return m_desc_list.program_descs_for_ddr_transfers(row_size, true, DDR_NUMBER_OF_ROWS_PER_INTERRUPT, - buffered_rows, initial_desc_offset, true); + const auto interrupts_domain = VdmaInterruptsDomain::NONE; + const auto total_size = m_buffer->descs_count() * m_buffer->desc_page_size(); + + auto desc_count_local = m_buffer->program_descriptors(total_size, + interrupts_domain, interrupts_domain, 0, true); + CHECK_EXPECTED(desc_count_local); + + return static_cast(desc_count_local.release()); } -Expected ExternalDescIntermediateBuffer::read() +uint64_t IntermediateBuffer::dma_address() const { - const auto size = m_transfer_size * m_transfers_count; - assert(size <= m_buffer.size()); + return m_buffer->dma_address(); +} + +uint32_t IntermediateBuffer::descriptors_in_frame() const +{ + return m_buffer->descriptors_in_buffer(m_transfer_size); +} + +uint16_t IntermediateBuffer::desc_page_size() const +{ + return m_buffer->desc_page_size(); +} + +uint32_t IntermediateBuffer::descs_count() const +{ + return m_buffer->descs_count(); +} + +uint8_t IntermediateBuffer::depth() +{ + // TODO HRT-6763: remove this function, use desc count instead + auto count = m_buffer->descs_count(); + + uint32_t depth = 0; + while (count >>= 1) { + ++depth; + } + assert(IS_FIT_IN_UINT8(depth)); + return static_cast(depth); +} + +Expected IntermediateBuffer::read() +{ + const auto size = m_transfer_size * m_dynamic_batch_size; + assert(size <= m_buffer->size()); auto res = Buffer::create(size); CHECK_EXPECTED(res); - auto status = m_buffer.read(res->data(), size, 0); + auto status = m_buffer->read(res->data(), size, 0); CHECK_SUCCESS_AS_EXPECTED(status); return res.release(); } +CONTROL_PROTOCOL__host_buffer_info_t IntermediateBuffer::get_host_buffer_info() const +{ + CONTROL_PROTOCOL__host_buffer_info_t buffer_info = {}; + + buffer_info.buffer_type = static_cast((m_buffer->type() == vdma::VdmaBuffer::Type::SCATTER_GATHER) ? + CONTROL_PROTOCOL__HOST_BUFFER_TYPE_EXTERNAL_DESC : + CONTROL_PROTOCOL__HOST_BUFFER_TYPE_CCB); + buffer_info.dma_address = dma_address(); + buffer_info.desc_page_size = desc_page_size(); + buffer_info.total_desc_count = descs_count(); + buffer_info.bytes_in_pattern = m_transfer_size; + + return buffer_info; +} + +Expected> IntermediateBuffer::create_sg_buffer(HailoRTDriver &driver, + const uint32_t transfer_size, const uint16_t batch_size) +{ + auto desc_sizes_pair = VdmaDescriptorList::get_desc_buffer_sizes_for_single_transfer(driver, + batch_size, batch_size, transfer_size); + CHECK_EXPECTED(desc_sizes_pair); + auto desc_page_size = desc_sizes_pair->first; + auto descs_count = desc_sizes_pair->second; + + // 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 buffer = vdma::SgBuffer::create(driver, descs_count, desc_page_size, + HailoRTDriver::DmaDirection::BOTH, channel_index); + CHECK_EXPECTED(buffer); + + auto buffer_ptr = make_unique_nothrow(buffer.release()); + CHECK_NOT_NULL_AS_EXPECTED(buffer_ptr, HAILO_OUT_OF_HOST_MEMORY); + + return std::unique_ptr(std::move(buffer_ptr)); +} + +IntermediateBuffer::IntermediateBuffer(std::unique_ptr &&buffer, uint32_t transfer_size, + uint16_t batch_size) : + m_buffer(std::move(buffer)), + m_transfer_size(transfer_size), + m_max_batch_size(batch_size), + m_dynamic_batch_size(batch_size) +{} + +hailo_status IntermediateBuffer::set_dynamic_batch_size(uint16_t batch_size) +{ + if (CONTROL_PROTOCOL__IGNORE_DYNAMIC_BATCH_SIZE == batch_size) { + LOGGER__TRACE("Received CONTROL_PROTOCOL__IGNORE_DYNAMIC_BATCH_SIZE == batch_size; " + "Leaving previously set value of {}", m_dynamic_batch_size); + } else { + CHECK(batch_size <= m_max_batch_size, HAILO_INVALID_ARGUMENT, + "batch_size ({}) must be <= than m_max_batch_size ({})", + batch_size, m_max_batch_size); + + LOGGER__TRACE("Setting intermediate buffer's batch_size to {}", batch_size); + m_dynamic_batch_size = batch_size; + } + + return HAILO_SUCCESS; +} + +Expected> IntermediateBuffer::create_ccb_buffer(HailoRTDriver &driver, + const uint32_t transfer_size, const uint16_t batch_size) +{ + // The first 12 channels in D2H CCB ("regular channels") requires that the amount of descriptors will be a power + // of 2. Altough the 4 last channels ("enhanced channels") don't have this requirements, we keep the code the same. + auto buffer_size = vdma::ContinuousBuffer::get_buffer_size_desc_power2(transfer_size * batch_size); + auto buffer = vdma::ContinuousBuffer::create(buffer_size, driver); + CHECK_EXPECTED(buffer); + + auto buffer_ptr = make_unique_nothrow(buffer.release()); + CHECK_NOT_NULL_AS_EXPECTED(buffer_ptr, HAILO_OUT_OF_HOST_MEMORY); + + return std::unique_ptr(std::move(buffer_ptr)); +} + +bool IntermediateBuffer::should_use_ccb(HailoRTDriver &driver, ChannelType channel_type) +{ + if (ChannelType::DDR == channel_type) { + // TODO HRT-6645: remove this if + return false; + } + + switch (driver.dma_type()) { + case HailoRTDriver::DmaType::PCIE: + return false; + case HailoRTDriver::DmaType::DRAM: + return true; + default: + assert(true); + return false; + } +} + } /* namespace hailort */ diff --git a/hailort/libhailort/src/intermediate_buffer.hpp b/hailort/libhailort/src/intermediate_buffer.hpp index 2020242..647733d 100644 --- a/hailort/libhailort/src/intermediate_buffer.hpp +++ b/hailort/libhailort/src/intermediate_buffer.hpp @@ -11,114 +11,55 @@ #define _HAILO_INTERMEDIATE_BUFFER_HPP_ #include "os/hailort_driver.hpp" -#include "vdma_buffer.hpp" -#include "vdma_descriptor_list.hpp" +#include "vdma/vdma_buffer.hpp" #include "hailo/expected.hpp" #include "hailo/buffer.hpp" +#include "control_protocol.h" namespace hailort { -class IntermediateBuffer { +class IntermediateBuffer final { public: - - enum class Type { - EXTERNAL_DESC + enum class ChannelType { + INTER_CONTEXT, + DDR }; - static Expected> 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; + static Expected create(HailoRTDriver &driver, + ChannelType channel_type, const uint32_t transfer_size, const uint16_t batch_size); + // TODO: create two subclasses - one for ddr buffers and one for intercontext buffers (HRT-6784) + hailo_status program_inter_context(); + // This is to be called after program_inter_context + hailo_status reprogram_inter_context(uint16_t batch_size); // Returns the amount of programed descriptors - virtual Expected program_ddr() = 0; - virtual Expected program_host_managed_ddr(uint16_t row_size, uint32_t buffered_rows, - uint16_t initial_desc_offset) = 0; + Expected program_ddr(); + uint64_t dma_address() const; + uint32_t descriptors_in_frame() const; + uint16_t desc_page_size() const; + uint32_t descs_count() const; + uint8_t depth(); + Expected read(); - 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 read() = 0; - -protected: - IntermediateBuffer() = default; -}; - -class ExternalDescIntermediateBuffer : public IntermediateBuffer -{ -public: - - static Expected> 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 program_ddr() override; - Expected 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(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(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 read() override; + CONTROL_PROTOCOL__host_buffer_info_t get_host_buffer_info() const; private: + IntermediateBuffer(std::unique_ptr &&buffer, uint32_t transfer_size, uint16_t batch_size); + hailo_status set_dynamic_batch_size(uint16_t batch_size); - VdmaBuffer m_buffer; - VdmaDescriptorList m_desc_list; + static Expected> create_sg_buffer(HailoRTDriver &driver, + const uint32_t transfer_size, const uint16_t batch_size); + static Expected> create_ccb_buffer(HailoRTDriver &driver, + const uint32_t transfer_size, const uint16_t batch_size); + + static bool should_use_ccb(HailoRTDriver &driver, ChannelType channel_type); + + std::unique_ptr m_buffer; const uint32_t m_transfer_size; - const uint32_t m_transfers_count; + const uint16_t m_max_batch_size; + uint16_t m_dynamic_batch_size; }; } /* namespace hailort */ diff --git a/hailort/libhailort/src/layer_info.hpp b/hailort/libhailort/src/layer_info.hpp index 3027463..82628e9 100644 --- a/hailort/libhailort/src/layer_info.hpp +++ b/hailort/libhailort/src/layer_info.hpp @@ -43,9 +43,7 @@ struct LayerInfo { hailo_nms_info_t nms_info; uint32_t height_gcd; std::vector 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; + std::string network_name; // Simulation Info BufferIndices buffer_indices; @@ -96,24 +94,24 @@ public: return false; } - static std::vector get_vstream_infos_from_layer_info(const LayerInfo &layer_info, const std::string &network_group_name) + static std::vector get_vstream_infos_from_layer_info(const LayerInfo &layer_info) { std::vector 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); + auto vstream_infos = get_vstream_infos_from_layer_info(pred); 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); + auto vstream_info = get_vstream_info_from_layer_info_impl(fused_nms); res.push_back(vstream_info); } } } else { - auto vstream_info = get_vstream_info_from_layer_info_impl(layer_info, network_group_name); + auto vstream_info = get_vstream_info_from_layer_info_impl(layer_info); res.push_back(vstream_info); } @@ -121,7 +119,7 @@ public: } private: - static hailo_vstream_info_t get_vstream_info_from_layer_info_impl(const LayerInfo &layer_info, const std::string &network_group_name) + static hailo_vstream_info_t get_vstream_info_from_layer_info_impl(const LayerInfo &layer_info) { hailo_vstream_info_t res = {}; res.format.type = layer_info.format.type; @@ -138,9 +136,8 @@ private: 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); + assert(layer_info.network_name.length() < HAILO_MAX_NETWORK_NAME_SIZE); + strncpy(res.network_name, layer_info.network_name.c_str(), layer_info.network_name.length() + 1); res.quant_info = layer_info.quant_info; return res; diff --git a/hailort/libhailort/src/mipi_stream.cpp b/hailort/libhailort/src/mipi_stream.cpp index 59cb243..4a9115c 100644 --- a/hailort/libhailort/src/mipi_stream.cpp +++ b/hailort/libhailort/src/mipi_stream.cpp @@ -100,7 +100,8 @@ CONTROL_PROTOCOL__mipi_input_config_params_t MipiInputStream::hailo_mipi_params_ return control_mipi_params; } -hailo_status MipiInputStream::activate_stream() +// Note: Mipi streams don't work with dynamic batch sizes +hailo_status MipiInputStream::activate_stream(uint16_t /* dynamic_batch_size */) { hailo_status status = HAILO_UNINITIALIZED; CONTROL_PROTOCOL__config_stream_params_t params = {}; diff --git a/hailort/libhailort/src/mipi_stream.hpp b/hailort/libhailort/src/mipi_stream.hpp index dfc6233..3ca6e43 100644 --- a/hailort/libhailort/src/mipi_stream.hpp +++ b/hailort/libhailort/src/mipi_stream.hpp @@ -51,7 +51,7 @@ public: virtual ~MipiInputStream(); - virtual hailo_status activate_stream() override; + virtual hailo_status activate_stream(uint16_t dynamic_batch_size) 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; diff --git a/hailort/libhailort/src/network_group_scheduler.cpp b/hailort/libhailort/src/network_group_scheduler.cpp new file mode 100644 index 0000000..164d232 --- /dev/null +++ b/hailort/libhailort/src/network_group_scheduler.cpp @@ -0,0 +1,640 @@ +/** + * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved. + * Distributed under the MIT license (https://opensource.org/licenses/MIT) + **/ +/** + * @file network_group_scheduler.cpp + * @brief: Network scheduler + **/ + +#include "network_group_scheduler.hpp" +#include "context_switch/network_group_internal.hpp" +#include "hef_internal.hpp" +#include "vdevice_stream.hpp" +#include "vdma_stream.hpp" + +namespace hailort +{ + +Expected NetworkGroupScheduler::create_shared(hailo_scheduling_algorithm_t algorithm) +{ + auto ptr = make_shared_nothrow(algorithm); + CHECK_AS_EXPECTED(nullptr != ptr, HAILO_OUT_OF_HOST_MEMORY); + + return ptr; +} + +hailo_status NetworkGroupScheduler::SchedulerIdleGuard::set_scheduler(std::shared_ptr scheduler) +{ + CHECK(nullptr != scheduler, HAILO_INTERNAL_FAILURE); + m_scheduler = scheduler; + m_scheduler->force_idle_state(); + return HAILO_SUCCESS; +} + +NetworkGroupScheduler::SchedulerIdleGuard::~SchedulerIdleGuard() +{ + if (m_scheduler) { + m_scheduler->resume_from_idle_state(); + } +} + +void NetworkGroupScheduler::force_idle_state() +{ + { + std::unique_lock lock(m_before_read_write_mutex); + m_write_read_cv.wait(lock, [this] { + return has_current_ng_finished(); + }); + deactivate_network_group(); + m_forced_idle_state = true; + } + m_write_read_cv.notify_all(); +} + +void NetworkGroupScheduler::resume_from_idle_state() +{ + { + std::unique_lock lock(m_before_read_write_mutex); + m_forced_idle_state = false; + } + m_write_read_cv.notify_all(); +} + +Expected NetworkGroupScheduler::add_network_group(std::weak_ptr added_cng) +{ + network_group_handle_t network_group_handle = INVALID_NETWORK_GROUP_HANDLE; + { + std::unique_lock lock(m_before_read_write_mutex); + network_group_handle = static_cast(m_cngs.size()); + + m_cngs.emplace_back(added_cng); + m_timeout_per_network_group[network_group_handle] = DEFAULT_SCHEDULER_TIMEOUT; + m_last_run_time_stamp[network_group_handle] = {}; // Default c'tor to mark this network_group didn't run yet + + auto added_cng_ptr = added_cng.lock(); + CHECK_AS_EXPECTED(added_cng_ptr, HAILO_INTERNAL_FAILURE); + + auto stream_infos = added_cng_ptr->get_all_stream_infos(); + CHECK_EXPECTED(stream_infos); + + // Prepare empty counters for the added cng + for (const auto &stream_info : stream_infos.value()) { + m_should_ng_stop[network_group_handle][stream_info.name] = false; + m_min_threshold_per_stream[network_group_handle][stream_info.name] = DEFAULT_SCHEDULER_MIN_THRESHOLD; + if (HAILO_H2D_STREAM == stream_info.direction) { + m_requested_write[network_group_handle][stream_info.name] = 0; + m_written_buffer[network_group_handle][stream_info.name] = 0; + m_sent_pending_buffer[network_group_handle][stream_info.name] = 0; + m_finished_sent_pending_buffer[network_group_handle][stream_info.name] = 0; + + auto event = Event::create_shared(Event::State::signalled); + CHECK_AS_EXPECTED(nullptr != event, HAILO_OUT_OF_HOST_MEMORY); + + m_write_buffer_events[network_group_handle][stream_info.name] = event; + } else { + m_allowed_read[network_group_handle][stream_info.name] = 0; + m_finished_read[network_group_handle][stream_info.name] = 0; + } + } + } + m_write_read_cv.notify_all(); + return network_group_handle; +} + +hailo_status NetworkGroupScheduler::wait_for_write(const network_group_handle_t &network_group_handle, const std::string &stream_name) +{ + { + std::unique_lock lock(m_before_read_write_mutex); + assert(contains(m_requested_write, network_group_handle)); + assert(contains(m_write_buffer_events, network_group_handle)); + if ((nullptr != m_ang) && m_switching_network_group && + (m_requested_write[network_group_handle][stream_name] == get_max_value_of_unordered_map(m_requested_write[network_group_handle]))) { + auto status = m_write_buffer_events[network_group_handle][stream_name]->reset(); + CHECK_SUCCESS(status); + } + } + + auto status = m_write_buffer_events[network_group_handle][stream_name]->wait(std::chrono::milliseconds(HAILO_INFINITE)); + CHECK_SUCCESS(status); + + { + std::unique_lock lock(m_before_read_write_mutex); + + assert(contains(m_should_ng_stop, network_group_handle)); + if (m_should_ng_stop[network_group_handle][stream_name]) { + return HAILO_STREAM_INTERNAL_ABORT; + } + + m_requested_write[network_group_handle][stream_name]++; + + if ((nullptr != m_ang) && (m_current_network_group == network_group_handle)) { + m_has_current_ng_finished = false; + } + + status = allow_writes_for_other_inputs_if_needed(network_group_handle); + CHECK_SUCCESS(status); + } + + m_write_read_cv.notify_all(); + + return HAILO_SUCCESS; +} + +hailo_status NetworkGroupScheduler::allow_writes_for_other_inputs_if_needed(const network_group_handle_t &network_group_handle) +{ + if (!m_has_current_ng_finished && m_switching_network_group) { + auto max_write = get_max_value_of_unordered_map(m_requested_write[network_group_handle]); + for (auto &name_event_pair : m_write_buffer_events[network_group_handle]) { + if (m_requested_write[network_group_handle][name_event_pair.first] < max_write) { + auto status = name_event_pair.second->signal(); + CHECK_SUCCESS(status); + } + } + } + return HAILO_SUCCESS; +} + +hailo_status NetworkGroupScheduler::signal_write_finish(const network_group_handle_t &network_group_handle, const std::string &stream_name) +{ + { + std::unique_lock lock(m_before_read_write_mutex); + + assert(contains(m_should_ng_stop, network_group_handle)); + if (m_should_ng_stop[network_group_handle][stream_name]) { + return HAILO_STREAM_INTERNAL_ABORT; + } + + assert(contains(m_written_buffer, network_group_handle)); + assert(contains(m_written_buffer[network_group_handle], stream_name)); + m_written_buffer[network_group_handle][stream_name]++; + + auto status = switch_network_group_if_idle(network_group_handle, lock); + CHECK_SUCCESS(status); + + status = switch_network_group_if_should_be_next(network_group_handle, lock); + CHECK_SUCCESS(status); + + status = send_all_pending_buffers(network_group_handle, lock); + if (HAILO_STREAM_INTERNAL_ABORT == status) { + LOGGER__INFO("send_all_pending_buffers has failed with status=HAILO_STREAM_INTERNAL_ABORT"); + return status; + } + CHECK_SUCCESS(status); + } + m_write_read_cv.notify_all(); + + return HAILO_SUCCESS; +} + +hailo_status NetworkGroupScheduler::switch_network_group_if_idle(const network_group_handle_t &network_group_handle, std::unique_lock &read_write_lock) +{ + if (!m_forced_idle_state && !m_switching_network_group && m_has_current_ng_finished && + ((nullptr == m_ang) || (network_group_handle != m_current_network_group)) && is_network_group_ready(network_group_handle)) { + auto status = activate_network_group(network_group_handle, read_write_lock); + if (HAILO_STREAM_INTERNAL_ABORT == status) { + return status; + } + CHECK_SUCCESS(status); + } + + return HAILO_SUCCESS; +} + +hailo_status NetworkGroupScheduler::activate_network_group(const network_group_handle_t &network_group_handle, std::unique_lock &read_write_lock) +{ + deactivate_network_group(); + + m_switching_network_group = false; + auto status = allow_all_writes(); + CHECK_SUCCESS(status); + + assert(m_cngs.size() > network_group_handle); + auto cng = m_cngs[network_group_handle].lock(); + CHECK(cng, HAILO_INTERNAL_FAILURE); + + auto cng_base = std::dynamic_pointer_cast(cng); + auto expected_ang = cng_base->force_activate(); + CHECK_EXPECTED_AS_STATUS(expected_ang); + m_last_run_time_stamp[network_group_handle] = std::chrono::steady_clock::now(); // Mark last timestamp on activation + + m_ang = expected_ang.release(); + + m_current_network_group = network_group_handle; + m_has_current_ng_finished = false; + + status = send_all_pending_buffers(network_group_handle, read_write_lock); + if (HAILO_STREAM_INTERNAL_ABORT == status) { + LOGGER__INFO("send_all_pending_buffers has failed with status=HAILO_STREAM_INTERNAL_ABORT"); + return status; + } + CHECK_SUCCESS(status); + + return HAILO_SUCCESS; +} + +hailo_status NetworkGroupScheduler::allow_all_writes() +{ + for (auto &name_dict_pair : m_write_buffer_events) { + for (auto &name_event_pair : name_dict_pair.second) { + auto status = name_event_pair.second->signal(); + CHECK_SUCCESS(status); + } + } + return HAILO_SUCCESS; +} + +hailo_status NetworkGroupScheduler::send_all_pending_buffers(const network_group_handle_t &network_group_handle, std::unique_lock &read_write_lock) +{ + if ((nullptr == m_ang) || (m_current_network_group != network_group_handle)) { + return HAILO_SUCCESS; + } + + while (true) { + uint32_t finished_sending_count = 0; + for (auto &name_counter_pair : m_written_buffer[network_group_handle]) { + if (m_sent_pending_buffer[network_group_handle][name_counter_pair.first] < name_counter_pair.second) { + auto status = send_pending_buffer(network_group_handle, name_counter_pair.first, read_write_lock); + if (HAILO_STREAM_INTERNAL_ABORT == status) { + LOGGER__INFO("send_pending_buffer has failed with status=HAILO_STREAM_INTERNAL_ABORT"); + return status; + } + CHECK_SUCCESS(status); + } else { + finished_sending_count++; + } + } + if (finished_sending_count == m_written_buffer[network_group_handle].size()) { + break; + } + } + + return HAILO_SUCCESS; +} + +hailo_status NetworkGroupScheduler::send_pending_buffer(const network_group_handle_t &network_group_handle, const std::string &stream_name, + std::unique_lock &read_write_lock) +{ + assert(m_cngs.size() > network_group_handle); + auto current_cng = m_cngs[network_group_handle].lock(); + CHECK(current_cng, HAILO_INTERNAL_FAILURE); + + auto input_stream = current_cng->get_input_stream_by_name(stream_name); + CHECK_EXPECTED_AS_STATUS(input_stream); + + m_has_current_ng_finished = false; + + VDeviceInputStream &vdevice_input = dynamic_cast(input_stream->get()); + auto pending_buffer_state = vdevice_input.send_pending_buffer(); + CHECK_EXPECTED_AS_STATUS(pending_buffer_state); + + assert(contains(m_sent_pending_buffer, network_group_handle)); + m_sent_pending_buffer[network_group_handle][stream_name]++; + + auto status = pending_buffer_state->finish(vdevice_input.get_timeout(), read_write_lock); + if (HAILO_STREAM_INTERNAL_ABORT == status) { + LOGGER__INFO("finish has failed with status=HAILO_STREAM_INTERNAL_ABORT"); + return status; + } + CHECK_SUCCESS(status); + + assert(contains(m_finished_sent_pending_buffer, network_group_handle)); + m_finished_sent_pending_buffer[network_group_handle][stream_name]++; + + // Update m_has_current_ng_finished here because after finishing send pending buffer the network group can actually be finished + m_has_current_ng_finished = has_current_ng_finished(); + + return HAILO_SUCCESS; +} + +void NetworkGroupScheduler::deactivate_network_group() +{ + if (m_ang) { + reset_current_ng_counters(); + m_ang.reset(); + m_last_run_time_stamp[m_current_network_group] = std::chrono::steady_clock::now(); // Mark last timestamp on deactivation + } +} + +hailo_status NetworkGroupScheduler::switch_network_group_if_should_be_next(const network_group_handle_t &network_group_handle, + std::unique_lock &read_write_lock) +{ + // Checking (nullptr == m_ang) for activating the first time the scheduler is running + if (!m_forced_idle_state && m_switching_network_group && m_has_current_ng_finished && is_network_group_ready(network_group_handle) && + ((nullptr == m_ang) || (m_next_network_group == network_group_handle))) { + auto status = activate_network_group(network_group_handle, read_write_lock); + if (HAILO_STREAM_INTERNAL_ABORT == status) { + return status; + } + } + + return HAILO_SUCCESS; +} + +bool NetworkGroupScheduler::is_network_group_ready(const network_group_handle_t &network_group_handle) +{ + assert(contains(m_written_buffer, network_group_handle)); + assert(contains(m_timeout_per_network_group, network_group_handle)); + assert(contains(m_min_threshold_per_stream, network_group_handle)); + + // TODO: move inside the for-loop when timeout-per-network will be supported + bool timeout_passed = (m_timeout_per_network_group[network_group_handle] < + (std::chrono::steady_clock::now() - m_last_run_time_stamp[network_group_handle])); + + for (auto &name_counter_pair : m_written_buffer[network_group_handle]) { + // Check if there arent any write requests + if (0 == name_counter_pair.second) { + return false; + } + + // Check if there arent enough write requests and timeout didnt passed + if ((name_counter_pair.second < m_min_threshold_per_stream[network_group_handle][name_counter_pair.first]) && (!timeout_passed)) { + return false; + } + } + + return true; +} + +hailo_status NetworkGroupScheduler::wait_for_read(const network_group_handle_t &network_group_handle, const std::string &stream_name) +{ + { + std::unique_lock lock(m_before_read_write_mutex); + assert(contains(m_allowed_read, network_group_handle)); + assert(contains(m_allowed_read[network_group_handle], stream_name)); + + hailo_status status = HAILO_UNINITIALIZED; + m_write_read_cv.wait(lock, [this, network_group_handle, stream_name, &status, &lock] { + assert(contains(m_should_ng_stop, network_group_handle)); + if (m_should_ng_stop[network_group_handle][stream_name]) { + status = HAILO_STREAM_INTERNAL_ABORT; + return true; // return true so that the wait will finish + } + + status = switch_network_group_if_idle(network_group_handle, lock); + if (HAILO_SUCCESS != status) { + return true; // return true so that the wait will finish + } + + status = switch_network_group_if_should_be_next(network_group_handle, lock); + if (HAILO_SUCCESS != status) { + return true; // return true so that the wait will finish + } + + return can_stream_read(network_group_handle, stream_name); + }); + if (HAILO_STREAM_INTERNAL_ABORT == status) { + return status; + } + CHECK_SUCCESS(status); + + assert(contains(m_allowed_read, network_group_handle)); + m_allowed_read[network_group_handle][stream_name]++; + } + m_write_read_cv.notify_all(); + + return HAILO_SUCCESS; +} + +bool NetworkGroupScheduler::can_stream_read(const network_group_handle_t &network_group_handle, const std::string &stream_name) +{ + if (nullptr == m_ang) { + return false; + } + + if (m_current_network_group != network_group_handle) { + return false; + } + + if (m_has_current_ng_finished) { + return false; + } + + assert(contains(m_allowed_read, network_group_handle)); + assert(contains(m_sent_pending_buffer, network_group_handle)); + return m_allowed_read[network_group_handle][stream_name].load() < get_max_value_of_unordered_map(m_sent_pending_buffer[network_group_handle]); +} + +hailo_status NetworkGroupScheduler::signal_read_finish(const network_group_handle_t &network_group_handle, const std::string &stream_name) +{ + { + std::unique_lock lock(m_before_read_write_mutex); + assert(contains(m_finished_read, network_group_handle)); + assert(contains(m_finished_read[network_group_handle], stream_name)); + + m_finished_read[network_group_handle][stream_name]++; + hailo_status status = mark_switching_ng_if_ready(); + CHECK_SUCCESS(status); + } + m_write_read_cv.notify_all(); + + return HAILO_SUCCESS; +} + +hailo_status NetworkGroupScheduler::mark_switching_ng_if_ready() +{ + if (!m_switching_network_group) { + for (uint32_t i = 0; i < m_cngs.size() - 1; i++) { + network_group_handle_t handle = m_current_network_group + i + 1; + handle %= static_cast(m_cngs.size()); + + if (is_network_group_ready(handle)) { + m_switching_network_group = true; + m_next_network_group = handle; + break; + } + } + } + m_has_current_ng_finished = has_current_ng_finished(); + + // Prevents integer overflow of the counters + decrease_current_ng_counters(); + + return HAILO_SUCCESS; +} + +bool NetworkGroupScheduler::has_current_ng_finished() +{ + uint32_t written_frames = get_max_value_of_unordered_map(m_requested_write[m_current_network_group]); + for (auto &name_counter_pair : m_finished_read[m_current_network_group]) { + if (name_counter_pair.second < written_frames) { + return false; + } + } + for (auto &name_counter_pair : m_finished_sent_pending_buffer[m_current_network_group]) { + if (name_counter_pair.second < written_frames) { + return false; + } + } + return true; +} + +void NetworkGroupScheduler::reset_current_ng_counters() +{ + uint32_t written_frames = get_max_value_of_unordered_map(m_sent_pending_buffer[m_current_network_group]); + + for (auto &name_counter_pair : m_requested_write[m_current_network_group]) { + name_counter_pair.second -= written_frames; + } + for (auto &name_counter_pair : m_written_buffer[m_current_network_group]) { + assert(name_counter_pair.second == written_frames); + name_counter_pair.second = 0; + } + for (auto &name_counter_pair : m_sent_pending_buffer[m_current_network_group]) { + assert(name_counter_pair.second == written_frames); + name_counter_pair.second = 0; + } + for (auto &name_counter_pair : m_finished_sent_pending_buffer[m_current_network_group]) { + assert(name_counter_pair.second == written_frames); + name_counter_pair.second = 0; + } + for (auto &name_counter_pair : m_allowed_read[m_current_network_group]) { + // TODO (HRT-6811): Recover from timeout, verify counters + name_counter_pair.second = 0; + } + for (auto &name_counter_pair : m_finished_read[m_current_network_group]) { + // TODO (HRT-6811): Recover from timeout, verify counters + name_counter_pair.second = 0; + } +} + +void NetworkGroupScheduler::decrease_current_ng_counters() +{ + if (nullptr == m_ang) { + return; + } + + // Decrease only if counter is 2 or bigger because reaching 0 can cause states to change + for (auto &name_counter_pair : m_requested_write[m_current_network_group]) { + if (name_counter_pair.second <= 1) { + return; + } + } + for (auto &name_counter_pair : m_written_buffer[m_current_network_group]) { + if (name_counter_pair.second <= 1) { + return; + } + } + for (auto &name_counter_pair : m_sent_pending_buffer[m_current_network_group]) { + if (name_counter_pair.second <= 1) { + return; + } + } + for (auto &name_counter_pair : m_finished_sent_pending_buffer[m_current_network_group]) { + if (name_counter_pair.second <= 1) { + return; + } + } + for (auto &name_counter_pair : m_allowed_read[m_current_network_group]) { + if (name_counter_pair.second <= 1) { + return; + } + } + for (auto &name_counter_pair : m_finished_read[m_current_network_group]) { + if (name_counter_pair.second <= 1) { + return; + } + } + + for (auto &name_counter_pair : m_requested_write[m_current_network_group]) { + name_counter_pair.second--; + } + for (auto &name_counter_pair : m_written_buffer[m_current_network_group]) { + name_counter_pair.second--; + } + for (auto &name_counter_pair : m_sent_pending_buffer[m_current_network_group]) { + name_counter_pair.second--; + } + for (auto &name_counter_pair : m_finished_sent_pending_buffer[m_current_network_group]) { + name_counter_pair.second--; + } + for (auto &name_counter_pair : m_allowed_read[m_current_network_group]) { + name_counter_pair.second--; + } + for (auto &name_counter_pair : m_finished_read[m_current_network_group]) { + name_counter_pair.second--; + } +} + +hailo_status NetworkGroupScheduler::enable_stream(const network_group_handle_t &network_group_handle, const std::string &stream_name) +{ + { + std::unique_lock lock(m_before_read_write_mutex); + + assert(contains(m_should_ng_stop, network_group_handle)); + if (!m_should_ng_stop[network_group_handle][stream_name]) { + return HAILO_SUCCESS; + } + + m_should_ng_stop[network_group_handle][stream_name] = false; + } + m_write_read_cv.notify_all(); + return HAILO_SUCCESS; +} + +hailo_status NetworkGroupScheduler::disable_stream(const network_group_handle_t &network_group_handle, const std::string &stream_name) +{ + { + std::unique_lock lock(m_before_read_write_mutex); + + assert(contains(m_should_ng_stop, network_group_handle)); + if (m_should_ng_stop[network_group_handle][stream_name]) { + return HAILO_SUCCESS; + } + + m_should_ng_stop[network_group_handle][stream_name] = true; + + // Signal event to exit infinite timeout on wait_for_write if actually an input stream + assert(contains(m_write_buffer_events, network_group_handle)); + if (contains(m_write_buffer_events[network_group_handle], stream_name)) { + auto status = m_write_buffer_events[network_group_handle][stream_name]->signal(); + CHECK_SUCCESS(status); + } + } + m_write_read_cv.notify_all(); + return HAILO_SUCCESS; +} + +hailo_status NetworkGroupScheduler::set_timeout(const network_group_handle_t &network_group_handle, const std::chrono::milliseconds &timeout, const std::string &network_name) +{ + (void)network_name; + + assert(contains(m_timeout_per_network_group, network_group_handle)); + assert(contains(m_last_run_time_stamp, network_group_handle)); + CHECK((std::chrono::time_point() == m_last_run_time_stamp[network_group_handle]), HAILO_INVALID_OPERATION, + "Setting scheduler timeout is allowed only before sending / receiving frames on the network group."); + m_timeout_per_network_group[network_group_handle] = timeout; + + assert(m_cngs.size() > network_group_handle); + auto cng = m_cngs[network_group_handle].lock(); + CHECK(cng, HAILO_INTERNAL_FAILURE); + + auto name = (network_name.empty()) ? cng->get_network_group_name() : network_name; + LOGGER__INFO("Setting scheduler timeout of {} to {}ms", name, timeout.count()); + + return HAILO_SUCCESS; +} + +hailo_status NetworkGroupScheduler::set_threshold(const network_group_handle_t &network_group_handle, uint32_t threshold, const std::string &network_name) +{ + (void)network_name; + + assert(contains(m_min_threshold_per_stream, network_group_handle)); + assert(contains(m_last_run_time_stamp, network_group_handle)); + CHECK((std::chrono::time_point() == m_last_run_time_stamp[network_group_handle]), HAILO_INVALID_OPERATION, + "Setting scheduler threshold is allowed only before sending / receiving frames on the network group."); + for (auto &threshold_per_stream_pair : m_min_threshold_per_stream[network_group_handle]) { + threshold_per_stream_pair.second = threshold; + } + + assert(m_cngs.size() > network_group_handle); + auto cng = m_cngs[network_group_handle].lock(); + CHECK(cng, HAILO_INTERNAL_FAILURE); + + auto name = (network_name.empty()) ? cng->get_network_group_name() : network_name; + LOGGER__INFO("Setting scheduler threshold of {} to {} frames", name, threshold); + + return HAILO_SUCCESS; +} + +} /* namespace hailort */ diff --git a/hailort/libhailort/src/network_group_scheduler.hpp b/hailort/libhailort/src/network_group_scheduler.hpp new file mode 100644 index 0000000..c262ebb --- /dev/null +++ b/hailort/libhailort/src/network_group_scheduler.hpp @@ -0,0 +1,143 @@ +/** + * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved. + * Distributed under the MIT license (https://opensource.org/licenses/MIT) + **/ +/** + * @file network_group_scheduler.hpp + * @brief Class declaration for NetworkGroupScheduler that schedules network groups to be active depending on the scheduling algorithm. + **/ + +#ifndef _HAILO_NETWORK_GROUP_SCHEDULER_HPP_ +#define _HAILO_NETWORK_GROUP_SCHEDULER_HPP_ + +#include "hailo/hailort.h" +#include "hailo/expected.hpp" +#include "common/utils.hpp" +#include "hailo/network_group.hpp" + +#include + +#define DEFAULT_SCHEDULER_TIMEOUT (std::chrono::milliseconds(0)) +#define DEFAULT_SCHEDULER_MIN_THRESHOLD (1) + +namespace hailort +{ + +#define INVALID_NETWORK_GROUP_HANDLE (UINT32_MAX) +using network_group_handle_t = uint32_t; + +const auto SCHEDULER_REFRESH_INTERVAL = std::chrono::milliseconds(500); + +class NetworkGroupScheduler; +using NetworkGroupSchedulerPtr = std::shared_ptr; + +// We use mostly weak pointer for the scheduler to prevent circular dependency of the pointers +using NetworkGroupSchedulerWeakPtr = std::weak_ptr; + +using stream_name_t = std::string; +class NetworkGroupScheduler final { +public: + static Expected create_shared(hailo_scheduling_algorithm_t algorithm); + NetworkGroupScheduler(hailo_scheduling_algorithm_t algorithm) + : m_algorithm(algorithm), m_before_read_write_mutex(), m_write_read_cv(), m_current_network_group(0), + m_switching_network_group(true), m_has_current_ng_finished(true), m_next_network_group(0), m_forced_idle_state(false) + { + // Temp solution until we'll implement timeout as timers + m_thread = std::thread([this] () { + while (!m_should_stop.load()) { + m_write_read_cv.notify_all(); + std::this_thread::sleep_for(SCHEDULER_REFRESH_INTERVAL); + } + }); + } + + ~NetworkGroupScheduler() + { + m_should_stop = true; + m_thread.join(); + } + + hailo_scheduling_algorithm_t algorithm() + { + return m_algorithm; + } + + Expected add_network_group(std::weak_ptr added_cng); + + hailo_status wait_for_write(const network_group_handle_t &network_group_handle, const std::string &stream_name); + hailo_status signal_write_finish(const network_group_handle_t &network_group_handle, const std::string &stream_name); + hailo_status wait_for_read(const network_group_handle_t &network_group_handle, const std::string &stream_name); + hailo_status signal_read_finish(const network_group_handle_t &network_group_handle, const std::string &stream_name); + + hailo_status enable_stream(const network_group_handle_t &network_group_handle, const std::string &stream_name); + hailo_status disable_stream(const network_group_handle_t &network_group_handle, const std::string &stream_name); + + hailo_status set_timeout(const network_group_handle_t &network_group_handle, const std::chrono::milliseconds &timeout, const std::string &network_name); + hailo_status set_threshold(const network_group_handle_t &network_group_handle, uint32_t threshold, const std::string &network_name); + + class SchedulerIdleGuard final { + /* After 'set_scheduler()' is called, the idle guard will guarantee nothing is running on the device. + Relevant for state and configs changes */ + public: + SchedulerIdleGuard() = default; + ~SchedulerIdleGuard(); + hailo_status set_scheduler(std::shared_ptr scheduler); + private: + std::shared_ptr m_scheduler; + }; + + static std::unique_ptr create_scheduler_idle_guard() + { + auto guard = make_unique_nothrow(); + return guard; + } + +private: + hailo_status switch_network_group_if_idle(const network_group_handle_t &network_group_handle, std::unique_lock &read_write_lock); + hailo_status activate_network_group(const network_group_handle_t &network_group_handle, std::unique_lock &read_write_lock); + hailo_status send_all_pending_buffers(const network_group_handle_t &network_group_handle, std::unique_lock &read_write_lock); + hailo_status send_pending_buffer(const network_group_handle_t &network_group_handle, const std::string &stream_name, std::unique_lock &read_write_lock); + hailo_status switch_network_group_if_should_be_next(const network_group_handle_t &network_group_handle, std::unique_lock &read_write_lock); + void deactivate_network_group(); + bool is_network_group_ready(const network_group_handle_t &network_group_handle); + hailo_status mark_switching_ng_if_ready(); + bool can_stream_read(const network_group_handle_t &network_group_handle, const std::string &stream_name); + bool has_current_ng_finished(); + void reset_current_ng_counters(); + void decrease_current_ng_counters(); + hailo_status allow_all_writes(); + hailo_status allow_writes_for_other_inputs_if_needed(const network_group_handle_t &network_group_handle); + + void force_idle_state(); + void resume_from_idle_state(); + + hailo_scheduling_algorithm_t m_algorithm; + std::mutex m_before_read_write_mutex; + std::condition_variable m_write_read_cv; + std::unordered_map> m_should_ng_stop; + std::unordered_map> m_requested_write; + std::unordered_map> m_written_buffer; + std::unordered_map> m_write_buffer_events; + std::unordered_map> m_sent_pending_buffer; + std::unordered_map> m_finished_sent_pending_buffer; + std::unordered_map> m_allowed_read; + std::unordered_map> m_finished_read; + network_group_handle_t m_current_network_group; + std::vector> m_cngs; + std::unique_ptr m_ang; + std::atomic_bool m_switching_network_group; + std::atomic_bool m_has_current_ng_finished; + network_group_handle_t m_next_network_group; + std::atomic_bool m_forced_idle_state; + + std::thread m_thread; + std::atomic_bool m_should_stop; + + std::unordered_map> m_min_threshold_per_stream; + std::unordered_map m_timeout_per_network_group; + std::unordered_map> m_last_run_time_stamp; +}; + +} /* namespace hailort */ + +#endif /* _HAILO_NETWORK_GROUP_SCHEDULER_HPP_ */ diff --git a/hailort/libhailort/src/os/CMakeLists.txt b/hailort/libhailort/src/os/CMakeLists.txt index 5e4ab74..1f866a6 100644 --- a/hailort/libhailort/src/os/CMakeLists.txt +++ b/hailort/libhailort/src/os/CMakeLists.txt @@ -27,10 +27,15 @@ set(files ${HAILO_FULL_OS_DIR}/event.cpp ) -if(UNIX) +if(CMAKE_SYSTEM_NAME STREQUAL QNX) + # QNX only modules + set(files ${files} + ${HAILO_OS_DIR}/qnx/pcie_driver_scan.cpp + ) +elseif(UNIX) # Unix only modules set(files ${files} - ${HAILO_OS_DIR}/pcie_driver_sysfs.cpp + ${HAILO_OS_DIR}/unix/pcie_driver_scan.cpp ) endif() diff --git a/hailort/libhailort/src/os/hailort_driver.hpp b/hailort/libhailort/src/os/hailort_driver.hpp index a3db6c8..817a0f7 100755 --- a/hailort/libhailort/src/os/hailort_driver.hpp +++ b/hailort/libhailort/src/os/hailort_driver.hpp @@ -37,6 +37,8 @@ static_assert((0 == ((PENDING_BUFFERS_SIZE - 1) & PENDING_BUFFERS_SIZE)), "PEND // 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 DESCRIPTORS_IN_BUFFER(buffer_size, desc_page_size) (((buffer_size) + (desc_page_size) - 1) / (desc_page_size)) + #define PCIE_EXPECTED_MD5_LENGTH (16) enum class PciBar { @@ -166,9 +168,6 @@ public: * Frees a vdma descriptors buffer allocated by 'create_descriptors_buffer'. */ hailo_status descriptors_list_release(uintptr_t desc_handle); - - Expected 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. @@ -176,6 +175,22 @@ public: hailo_status descriptors_list_bind_vdma_buffer(uintptr_t desc_handle, VdmaBufferHandle buffer_handle, uint16_t desc_page_size, uint8_t channel_index); + Expected vdma_low_memory_buffer_alloc(size_t size); + hailo_status vdma_low_memory_buffer_free(uintptr_t buffer_handle); + + /** + * Allocate continuous vdma buffer. + * + * @param[in] size - Buffer size + * @return pair . + */ + Expected> vdma_continuous_buffer_alloc(size_t size); + + /** + * Frees a vdma continuous buffer allocated by 'vdma_continuous_buffer_alloc'. + */ + hailo_status vdma_continuous_buffer_free(uintptr_t buffer_handle); + /** * The actual desc page size might be smaller than the once requested, depends on the host capabilities. */ diff --git a/hailort/libhailort/src/os/posix/hailort_driver.cpp b/hailort/libhailort/src/os/posix/hailort_driver.cpp index 8682813..acffa8e 100755 --- a/hailort/libhailort/src/os/posix/hailort_driver.cpp +++ b/hailort/libhailort/src/os/posix/hailort_driver.cpp @@ -1,5 +1,5 @@ #include "os/hailort_driver.hpp" -#include "os/posix/pcie_driver_sysfs.hpp" +#include "os/posix/pcie_driver_scan.hpp" #include "hailo_ioctl_common.h" #include "common/logger_macros.hpp" #include "common/utils.hpp" @@ -182,9 +182,10 @@ hailo_status HailoRTDriver::disable_notifications() return HAILO_SUCCESS; } +#if defined(__linux__) Expected> HailoRTDriver::scan_pci() { - auto device_names = list_sysfs_pcie_devices(); + auto device_names = list_pcie_devices(); CHECK_EXPECTED(device_names, "Failed listing pcie devices"); std::vector devices_info; @@ -195,6 +196,26 @@ Expected> HailoRTDriver::scan_pci() } return devices_info; } +#elif defined(__QNX__) +Expected> HailoRTDriver::scan_pci() +{ + auto device_names = list_pcie_devices(); + CHECK_EXPECTED(device_names, "Failed listing pcie devices"); + + // TODO: HRT-6785 - support multiple devices - currently device_names is vector of one device - in future will be multiple + std::vector devices_info; + uint32_t index = 0; + for (const auto &device_name : device_names.value()) { + auto device_info = query_device_info(device_name, index); + CHECK_EXPECTED(device_info, "failed parsing device info for {}", device_name); + devices_info.push_back(device_info.release()); + index++; + } + return devices_info; +} +#else +static_assert(true, "Error, Unsupported Platform"); +#endif //defined (__linux__) Expected HailoRTDriver::read_vdma_channel_registers(off_t offset, size_t size) { @@ -247,14 +268,21 @@ hailo_status HailoRTDriver::read_bar(PciBar bar, off_t offset, size_t size, void .bar_index = static_cast(bar), .offset = offset, .count = size, - .buffer = buf + .buffer = {0} }; + if (size > sizeof(transfer.buffer)) { + LOGGER__ERROR("Invalid size to read, size given {} is larger than max size {}", size, sizeof(transfer.buffer)); + return HAILO_INVALID_ARGUMENT; + } + if (0 > ioctl(this->m_fd, HAILO_BAR_TRANSFER, &transfer)) { LOGGER__ERROR("HailoRTDriver::read_bar failed with errno:{}", errno); return HAILO_PCIE_DRIVER_FAIL; } + memcpy(buf, transfer.buffer, transfer.count); + return HAILO_SUCCESS; } @@ -275,9 +303,16 @@ hailo_status HailoRTDriver::write_bar(PciBar bar, off_t offset, size_t size, con .bar_index = static_cast(bar), .offset = offset, .count = size, - .buffer = (void*)buf + .buffer = {0} }; + if (size > sizeof(transfer.buffer)) { + LOGGER__ERROR("Invalid size to read, size given {} is larger than max size {}", size, sizeof(transfer.buffer)); + return HAILO_INVALID_ARGUMENT; + } + + memcpy(transfer.buffer, buf, transfer.count); + if (0 > ioctl(this->m_fd, HAILO_BAR_TRANSFER, &transfer)) { LOGGER__ERROR("HailoRTDriver::write_bar failed with errno:{}", errno); return HAILO_PCIE_DRIVER_FAIL; @@ -419,16 +454,23 @@ hailo_status HailoRTDriver::read_log(uint8_t *buffer, size_t buffer_size, size_t hailo_read_log_params params { .cpu_id = translate_cpu_id(cpu_id), - .buffer = buffer, + .buffer = {0}, .buffer_size = buffer_size, .read_bytes = 0 }; + CHECK(buffer_size <= sizeof(params.buffer), HAILO_PCIE_DRIVER_FAIL, + "Given buffer size {} is bigger than buffer size used to read logs {}", buffer_size, sizeof(params.buffer)); + if (0 > ioctl(this->m_fd, HAILO_READ_LOG, ¶ms)) { LOGGER__ERROR("Failed to read log with errno:{}", errno); return HAILO_PCIE_DRIVER_FAIL; } + CHECK(params.read_bytes <= sizeof(params.buffer), HAILO_PCIE_DRIVER_FAIL, + "Amount of bytes read from log {} is bigger than size of buffer {}", params.read_bytes, sizeof(params.buffer)); + + memcpy(buffer, params.buffer, params.read_bytes); *read_bytes = params.read_bytes; return HAILO_SUCCESS; @@ -561,7 +603,7 @@ Expected 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 = { + hailo_allocate_low_memory_buffer_params allocate_params = { .buffer_size = size, .buffer_handle = 0 }; @@ -587,6 +629,28 @@ hailo_status HailoRTDriver::vdma_low_memory_buffer_free(uintptr_t buffer_handle) return HAILO_SUCCESS; } +Expected> HailoRTDriver::vdma_continuous_buffer_alloc(size_t size) +{ + hailo_allocate_continuous_buffer_params params { .buffer_size = size, .buffer_handle = 0, .dma_address = 0 }; + + if (0 > ioctl(this->m_fd, HAILO_VDMA_CONTINUOUS_BUFFER_ALLOC, ¶ms)) { + LOGGER__ERROR("Failed allocate continuous buffer with errno:{}", errno); + return make_unexpected(HAILO_PCIE_DRIVER_FAIL); + } + + return std::make_pair(params.buffer_handle, params.dma_address); +} + +hailo_status HailoRTDriver::vdma_continuous_buffer_free(uintptr_t buffer_handle) +{ + if (0 > ioctl(this->m_fd, HAILO_VDMA_CONTINUOUS_BUFFER_FREE, buffer_handle)) { + LOGGER__ERROR("Failed to free continuous 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 = { diff --git a/hailort/libhailort/src/os/posix/mmap_buffer.cpp b/hailort/libhailort/src/os/posix/mmap_buffer.cpp index 1fdd3bf..fbf7988 100644 --- a/hailort/libhailort/src/os/posix/mmap_buffer.cpp +++ b/hailort/libhailort/src/os/posix/mmap_buffer.cpp @@ -12,7 +12,7 @@ #include #include -#if defined(__unix__) +#if defined(__linux__) #include #endif diff --git a/hailort/libhailort/src/os/posix/pcie_driver_sysfs.hpp b/hailort/libhailort/src/os/posix/pcie_driver_scan.hpp similarity index 51% rename from hailort/libhailort/src/os/posix/pcie_driver_sysfs.hpp rename to hailort/libhailort/src/os/posix/pcie_driver_scan.hpp index 32b60af..4536cfe 100644 --- a/hailort/libhailort/src/os/posix/pcie_driver_sysfs.hpp +++ b/hailort/libhailort/src/os/posix/pcie_driver_scan.hpp @@ -3,8 +3,8 @@ * Distributed under the MIT license (https://opensource.org/licenses/MIT) **/ /** - * @file pcie_driver_sysfs.hpp - * @brief Parse pcie driver sysfs + * @file pcie_driver_scan.hpp + * @brief Get list and parse pcie driver info **/ #include "os/hailort_driver.hpp" @@ -12,7 +12,11 @@ namespace hailort { -Expected> list_sysfs_pcie_devices(); +Expected> list_pcie_devices(); +#if defined(__linux__) Expected query_device_info(const std::string &device_name); +#elif defined(__QNX__) +Expected query_device_info(const std::string &device_name, uint32_t index); +#endif // defined(__linux__) } /* namespace hailort */ diff --git a/hailort/libhailort/src/os/posix/qnx/event.cpp b/hailort/libhailort/src/os/posix/qnx/event.cpp index 6109676..d205f0c 100644 --- a/hailort/libhailort/src/os/posix/qnx/event.cpp +++ b/hailort/libhailort/src/os/posix/qnx/event.cpp @@ -6,7 +6,8 @@ * @file event.cpp * @brief Event & Semaphore wrapper for QNX * - * TODO: doc + * This class implements our Events API over the neosmart pevents events. It also implement the Semaphore behavior and API + * Using the pevents events. For more information check out the implementation of pevents https://github.com/neosmart/pevents **/ #include "hailo/event.hpp" #include "hailo/hailort.h" @@ -16,71 +17,84 @@ #include #include -#define INVALID_FD (-1) +#define WFMO +#include "pevents.h" +#undef WFMO + +#define INVALID_EVENT_HANDLE (nullptr) +#define WAIT_OBJECT_0 (0) namespace hailort { -Waitable::Waitable(underlying_handle_t handle) : +Waitable::Waitable(underlying_waitable_handle_t handle) : m_handle(handle) {} Waitable::~Waitable() { - if (-1 != m_handle) { - (void) close(m_handle); + if (INVALID_EVENT_HANDLE != m_handle) { + int err = 0; + if (0 != (err = neosmart::DestroyEvent(m_handle))) { + LOGGER__ERROR("Error destroying event handle, returned error={}", err); + } } } Waitable::Waitable(Waitable&& other) : - m_handle(std::exchange(other.m_handle, INVALID_FD)) + m_handle(std::exchange(other.m_handle, INVALID_EVENT_HANDLE)) {} -underlying_handle_t Waitable::get_underlying_handle() +underlying_waitable_handle_t Waitable::get_underlying_handle() { return m_handle; } -hailo_status Waitable::eventfd_poll(underlying_handle_t fd, std::chrono::milliseconds timeout) +hailo_status Waitable::wait_for_single_object(underlying_waitable_handle_t handle, 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; + const size_t timeout_ms = (timeout.count() > INT_MAX) ? INT_MAX : static_cast(timeout.count()); + const auto wait_result = neosmart::WaitForEvent(handle, timeout_ms); + switch (wait_result) { + case WAIT_OBJECT_0: + return HAILO_SUCCESS; + case ETIMEDOUT: + return HAILO_TIMEOUT; + default: + LOGGER__ERROR("WaitForEvent failed, returned error={}", wait_result); + return HAILO_INTERNAL_FAILURE; + } } Expected Event::create(const State& initial_state) { - (void) initial_state; - return make_unexpected(HAILO_NOT_IMPLEMENTED); + const auto handle = open_event_handle(initial_state); + if (INVALID_EVENT_HANDLE == handle) { + return make_unexpected(HAILO_INTERNAL_FAILURE); + } + return std::move(Event(handle)); } EventPtr Event::create_shared(const State& initial_state) { - (void) initial_state; - return make_shared_nothrow(INVALID_FD); + const auto handle = open_event_handle(initial_state); + if (INVALID_EVENT_HANDLE == handle) { + return nullptr; + } + + return make_shared_nothrow(handle); } hailo_status Event::wait(std::chrono::milliseconds timeout) { - (void) timeout; - return HAILO_NOT_IMPLEMENTED; + return wait_for_single_object(m_handle, timeout); } hailo_status Event::signal() { - return HAILO_NOT_IMPLEMENTED; + const auto result = neosmart::SetEvent(m_handle); + CHECK(0 != result, HAILO_INTERNAL_FAILURE, "SetEvent failed with error {}" , result); + + return HAILO_SUCCESS; } bool Event::is_auto_reset() @@ -90,36 +104,76 @@ bool Event::is_auto_reset() hailo_status Event::reset() { - return HAILO_NOT_IMPLEMENTED; + const auto result = neosmart::ResetEvent(m_handle); + CHECK(0 != result, HAILO_INTERNAL_FAILURE, "ResetEvent failed with error {}", result); + + return HAILO_SUCCESS; } -underlying_handle_t Event::open_event_handle(const State& initial_state) +underlying_waitable_handle_t Event::open_event_handle(const State& initial_state) { - (void) initial_state; - return INVALID_FD; + static const auto manual_reset = true; + const bool state = (initial_state == State::signalled ? true : false); + auto event = neosmart::CreateEvent(manual_reset, state); + if (INVALID_EVENT_HANDLE == event) { + LOGGER__ERROR("Call to CreateEvent failed"); + } + return event; } Expected Semaphore::create(uint32_t initial_count) { - (void) initial_count; - return make_unexpected(HAILO_NOT_IMPLEMENTED); + const auto handle = open_semaphore_handle(initial_count); + if (INVALID_EVENT_HANDLE == handle) { + return make_unexpected(HAILO_INTERNAL_FAILURE); + } + return std::move(Semaphore(handle, initial_count)); } SemaphorePtr Semaphore::create_shared(uint32_t initial_count) { - (void) initial_count; - return make_shared_nothrow(INVALID_FD); + const auto handle = open_semaphore_handle(initial_count); + if (INVALID_EVENT_HANDLE == handle) { + return nullptr; + } + + return make_shared_nothrow(handle, initial_count); } hailo_status Semaphore::wait(std::chrono::milliseconds timeout) { - (void) timeout; - return HAILO_NOT_IMPLEMENTED; + auto wait_result = wait_for_single_object(m_handle, timeout); + if (HAILO_SUCCESS == wait_result) { + m_sem_mutex.lock(); + if (0 == m_count.load()) { + LOGGER__ERROR("Waiting on semaphore with 0 value"); + } + if (m_count > 0) { + m_count--; + } + // After decrementing the value of the semaphore - check if the new value is bigger than 0 and if it is signal the event + if (m_count > 0) { + neosmart::SetEvent(m_handle); + } + m_sem_mutex.unlock(); + } + + return wait_result; } hailo_status Semaphore::signal() { - return HAILO_NOT_IMPLEMENTED; + m_sem_mutex.lock(); + const auto result = neosmart::SetEvent(m_handle); + if (0 != result) { + LOGGER__ERROR("SetEvent failed with error {}", result); + m_sem_mutex.unlock(); + return HAILO_INTERNAL_FAILURE; + } + m_count++; + m_sem_mutex.unlock(); + + return HAILO_SUCCESS; } bool Semaphore::is_auto_reset() @@ -127,39 +181,94 @@ bool Semaphore::is_auto_reset() return true; } -underlying_handle_t Semaphore::open_semaphore_handle(uint32_t initial_count) +underlying_waitable_handle_t Semaphore::open_semaphore_handle(uint32_t initial_count) { - (void) initial_count; - return INVALID_FD; + static const auto manual_reset = false; + static const auto state = (initial_count > 0 ? true : false); + auto event = neosmart::CreateEvent(manual_reset, state); + if (INVALID_EVENT_HANDLE == event) { + LOGGER__ERROR("Call to CreateEvent failed"); + } + return event; } -WaitOrShutdown::WaitOrShutdown(WaitablePtr waitable, EventPtr shutdown_event) +Semaphore::Semaphore(underlying_waitable_handle_t handle, uint32_t initial_count) : + Waitable(handle), m_count(initial_count) +{} + +Semaphore::Semaphore(Semaphore&& other) : + Waitable(std::move(other)) { - (void) waitable; - (void) shutdown_event; + other.m_sem_mutex.lock(); + m_count.store(other.m_count.load()); + other.m_sem_mutex.unlock(); +} + +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)) +{} + +void Event::post_wait() +{} + +void Semaphore::post_wait(){ + m_sem_mutex.lock(); + if (0 == m_count.load()) { + LOGGER__ERROR("Wait Returned on semaphore with 0 value"); + } + if (m_count > 0) { + m_count--; + } + // After decrementing the value of the semaphore - check if the new value is bigger than 0 and if it is signal the event + if (m_count > 0) { + neosmart::SetEvent(m_handle); + } + m_sem_mutex.unlock(); } hailo_status WaitOrShutdown::wait(std::chrono::milliseconds timeout) { - (void) timeout; - return HAILO_NOT_IMPLEMENTED; + int wait_index = -1; + const uint64_t timeout_ms = (timeout.count() > INT_MAX) ? INT_MAX : static_cast(timeout.count()); + const auto wait_result = neosmart::WaitForMultipleEvents(m_wait_handle_array.data(), static_cast(m_wait_handle_array.size()), + false, timeout_ms, wait_index); + // If semaphore need to subtract from counter + if (0 != wait_result) { + if (ETIMEDOUT == wait_result) { + return HAILO_TIMEOUT; + } else { + LOGGER__ERROR("WaitForMultipleEvents Failed, error: {}", wait_result); + return HAILO_INTERNAL_FAILURE; + } + } + + if (WAITABLE_INDEX == wait_index) { + // Meaning it can be a semaphore object + m_waitable->post_wait(); + return HAILO_SUCCESS; + } else if (SHUTDOWN_INDEX == wait_index) { + return HAILO_SHUTDOWN_EVENT_SIGNALED; + } else { + LOGGER__ERROR("Invalid event index signalled in WaitForMultipleEventsFailed, index: {}", wait_index); + return HAILO_INTERNAL_FAILURE; + } } hailo_status WaitOrShutdown::signal() { - return HAILO_NOT_IMPLEMENTED; + return m_waitable->signal(); } 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; + WaitHandleArray handles{ + shutdown_event->get_underlying_handle(), + waitable->get_underlying_handle() + }; + return handles; } } /* namespace hailort */ diff --git a/hailort/libhailort/src/os/posix/qnx/pcie_driver_scan.cpp b/hailort/libhailort/src/os/posix/qnx/pcie_driver_scan.cpp new file mode 100644 index 0000000..05b5de3 --- /dev/null +++ b/hailort/libhailort/src/os/posix/qnx/pcie_driver_scan.cpp @@ -0,0 +1,86 @@ +/** + * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved. + * Distributed under the MIT license (https://opensource.org/licenses/MIT) + **/ +/** + * @file pcie_driver_scan.cpp + * @brief Get list and parse pcie driver info + **/ + +#include "os/posix/pcie_driver_scan.hpp" +#include +extern "C" { +#include +} + +namespace hailort +{ + +#define HAILO_VENDOR_ID (0x1e60) +#define HAILO_PCIE_CLASS_PATH ("/dev/") +// Every device name will start with "hailo" +#define HAILO_PCIE_DEVICE_NAME_PREFIX ("hailo") + +Expected> list_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 devices; + struct dirent *dir = nullptr; + while ((dir = readdir(dir_iter)) != nullptr) { + std::string device_name(dir->d_name); + if (device_name == "." || device_name == "..") { + continue; + } + // Check that it is hailo device + if (std::string::npos == device_name.find(HAILO_PCIE_DEVICE_NAME_PREFIX)) { + continue; + } + devices.push_back(device_name); + } + + closedir(dir_iter); + return devices; +} + +Expected query_device_info(const std::string &device_name, uint32_t index) +{ + HailoRTDriver::DeviceInfo dev_info = {}; + pci_err_t err; + + // pci_device_find finds all relevant devices - find specific using index + pci_bdf_t pci_dev = pci_device_find(index, HAILO_VENDOR_ID, PCI_DID_ANY, PCI_CCODE_ANY); + if (PCI_BDF_NONE == pci_dev) { + LOGGER__ERROR("Error finding relevant device"); + make_unexpected(HAILO_INVALID_ARGUMENT); + } + + pci_did_t device_id; + if (PCI_ERR_OK != (err = pci_device_read_did(pci_dev, &device_id))) { + LOGGER__ERROR("Failed reading Device ID, error {}", err); + make_unexpected(HAILO_INTERNAL_FAILURE); + } + + dev_info.dev_path = std::move(std::string(HAILO_PCIE_CLASS_PATH) + device_name); + dev_info.vendor_id = HAILO_VENDOR_ID; + dev_info.device_id = device_id; + dev_info.domain = 0; + dev_info.bus = PCI_BUS(pci_dev); + dev_info.device = PCI_DEV(pci_dev); + dev_info.func = PCI_FUNC(pci_dev); + + return dev_info; +} + +} /* namespace hailort */ diff --git a/hailort/libhailort/src/os/posix/unix/event.cpp b/hailort/libhailort/src/os/posix/unix/event.cpp index 2e7a898..465b4c8 100644 --- a/hailort/libhailort/src/os/posix/unix/event.cpp +++ b/hailort/libhailort/src/os/posix/unix/event.cpp @@ -20,7 +20,7 @@ namespace hailort { -Waitable::Waitable(underlying_handle_t handle) : +Waitable::Waitable(underlying_waitable_handle_t handle) : m_handle(handle) {} @@ -35,12 +35,12 @@ Waitable::Waitable(Waitable&& other) : m_handle(std::exchange(other.m_handle, -1)) {} -underlying_handle_t Waitable::get_underlying_handle() +underlying_waitable_handle_t Waitable::get_underlying_handle() { return m_handle; } -hailo_status Waitable::eventfd_poll(underlying_handle_t fd, std::chrono::milliseconds timeout) +hailo_status Waitable::eventfd_poll(underlying_waitable_handle_t fd, std::chrono::milliseconds timeout) { hailo_status status = HAILO_UNINITIALIZED; struct pollfd pfd{}; @@ -84,7 +84,7 @@ l_exit: return status; } -hailo_status Waitable::eventfd_read(underlying_handle_t fd) +hailo_status Waitable::eventfd_read(underlying_waitable_handle_t fd) { hailo_status status = HAILO_UNINITIALIZED; ssize_t read_ret = -1; @@ -104,7 +104,7 @@ l_exit: return status; } -hailo_status Waitable::eventfd_write(underlying_handle_t fd) +hailo_status Waitable::eventfd_write(underlying_waitable_handle_t fd) { hailo_status status = HAILO_UNINITIALIZED; ssize_t write_ret = -1; @@ -167,7 +167,7 @@ hailo_status Event::reset() return eventfd_read(m_handle); } -underlying_handle_t Event::open_event_handle(const State& initial_state) +underlying_waitable_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; @@ -223,7 +223,7 @@ bool Semaphore::is_auto_reset() return true; } -underlying_handle_t Semaphore::open_semaphore_handle(uint32_t initial_count) +underlying_waitable_handle_t Semaphore::open_semaphore_handle(uint32_t initial_count) { static const int SEMAPHORE = EFD_SEMAPHORE; const auto handle = eventfd(initial_count, SEMAPHORE); diff --git a/hailort/libhailort/src/os/posix/pcie_driver_sysfs.cpp b/hailort/libhailort/src/os/posix/unix/pcie_driver_scan.cpp similarity index 96% rename from hailort/libhailort/src/os/posix/pcie_driver_sysfs.cpp rename to hailort/libhailort/src/os/posix/unix/pcie_driver_scan.cpp index ddfe4c4..531778a 100644 --- a/hailort/libhailort/src/os/posix/pcie_driver_sysfs.cpp +++ b/hailort/libhailort/src/os/posix/unix/pcie_driver_scan.cpp @@ -3,11 +3,11 @@ * Distributed under the MIT license (https://opensource.org/licenses/MIT) **/ /** - * @file pcie_driver_sysfs.cpp + * @file pcie_driver_scan.cpp * @brief Parse pcie driver sysfs **/ -#include "os/posix/pcie_driver_sysfs.hpp" +#include "os/posix/pcie_driver_scan.hpp" #include #include @@ -19,7 +19,7 @@ namespace hailort #define HAILO_DEVICE_ID_FILENAME ("device_id") -Expected> list_sysfs_pcie_devices() +Expected> list_pcie_devices() { DIR *dir_iter = opendir(HAILO_PCIE_CLASS_PATH); if (!dir_iter) { diff --git a/hailort/libhailort/src/os/windows/event.cpp b/hailort/libhailort/src/os/windows/event.cpp index fd019c9..dd053ac 100644 --- a/hailort/libhailort/src/os/windows/event.cpp +++ b/hailort/libhailort/src/os/windows/event.cpp @@ -17,7 +17,7 @@ namespace hailort { -Waitable::Waitable(underlying_handle_t handle) : +Waitable::Waitable(underlying_waitable_handle_t handle) : m_handle(handle) {} @@ -32,7 +32,7 @@ Waitable::Waitable(Waitable&& other) : m_handle(std::exchange(other.m_handle, nullptr)) {} -underlying_handle_t Waitable::get_underlying_handle() +underlying_waitable_handle_t Waitable::get_underlying_handle() { return m_handle; } @@ -46,7 +46,7 @@ static DWORD timeout_millies(long long value) return millies; } -hailo_status Waitable::wait_for_single_object(underlying_handle_t handle, std::chrono::milliseconds timeout) +hailo_status Waitable::wait_for_single_object(underlying_waitable_handle_t handle, std::chrono::milliseconds timeout) { DWORD wait_millies = timeout_millies(timeout.count()); assert(nullptr != handle); @@ -117,7 +117,7 @@ hailo_status Event::reset() return HAILO_SUCCESS; } -underlying_handle_t Event::open_event_handle(const State& initial_state) +underlying_waitable_handle_t Event::open_event_handle(const State& initial_state) { static const LPSECURITY_ATTRIBUTES NO_INHERITANCE = nullptr; static const BOOL MANUAL_RESET = true; @@ -172,7 +172,7 @@ bool Semaphore::is_auto_reset() return true; } -underlying_handle_t Semaphore::open_semaphore_handle(uint32_t initial_count) +underlying_waitable_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::max(); diff --git a/hailort/libhailort/src/os/windows/hailort_driver.cpp b/hailort/libhailort/src/os/windows/hailort_driver.cpp index a8854d1..92e1ce6 100644 --- a/hailort/libhailort/src/os/windows/hailort_driver.cpp +++ b/hailort/libhailort/src/os/windows/hailort_driver.cpp @@ -603,13 +603,18 @@ hailo_status HailoRTDriver::read_bar(PciBar bar, off_t offset, size_t size, void transfer.bar_index = static_cast(bar); transfer.offset = offset; transfer.count = size; - transfer.buffer = buf; + memset(transfer.buffer, 0, sizeof(transfer.buffer)); + + CHECK(size <= sizeof(transfer.buffer), HAILO_INVALID_ARGUMENT, + "Invalid size to read, size given {} is larger than max size {}", size, sizeof(transfer.buffer)); if (0 > ioctl(m_fd, HAILO_BAR_TRANSFER, &data)) { LOGGER__ERROR("HailoRTDriver::read_bar failed with errno:{}", errno); return HAILO_PCIE_DRIVER_FAIL; } + memcpy(buf, transfer.buffer, transfer.count); + return HAILO_SUCCESS; } @@ -631,7 +636,12 @@ hailo_status HailoRTDriver::write_bar(PciBar bar, off_t offset, size_t size, con transfer.bar_index = static_cast(bar); transfer.offset = offset; transfer.count = size; - transfer.buffer = (void*)buf; + memset(transfer.buffer, 0, sizeof(transfer.buffer)); + + CHECK(size <= sizeof(transfer.buffer), HAILO_INVALID_ARGUMENT, + "Invalid size to write, size given {} is larger than max size {}", size, sizeof(transfer.buffer)); + + memcpy(transfer.buffer, buf, transfer.count); if (0 > ioctl(this->m_fd, HAILO_BAR_TRANSFER, &data)) { LOGGER__ERROR("HailoRTDriver::write_bar failed with errno: {}", errno); @@ -965,6 +975,18 @@ hailo_status HailoRTDriver::vdma_low_memory_buffer_free(uintptr_t buffer_handle) return HAILO_INVALID_OPERATION; } +Expected> HailoRTDriver::vdma_continuous_buffer_alloc(size_t size) +{ + (void) size; + return make_unexpected(HAILO_INVALID_OPERATION); +} + +hailo_status HailoRTDriver::vdma_continuous_buffer_free(uintptr_t buffer_handle) +{ + (void) buffer_handle; + return HAILO_INVALID_OPERATION; +} + hailo_status HailoRTDriver::mark_as_used() { tCompatibleHailoIoctlData data = {}; diff --git a/hailort/libhailort/src/pcie_stream.cpp b/hailort/libhailort/src/pcie_stream.cpp index 9e51d25..c7f46e8 100644 --- a/hailort/libhailort/src/pcie_stream.cpp +++ b/hailort/libhailort/src/pcie_stream.cpp @@ -13,27 +13,26 @@ namespace hailort PcieInputStream::PcieInputStream( PcieDevice &device, - uint8_t channel_index, + std::shared_ptr channel, 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) + VdmaInputStream(device, std::move(channel), edge_layer, network_group_activated_event, + batch_size, transfer_timeout, HAILO_STREAM_INTERFACE_PCIE, status) {} Expected> PcieInputStream::create(Device &device, - uint8_t channel_index, const LayerInfo &edge_layer, uint16_t batch_size, - EventPtr network_group_activated_event, LatencyMeterPtr latency_meter) + std::shared_ptr channel, const LayerInfo &edge_layer, + uint16_t batch_size, EventPtr network_group_activated_event) { hailo_status status = HAILO_UNINITIALIZED; PcieDevice *pcie_device = reinterpret_cast(&device); std::unique_ptr 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)); + std::move(channel), edge_layer, std::move(network_group_activated_event), batch_size, + DEFAULT_TRANSFER_TIMEOUT, status)); CHECK((nullptr != local_stream), make_unexpected(HAILO_OUT_OF_HOST_MEMORY)); CHECK_SUCCESS_AS_EXPECTED(status); @@ -41,15 +40,15 @@ Expected> PcieInputStream::create(Device &devic } Expected> PcieOutputStream::create(Device &device, - uint8_t channel_index, const LayerInfo &edge_layer, uint16_t batch_size, - EventPtr network_group_activated_event, LatencyMeterPtr latency_meter) + std::shared_ptr channel, const LayerInfo &edge_layer, uint16_t batch_size, + EventPtr network_group_activated_event) { hailo_status status = HAILO_UNINITIALIZED; PcieDevice *pcie_device = reinterpret_cast(&device); std::unique_ptr 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)); + std::move(channel), edge_layer, std::move(network_group_activated_event), + batch_size, DEFAULT_TRANSFER_TIMEOUT, status)); CHECK((nullptr != local_stream), make_unexpected(HAILO_OUT_OF_HOST_MEMORY)); CHECK_SUCCESS_AS_EXPECTED(status); @@ -58,15 +57,14 @@ Expected> PcieOutputStream::create(Device &dev PcieOutputStream::PcieOutputStream( PcieDevice &device, - uint8_t channel_index, + std::shared_ptr channel, 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) + VdmaOutputStream(device, std::move(channel), edge_layer, + network_group_activated_event, batch_size, transfer_timeout, status) {} } /* namespace hailort */ diff --git a/hailort/libhailort/src/pcie_stream.hpp b/hailort/libhailort/src/pcie_stream.hpp index 314537c..40a331b 100644 --- a/hailort/libhailort/src/pcie_stream.hpp +++ b/hailort/libhailort/src/pcie_stream.hpp @@ -21,20 +21,19 @@ public: PcieInputStream(PcieInputStream &&other) = default; virtual ~PcieInputStream() = default; - static Expected> create(Device &device, uint8_t channel_index, - const LayerInfo &edge_layer, uint16_t batch_size, EventPtr network_group_activated_event, - LatencyMeterPtr latency_meter = nullptr); + static Expected> create(Device &device, + std::shared_ptr channel, const LayerInfo &edge_layer, uint16_t batch_size, + EventPtr network_group_activated_event); virtual hailo_stream_interface_t get_interface() const override { return HAILO_STREAM_INTERFACE_PCIE; } private: PcieInputStream( PcieDevice &device, - uint8_t channel_index, + std::shared_ptr channel, 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); @@ -46,9 +45,9 @@ public: PcieOutputStream(PcieOutputStream &&other) = default; virtual ~PcieOutputStream() = default; - static Expected> create(Device &device, uint8_t channel_index, - const LayerInfo &edge_layer, uint16_t batch_size, EventPtr network_group_activated_event, - LatencyMeterPtr latency_meter); + static Expected> create(Device &device, + std::shared_ptr channel, const LayerInfo &edge_layer, uint16_t batch_size, + EventPtr network_group_activated_event); virtual hailo_stream_interface_t get_interface() const override { return HAILO_STREAM_INTERFACE_PCIE; } @@ -57,11 +56,10 @@ public: private: explicit PcieOutputStream( PcieDevice &device, - uint8_t channel_index, + std::shared_ptr channel, 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); }; diff --git a/hailort/libhailort/src/pipeline.cpp b/hailort/libhailort/src/pipeline.cpp index c65122e..de3ae91 100644 --- a/hailort/libhailort/src/pipeline.cpp +++ b/hailort/libhailort/src/pipeline.cpp @@ -10,6 +10,7 @@ #include "pipeline.hpp" #include "common/utils.hpp" #include "common/runtime_statistics_internal.hpp" +#include "microprofile.h" namespace hailort { @@ -629,6 +630,11 @@ BaseQueueElement::BaseQueueElement(SpscQueue &&queue, EventPtr s void BaseQueueElement::start_thread() { m_thread = std::thread([this] () { + // Microprofile the thread + MicroProfileOnThreadCreate(name().c_str()); + MicroProfileSetEnableAllGroups(true); + MicroProfileSetForceMetaCounters(true); + while (m_is_thread_running.load()) { auto status = m_activation_event.wait(INIFINITE_TIMEOUT()); @@ -669,6 +675,8 @@ void BaseQueueElement::start_thread() } } } + // TODO: Should we use MicroProfileShutdown? + MicroProfileOnThreadExit(); }); } @@ -846,14 +854,13 @@ hailo_status PushQueueElement::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? - } + CHECK_SUCCESS(deactivation_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); + } + else { + LOGGER__ERROR("enqueue() in element {} failed, got status = {}", name(), status); + return status; } } @@ -980,14 +987,9 @@ Expected PullQueueElement::run_pull(PipelineBuffer &&optional, c 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); - } + auto shutdown_event_status = m_shutdown_event->signal(); + CHECK_SUCCESS(status); + CHECK_SUCCESS(shutdown_event_status); return HAILO_SUCCESS; } @@ -1349,9 +1351,6 @@ hailo_status BaseDemuxElement::deactivate() // 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, @@ -1361,6 +1360,8 @@ hailo_status BaseDemuxElement::deactivate() } m_cv.notify_all(); + CHECK_SUCCESS(status); + return HAILO_SUCCESS; } diff --git a/hailort/libhailort/src/stream.cpp b/hailort/libhailort/src/stream.cpp index 07ee85b..039e81b 100644 --- a/hailort/libhailort/src/stream.cpp +++ b/hailort/libhailort/src/stream.cpp @@ -13,6 +13,7 @@ #include "hailo/transform.hpp" #include "common/utils.hpp" #include "hef_internal.hpp" +#include "microprofile.h" #include @@ -26,13 +27,16 @@ hailo_status InputStream::flush() hailo_status InputStream::write(const MemoryView &buffer) { + MICROPROFILE_SCOPEI("Stream", "Write", 0); 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(buffer.data()), 0, buffer.size()); + auto status = sync_write_all_raw_buffer_no_transform_impl(const_cast(buffer.data()), 0, buffer.size()); + MicroProfileFlip(nullptr); + return status; } std::string InputStream::to_string() const @@ -78,6 +82,10 @@ hailo_status OutputStream::read_nms(void *buffer, size_t offset, size_t size) 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_DUMMY_DELIMITER == *(uint64_t*)((uint8_t*)buffer + offset)) { + continue; + } + if (HailoRTCommon::NMS_DELIMITER == *(uint64_t*)((uint8_t*)buffer + offset)) { break; } @@ -96,6 +104,7 @@ hailo_status OutputStream::read_nms(void *buffer, size_t offset, size_t size) hailo_status OutputStream::read(MemoryView buffer) { + MICROPROFILE_SCOPEI("Stream", "Read", 0); 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); diff --git a/hailort/libhailort/src/stream_internal.cpp b/hailort/libhailort/src/stream_internal.cpp index 0b80c01..652b326 100644 --- a/hailort/libhailort/src/stream_internal.cpp +++ b/hailort/libhailort/src/stream_internal.cpp @@ -22,9 +22,19 @@ EventPtr &InputStreamBase::get_network_group_activated_event() return m_network_group_activated_event; } +bool InputStreamBase::is_scheduled() +{ + return false; +} + EventPtr &OutputStreamBase::get_network_group_activated_event() { return m_network_group_activated_event; } +bool OutputStreamBase::is_scheduled() +{ + return false; +} + } /* namespace hailort */ diff --git a/hailort/libhailort/src/stream_internal.hpp b/hailort/libhailort/src/stream_internal.hpp index 4ba4467..85ce20e 100644 --- a/hailort/libhailort/src/stream_internal.hpp +++ b/hailort/libhailort/src/stream_internal.hpp @@ -83,6 +83,7 @@ protected: } virtual EventPtr &get_network_group_activated_event() override; + virtual bool is_scheduled() override; private: EventPtr m_network_group_activated_event; @@ -124,6 +125,7 @@ protected: } virtual EventPtr &get_network_group_activated_event() override; + virtual bool is_scheduled() override; LayerInfo m_layer_info; diff --git a/hailort/libhailort/src/transform.cpp b/hailort/libhailort/src/transform.cpp index 65ffe22..509c198 100644 --- a/hailort/libhailort/src/transform.cpp +++ b/hailort/libhailort/src/transform.cpp @@ -17,6 +17,7 @@ #include "common/logger_macros.hpp" #include "common/utils.hpp" #include "transform_internal.hpp" +#include "microprofile.h" #include #include @@ -829,7 +830,7 @@ hailo_status reorder_input_stream(const void *src_ptr, hailo_3d_image_shape_t sr return HAILO_SUCCESS; } - if ((HAILO_FORMAT_ORDER_FCR == src_format.order) && + if (((HAILO_FORMAT_ORDER_FCR == src_format.order) || (HAILO_FORMAT_ORDER_NHWC == src_format.order)) && (HAILO_FORMAT_ORDER_FCR == dst_format.order)) { assert(0 == (dst_image_shape.features % 8)); switch (dst_format.type) { @@ -846,7 +847,7 @@ hailo_status reorder_input_stream(const void *src_ptr, hailo_3d_image_shape_t sr return HAILO_SUCCESS; } - if ((HAILO_FORMAT_ORDER_F8CR == src_format.order) && + if (((HAILO_FORMAT_ORDER_F8CR == src_format.order) || (HAILO_FORMAT_ORDER_NHWC == src_format.order)) && (HAILO_FORMAT_ORDER_F8CR == dst_format.order)) { switch (dst_format.type) { case HAILO_FORMAT_TYPE_UINT8: @@ -993,7 +994,7 @@ hailo_status reorder_output_stream(const void *src_ptr, hailo_3d_image_shape_t s } } else if ((HAILO_FORMAT_ORDER_FCR == src_format.order) && - (HAILO_FORMAT_ORDER_FCR == dst_format.order)) { + ((HAILO_FORMAT_ORDER_FCR == dst_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*)src_ptr, &src_image_shape, (uint8_t*)dst_ptr, &dst_image_shape); @@ -1007,7 +1008,7 @@ hailo_status reorder_output_stream(const void *src_ptr, hailo_3d_image_shape_t s } } else if ((HAILO_FORMAT_ORDER_F8CR == src_format.order) && - (HAILO_FORMAT_ORDER_F8CR == dst_format.order)) { + ((HAILO_FORMAT_ORDER_F8CR == dst_format.order) || (HAILO_FORMAT_ORDER_NHWC == dst_format.order))) { switch (src_format.type) { case HAILO_FORMAT_TYPE_UINT8: transform__d2h_F8CR((uint8_t*)src_ptr, &src_image_shape, (uint8_t*)dst_ptr, &dst_image_shape); @@ -1230,6 +1231,7 @@ hailo_status FrameOutputTransformContext::transform_inner(const void *src_ptr, v hailo_status transform_demux_raw_frame(const void *src, uint32_t offset, hailo_mux_info_t *mux_info, uint32_t mux_row_count) { + MICROPROFILE_SCOPEI("Transformations", "Demux", 0); // 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; @@ -1470,6 +1472,7 @@ InputTransformContext::InputTransformContext(size_t src_frame_size, const hailo_ hailo_status InputTransformContext::transform(const MemoryView src, MemoryView dst) { + MICROPROFILE_SCOPEI("Transformations", "H2D transform", 0); /* Check sizes */ CHECK(src.size() == m_src_frame_size, HAILO_INVALID_ARGUMENT, "src size must be {}. passed size - {}", m_src_frame_size, src.size()); @@ -1636,6 +1639,7 @@ Expected> NMSOutputTransformContext::cre hailo_status FrameOutputTransformContext::transform(const MemoryView src, MemoryView dst) { + MICROPROFILE_SCOPEI("Transformations", "D2H transform", 0); /* Check sizes */ CHECK(src.size() == m_src_frame_size, HAILO_INVALID_ARGUMENT, "src size must be {}. passed size - {}", m_src_frame_size, src.size()); @@ -1650,6 +1654,7 @@ hailo_status FrameOutputTransformContext::transform(const MemoryView src, Memory hailo_status NMSOutputTransformContext::transform(const MemoryView src, MemoryView dst) { + MICROPROFILE_SCOPEI("Transformations", "D2H NMS transform", 0); /* Check sizes */ CHECK(src.size() == m_src_frame_size, HAILO_INVALID_ARGUMENT, "src size must be {}. passed size - {}", m_src_frame_size, src.size()); @@ -1826,6 +1831,7 @@ hailo_status OutputDemuxerBase::get_mux_info_from_layer_info_impl(hailo_mux_info hailo_status fuse_buffers(const std::vector &buffers, const std::vector &infos_of_buffers, MemoryView dst) { + MICROPROFILE_SCOPEI("Transformations", "Fuse NMS", 0); 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!"); diff --git a/hailort/libhailort/src/vdevice.cpp b/hailort/libhailort/src/vdevice.cpp index aa87124..5fdf5e0 100644 --- a/hailort/libhailort/src/vdevice.cpp +++ b/hailort/libhailort/src/vdevice.cpp @@ -22,6 +22,9 @@ Expected> VDevice::create(const hailo_vdevice_params_t { CHECK_AS_EXPECTED(0 != params.device_count, HAILO_INVALID_ARGUMENT, "VDevice creation failed. invalid device_count ({}).", params.device_count); + + CHECK_AS_EXPECTED((HAILO_SCHEDULING_ALGORITHM_NONE == params.scheduling_algorithm) || (1 == params.device_count), HAILO_INVALID_ARGUMENT, + "Network group scheduler can be active only when using one device in the vDevice!"); auto vdevice = VDeviceBase::create(params); CHECK_EXPECTED(vdevice); @@ -38,6 +41,13 @@ Expected> VDevice::create() Expected> VDeviceBase::create(const hailo_vdevice_params_t ¶ms) { + NetworkGroupSchedulerPtr scheduler_ptr; + if (HAILO_SCHEDULING_ALGORITHM_NONE != params.scheduling_algorithm) { + auto network_group_scheduler = NetworkGroupScheduler::create_shared(params.scheduling_algorithm); + CHECK_EXPECTED(network_group_scheduler); + scheduler_ptr = network_group_scheduler.release(); + } + auto scan_res = PcieDevice::scan(); CHECK_EXPECTED(scan_res); @@ -70,25 +80,36 @@ Expected> VDeviceBase::create(const hailo_vdevice_p 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(new (std::nothrow) VDeviceBase(std::move(devices))); + + std::string vdevice_infos = "VDevice Infos:"; + for (const auto &device : devices) { + auto info_str = PcieDevice::pcie_device_info_to_string(device->get_device_info()); + CHECK_EXPECTED(info_str); + + vdevice_infos += " " + info_str.value(); + } + LOGGER__INFO("{}", vdevice_infos); + + auto vdevice = std::unique_ptr(new (std::nothrow) VDeviceBase(std::move(devices), scheduler_ptr)); CHECK_AS_EXPECTED(nullptr != vdevice, HAILO_OUT_OF_HOST_MEMORY); return vdevice; } +// TODO - make this function thread-safe. Expected 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(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); + bool is_scheduler_used = (m_network_group_scheduler != nullptr); + auto network_groups = m_context_switch_manager->add_hef(hef, configure_params, is_scheduler_used); CHECK_EXPECTED(network_groups); auto elapsed_time_ms = std::chrono::duration(std::chrono::steady_clock::now() - start_time).count(); diff --git a/hailort/libhailort/src/vdevice_internal.hpp b/hailort/libhailort/src/vdevice_internal.hpp index abf2632..d973526 100644 --- a/hailort/libhailort/src/vdevice_internal.hpp +++ b/hailort/libhailort/src/vdevice_internal.hpp @@ -20,6 +20,7 @@ #include "hailo/vdevice.hpp" #include "pcie_device.hpp" #include "context_switch/multi_context/vdma_config_manager.hpp" +#include "network_group_scheduler.hpp" namespace hailort @@ -59,13 +60,19 @@ public: return devices_infos; } + const NetworkGroupSchedulerPtr &network_group_scheduler() + { + return m_network_group_scheduler; + } + private: - VDeviceBase(std::vector> &&devices) : m_devices(std::move(devices)) + VDeviceBase(std::vector> &&devices, NetworkGroupSchedulerPtr network_group_scheduler) : + m_devices(std::move(devices)), m_network_group_scheduler(network_group_scheduler) {} std::vector> m_devices; std::unique_ptr m_context_switch_manager; - + NetworkGroupSchedulerPtr m_network_group_scheduler; }; } /* namespace hailort */ diff --git a/hailort/libhailort/src/vdevice_stream.cpp b/hailort/libhailort/src/vdevice_stream.cpp index c3cb113..79eab93 100644 --- a/hailort/libhailort/src/vdevice_stream.cpp +++ b/hailort/libhailort/src/vdevice_stream.cpp @@ -48,10 +48,10 @@ VDeviceInputStream::~VDeviceInputStream() } } -hailo_status VDeviceInputStream::activate_stream() +hailo_status VDeviceInputStream::activate_stream(uint16_t dynamic_batch_size) { for (auto &stream : m_streams) { - auto status = stream->activate_stream(); + auto status = stream->activate_stream(dynamic_batch_size); if (HAILO_SUCCESS != status) { LOGGER__ERROR("Failed to activate input stream. (device: {})", stream->get_dev_id()); deactivate_stream(); @@ -64,18 +64,44 @@ hailo_status VDeviceInputStream::activate_stream() Expected 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()); + size_t written_bytes = 0; + auto network_group_scheduler = m_network_group_scheduler.lock(); + if (network_group_scheduler) { + auto status = network_group_scheduler->wait_for_write(m_network_group_handle, name()); + if (HAILO_STREAM_INTERNAL_ABORT == status) { + LOGGER__INFO("Write to stream was aborted."); + return make_unexpected(status); + } + CHECK_SUCCESS_AS_EXPECTED(status); + + status = m_streams[m_next_transfer_stream_index]->write_buffer_only(buffer); + if (HAILO_SUCCESS != status) { + LOGGER__INFO("Write to stream has failed! status = {}", status); + return make_unexpected(status); + } + + status = network_group_scheduler->signal_write_finish(m_network_group_handle, name()); + if (HAILO_STREAM_INTERNAL_ABORT == status) { + return make_unexpected(status); + } + CHECK_SUCCESS_AS_EXPECTED(status); + + written_bytes = buffer.size(); + } else { + auto expected_written_bytes = m_streams[m_next_transfer_stream_index]->sync_write_raw_buffer(buffer); + if (HAILO_SUCCESS != expected_written_bytes.status()) { + LOGGER__INFO("Write to stream has failed! status = {}", expected_written_bytes.status()); + return make_unexpected(expected_written_bytes.status()); + } + written_bytes = expected_written_bytes.value(); } // Update m_next_transfer_stream_index only if 'batch' frames has been transferred - if (0 == (++m_acc_frames % m_streams[0]->get_batch_size())) { + if (0 == (++m_acc_frames % m_streams[0]->get_dynamic_batch_size())) { m_next_transfer_stream_index = static_cast((m_next_transfer_stream_index + 1) % m_streams.size()); m_acc_frames = 0; } - return written_bytes.release(); + return written_bytes; } hailo_status VDeviceInputStream::sync_write_all_raw_buffer_no_transform_impl(void *buffer, size_t offset, size_t size) @@ -85,30 +111,35 @@ hailo_status VDeviceInputStream::sync_write_all_raw_buffer_no_transform_impl(voi return sync_write_raw_buffer(MemoryView(static_cast(buffer) + offset, size)).status(); } +Expected VDeviceInputStream::send_pending_buffer() +{ + assert(1 == m_streams.size()); + VdmaInputStream &vdma_input = dynamic_cast(*m_streams[m_next_transfer_stream_index].get()); + return vdma_input.send_pending_buffer(); +} + +// TODO - HRT-6830 - make create_input/output_stream_from_net_group as virutal function Expected> VDeviceInputStream::create_input_stream_from_net_group( std::vector> &resources_managers, - const LayerInfo &edge_layer, const std::string &stream_name, - EventPtr &&network_group_activated_event, LatencyMeterPtr latency_meter) + const LayerInfo &edge_layer, const std::string &stream_name, const network_group_handle_t &network_group_handle, + EventPtr &&network_group_activated_event, NetworkGroupSchedulerWeakPtr network_group_scheduler) { 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 devices; std::vector> 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(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); + auto vdma_channel_ptr = resources_manager->get_boundary_vdma_channel_by_stream_name(stream_name); + CHECK_EXPECTED(vdma_channel_ptr); + + auto batch_size = resources_manager->get_network_batch_size(edge_layer.network_name); + auto local_stream = PcieInputStream::create(pcie_device, + vdma_channel_ptr.release(), edge_layer, batch_size.value(), network_group_activated_event); CHECK_EXPECTED(local_stream); devices.push_back(&pcie_device); @@ -116,7 +147,7 @@ Expected> VDeviceInputStream::create_input_s } std::unique_ptr local_vdevice_stream(new (std::nothrow) VDeviceInputStream(devices, - std::move(streams), std::move(network_group_activated_event), edge_layer, status)); + std::move(streams), network_group_handle, std::move(network_group_activated_event), edge_layer, network_group_scheduler, status)); CHECK_AS_EXPECTED((nullptr != local_vdevice_stream), HAILO_OUT_OF_HOST_MEMORY); CHECK_SUCCESS_AS_EXPECTED(status); @@ -124,12 +155,13 @@ Expected> VDeviceInputStream::create_input_s } Expected> VDeviceInputStream::create(std::vector> &resources_managers, - const LayerInfo &edge_layer, const std::string &stream_name, EventPtr network_group_activated_event, LatencyMeterPtr latency_meter) + const LayerInfo &edge_layer, const std::string &stream_name, const network_group_handle_t &network_group_handle, EventPtr network_group_activated_event, + NetworkGroupSchedulerWeakPtr network_group_scheduler) { 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); + stream_name, network_group_handle, std::move(network_group_activated_event), network_group_scheduler); CHECK_EXPECTED(input_stream); return input_stream.release(); @@ -173,6 +205,16 @@ hailo_status VDeviceInputStream::abort() status = abort_status; } } + + auto network_group_scheduler = m_network_group_scheduler.lock(); + if (network_group_scheduler) { + auto disable_status = network_group_scheduler->disable_stream(m_network_group_handle, name()); + if (HAILO_SUCCESS != disable_status) { + LOGGER__ERROR("Failed to disable stream in the network group scheduler. (status: {})", disable_status); + status = disable_status; + } + } + return status; } @@ -181,14 +223,33 @@ 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()); + if ((HAILO_SUCCESS != clear_abort_status) && (HAILO_STREAM_NOT_ACTIVATED != clear_abort_status)) { + LOGGER__ERROR("Failed to clear abort input stream. (status: {} device: {})", clear_abort_status, stream->get_dev_id()); status = clear_abort_status; } } + + auto network_group_scheduler = m_network_group_scheduler.lock(); + if (network_group_scheduler) { + auto enable_status = network_group_scheduler->enable_stream(m_network_group_handle, name()); + if (HAILO_SUCCESS != enable_status) { + LOGGER__ERROR("Failed to enable stream in the network group scheduler. (status: {})", enable_status); + status = enable_status; + } + } + return status; } +bool VDeviceInputStream::is_scheduled() +{ + auto network_group_scheduler = m_network_group_scheduler.lock(); + if (!network_group_scheduler) { + return false; + } + return (HAILO_SCHEDULING_ALGORITHM_NONE != network_group_scheduler->algorithm()); +} + /** Output stream **/ hailo_status VDeviceOutputStream::deactivate_stream() { @@ -213,10 +274,10 @@ VDeviceOutputStream::~VDeviceOutputStream() } } -hailo_status VDeviceOutputStream::activate_stream() +hailo_status VDeviceOutputStream::activate_stream(uint16_t dynamic_batch_size) { for (auto &stream : m_streams) { - auto status = stream->activate_stream(); + auto status = stream->activate_stream(dynamic_batch_size); if (HAILO_SUCCESS != status) { LOGGER__ERROR("Failed to activate output stream. (device: {})", stream->get_dev_id()); deactivate_stream(); @@ -241,6 +302,16 @@ Expected VDeviceOutputStream::sync_read_raw_buffer(MemoryView &/*buffer* hailo_status VDeviceOutputStream::read(MemoryView buffer) { + auto network_group_scheduler = m_network_group_scheduler.lock(); + if (network_group_scheduler) { + auto status = network_group_scheduler->wait_for_read(m_network_group_handle, name()); + if (HAILO_STREAM_INTERNAL_ABORT == status) { + LOGGER__INFO("Read from stream was aborted."); + return status; + } + CHECK_SUCCESS(status); + } + auto status = m_streams[m_next_transfer_stream_index]->read(buffer); if (HAILO_SUCCESS != status) { LOGGER__INFO("Read from stream has failed! status = {}", status); @@ -248,46 +319,52 @@ hailo_status VDeviceOutputStream::read(MemoryView buffer) } // Update m_next_transfer_stream_index only if 'batch' frames has been transferred - if (0 == (++m_acc_frames % m_streams[0]->get_batch_size())) { + if (0 == (++m_acc_frames % m_streams[0]->get_dynamic_batch_size())) { m_next_transfer_stream_index = static_cast((m_next_transfer_stream_index + 1) % m_streams.size()); m_acc_frames = 0; } + + if (network_group_scheduler) { + status = network_group_scheduler->signal_read_finish(m_network_group_handle, name()); + if (HAILO_STREAM_INTERNAL_ABORT == status) { + return status; + } + CHECK_SUCCESS(status); + } + return HAILO_SUCCESS; } +// TODO - HRT-6830 - make create_input/output_stream_from_net_group as virutal function Expected> VDeviceOutputStream::create_output_stream_from_net_group( std::vector> &resources_managers, - const LayerInfo &edge_layer, const std::string &stream_name, - EventPtr &&network_group_activated_event, LatencyMeterPtr latency_meter) + const LayerInfo &edge_layer, const std::string &stream_name, const network_group_handle_t &network_group_handle, + EventPtr &&network_group_activated_event, NetworkGroupSchedulerWeakPtr network_group_scheduler) { 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 devices; std::vector> 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(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); + auto vdma_channel_ptr = resources_manager->get_boundary_vdma_channel_by_stream_name(stream_name); + CHECK_EXPECTED(vdma_channel_ptr); + + auto batch_size = resources_manager->get_network_batch_size(edge_layer.network_name); + auto local_stream = PcieOutputStream::create(pcie_device, + vdma_channel_ptr.release(), edge_layer, batch_size.value(), network_group_activated_event); CHECK_EXPECTED(local_stream); devices.push_back(&pcie_device); streams.emplace_back(local_stream.release()); } - std::unique_ptr local_vdevice_stream(new (std::nothrow) VDeviceOutputStream(devices, std::move(streams), - edge_layer, std::move(network_group_activated_event), status)); + std::unique_ptr local_vdevice_stream(new (std::nothrow) VDeviceOutputStream(devices, std::move(streams), network_group_handle, + edge_layer, std::move(network_group_activated_event), network_group_scheduler, status)); CHECK_AS_EXPECTED((nullptr != local_vdevice_stream), HAILO_OUT_OF_HOST_MEMORY); CHECK_SUCCESS_AS_EXPECTED(status); @@ -295,12 +372,13 @@ Expected> VDeviceOutputStream::create_outpu } Expected> VDeviceOutputStream::create(std::vector> &resources_managers, - const LayerInfo &edge_layer, const std::string &stream_name, EventPtr network_group_activated_event, LatencyMeterPtr latency_meter) + const LayerInfo &edge_layer, const std::string &stream_name, const network_group_handle_t &network_group_handle, EventPtr network_group_activated_event, + NetworkGroupSchedulerWeakPtr network_group_scheduler) { 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); + stream_name, network_group_handle, std::move(network_group_activated_event), network_group_scheduler); CHECK_EXPECTED(output_stream); return output_stream.release(); @@ -331,6 +409,16 @@ hailo_status VDeviceOutputStream::abort() status = abort_status; } } + + auto network_group_scheduler = m_network_group_scheduler.lock(); + if (network_group_scheduler) { + auto disable_status = network_group_scheduler->disable_stream(m_network_group_handle, name()); + if (HAILO_SUCCESS != disable_status) { + LOGGER__ERROR("Failed to disable stream in the network group scheduler. (status: {})", disable_status); + status = disable_status; + } + } + return status; } @@ -339,12 +427,31 @@ 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()); + if ((HAILO_SUCCESS != clear_abort_status) && (HAILO_STREAM_NOT_ACTIVATED != clear_abort_status)) { + LOGGER__ERROR("Failed to clear abort output stream. (status: {} device: {})", clear_abort_status, stream->get_dev_id()); status = clear_abort_status; } } + + auto network_group_scheduler = m_network_group_scheduler.lock(); + if (network_group_scheduler) { + auto enable_status = network_group_scheduler->enable_stream(m_network_group_handle, name()); + if (HAILO_SUCCESS != enable_status) { + LOGGER__ERROR("Failed to enable stream in the network group scheduler. (status: {})", enable_status); + status = enable_status; + } + } + return status; } +bool VDeviceOutputStream::is_scheduled() +{ + auto network_group_scheduler = m_network_group_scheduler.lock(); + if (!network_group_scheduler) { + return false; + } + return (HAILO_SCHEDULING_ALGORITHM_NONE != network_group_scheduler->algorithm()); +} + } /* namespace hailort */ diff --git a/hailort/libhailort/src/vdevice_stream.hpp b/hailort/libhailort/src/vdevice_stream.hpp index 4b8d817..44475ff 100644 --- a/hailort/libhailort/src/vdevice_stream.hpp +++ b/hailort/libhailort/src/vdevice_stream.hpp @@ -14,6 +14,7 @@ #include "stream_internal.hpp" #include "hailo/hailort.h" +#include "vdevice_internal.hpp" #include "pcie_device.hpp" #include "pcie_stream.hpp" #include "hailo/expected.hpp" @@ -25,6 +26,8 @@ class VDeviceInputStream : public InputStreamBase { public: VDeviceInputStream(VDeviceInputStream &&other) : InputStreamBase(std::move(other)), + m_network_group_handle(std::move(other.m_network_group_handle)), + m_network_group_scheduler(std::move(other.m_network_group_scheduler)), 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)), @@ -35,15 +38,18 @@ public: virtual ~VDeviceInputStream(); static Expected> create(std::vector> &resources_managers, - const LayerInfo &edge_layer, const std::string &stream_name, EventPtr network_group_activated_event, - LatencyMeterPtr latency_meter = nullptr); + const LayerInfo &edge_layer, const std::string &stream_name, const network_group_handle_t &network_group_handle, EventPtr network_group_activated_event, + NetworkGroupSchedulerWeakPtr network_group_scheduler); - virtual hailo_status activate_stream() override; + virtual hailo_status activate_stream(uint16_t dynamic_batch_size) 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; + virtual bool is_scheduled() override; + + Expected send_pending_buffer(); protected: virtual Expected sync_write_raw_buffer(const MemoryView &buffer) override; @@ -53,10 +59,14 @@ private: explicit VDeviceInputStream( std::vector devices, std::vector> &&streams, + const network_group_handle_t &network_group_handle, EventPtr &&network_group_activated_event, const LayerInfo &layer_info, + NetworkGroupSchedulerWeakPtr network_group_scheduler, hailo_status &status) : InputStreamBase(layer_info, HAILO_STREAM_INTERFACE_PCIE, std::move(network_group_activated_event), status), + m_network_group_handle(network_group_handle), + m_network_group_scheduler(network_group_scheduler), m_devices(devices), m_streams(std::move(streams)), m_is_stream_activated(false), @@ -69,9 +79,11 @@ private: static Expected> create_input_stream_from_net_group( std::vector> &resources_managers, - const LayerInfo &edge_layer, const std::string &stream_name, - EventPtr &&network_group_activated_event, LatencyMeterPtr latency_meter); + const LayerInfo &edge_layer, const std::string &stream_name, const network_group_handle_t &network_group_handle, + EventPtr &&network_group_activated_event, NetworkGroupSchedulerWeakPtr network_group_scheduler); + network_group_handle_t m_network_group_handle; + NetworkGroupSchedulerWeakPtr m_network_group_scheduler; std::vector m_devices; std::vector> m_streams; bool m_is_stream_activated; @@ -83,6 +95,8 @@ class VDeviceOutputStream : public OutputStreamBase { public: VDeviceOutputStream(VDeviceOutputStream &&other) : OutputStreamBase(std::move(other)), + m_network_group_handle(std::move(other.m_network_group_handle)), + m_network_group_scheduler(std::move(other.m_network_group_scheduler)), 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)), @@ -93,15 +107,16 @@ public: virtual ~VDeviceOutputStream(); static Expected> create(std::vector> &resources_managers, - const LayerInfo &edge_layer, const std::string &stream_name, - EventPtr network_group_activated_event, LatencyMeterPtr latency_meter = nullptr); + const LayerInfo &edge_layer, const std::string &stream_name, const network_group_handle_t &network_group_handle, + EventPtr network_group_activated_event, NetworkGroupSchedulerWeakPtr network_group_scheduler); - virtual hailo_status activate_stream() override; + virtual hailo_status activate_stream(uint16_t dynamic_batch_size) 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; + virtual bool is_scheduled() override; protected: virtual Expected sync_read_raw_buffer(MemoryView &buffer) override; @@ -110,10 +125,14 @@ private: explicit VDeviceOutputStream( std::vector devices, std::vector> &&streams, + const network_group_handle_t &network_group_handle, const LayerInfo &layer_info, EventPtr &&network_group_activated_event, + NetworkGroupSchedulerWeakPtr network_group_scheduler, hailo_status &status) : OutputStreamBase(layer_info, std::move(network_group_activated_event), status), + m_network_group_handle(network_group_handle), + m_network_group_scheduler(network_group_scheduler), m_devices(devices), m_streams(std::move(streams)), m_is_stream_activated(false), @@ -127,8 +146,11 @@ private: static Expected> create_output_stream_from_net_group( std::vector> &resources_managers, const LayerInfo &edge_layer, - const std::string &stream_name, EventPtr &&network_group_activated_event, LatencyMeterPtr latency_meter); + const std::string &stream_name, const network_group_handle_t &network_group_handle, EventPtr &&network_group_activated_event, + NetworkGroupSchedulerWeakPtr network_group_scheduler); + network_group_handle_t m_network_group_handle; + NetworkGroupSchedulerWeakPtr m_network_group_scheduler; std::vector m_devices; std::vector> m_streams; bool m_is_stream_activated; diff --git a/hailort/libhailort/src/vdma/continuous_buffer.cpp b/hailort/libhailort/src/vdma/continuous_buffer.cpp new file mode 100644 index 0000000..562cdcc --- /dev/null +++ b/hailort/libhailort/src/vdma/continuous_buffer.cpp @@ -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 continuous_buffer.hpp + * @brief Continuous physical vdma buffer. + **/ + +#include "continuous_buffer.hpp" + +namespace hailort { +namespace vdma { + +// Minimum size of ccb buffers in descriptors, taken from the CCB spec. +#define MIN_CCB_DESCRIPTORS_COUNT (16) + +static uint32_t align(uint32_t size, uint32_t align) +{ + assert(is_powerof2(align)); + const uint32_t mask = align - 1; + return (size + mask) & ~mask; +} + +Expected ContinuousBuffer::create(size_t size, HailoRTDriver &driver) +{ + auto result = driver.vdma_continuous_buffer_alloc(size); + CHECK_EXPECTED(result, "Failed allocating continuous buffer, size {}", size); + + uintptr_t handle = 0; + uint64_t dma_address = 0; + std::tie(handle, dma_address) = result.release(); + + auto mmap = MmapBuffer::create_file_map(size, driver.fd(), handle); + if (!mmap) { + LOGGER__ERROR("Failed mmap continuous buffer"); + driver.vdma_continuous_buffer_free(handle); + return make_unexpected(mmap.status()); + } + + return ContinuousBuffer(size, driver, handle, dma_address, mmap.release()); +} + +uint32_t ContinuousBuffer::get_buffer_size(uint32_t buffer_size) +{ + const uint16_t page_size = DEFAULT_DESC_PAGE_SIZE; + const auto aligned_buffer_size = align(buffer_size, page_size); + + const uint32_t min_buffer_size = page_size * MIN_CCB_DESCRIPTORS_COUNT; + return std::max(aligned_buffer_size, min_buffer_size); +} + +uint32_t ContinuousBuffer::get_buffer_size_desc_power2(uint32_t buffer_size) +{ + const uint16_t page_size = DEFAULT_DESC_PAGE_SIZE; + const auto descriptors_in_buffer = DESCRIPTORS_IN_BUFFER(buffer_size, page_size); + const auto actual_descriptors_count = get_nearest_powerof_2(descriptors_in_buffer, MIN_CCB_DESCRIPTORS_COUNT); + return actual_descriptors_count * page_size; +} + +ContinuousBuffer::~ContinuousBuffer() +{ + if (0 != m_handle) { + auto status = m_mmap.unmap(); + if (HAILO_SUCCESS != status) { + LOGGER__ERROR("Failed unmap mmap buffer {}", status); + } + + status = m_driver.vdma_continuous_buffer_free(m_handle); + if (HAILO_SUCCESS != status) { + LOGGER__ERROR("Failed free continuous buffer, {}", status); + } + + m_handle = 0; + } +} + +size_t ContinuousBuffer::size() const +{ + return m_size; +} + +uint64_t ContinuousBuffer::dma_address() const +{ + return m_dma_address; +} + +uint16_t ContinuousBuffer::desc_page_size() const +{ + // Currently we support only the default desc page size, TODO: HRT-5381 support more desc page size? + return DEFAULT_DESC_PAGE_SIZE; +} + +uint32_t ContinuousBuffer::descs_count() const +{ + return descriptors_in_buffer(m_size); +} + +hailo_status ContinuousBuffer::read(void *buf_dst, size_t count, size_t offset) +{ + CHECK((count + offset) <= m_size, HAILO_INSUFFICIENT_BUFFER, + "Requested size {} from offset {} is more than the buffer size {}", count, offset, m_size); + // We use dma coherent mmap, so no need to sync the buffer after the memcpy. + const auto src_address = reinterpret_cast(m_mmap.get()) + offset; + memcpy(buf_dst, src_address, count); + return HAILO_SUCCESS; +} + +hailo_status ContinuousBuffer::write(const void *buf_src, size_t count, size_t offset) +{ + CHECK((count + offset) <= m_size, HAILO_INSUFFICIENT_BUFFER, + "Requested size {} from offset {} is more than the buffer size {}", count, offset, m_size); + // We use dma coherent mmap, so no need to sync the buffer after the memcpy. + const auto dst_address = reinterpret_cast(m_mmap.get()) + offset; + memcpy(dst_address, buf_src, count); + return HAILO_SUCCESS; +} + +Expected ContinuousBuffer::program_descriptors(size_t transfer_size, VdmaInterruptsDomain first_desc_interrupts_domain, + VdmaInterruptsDomain last_desc_interrupts_domain, size_t desc_offset, bool is_circular) +{ + (void)first_desc_interrupts_domain; + (void)last_desc_interrupts_domain; + (void)desc_offset; + (void)is_circular; + + // The descriptors in continuous mode are programmed by the hw, nothing to do here. + return descriptors_in_buffer(transfer_size); +} + +hailo_status ContinuousBuffer::reprogram_device_interrupts_for_end_of_batch(size_t transfer_size, uint16_t batch_size, + VdmaInterruptsDomain new_interrupts_domain) +{ + (void)transfer_size; + (void)batch_size; + (void)new_interrupts_domain; + + // The descriptors in continuous mode are programmed by the hw, nothing to do here. + return HAILO_SUCCESS; +} + +ContinuousBuffer::ContinuousBuffer(size_t size, HailoRTDriver &driver, uintptr_t handle, uint64_t dma_address, + MmapBuffer &&mmap) : + m_size(size), + m_driver(driver), + m_handle(handle), + m_dma_address(dma_address), + m_mmap(std::move(mmap)) +{} + +}; /* namespace vdma */ +}; /* namespace hailort */ diff --git a/hailort/libhailort/src/vdma/continuous_buffer.hpp b/hailort/libhailort/src/vdma/continuous_buffer.hpp new file mode 100644 index 0000000..fcaec99 --- /dev/null +++ b/hailort/libhailort/src/vdma/continuous_buffer.hpp @@ -0,0 +1,82 @@ +/** + * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved. + * Distributed under the MIT license (https://opensource.org/licenses/MIT) + **/ +/** + * @file continuous_buffer.hpp + * @brief Continuous physical vdma buffer. + **/ + +#ifndef _HAILO_VDMA_CONTINUOUS_BUFFER_HPP_ +#define _HAILO_VDMA_CONTINUOUS_BUFFER_HPP_ + +#include "os/hailort_driver.hpp" +#include "os/mmap_buffer.hpp" +#include "vdma/vdma_buffer.hpp" + +namespace hailort { +namespace vdma { + +class ContinuousBuffer final : public VdmaBuffer { +public: + static Expected create(size_t size, HailoRTDriver &driver); + + static uint32_t get_buffer_size(uint32_t buffer_size); + // Get buffer size with the requirment that the amount of descriptors is a power of 2. + static uint32_t get_buffer_size_desc_power2(uint32_t buffer_size); + + ContinuousBuffer(const ContinuousBuffer &) = delete; + ContinuousBuffer& operator=(const ContinuousBuffer &) = delete; + ContinuousBuffer& operator=(ContinuousBuffer &&) = delete; + + virtual ~ContinuousBuffer(); + + ContinuousBuffer(ContinuousBuffer &&other) noexcept : + VdmaBuffer(std::move(other)), + m_size(other.m_size), + m_driver(other.m_driver), + m_handle(std::exchange(other.m_handle, 0)), + m_dma_address(std::exchange(other.m_dma_address, 0)), + m_mmap(std::move(other.m_mmap)) + {} + + virtual Type type() const override + { + return Type::CONTINUOUS; + } + + virtual size_t size() const override; + virtual uint64_t dma_address() const override; + virtual uint16_t desc_page_size() const override; + virtual uint32_t descs_count() const override; + + // Not used in this flow + virtual ExpectedRef get_desc_list() override + { + LOGGER__ERROR("Can't get descriptor list on continuous buffer"); + return make_unexpected(HAILO_INVALID_OPERATION); + } + + virtual hailo_status read(void *buf_dst, size_t count, size_t offset) override; + virtual hailo_status write(const void *buf_src, size_t count, size_t offset) override; + + virtual Expected program_descriptors(size_t transfer_size, VdmaInterruptsDomain first_desc_interrupts_domain, + VdmaInterruptsDomain last_desc_interrupts_domain, size_t desc_offset, bool is_circular) override; + virtual hailo_status reprogram_device_interrupts_for_end_of_batch(size_t transfer_size, uint16_t batch_size, + VdmaInterruptsDomain new_interrupts_domain) override; + +private: + ContinuousBuffer(size_t size, HailoRTDriver &driver, uintptr_t handle, uint64_t dma_address, + MmapBuffer &&mmap); + + const size_t m_size; + HailoRTDriver &m_driver; + uintptr_t m_handle; + uint64_t m_dma_address; + MmapBuffer m_mmap; +}; + +}; /* namespace vdma */ +}; /* namespace hailort */ + +#endif /* _HAILO_VDMA_CONTINUOUS_BUFFER_HPP_ */ diff --git a/hailort/libhailort/src/vdma_buffer.cpp b/hailort/libhailort/src/vdma/mapped_buffer.cpp similarity index 77% rename from hailort/libhailort/src/vdma_buffer.cpp rename to hailort/libhailort/src/vdma/mapped_buffer.cpp index 6dd9565..9195178 100644 --- a/hailort/libhailort/src/vdma_buffer.cpp +++ b/hailort/libhailort/src/vdma/mapped_buffer.cpp @@ -1,13 +1,14 @@ -#include "vdma_buffer.hpp" +#include "mapped_buffer.hpp" +#include "microprofile.h" -namespace hailort -{ +namespace hailort { +namespace vdma { -Expected VdmaBuffer::create(size_t required_size, HailoRTDriver::DmaDirection data_direction, +Expected MappedBuffer::create(size_t required_size, HailoRTDriver::DmaDirection data_direction, HailoRTDriver &driver) { hailo_status status = HAILO_UNINITIALIZED; - VdmaBuffer object(required_size, data_direction, driver, status); + MappedBuffer object(required_size, data_direction, driver, status); if (HAILO_SUCCESS != status) { return make_unexpected(status); } @@ -15,7 +16,7 @@ Expected VdmaBuffer::create(size_t required_size, HailoRTDriver::Dma return object; } -VdmaBuffer::VdmaBuffer( +MappedBuffer::MappedBuffer( 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) @@ -37,7 +38,7 @@ VdmaBuffer::VdmaBuffer( status = HAILO_SUCCESS; } -VdmaBuffer::~VdmaBuffer() +MappedBuffer::~MappedBuffer() { if (m_user_address) { m_driver.vdma_buffer_unmap(m_handle); @@ -48,10 +49,10 @@ VdmaBuffer::~VdmaBuffer() } } -hailo_status VdmaBuffer::write(const void *buf_src, size_t count, size_t offset) +hailo_status MappedBuffer::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); + LOGGER__ERROR("Requested size {} from offset {} is more than the MappedBuffer size {}", count, offset, m_size); return HAILO_INSUFFICIENT_BUFFER; } @@ -69,10 +70,10 @@ hailo_status VdmaBuffer::write(const void *buf_src, size_t count, size_t offset) return HAILO_SUCCESS; } -hailo_status VdmaBuffer::read(void *buf_dst, size_t count, size_t offset) +hailo_status MappedBuffer::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); + LOGGER__ERROR("Requested size {} from offset {} is more than the MappedBuffer size {}", count, offset, m_size); return HAILO_INSUFFICIENT_BUFFER; } @@ -90,10 +91,11 @@ hailo_status VdmaBuffer::read(void *buf_dst, size_t count, size_t offset) return HAILO_SUCCESS; } -hailo_status VdmaBuffer::write_cyclic(const void *buf_src, size_t count, size_t offset) +hailo_status MappedBuffer::write_cyclic(const void *buf_src, size_t count, size_t offset) { + MICROPROFILE_SCOPEI("vDMA", "Write buffer", 0); if (count > m_size) { - LOGGER__ERROR("Requested size({}) is more than the VdmaBuffer size {}", count, m_size); + LOGGER__ERROR("Requested size({}) is more than the MappedBuffer size {}", count, m_size); return HAILO_INSUFFICIENT_BUFFER; } @@ -115,10 +117,11 @@ hailo_status VdmaBuffer::write_cyclic(const void *buf_src, size_t count, size_t return HAILO_SUCCESS; } -hailo_status VdmaBuffer::read_cyclic(void *buf_dst, size_t count, size_t offset) +hailo_status MappedBuffer::read_cyclic(void *buf_dst, size_t count, size_t offset) { + MICROPROFILE_SCOPEI("vDMA", "Read buffer", 0); if (count > m_size) { - LOGGER__ERROR("Requested size({}) is more than the VdmaBuffer size {}", count, m_size); + LOGGER__ERROR("Requested size({}) is more than the MappedBuffer size {}", count, m_size); return HAILO_INSUFFICIENT_BUFFER; } @@ -140,7 +143,7 @@ hailo_status VdmaBuffer::read_cyclic(void *buf_dst, size_t count, size_t offset) return HAILO_SUCCESS; } -Expected> VdmaBuffer::allocate_vdma_buffer(HailoRTDriver &driver, size_t required_size, +Expected> MappedBuffer::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 @@ -157,4 +160,5 @@ Expected> VdmaBuffer::allocate_vdma_buffer(HailoRTDriver &drive } } +} /* namespace vdma */ } /* namespace hailort */ diff --git a/hailort/libhailort/src/vdma/mapped_buffer.hpp b/hailort/libhailort/src/vdma/mapped_buffer.hpp new file mode 100644 index 0000000..2fc2d68 --- /dev/null +++ b/hailort/libhailort/src/vdma/mapped_buffer.hpp @@ -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 mapped_buffer.hpp + * @brief The mapped buffer that is continuous in virtual memory, but not on physical memory. + * We map the buffer to the IOMMU. + * + * The buffer can be used only with the help of a descriptors list that contains pointers to a physical + * continuous "dma pages". + * + * There are 2 options to allocated the buffer: + * 1. User mode allocation - the user mode calls `malloc` or `mmap` to allocate the buffer, then + * using HailoRTDriver we map the driver to the IOMMU (and pin the pages to avoid pagigs). + * This is the default option + * 2. Kernel mode allocation - on some systems, the user mode doesn't allocate the memory in a "dma-able" address, + * so we need to allocate the pages in driver. + **/ + +#ifndef _HAILO_VDMA_MAPPED_BUFFER_HPP_ +#define _HAILO_VDMA_MAPPED_BUFFER_HPP_ + +#include "os/mmap_buffer.hpp" +#include "os/hailort_driver.hpp" +#include "hailo/expected.hpp" + +namespace hailort { +namespace vdma { + +class MappedBuffer final +{ +public: + static Expected create(size_t required_size, HailoRTDriver::DmaDirection data_direction, + HailoRTDriver &driver); + + MappedBuffer(size_t required_size, HailoRTDriver::DmaDirection data_direction, + HailoRTDriver &driver, hailo_status &status); + ~MappedBuffer(); + + MappedBuffer(const MappedBuffer &other) = delete; + MappedBuffer &operator=(const MappedBuffer &other) = delete; + MappedBuffer(MappedBuffer &&other) noexcept = default; + MappedBuffer &operator=(MappedBuffer &&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 MappedBuffer. + * + * @note (offset + count) MUST be smaller than this MappedBuffer 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 MappedBuffer to copy the data to + */ + hailo_status write(const void *buf_src, size_t count, size_t offset); + + /** + * Copy data from this MappedBuffer to buf_dst. + * + * @note (offset + count) MUST be smaller than this MappedBuffer 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 MappedBuffer 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 MappedBuffer. + * + * Similar to 'write' but if (offset + count) is larger than the MappedBuffer size, the copy continues + * from the start of the MappedBuffer. + * + * @note count MUST be smaller than this MappedBuffer 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 MappedBuffer to copy the data to + */ + hailo_status write_cyclic(const void *buf_src, size_t count, size_t offset); + + /** + * Copy data from this MappedBuffer to buf_dst. + * + * Similar to 'read' but if (offset + count) is larger than the MappedBuffer size, the copy continues + * from the start of the MappedBuffer. + * + * @note count MUST be smaller than this MappedBuffer 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 MappedBuffer to copy the data from + */ + hailo_status read_cyclic(void *buf_dst, size_t count, size_t offset); + +private: + + static Expected> allocate_vdma_buffer(HailoRTDriver &driver, size_t required_size, + uintptr_t &driver_buff_handle); + + MmapBuffer m_user_address; + HailoRTDriver::VdmaBufferHandle m_handle; + size_t m_size; + HailoRTDriver &m_driver; + uintptr_t m_driver_buff_handle; +}; + +} /* namespace vdma */ +} /* namespace hailort */ + +#endif /* _HAILO_VDMA_MAPPED_BUFFER_HPP_ */ \ No newline at end of file diff --git a/hailort/libhailort/src/vdma/sg_buffer.cpp b/hailort/libhailort/src/vdma/sg_buffer.cpp new file mode 100644 index 0000000..01c3583 --- /dev/null +++ b/hailort/libhailort/src/vdma/sg_buffer.cpp @@ -0,0 +1,88 @@ +/** + * Copyright (c) 2020-2022 Hailo Technologies Ltd. All rights reserved. + * Distributed under the MIT license (https://opensource.org/licenses/MIT) + **/ +/** + * @file vdma_sg_buffer.cpp + * @brief Scatter-gather vdma buffer. + **/ + +#include "sg_buffer.hpp" + +namespace hailort { +namespace vdma { + +Expected SgBuffer::create(HailoRTDriver &driver, uint32_t desc_count, uint16_t desc_page_size, + HailoRTDriver::DmaDirection data_direction, uint8_t channel_index) +{ + auto desc_list = VdmaDescriptorList::create(desc_count, desc_page_size, driver); + CHECK_EXPECTED(desc_list); + + assert((desc_count * desc_page_size) <= std::numeric_limits::max()); + auto mapped_buffer = MappedBuffer::create(desc_count * desc_page_size, data_direction, driver); + CHECK_EXPECTED(mapped_buffer); + + auto status = desc_list->configure_to_use_buffer(mapped_buffer.value(), channel_index); + CHECK_SUCCESS_AS_EXPECTED(status); + + return SgBuffer(desc_list.release(), mapped_buffer.release()); +} + +size_t SgBuffer::size() const +{ + return m_mapped_buffer.size(); +} + +uint64_t SgBuffer::dma_address() const +{ + return m_desc_list.dma_address(); +} + +uint16_t SgBuffer::desc_page_size() const +{ + return m_desc_list.desc_page_size(); +} + +uint32_t SgBuffer::descs_count() const +{ + return (uint32_t)m_desc_list.count(); +} + +uint8_t SgBuffer::depth() const +{ + return m_desc_list.depth(); +} + +ExpectedRef SgBuffer::get_desc_list() +{ + return std::ref(m_desc_list); +} + +hailo_status SgBuffer::read(void *buf_dst, size_t count, size_t offset) +{ + return m_mapped_buffer.read(buf_dst, count, offset); +} + +hailo_status SgBuffer::write(const void *buf_src, size_t count, size_t offset) +{ + return m_mapped_buffer.write(buf_src, count, offset); +} + +Expected SgBuffer::program_descriptors(size_t transfer_size, VdmaInterruptsDomain first_desc_interrupts_domain, + VdmaInterruptsDomain last_desc_interrupts_domain, size_t desc_offset, bool is_circular) +{ + return m_desc_list.program_descriptors(transfer_size, first_desc_interrupts_domain, last_desc_interrupts_domain, + desc_offset, is_circular); +} + +hailo_status SgBuffer::reprogram_device_interrupts_for_end_of_batch(size_t transfer_size, uint16_t batch_size, + VdmaInterruptsDomain new_interrupts_domain) +{ + const auto desc_per_transfer = m_desc_list.descriptors_in_buffer(transfer_size); + const auto num_desc_in_batch = desc_per_transfer * batch_size; + const auto last_desc_index_in_batch = num_desc_in_batch - 1; + return m_desc_list.reprogram_descriptor_interrupts_domain(last_desc_index_in_batch, new_interrupts_domain); +} + +} +} \ No newline at end of file diff --git a/hailort/libhailort/src/vdma/sg_buffer.hpp b/hailort/libhailort/src/vdma/sg_buffer.hpp new file mode 100644 index 0000000..c1472a1 --- /dev/null +++ b/hailort/libhailort/src/vdma/sg_buffer.hpp @@ -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 sg_buffer.hpp + * @brief Scatter-gather vdma buffer, from the user-mode point of view the buffer is continuous, + * but not from the physical-memory point of view. + * The sg buffer contains 2 parts: + * - MappedBuffer - the actual buffer stores the data. + * - Descriptors list - each descritpor points to a single "dma page" in the MappedBuffer. + * The hw accept the descriptors list address and parses it to get the actual data. + **/ + +#ifndef _HAILO_VDMA_SG_BUFFER_HPP_ +#define _HAILO_VDMA_SG_BUFFER_HPP_ + +#include "os/hailort_driver.hpp" +#include "vdma/vdma_buffer.hpp" +#include "vdma_descriptor_list.hpp" +#include "vdma/mapped_buffer.hpp" + +namespace hailort { +namespace vdma { + +class SgBuffer final : public VdmaBuffer { +public: + static Expected create(HailoRTDriver &driver, uint32_t desc_count, uint16_t desc_page_size, + HailoRTDriver::DmaDirection data_direction, uint8_t channel_index = 0); + + virtual ~SgBuffer() = default; + + SgBuffer(const SgBuffer &) = delete; + SgBuffer(SgBuffer &&) = default; + SgBuffer& operator=(const SgBuffer &) = delete; + SgBuffer& operator=(SgBuffer &&) = delete; + + virtual Type type() const override + { + return Type::SCATTER_GATHER; + } + + virtual size_t size() const override; + virtual uint64_t dma_address() const override; + virtual uint16_t desc_page_size() const override; + virtual uint32_t descs_count() const override; + uint8_t depth() const; + + // 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 ExpectedRef get_desc_list() override; + + virtual hailo_status read(void *buf_dst, size_t count, size_t offset) override; + virtual hailo_status write(const void *buf_src, size_t count, size_t offset) override; + + virtual Expected program_descriptors(size_t transfer_size, VdmaInterruptsDomain first_desc_interrupts_domain, + VdmaInterruptsDomain last_desc_interrupts_domain, size_t desc_offset, bool is_circular) override; + virtual hailo_status reprogram_device_interrupts_for_end_of_batch(size_t transfer_size, uint16_t batch_size, + VdmaInterruptsDomain new_interrupts_domain) override; + +private: + SgBuffer(VdmaDescriptorList &&desc_list, MappedBuffer &&mapped_buffer) : + m_desc_list(std::move(desc_list)), + m_mapped_buffer(std::move(mapped_buffer)) + {} + + VdmaDescriptorList m_desc_list; + MappedBuffer m_mapped_buffer; +}; + +} /* vdma */ +} /* hailort */ + +#endif /* _HAILO_VDMA_SG_BUFFER_HPP_ */ diff --git a/hailort/libhailort/src/vdma/vdma_buffer.hpp b/hailort/libhailort/src/vdma/vdma_buffer.hpp new file mode 100644 index 0000000..e28187f --- /dev/null +++ b/hailort/libhailort/src/vdma/vdma_buffer.hpp @@ -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 vdma_buffer.hpp + * @brief Abstract layer representing a vdma buffer (buffer that can be read/written to the device over vdma.) + * The buffer can be either non-continuous with attach descriptors list (SgBuffer) or continuous buffer. + **/ + +#ifndef _HAILO_VDMA_VDMA_BUFFER_HPP_ +#define _HAILO_VDMA_VDMA_BUFFER_HPP_ + +#include "os/hailort_driver.hpp" +#include "vdma_descriptor_list.hpp" + +namespace hailort { +namespace vdma { + +class VdmaBuffer { +public: + + enum class Type { + SCATTER_GATHER, + CONTINUOUS + }; + + virtual ~VdmaBuffer() = default; + + VdmaBuffer() = default; + VdmaBuffer(const VdmaBuffer &) = delete; + VdmaBuffer(VdmaBuffer &&) = default; + VdmaBuffer& operator=(const VdmaBuffer &) = delete; + VdmaBuffer& operator=(VdmaBuffer &&) = delete; + + virtual Type type() const = 0; + virtual size_t size() const = 0; + virtual uint64_t dma_address() const = 0; + virtual uint16_t desc_page_size() const = 0; + virtual uint32_t descs_count() const = 0; + + uint32_t descriptors_in_buffer(size_t buffer_size) const + { + assert(buffer_size < std::numeric_limits::max()); + const auto page_size = desc_page_size(); + return static_cast(DESCRIPTORS_IN_BUFFER(buffer_size, page_size)); + } + + // If there's no descriptor list then Unexpected(HAILO_INVALID_OPERATION) will be returned + // (E.g. for CCB, where there is no descriptors list) + virtual ExpectedRef get_desc_list() = 0; + + virtual hailo_status read(void *buf_dst, size_t count, size_t offset) = 0; + virtual hailo_status write(const void *buf_src, size_t count, size_t offset) = 0; + + virtual Expected program_descriptors(size_t transfer_size, VdmaInterruptsDomain first_desc_interrupts_domain, + VdmaInterruptsDomain last_desc_interrupts_domain, size_t desc_offset, bool is_circular) = 0; + virtual hailo_status reprogram_device_interrupts_for_end_of_batch(size_t transfer_size, uint16_t batch_size, + VdmaInterruptsDomain new_interrupts_domain) = 0; +}; + +} /* vdma */ +} /* hailort */ + +#endif /* _HAILO_VDMA_VDMA_BUFFER_HPP_ */ diff --git a/hailort/libhailort/src/vdma_buffer.hpp b/hailort/libhailort/src/vdma_buffer.hpp deleted file mode 100644 index b11d4e6..0000000 --- a/hailort/libhailort/src/vdma_buffer.hpp +++ /dev/null @@ -1,104 +0,0 @@ -/** - * 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 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> allocate_vdma_buffer(HailoRTDriver &driver, size_t required_size, - uintptr_t &driver_buff_handle); - - MmapBuffer 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 index cfbe70d..79f4fbf 100644 --- a/hailort/libhailort/src/vdma_channel.cpp +++ b/hailort/libhailort/src/vdma_channel.cpp @@ -3,6 +3,7 @@ #include "hw_consts.hpp" #include "common/logger_macros.hpp" #include "common/utils.hpp" +#include "microprofile.h" #include #include @@ -73,11 +74,12 @@ Expected VdmaChannel::create(uint8_t channel_index, Direction direc 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_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) + m_transfers_per_axi_intr(transfers_per_axi_intr), m_pending_buffers_sizes(0), m_pending_num_avail_offset(0), m_is_waiting_for_channel_completion(false), + m_is_aborted(false) { // channel index invalid if (channel_index >= MAX_HOST_CHANNELS_COUNT) { @@ -128,7 +130,7 @@ VdmaChannel::~VdmaChannel() } VdmaChannel::VdmaChannel(VdmaChannel &&other) noexcept: -channel_index(other.channel_index), +m_channel_index(other.m_channel_index), m_direction(other.m_direction), m_driver(other.m_driver), m_host_registers(std::move(other.m_host_registers)), @@ -141,7 +143,11 @@ channel_index(other.channel_index), 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)) + m_transfers_per_axi_intr(std::move(other.m_transfers_per_axi_intr)), + m_pending_buffers_sizes(std::move(other.m_pending_buffers_sizes)), + m_pending_num_avail_offset(std::move(other.m_pending_num_avail_offset)), + m_is_waiting_for_channel_completion(other.m_is_waiting_for_channel_completion.load()), + m_is_aborted(std::move(other.m_is_aborted)) {} hailo_status VdmaChannel::stop_channel() @@ -150,7 +156,12 @@ hailo_status VdmaChannel::stop_channel() 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); + CHECK_SUCCESS(status, "Failed to disable channel {}", m_channel_index); + } + + if (m_state) { + std::lock_guard state_guard(*m_state); + reset_internal_counters(); } return HAILO_SUCCESS; @@ -164,12 +175,15 @@ uint16_t VdmaChannel::get_page_size() hailo_status VdmaChannel::abort() { - return m_driver.vdma_channel_abort(channel_index, *m_channel_handle); + m_is_aborted = true; + m_can_write_buffer_cv.notify_one(); + return m_driver.vdma_channel_abort(m_channel_index, *m_channel_handle); } hailo_status VdmaChannel::clear_abort() { - return m_driver.vdma_channel_clear_abort(channel_index, *m_channel_handle); + m_is_aborted = false; + return m_driver.vdma_channel_clear_abort(m_channel_index, *m_channel_handle); } hailo_status VdmaChannel::prepare_d2h_pending_descriptors(uint32_t descs_count, uint32_t transfer_size) @@ -183,7 +197,7 @@ hailo_status VdmaChannel::prepare_d2h_pending_descriptors(uint32_t descs_count, 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(m_transfers_per_axi_intr -1) == (i % m_transfers_per_axi_intr)) ? + (static_cast(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); @@ -225,6 +239,7 @@ hailo_status VdmaChannel::allocate_resources(uint32_t descs_count) #endif m_state = state.release(); + m_pending_buffers_sizes = CircularArray(descs_count); // 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(m_state->m_pending_buffers.size()) : @@ -262,13 +277,11 @@ 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_guard(*m_state); - reset_internal_counters(); auto status = start_channel(*m_descriptors_buffer); - CHECK_SUCCESS(status, "failed to start channel {}", channel_index); + CHECK_SUCCESS(status, "failed to start channel {}", m_channel_index); if ((Direction::D2H == m_direction) && (transfer_size != 0)) { auto descs_count = CB_SIZE(m_state->m_descs); @@ -278,6 +291,7 @@ hailo_status VdmaChannel::start_allocated_channel(uint32_t transfer_size) } return status; } + return HAILO_SUCCESS; } @@ -305,14 +319,8 @@ hailo_status VdmaChannel::wait(size_t buffer_size, const std::chrono::millisecon 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; - } + CHECK((nullptr != buf) && (0 < count), HAILO_INVALID_ARGUMENT); + CHECK(nullptr != m_mapped_user_buffer, HAILO_INVALID_OPERATION, "Transfer called without allocating buffers"); hailo_status status = HAILO_UNINITIALIZED; assert(m_state); @@ -321,14 +329,14 @@ hailo_status VdmaChannel::transfer(void *buf, size_t count) if (Direction::H2D == m_direction) { status = transfer_h2d(buf, count); if (HAILO_SUCCESS != status) { - LOGGER__ERROR("Transfer failed for channel {}", channel_index); + LOGGER__ERROR("Transfer failed for channel {}", m_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); + LOGGER__ERROR("Transfer failed for channel {} status {}", m_channel_index, status); return status; } return HAILO_SUCCESS; @@ -337,6 +345,131 @@ hailo_status VdmaChannel::transfer(void *buf, size_t count) return HAILO_SUCCESS; } +hailo_status VdmaChannel::write_buffer_impl(const MemoryView &buffer) +{ + CHECK(nullptr != m_mapped_user_buffer, HAILO_INVALID_OPERATION, "Transfer called without allocating buffers"); + + size_t desired_desc_num = m_descriptors_buffer->descriptors_in_buffer(buffer.size()); + uint32_t desc_avail = (get_num_available() + m_pending_num_avail_offset) & m_state->m_descs.size_mask; + + assert(CB_AVAIL(m_state->m_descs, desc_avail, CB_TAIL(m_state->m_descs)) >= static_cast(desired_desc_num)); + + /* Copy buffer into the PLDA data struct */ + auto offset = desc_avail * m_desc_page_size; + auto status = m_mapped_user_buffer->write_cyclic(buffer.data(), buffer.size(), offset); + CHECK_SUCCESS(status); + + m_pending_num_avail_offset = static_cast(m_pending_num_avail_offset + desired_desc_num); + + CHECK(!m_pending_buffers_sizes.full(), HAILO_INVALID_OPERATION, "Cannot add more pending buffers!"); + m_pending_buffers_sizes.push_back(buffer.size()); + return HAILO_SUCCESS; +} + +hailo_status VdmaChannel::write_buffer(const MemoryView &buffer, std::chrono::milliseconds timeout) +{ + assert(m_state); + std::unique_lock state_guard(*m_state); + + hailo_status status = HAILO_UNINITIALIZED; + size_t desired_desc_num = m_descriptors_buffer->descriptors_in_buffer(buffer.size()); + bool was_successful = m_can_write_buffer_cv.wait_for(state_guard, timeout, [this, &status, desired_desc_num] () { + if ((!m_channel_enabled) || (m_is_aborted)) { + status = HAILO_STREAM_INTERNAL_ABORT; + return true; + } + + uint32_t desc_avail = (get_num_available() + m_pending_num_avail_offset) & m_state->m_descs.size_mask; + int num_free = CB_AVAIL(m_state->m_descs, desc_avail, CB_TAIL(m_state->m_descs)); + return (num_free >= static_cast(desired_desc_num)); + }); + if (HAILO_STREAM_INTERNAL_ABORT == status) { + LOGGER__INFO("wait_for in write_buffer was aborted!"); + return status; + } + CHECK(was_successful, HAILO_TIMEOUT, "Waiting for descriptors in write_buffer has reached a timeout!"); + + return write_buffer_impl(buffer); +} + +hailo_status VdmaChannel::send_pending_buffer_impl() +{ + CHECK(!m_pending_buffers_sizes.empty(), HAILO_INVALID_OPERATION, "There are no pending buffers to send!"); + + // For h2d, only the host need to get transfer done interrupts + VdmaInterruptsDomain last_desc_interrupts_domain = VdmaInterruptsDomain::HOST; + // If we measure latency, we need interrupt on the first descriptor + VdmaInterruptsDomain first_desc_interrupts_domain = (m_latency_meter != nullptr) ? + VdmaInterruptsDomain::HOST : VdmaInterruptsDomain::NONE; + + auto status = prepare_descriptors(m_pending_buffers_sizes.front(), 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; + + size_t desired_desc_num = m_descriptors_buffer->descriptors_in_buffer(m_pending_buffers_sizes.front()); + m_pending_num_avail_offset = static_cast(m_pending_num_avail_offset - desired_desc_num); + + m_pending_buffers_sizes.pop_front(); + + return HAILO_SUCCESS; +} + +Expected VdmaChannel::send_pending_buffer() +{ + size_t next_buffer_desc_num = 0; + { + assert(m_state); + std::lock_guard state_guard(*m_state); + + // Save before calling send_pending_buffer_impl because we pop from m_pending_buffers_sizes there + next_buffer_desc_num = m_descriptors_buffer->descriptors_in_buffer(m_pending_buffers_sizes.front()); + + auto status = send_pending_buffer_impl(); + CHECK_SUCCESS_AS_EXPECTED(status); + } + m_can_write_buffer_cv.notify_one(); + + return PendingBufferState(*this, next_buffer_desc_num); +} + +hailo_status PendingBufferState::finish(std::chrono::milliseconds timeout, std::unique_lock &lock) +{ + unlock_guard> unlock(lock); + + while (true) { + { + std::lock_guard state_guard(*m_vdma_channel.m_state); + + // Make sure that only one thread is waiting for channel completion + if (m_vdma_channel.m_is_waiting_for_channel_completion) { + break; + } + + // When all pending buffers have been sent but no buffers were processed yet (there are no free descriptors) we want to wait + // for channel completion to make free room to the next buffers + // TODO: This assumes the next buffer is the same size as the current one, so consider moving this to the write_buffer function + int num_free = CB_AVAIL(m_vdma_channel.m_state->m_descs, m_vdma_channel.get_num_available(), CB_TAIL(m_vdma_channel.m_state->m_descs)); + + // We use m_next_buffer_desc_num to check if the next buffer has enough descriptors + bool should_free_descs = (0 == m_vdma_channel.m_pending_num_avail_offset) && (num_free < static_cast(m_next_buffer_desc_num)); + m_vdma_channel.m_is_waiting_for_channel_completion = should_free_descs; + if (!should_free_descs) { + break; + } + } + + auto status = m_vdma_channel.wait_for_channel_completion(timeout); + if (HAILO_STREAM_INTERNAL_ABORT == status) { + LOGGER__INFO("wait_for_channel_completion has failed with status=HAILO_STREAM_INTERNAL_ABORT"); + return status; + } + CHECK_SUCCESS(status); + } + + return HAILO_SUCCESS; +} + hailo_status VdmaChannel::flush(const std::chrono::milliseconds &timeout) { assert(m_state); @@ -356,44 +489,12 @@ hailo_status VdmaChannel::flush(const std::chrono::milliseconds &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); + auto status = write_buffer_impl(MemoryView(buf, count)); CHECK_SUCCESS(status); - status = prepare_descriptors(count, first_desc_interrupts_domain, last_desc_interrupts_domain); + status = send_pending_buffer_impl(); CHECK_SUCCESS(status); - m_state->m_accumulated_transfers = (m_state->m_accumulated_transfers + 1) % m_transfers_per_axi_intr; - return HAILO_SUCCESS; } @@ -432,7 +533,6 @@ hailo_status VdmaChannel::transfer_d2h(void *buf, size_t count) 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() @@ -446,7 +546,13 @@ uint16_t VdmaChannel::get_num_available() 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()); + + auto is_aborted_exp = is_aborted(); + assert(is_aborted_exp); + + if ((HailoRTDriver::INVALID_VDMA_CHANNEL_HANDLE != *m_channel_handle) && !is_aborted_exp.value()) { + assert(hw_num_avail.value() == num_available); + } #endif return num_available; } @@ -465,24 +571,6 @@ Expected VdmaChannel::get_hw_num_processed() return static_cast(hw_num_processed.value() & m_state->m_descs.size_mask); } -Expected 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(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); @@ -494,18 +582,14 @@ hailo_status VdmaChannel::set_num_avail_value(uint16_t new_value) 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) +hailo_status VdmaChannel::set_transfers_per_axi_intr(uint16_t transfers_per_axi_intr) { - //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((num_available.value() + value) & size_mask); - return set_num_avail_value(new_value); + CHECK(0 != transfers_per_axi_intr, HAILO_INVALID_ARGUMENT, "Invalid transfers per axi interrupt"); + m_transfers_per_axi_intr = transfers_per_axi_intr; + return HAILO_SUCCESS; } hailo_status VdmaChannel::inc_num_available(uint16_t value) @@ -516,9 +600,6 @@ hailo_status VdmaChannel::inc_num_available(uint16_t value) 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; } @@ -549,9 +630,9 @@ VdmaChannel::Direction VdmaChannel::other_direction(Direction direction) hailo_status VdmaChannel::unregister_fw_controlled_channel() { - auto status = m_driver.vdma_channel_disable(channel_index, *m_channel_handle); + auto status = m_driver.vdma_channel_disable(m_channel_index, *m_channel_handle); *m_channel_handle = HailoRTDriver::INVALID_VDMA_CHANNEL_HANDLE; - CHECK_SUCCESS(status, "Failed to disable channel {}", channel_index); + CHECK_SUCCESS(status, "Failed to disable channel {}", m_channel_index); return HAILO_SUCCESS; } @@ -560,8 +641,8 @@ 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); + auto channel_handle = m_driver.vdma_channel_enable(m_channel_index, m_direction, desc_handle, measure_latency); + CHECK_EXPECTED_AS_STATUS(channel_handle, "Failed to enable channel {}", m_channel_index); *m_channel_handle = channel_handle.release(); return HAILO_SUCCESS; @@ -572,7 +653,7 @@ 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); + CHECK_SUCCESS(status, "Failed to enable channel {}", m_channel_index); return HAILO_SUCCESS; } @@ -592,17 +673,17 @@ void VdmaChannel::clear_descriptor_list() } } -hailo_status VdmaChannel::allocate_buffer(const size_t buffer_size) +hailo_status VdmaChannel::allocate_buffer(const uint32_t buffer_size) { assert((buffer_size % m_desc_page_size) == 0); - size_t desc_count = buffer_size / m_desc_page_size; + uint32_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); + auto mapped_buffer = vdma::MappedBuffer::create(buffer_size, m_direction, m_driver); if(!mapped_buffer) { LOGGER__ERROR("create mapped buffer failed"); return mapped_buffer.status(); @@ -614,13 +695,13 @@ hailo_status VdmaChannel::allocate_buffer(const size_t buffer_size) return descriptors.status(); } - auto status = descriptors->configure_to_use_buffer(mapped_buffer.value(), channel_index); + auto status = descriptors->configure_to_use_buffer(mapped_buffer.value(), m_channel_index); if (status != HAILO_SUCCESS) { LOGGER__ERROR("connect descriptor list to buffer failed"); return status; } - m_mapped_user_buffer = make_unique_nothrow(mapped_buffer.release()); + m_mapped_user_buffer = make_unique_nothrow(mapped_buffer.release()); CHECK_NOT_NULL(m_mapped_user_buffer, HAILO_OUT_OF_HOST_MEMORY); m_descriptors_buffer = make_unique_nothrow(descriptors.release()); @@ -655,6 +736,7 @@ hailo_status VdmaChannel::trigger_channel_completion(uint16_t hw_num_processed) // situation correctly. assert(m_state); + std::lock_guard state_guard(*m_state); int processed_no = 0; int head = CB_HEAD(m_state->m_buffers); @@ -664,7 +746,7 @@ hailo_status VdmaChannel::trigger_channel_completion(uint16_t hw_num_processed) 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()); + CHECK(0 == channel_error.value(), HAILO_INTERNAL_FAILURE, "Vdma channel {} in error state {}", m_channel_index, channel_error.value()); uint16_t last_num_processed = static_cast(CB_TAIL(m_state->m_descs)); @@ -672,14 +754,13 @@ hailo_status VdmaChannel::trigger_channel_completion(uint16_t hw_num_processed) uint16_t last_desc_index = static_cast(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()); + 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, + LOGGER__ERROR("Error while processing descriptor {} of DMA {} on board {}.", last_desc_index, m_channel_index, m_driver.dev_path()); return HAILO_INTERNAL_FAILURE; } @@ -701,8 +782,13 @@ hailo_status VdmaChannel::trigger_channel_completion(uint16_t hw_num_processed) // 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); + + if (Direction::H2D == m_direction) { + m_can_write_buffer_cv.notify_one(); + } } + m_is_waiting_for_channel_completion = false; return HAILO_SUCCESS; } @@ -762,6 +848,8 @@ bool VdmaChannel::is_ready_for_transfer_d2h(size_t buffer_size) hailo_status VdmaChannel::prepare_descriptors(size_t transfer_size, VdmaInterruptsDomain first_desc_interrupts_domain, VdmaInterruptsDomain last_desc_interrupts_domain) { + MICROPROFILE_SCOPEI("vDMA Channel", "Trigger vDMA", 0); + assert(m_descriptors_buffer); assert(m_state); auto &desc_info = *m_descriptors_buffer; @@ -781,7 +869,7 @@ hailo_status VdmaChannel::prepare_descriptors(size_t transfer_size, VdmaInterrup 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); + LOGGER__ERROR("Failed to program desc_list for channel {}", m_channel_index); return actual_desc_count.status(); } assert (actual_desc_count.value() == desc_num); @@ -834,7 +922,6 @@ Expected VdmaChannel::is_aborted() hailo_status VdmaChannel::wait_for_condition(std::function 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) { @@ -842,28 +929,10 @@ hailo_status VdmaChannel::wait_for_condition(std::function condition, st 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; - } + auto status = wait_for_channel_completion(timeout); + if (HAILO_SUCCESS != status) { + return status; } - 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::steady_clock::now() - start_time); } @@ -871,11 +940,39 @@ hailo_status VdmaChannel::wait_for_condition(std::function condition, st return condition() ? HAILO_SUCCESS : HAILO_TIMEOUT; } +hailo_status VdmaChannel::wait_for_channel_completion(std::chrono::milliseconds timeout) +{ + 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!", m_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); + + auto status = trigger_channel_completion(hw_num_processed.value()); + CHECK_SUCCESS(status); + + return HAILO_SUCCESS; +} + Expected VdmaChannel::wait_interrupts(std::chrono::milliseconds timeout) { assert(m_state); - auto irq_data = m_driver.wait_channel_interrupts(channel_index, *m_channel_handle, timeout); + auto irq_data = m_driver.wait_channel_interrupts(m_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!"); diff --git a/hailort/libhailort/src/vdma_channel.hpp b/hailort/libhailort/src/vdma_channel.hpp index d2a9f92..0ceb03f 100644 --- a/hailort/libhailort/src/vdma_channel.hpp +++ b/hailort/libhailort/src/vdma_channel.hpp @@ -18,15 +18,30 @@ #include "vdma_channel_regs.hpp" #include "hailo/expected.hpp" #include "os/hailort_driver.hpp" -#include "vdma_buffer.hpp" +#include "vdma/mapped_buffer.hpp" #include "vdma_descriptor_list.hpp" +#include "hailo/buffer.hpp" #include #include +#include namespace hailort { +class VdmaChannel; +class PendingBufferState final +{ +public: + PendingBufferState(VdmaChannel &vdma_channel, size_t next_buffer_desc_num) : m_vdma_channel(vdma_channel), + m_next_buffer_desc_num(next_buffer_desc_num) {} + hailo_status finish(std::chrono::milliseconds timeout, std::unique_lock &lock); + +private: + VdmaChannel &m_vdma_channel; + size_t m_next_buffer_desc_num; +}; + class VdmaChannel final { public: @@ -48,6 +63,8 @@ public: hailo_status wait(size_t buffer_size, const std::chrono::milliseconds &timeout); hailo_status transfer(void *buf, size_t count); + hailo_status write_buffer(const MemoryView &buffer, std::chrono::milliseconds timeout); + Expected send_pending_buffer(); 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 @@ -57,8 +74,8 @@ public: 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 set_transfers_per_axi_intr(uint16_t transfers_per_axi_intr); 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 get_hw_num_processed_ddr(uint32_t size_mask); /*Used for DDR channels only. TODO - remove */ hailo_status start_channel(VdmaDescriptorList &desc_list); @@ -71,6 +88,11 @@ public: hailo_status abort(); hailo_status clear_abort(); + uint8_t get_channel_index() + { + return m_channel_index; + } + VdmaChannel(const VdmaChannel &other) = delete; VdmaChannel &operator=(const VdmaChannel &other) = delete; VdmaChannel(VdmaChannel &&other) noexcept; @@ -80,7 +102,9 @@ public: uint16_t requested_desc_page_size); - const uint8_t channel_index; + const uint8_t m_channel_index; + + friend class PendingBufferState; private: struct PendingBuffer { @@ -115,11 +139,13 @@ private: 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); + hailo_status allocate_buffer(const uint32_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); + hailo_status write_buffer_impl(const MemoryView &buffer); + hailo_status send_pending_buffer_impl(); uint16_t get_num_available(); Expected get_hw_num_processed(); void add_pending_buffer(uint32_t first_desc, uint32_t last_desc); @@ -131,6 +157,7 @@ private: VdmaInterruptsDomain last_desc_interrupts_domain); hailo_status prepare_d2h_pending_descriptors(uint32_t descs_count, uint32_t transfer_size); void reset_internal_counters(); + hailo_status wait_for_channel_completion(std::chrono::milliseconds timeout); uint32_t calculate_descriptors_count(uint32_t buffer_size); @@ -158,7 +185,7 @@ private: // TODO: remove the unique_ptr, instead allocate the buffer in the ctor (needs to move ddr channel to // other class) - std::unique_ptr m_mapped_user_buffer; + std::unique_ptr m_mapped_user_buffer; std::unique_ptr m_descriptors_buffer; uint32_t m_stream_index; LatencyMeterPtr m_latency_meter; @@ -171,6 +198,12 @@ private: bool m_channel_enabled; uint16_t m_transfers_per_axi_intr; + // Using CircularArray because it won't allocate or free memory wile pushing and poping. The fact that it is circural is not relevant here + CircularArray m_pending_buffers_sizes; + uint16_t m_pending_num_avail_offset; + std::condition_variable_any m_can_write_buffer_cv; + std::atomic_bool m_is_waiting_for_channel_completion; + bool m_is_aborted; }; } /* namespace hailort */ diff --git a/hailort/libhailort/src/vdma_descriptor_list.cpp b/hailort/libhailort/src/vdma_descriptor_list.cpp index 6d02368..a74133e 100644 --- a/hailort/libhailort/src/vdma_descriptor_list.cpp +++ b/hailort/libhailort/src/vdma_descriptor_list.cpp @@ -13,11 +13,12 @@ #define DESC_PAGE_SIZE_SHIFT (8) #define DESC_PAGE_SIZE_MASK (0xFFFFFF00) +#define DESC_IRQ_MASK (0x0000003C) namespace hailort { -Expected VdmaDescriptorList::create(size_t desc_count, uint16_t requested_desc_page_size, +Expected VdmaDescriptorList::create(uint32_t desc_count, uint16_t requested_desc_page_size, HailoRTDriver &driver) { hailo_status status = HAILO_UNINITIALIZED; @@ -30,7 +31,7 @@ Expected VdmaDescriptorList::create(size_t desc_count, uint1 return object; } -VdmaDescriptorList::VdmaDescriptorList(size_t desc_count, HailoRTDriver &driver, uint16_t desc_page_size, +VdmaDescriptorList::VdmaDescriptorList(uint32_t desc_count, HailoRTDriver &driver, uint16_t desc_page_size, hailo_status &status) : m_mapped_list(), m_count(desc_count), @@ -107,13 +108,13 @@ Expected VdmaDescriptorList::calculate_desc_list_depth(size_t count) return static_cast(depth); } -hailo_status VdmaDescriptorList::configure_to_use_buffer(VdmaBuffer& buffer, uint8_t channel_index) +hailo_status VdmaDescriptorList::configure_to_use_buffer(vdma::MappedBuffer& 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) +hailo_status VdmaDescriptorList::configure_to_use_buffer(vdma::MappedBuffer& buffer) { return configure_to_use_buffer(buffer, HailoRTDriver::INVALID_VDMA_CHANNEL_INDEX); } @@ -125,7 +126,7 @@ Expected VdmaDescriptorList::program_descriptors(size_t transfer_size, 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); + LOGGER__ERROR("Requested transfer size ({}) result in more descriptors than available ({})", transfer_size, m_count); return make_unexpected(HAILO_OUT_OF_DESCRIPTORS); } @@ -144,23 +145,15 @@ Expected VdmaDescriptorList::program_descriptors(size_t transfer_size, return std::move(static_cast(required_descriptors)); } -Expected 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) +hailo_status VdmaDescriptorList::reprogram_descriptor_interrupts_domain(size_t desc_index, + VdmaInterruptsDomain interrupts_domain) { - 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(programmed_descs + desc_count_local.value()); + if (desc_index >= m_count){ + LOGGER__ERROR("Requested desc (index={}) exceeds the number of descriptors in the list ({})", desc_index, m_count); + return HAILO_OUT_OF_DESCRIPTORS; } - return programmed_descs; + reprogram_single_descriptor_interrupts_domain((*this)[desc_index], interrupts_domain); + return HAILO_SUCCESS; } uint32_t VdmaDescriptorList::descriptors_in_buffer(size_t buffer_size) const @@ -171,7 +164,7 @@ uint32_t VdmaDescriptorList::descriptors_in_buffer(size_t buffer_size) const uint32_t VdmaDescriptorList::descriptors_in_buffer(size_t buffer_size, uint16_t desc_page_size) { assert(buffer_size < std::numeric_limits::max()); - return static_cast(((buffer_size) + desc_page_size - 1) / desc_page_size); + return static_cast(DESCRIPTORS_IN_BUFFER(buffer_size, desc_page_size)); } uint32_t VdmaDescriptorList::calculate_descriptors_count(uint32_t buffer_size, uint16_t batch_size, uint16_t desc_page_size) @@ -257,8 +250,9 @@ Expected> VdmaDescriptorList::get_desc_buffer_size 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); + "Required descriptors count: {}, max allowed on the driver: {}. (A common cause for this error could be the" + "Batch size - which is {}).", + (batch_size * acc_desc_count), MAX_DESCS_COUNT, batch_size); CHECK_AS_EXPECTED(IS_FIT_IN_UINT16(local_desc_page_size), HAILO_INTERNAL_FAILURE, "Descriptor page size needs to fit in 16B"); @@ -322,16 +316,31 @@ void VdmaDescriptorList::program_single_descriptor(VdmaDescriptor &descriptor, u (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); + // Update the desc_control + descriptor.PageSize_DescControl |= (DESC_REQUREST_IRQ_PROCESSED | DESC_REQUREST_IRQ_ERR | + get_interrupts_bitmask(interrupts_domain)); #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; } +void VdmaDescriptorList::reprogram_single_descriptor_interrupts_domain(VdmaDescriptor &descriptor, + VdmaInterruptsDomain interrupts_domain) +{ + // Set the IRQ control bits to zero + descriptor.PageSize_DescControl &= ~DESC_IRQ_MASK; + + if (VdmaInterruptsDomain::NONE == interrupts_domain) { + // Nothing else to do + return; + } + + descriptor.PageSize_DescControl |= (DESC_REQUREST_IRQ_PROCESSED | DESC_REQUREST_IRQ_ERR | + get_interrupts_bitmask(interrupts_domain)); +} + } /* namespace hailort */ diff --git a/hailort/libhailort/src/vdma_descriptor_list.hpp b/hailort/libhailort/src/vdma_descriptor_list.hpp index f09a3a3..28cd03d 100644 --- a/hailort/libhailort/src/vdma_descriptor_list.hpp +++ b/hailort/libhailort/src/vdma_descriptor_list.hpp @@ -14,7 +14,7 @@ #include "os/hailort_driver.hpp" #include "hailo/expected.hpp" #include "os/mmap_buffer.hpp" -#include "vdma_buffer.hpp" +#include "vdma/mapped_buffer.hpp" #include "common/utils.hpp" namespace hailort @@ -81,7 +81,7 @@ inline bool device_interuptes_enabled(VdmaInterruptsDomain interrupts_domain) class VdmaDescriptorList { public: - static Expected create(size_t desc_count, uint16_t requested_desc_page_size, + static Expected create(uint32_t desc_count, uint16_t requested_desc_page_size, HailoRTDriver &driver); ~VdmaDescriptorList(); @@ -96,7 +96,7 @@ public: return m_depth; } - size_t count() const + uint32_t count() const { return m_count; } @@ -122,15 +122,13 @@ public: return m_desc_handle; } - hailo_status configure_to_use_buffer(VdmaBuffer& buffer, uint8_t channel_index); + hailo_status configure_to_use_buffer(vdma::MappedBuffer& buffer, uint8_t channel_index); // On hailo8, we allow configuring buffer without specific channel index. - hailo_status configure_to_use_buffer(VdmaBuffer& buffer); + hailo_status configure_to_use_buffer(vdma::MappedBuffer& buffer); Expected program_descriptors(size_t transfer_size, VdmaInterruptsDomain first_desc_interrupts_domain, VdmaInterruptsDomain last_desc_interrupts_domain, size_t desc_offset, bool is_circular); - - Expected 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); + hailo_status reprogram_descriptor_interrupts_domain(size_t desc_index, VdmaInterruptsDomain interrupts_domain); 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); @@ -141,10 +139,11 @@ public: uint16_t batch_size, const std::vector &transfer_sizes); private: - VdmaDescriptorList(size_t desc_count, HailoRTDriver &driver, uint16_t desc_page_size, hailo_status &status); + VdmaDescriptorList(uint32_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); + void reprogram_single_descriptor_interrupts_domain(VdmaDescriptor &descriptor, VdmaInterruptsDomain interrupts_domain); static Expected calculate_desc_list_depth(size_t count); // Note: initial_desc_page_size should be the optimal descriptor page size. static Expected> get_desc_buffer_sizes_for_single_transfer_impl( @@ -155,7 +154,7 @@ private: uint16_t initial_desc_page_size); MmapBuffer m_mapped_list; - size_t m_count; + uint32_t m_count; uint8_t m_depth; uintptr_t m_desc_handle; uint64_t m_dma_address; diff --git a/hailort/libhailort/src/vdma_stream.cpp b/hailort/libhailort/src/vdma_stream.cpp index e1aa94d..60af946 100644 --- a/hailort/libhailort/src/vdma_stream.cpp +++ b/hailort/libhailort/src/vdma_stream.cpp @@ -11,28 +11,23 @@ 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, +VdmaInputStream::VdmaInputStream(VdmaDevice &device, std::shared_ptr channel, + const LayerInfo &edge_layer, EventPtr network_group_activated_event, uint16_t batch_size, + 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), + m_channel(std::move(channel)), is_stream_activated(false), m_channel_timeout(transfer_timeout), - m_latency_meter(latency_meter), - m_batch_size(batch_size) + m_max_batch_size(batch_size), + m_dynamic_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; } @@ -52,12 +47,11 @@ VdmaInputStream::~VdmaInputStream() 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) + m_max_batch_size(other.m_max_batch_size), + m_dynamic_batch_size(other.m_dynamic_batch_size) {} std::chrono::milliseconds VdmaInputStream::get_timeout() const @@ -83,12 +77,15 @@ hailo_status VdmaInputStream::clear_abort() hailo_status VdmaInputStream::flush() { - return m_channel->flush((m_channel_timeout * m_batch_size)); + return m_channel->flush((m_channel_timeout * m_dynamic_batch_size)); } -hailo_status VdmaInputStream::activate_stream() +hailo_status VdmaInputStream::activate_stream(uint16_t dynamic_batch_size) { - auto status = m_channel->start_allocated_channel(0); + auto status = set_dynamic_batch_size(dynamic_batch_size); + CHECK_SUCCESS(status); + + status = m_channel->start_allocated_channel(0); CHECK_SUCCESS(status); this->is_stream_activated = true; @@ -104,10 +101,10 @@ hailo_status VdmaInputStream::deactivate_stream() /* 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); + LOGGER__INFO("Flush input_channel is not needed because channel was aborted. (channel {})", m_channel->get_channel_index()); status = HAILO_SUCCESS; } else if (HAILO_SUCCESS != status) { - LOGGER__ERROR("Failed to flush input_channel. (status {} channel {})", status, m_channel_index); + LOGGER__ERROR("Failed to flush input_channel. (status {} channel {})", status, m_channel->get_channel_index()); } /* Close channel is best effort. */ @@ -137,6 +134,32 @@ Expected VdmaInputStream::sync_write_raw_buffer(const MemoryView &buffer return buffer.size(); } +hailo_status VdmaInputStream::write_buffer_only(const MemoryView &buffer) +{ + return m_channel->write_buffer(buffer, m_channel_timeout); +} + +Expected VdmaInputStream::send_pending_buffer() +{ + hailo_status status = m_channel->wait(get_frame_size(), m_channel_timeout); + if ((HAILO_STREAM_INTERNAL_ABORT == status) || (HAILO_STREAM_NOT_ACTIVATED == status)) { + return make_unexpected(status); + } + CHECK_SUCCESS_AS_EXPECTED(status); + + return m_channel->send_pending_buffer(); +} + +uint16_t VdmaInputStream::get_dynamic_batch_size() const +{ + return m_dynamic_batch_size; +} + +const char* VdmaInputStream::get_dev_id() const +{ + return m_device->get_dev_id(); +} + hailo_status VdmaInputStream::sync_write_all_raw_buffer_no_transform_impl(void *buffer, size_t offset, size_t size) { ASSERT(NULL != buffer); @@ -144,52 +167,37 @@ hailo_status VdmaInputStream::sync_write_all_raw_buffer_no_transform_impl(void * return sync_write_raw_buffer(MemoryView(static_cast(buffer) + offset, size)).status(); } -hailo_status VdmaInputStream::config_stream() +hailo_status VdmaInputStream::set_dynamic_batch_size(uint16_t dynamic_batch_size) { - 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(dynamic_batch_size <= m_max_batch_size, HAILO_INVALID_ARGUMENT, + "Dynamic batch size ({}) must be <= than the configured batch size ({})", + dynamic_batch_size, m_max_batch_size); + + if (CONTROL_PROTOCOL__IGNORE_DYNAMIC_BATCH_SIZE == dynamic_batch_size) { + LOGGER__TRACE("Received CONTROL_PROTOCOL__IGNORE_DYNAMIC_BATCH_SIZE == dynamic_batch_size; " + "Leaving previously set value of {}", m_dynamic_batch_size); + } else { + LOGGER__TRACE("Setting stream's dynamic_batch_size to {}", dynamic_batch_size); + m_dynamic_batch_size = dynamic_batch_size; - 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(min_active_trans), static_cast(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(channel.release()); - CHECK(nullptr != m_channel, HAILO_OUT_OF_HOST_MEMORY); - - status = m_channel->allocate_resources(descs_count); - CHECK_SUCCESS(status); + const auto status = m_channel->set_transfers_per_axi_intr(m_dynamic_batch_size); + 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) : +VdmaOutputStream::VdmaOutputStream(VdmaDevice &device, std::shared_ptr channel, + const LayerInfo &edge_layer, EventPtr network_group_activated_event, uint16_t batch_size, + 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), + m_channel(std::move(channel)), is_stream_activated(false), m_transfer_timeout(transfer_timeout), - m_latency_meter(latency_meter), - m_batch_size(batch_size), + m_max_batch_size(batch_size), + m_dynamic_batch_size(batch_size), m_transfer_size(get_transfer_size(m_stream_info)) { // Check status for base class c'tor @@ -197,23 +205,17 @@ VdmaOutputStream::VdmaOutputStream(VdmaDevice &device, uint8_t channel_index, co 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_max_batch_size(other.m_max_batch_size), + m_dynamic_batch_size(other.m_dynamic_batch_size), m_transfer_size(other.m_transfer_size) {} @@ -252,9 +254,22 @@ hailo_status VdmaOutputStream::clear_abort() return m_channel->clear_abort(); } -hailo_status VdmaOutputStream::activate_stream() +uint16_t VdmaOutputStream::get_dynamic_batch_size() const { - auto status = m_channel->start_allocated_channel(m_transfer_size); + return m_dynamic_batch_size; +} + +const char* VdmaOutputStream::get_dev_id() const +{ + return m_device->get_dev_id(); +} + +hailo_status VdmaOutputStream::activate_stream(uint16_t dynamic_batch_size) +{ + auto status = set_dynamic_batch_size(dynamic_batch_size); + CHECK_SUCCESS(status); + + status = m_channel->start_allocated_channel(m_transfer_size); CHECK_SUCCESS(status); this->is_stream_activated = true; @@ -291,36 +306,6 @@ Expected VdmaOutputStream::sync_read_raw_buffer(MemoryView &buffer) 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(min_active_trans), static_cast(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(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, @@ -336,4 +321,24 @@ uint32_t VdmaOutputStream::get_transfer_size(const hailo_stream_info_t &stream_i stream_info.nms_info.bbox_size : stream_info.hw_frame_size; } +hailo_status VdmaOutputStream::set_dynamic_batch_size(uint16_t dynamic_batch_size) +{ + CHECK(dynamic_batch_size <= m_max_batch_size, HAILO_INVALID_ARGUMENT, + "Dynamic batch size ({}) must be <= than the configured batch size ({})", + dynamic_batch_size, m_max_batch_size); + + if (CONTROL_PROTOCOL__IGNORE_DYNAMIC_BATCH_SIZE == dynamic_batch_size) { + LOGGER__TRACE("Received CONTROL_PROTOCOL__IGNORE_DYNAMIC_BATCH_SIZE == dynamic_batch_size; " + "Leaving previously set value of {}", m_dynamic_batch_size); + } else { + LOGGER__TRACE("Setting stream's dynamic_batch_size to {}", dynamic_batch_size); + m_dynamic_batch_size = dynamic_batch_size; + + const auto status = m_channel->set_transfers_per_axi_intr(m_dynamic_batch_size); + CHECK_SUCCESS(status); + } + + return HAILO_SUCCESS; +} + } /* namespace hailort */ diff --git a/hailort/libhailort/src/vdma_stream.hpp b/hailort/libhailort/src/vdma_stream.hpp index 4f07b22..8eb9faf 100644 --- a/hailort/libhailort/src/vdma_stream.hpp +++ b/hailort/libhailort/src/vdma_stream.hpp @@ -30,39 +30,32 @@ public: 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(); - } + hailo_status write_buffer_only(const MemoryView &buffer); + Expected send_pending_buffer(); + uint16_t get_dynamic_batch_size() const; + const char* get_dev_id() const; protected: - VdmaInputStream(VdmaDevice &device, uint8_t channel_index, const LayerInfo &edge_layer, - EventPtr network_group_activated_event, uint16_t batch_size, LatencyMeterPtr latency_meter, + VdmaInputStream(VdmaDevice &device, std::shared_ptr channel, const LayerInfo &edge_layer, + EventPtr network_group_activated_event, uint16_t batch_size, std::chrono::milliseconds transfer_timeout, hailo_stream_interface_t stream_interface, hailo_status &status); - virtual hailo_status activate_stream() override; + virtual hailo_status activate_stream(uint16_t dynamic_batch_size) override; virtual hailo_status deactivate_stream() override; virtual Expected 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 m_channel; + std::shared_ptr m_channel; private: - hailo_status config_stream(); + hailo_status set_dynamic_batch_size(uint16_t dynamic_batch_size); bool is_stream_activated; std::chrono::milliseconds m_channel_timeout; - LatencyMeterPtr m_latency_meter; - uint16_t m_batch_size; + const uint16_t m_max_batch_size; + uint16_t m_dynamic_batch_size; }; class VdmaOutputStream : public OutputStreamBase { @@ -74,39 +67,30 @@ public: 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(); - } + uint16_t get_dynamic_batch_size() const; + const char* get_dev_id() const; protected: - VdmaOutputStream(VdmaDevice &device, uint8_t channel_index, const LayerInfo &edge_layer, - EventPtr network_group_activated_event, uint16_t batch_size, LatencyMeterPtr latency_meter, + VdmaOutputStream(VdmaDevice &device, std::shared_ptr channel, const LayerInfo &edge_layer, + EventPtr network_group_activated_event, uint16_t batch_size, std::chrono::milliseconds transfer_timeout, hailo_status &status); - virtual hailo_status activate_stream() override; + virtual hailo_status activate_stream(uint16_t dynamic_batch_size) override; virtual hailo_status deactivate_stream() override; virtual Expected sync_read_raw_buffer(MemoryView &buffer); VdmaDevice *m_device; - const uint8_t m_channel_index; - std::unique_ptr m_channel; + std::shared_ptr 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); + hailo_status set_dynamic_batch_size(uint16_t dynamic_batch_size); bool is_stream_activated; std::chrono::milliseconds m_transfer_timeout; - LatencyMeterPtr m_latency_meter; - uint16_t m_batch_size; + const uint16_t m_max_batch_size; + uint16_t m_dynamic_batch_size; const uint32_t m_transfer_size; }; diff --git a/hailort/libhailort/src/vstream.cpp b/hailort/libhailort/src/vstream.cpp index 080c20c..ffd14e5 100644 --- a/hailort/libhailort/src/vstream.cpp +++ b/hailort/libhailort/src/vstream.cpp @@ -348,8 +348,9 @@ Expected> TransformDemuxElement::action(PipelineBuff BaseVStream::BaseVStream(const hailo_vstream_info_t &vstream_info, const hailo_vstream_params_t &vstream_params, std::shared_ptr pipeline_entry, std::vector> &&pipeline, - std::shared_ptr> &&pipeline_status, EventPtr shutdown_event, - AccumulatorPtr pipeline_latency_accumulator, EventPtr &&network_group_activated_event, hailo_status &output_status) : + std::shared_ptr> &&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), @@ -443,13 +444,13 @@ hailo_status BaseVStream::stop_vstream() 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, + 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 = stop_vstream(); + CHECK_SUCCESS(status); + status = m_entry_element->clear(); CHECK_SUCCESS(status, "Failed clearing vstream {}", name()); @@ -584,8 +585,8 @@ InputVStream::InputVStream(const hailo_vstream_info_t &vstream_info, const hailo std::shared_ptr> &&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) + 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; @@ -595,11 +596,14 @@ InputVStream::InputVStream(const hailo_vstream_info_t &vstream_info, const hailo 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 (nullptr != m_network_group_activated_event) { + 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()); + } + + auto 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(); @@ -665,7 +669,6 @@ Expected OutputVStream::create(const hailo_vstream_info_t &vstrea std::shared_ptr pipeline_entry, std::vector> &&pipeline, std::shared_ptr> &&pipeline_status, EventPtr shutdown_event, EventPtr network_group_activated_event, AccumulatorPtr pipeline_latency_accumulator) - { hailo_status status = HAILO_UNINITIALIZED; @@ -693,8 +696,8 @@ OutputVStream::OutputVStream(const hailo_vstream_info_t &vstream_info, const hai std::shared_ptr> &&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) + 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; @@ -705,17 +708,19 @@ OutputVStream::OutputVStream(const hailo_vstream_info_t &vstream_info, const hai 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; + if (nullptr != m_network_group_activated_event) { + 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); } - 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(); + auto status = recv_buffer.status(); if (HAILO_SHUTDOWN_EVENT_SIGNALED == status) { LOGGER__INFO("Receiving to VStream was shutdown!"); status = m_pipeline_status->load(); @@ -850,18 +855,25 @@ Expected HwReadElement::run_pull(PipelineBuffer &&optional, cons 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 (!m_stream.is_scheduled()) { + 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); + } else { + auto status = m_activation_wait_or_shutdown.wait(std::chrono::milliseconds(0)); + 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); + auto status = m_stream.read(buffer_view); m_duration_collector.complete_measurement(); if (HAILO_INVALID_FRAME == status) { m_stream.increase_invalid_frames_count(1); @@ -992,7 +1004,7 @@ hailo_status HwWriteElement::clear() hailo_status HwWriteElement::flush() { - hailo_status status = m_got_flush_event->wait(std::chrono::milliseconds(HAILO_DEFAULT_VSTREAM_TIMEOUT_MS)); + hailo_status status = m_got_flush_event->wait(m_stream.get_timeout()); CHECK_SUCCESS(status); status = m_got_flush_event->reset(); @@ -1195,7 +1207,10 @@ Expected> VStreamsBuilderUtils::create_inputs(InputStr std::vector> elements; std::vector vstreams; - auto network_group_activated_event = input_stream.get_network_group_activated_event(); + EventPtr network_group_activated_event = nullptr; + if (!input_stream.is_scheduled()) { + 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); @@ -1261,7 +1276,10 @@ Expected> VStreamsBuilderUtils::create_outputs(Output std::vector> elements; std::vector vstreams; - auto network_group_activated_event = output_stream.get_network_group_activated_event(); + EventPtr network_group_activated_event = nullptr; + if (!output_stream.is_scheduled()) { + 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); @@ -1336,8 +1354,8 @@ Expected> VStreamsBuilderUtils::create_outputs(Output 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()); + 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 { @@ -1421,7 +1439,10 @@ hailo_status VStreamsBuilderUtils::add_demux(OutputStream &output_stream, NameTo 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(); + EventPtr network_group_activated_event = nullptr; + if (!output_stream.is_scheduled()) { + 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()) { @@ -1471,9 +1492,8 @@ hailo_status VStreamsBuilderUtils::add_demux(OutputStream &output_stream, NameTo 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()); + 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 { @@ -1485,9 +1505,8 @@ hailo_status VStreamsBuilderUtils::add_demux(OutputStream &output_stream, NameTo 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()); + 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()); } @@ -1550,6 +1569,11 @@ hailo_status VStreamsBuilderUtils::add_nms_fuse(OutputStreamRefVector &output_st auto should_transform = OutputTransformContext::is_transformation_required({}, src_stream_format, {}, vstreams_params.user_buffer_format, vstream_info->second.quant_info); + + EventPtr network_group_activated_event = nullptr; + if (!output_streams[0].get().is_scheduled()) { + network_group_activated_event = output_streams[0].get().get_network_group_activated_event(); + } if (should_transform) { auto nms_queue_elem = PullQueueElement::create( @@ -1574,15 +1598,13 @@ hailo_status VStreamsBuilderUtils::add_nms_fuse(OutputStreamRefVector &output_st 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()); + auto vstream = OutputVStream::create(vstream_info->second, vstreams_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_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()); + auto vstream = OutputVStream::create(vstream_info->second, vstreams_params, nms_elem.release(), std::move(elements), + std::move(pipeline_status), shutdown_event, network_group_activated_event, pipeline_latency_accumulator.release()); CHECK_EXPECTED_AS_STATUS(vstream); vstreams.emplace_back(vstream.release()); } diff --git a/hailort/pre_build/CMakeLists.txt b/hailort/pre_build/CMakeLists.txt index 6eb2276..47a43c5 100644 --- a/hailort/pre_build/CMakeLists.txt +++ b/hailort/pre_build/CMakeLists.txt @@ -1,14 +1,18 @@ 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") +if(NOT HAILO_OFFLINE_COMPILATION) + 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") +else() + message("Offline compilation, skipped dependencies download") +endif() 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 index 11c34b0..c807e11 100644 --- a/hailort/pre_build/external/CMakeLists.txt +++ b/hailort/pre_build/external/CMakeLists.txt @@ -16,7 +16,7 @@ function(git_clone proj repo tag) ) endfunction() -git_clone(pybind11 https://github.com/pybind/pybind11.git 502ffe50a9f22c04637bbc8ec0019488458ba948) +git_clone(pybind11 https://github.com/pybind/pybind11.git 8de7772cc72daca8e947b79b83fea46214931604) 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) @@ -24,4 +24,6 @@ git_clone(protobuf https://github.com/protocolbuffers/protobuf.git 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 +git_clone(benchmark https://github.com/google/benchmark.git f91b6b42b1b9854772a90ae9501464a161707d1e) +git_clone(pevents https://github.com/neosmart/pevents.git 1209b1fd1bd2e75daab4380cf43d280b90b45366) +git_clone(microprofile https://github.com/jonasmr/microprofile 03c34f96840defe0f4c196309628815d02b98059) diff --git a/hailort/scripts/download_hefs.cmd b/hailort/scripts/download_hefs.cmd index aea8c58..98c8fb6 100644 --- a/hailort/scripts/download_hefs.cmd +++ b/hailort/scripts/download_hefs.cmd @@ -1,7 +1,7 @@ :: cmd @ECHO OFF set BASE_URI=https://hailo-hailort.s3.eu-west-2.amazonaws.com -set HRT_VERSION=4.6.0 +set HRT_VERSION=4.8.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 diff --git a/hailort/scripts/download_hefs.sh b/hailort/scripts/download_hefs.sh index 9431218..28d8479 100755 --- a/hailort/scripts/download_hefs.sh +++ b/hailort/scripts/download_hefs.sh @@ -2,7 +2,7 @@ set -e readonly BASE_URI="https://hailo-hailort.s3.eu-west-2.amazonaws.com" -readonly HRT_VERSION=4.6.0 +readonly HRT_VERSION=4.8.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/"