From cefc7e1cacdd3028b46325b3a1f6c15416914b2f Mon Sep 17 00:00:00 2001 From: Richard Burton Date: Wed, 6 Dec 2023 17:13:10 +0000 Subject: MLECO-4503: Adding video VSI for object detection * Added Board support - Arm Corstone 300 and 310 * Added Python Scripts for Video VSI * Added source files for Video VSI * Add new usecase handler for OD use case * Bumped resampy version to resolve issue with slowdown Signed-off-by: Idriss Chaouch Signed-off-by: Richard Burton Change-Id: Ie59ae955d4d85f672a49c63733052624542aec85 --- .pylintrc | 6 +- build_default.py | 4 +- docs/sections/building.md | 10 +- docs/use_cases/object_detection.md | 81 +++ scripts/cmake/common_user_options.cmake | 17 +- .../cmake/platforms/mps3/sse-310/mps3-sse-310.sct | 7 +- scripts/cmake/source_gen_utils.cmake | 22 +- scripts/py/gen_rgb_cpp.py | 43 +- scripts/py/templates/Images.cc.template | 20 +- scripts/py/templates/Images.hpp.template | 11 +- scripts/py/vsi/arm_vsi4.py | 207 +++++++ scripts/py/vsi/arm_vsi5.py | 207 +++++++ scripts/py/vsi/arm_vsi6.py | 207 +++++++ scripts/py/vsi/arm_vsi7.py | 207 +++++++ scripts/py/vsi/vsi_video.py | 461 ++++++++++++++++ scripts/py/vsi/vsi_video_server.py | 447 +++++++++++++++ .../main/include/UseCaseCommonUtils.hpp | 17 +- source/hal/source/components/vsi/CMakeLists.txt | 79 +++ source/hal/source/components/vsi/include/arm_vsi.h | 121 +++++ .../hal/source/components/vsi/include/video_drv.h | 142 +++++ .../hal/source/components/vsi/source/video_drv.c | 600 +++++++++++++++++++++ source/hal/source/platform/mps3/CMakeLists.txt | 14 +- .../mps3/include/sse-300/peripheral_irqs.h | 10 +- .../mps3/include/sse-310/peripheral_irqs.h | 10 +- source/hal/source/platform/mps3/include/vsi_mps3.h | 115 ++++ .../source/platform/mps3/source/platform_drivers.c | 12 +- source/hal/source/platform/mps3/source/vsi_mps3.c | 62 +++ source/use_case/img_class/usecase.cmake | 12 +- .../object_detection/include/UseCaseHandler.hpp | 30 +- source/use_case/object_detection/src/MainLoop.cc | 14 +- .../object_detection/src/UseCaseHandler.cc | 231 +++++++- source/use_case/object_detection/usecase.cmake | 24 +- source/use_case/vww/usecase.cmake | 12 +- 33 files changed, 3398 insertions(+), 64 deletions(-) create mode 100644 scripts/py/vsi/arm_vsi4.py create mode 100644 scripts/py/vsi/arm_vsi5.py create mode 100644 scripts/py/vsi/arm_vsi6.py create mode 100644 scripts/py/vsi/arm_vsi7.py create mode 100644 scripts/py/vsi/vsi_video.py create mode 100644 scripts/py/vsi/vsi_video_server.py create mode 100644 source/hal/source/components/vsi/CMakeLists.txt create mode 100644 source/hal/source/components/vsi/include/arm_vsi.h create mode 100644 source/hal/source/components/vsi/include/video_drv.h create mode 100644 source/hal/source/components/vsi/source/video_drv.c create mode 100644 source/hal/source/platform/mps3/include/vsi_mps3.h create mode 100644 source/hal/source/platform/mps3/source/vsi_mps3.c diff --git a/.pylintrc b/.pylintrc index b6cb7ee..2f339b1 100644 --- a/.pylintrc +++ b/.pylintrc @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: Copyright 2023 Arm Limited and/or its affiliates +# SPDX-FileCopyrightText: Copyright 2023-2024 Arm Limited and/or its affiliates # SPDX-License-Identifier: Apache-2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -67,7 +67,9 @@ ignore=CVS # ignore-list. The regex matches against paths and can be in Posix or Windows # format. Because '\\' represents the directory delimiter on Windows systems, # it can't be used as an escape character. -ignore-paths= +ignore-paths=^dependencies/.*$, + ^resources_downloaded/.*$, + ^scripts/py/vsi/.*$ # Files or directories matching the regular expression patterns are skipped. # The regex matches against base names, not paths. The default value ignores diff --git a/build_default.py b/build_default.py index 907bf4d..8b7e1ef 100755 --- a/build_default.py +++ b/build_default.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# SPDX-FileCopyrightText: Copyright 2021-2023 Arm Limited and/or its affiliates +# SPDX-FileCopyrightText: Copyright 2021-2024 Arm Limited and/or its affiliates # SPDX-License-Identifier: Apache-2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -186,7 +186,7 @@ def run(args: BuildArgs): - toolchain - download_resources - run_vela_on_models - - np_config_name + - npu_config_name toolchain (str) : Specifies if 'gnu' or 'arm' toolchain needs to be used. download_resources (bool) : Specifies if 'Download resources' step is performed. run_vela_on_models (bool) : Only if `download_resources` is True, specifies if diff --git a/docs/sections/building.md b/docs/sections/building.md index d20e570..dcb6e21 100644 --- a/docs/sections/building.md +++ b/docs/sections/building.md @@ -917,10 +917,12 @@ For example, the generations call for image classification, `source/use_case/img ```c++ # Generate input files -generate_images_code("${${use_case}_FILE_PATH}" - ${SRC_GEN_DIR} - ${INC_GEN_DIR} - "${${use_case}_IMAGE_SIZE}") +generate_images_code( + INPUT_DIR "${${use_case}_FILE_PATH}" + SRC_OUT ${SRC_GEN_DIR} + HDR_OUT ${INC_GEN_DIR} + IMG_SIZE "${${use_case}_IMAGE_SIZE}" + ) # Generate labels file set(${use_case}_LABELS_CPP_FILE Labels) diff --git a/docs/use_cases/object_detection.md b/docs/use_cases/object_detection.md index e946c1b..583a8e5 100644 --- a/docs/use_cases/object_detection.md +++ b/docs/use_cases/object_detection.md @@ -6,6 +6,7 @@ - [Building the code sample application from sources](./object_detection.md#building-the-code-sample-application-from-sources) - [Build options](./object_detection.md#build-options) - [Build process](./object_detection.md#build-process) + - [Build with VSI support](./object_detection.md#build-with-vsi-support) - [Add custom input](./object_detection.md#add-custom-input) - [Add custom model](./object_detection.md#add-custom-model) - [Setting up and running Ethos-U NPU code sample](./object_detection.md#setting-up-and-running-ethos_u-npu-code-sample) @@ -64,6 +65,14 @@ specifies: - `object_detection_ACTIVATION_BUF_SZ`: The intermediate, or activation, buffer size reserved for the NN model. By default, it is set to 2MiB and is enough for most models. +- `VSI_ENABLED`: Build the application with support for the [Virtual Streaming Interface (VSI)](https://arm-software.github.io/AVH/main/simulation/html/group__arm__vsi.html) + available on the Arm® Corstone™-300 and Arm® Corstone™-310 FVPs. + This adds the option to run the application using frames consumed from the host's webcam as input in place of static images. + +- `VSI_IMAGE_INPUT`: When used with the `VSI_ENABLED` flag, the VSI option in the application will consume images from the + filesystem specified by the `object_detection_FILE_PATH` path over VSI instead of consuming frames from the host's webcam. + This can be useful for automated testing. + To build **ONLY** the Object Detection example application, add `-DUSE_CASE_BUILD=object_detection` to the `cmake` command line, as specified in: [Building](../documentation.md#Building). @@ -142,6 +151,31 @@ The `bin` folder contains the following files: - `Images-object_detection.txt`: Tells the FPGA which memory regions to use for loading the binaries in the `sectors/...` folder. +### Build with VSI support + +The Object Detection use case can be compiled to consume input from the +[Virtual Streaming Interface (VSI)](https://arm-software.github.io/AVH/main/simulation/html/group__arm__vsi.html) +available on the Arm® Corstone™-300 and Arm® Corstone™-310 FVPs. + +By default, this consumes frames from the webcam attached to the host which are then used to perform face detection. + +To build the use case with VSI support, supply the additional argument to CMake: + +```commandline +-DVSI_ENABLED=1 +``` + +For testing purposes, it can be useful to specify still images to be consumed over VSI instead of the webcam feed. +To do this, supply the following arguments to CMake: + +```commandline +-DVSI_ENABLED=1 +-DVSI_IMAGE_INPUT=1 +``` + +When `VSI_IMAGE_INPUT` is set, images will be read from the default location. +This can be overriden - see the [Add custom input](./object_detection.md#add-custom-input) section below. + ### Add custom input The application object detection is set up to perform inferences on data found in the folder, or an individual file, @@ -259,6 +293,27 @@ using: ~/FVP_install_location/models/Linux64_GCC-6.4/FVP_Corstone_SSE-300_Ethos-U55 ./bin/mps3-sse-300/ethos-u-object_detection.axf ``` +If the application has been built with VSI support, additional arguments are needed: + +```commandline +~/FVP_install_location/models/Linux64_GCC-9.3/FVP_Corstone_SSE-300_Ethos-U55 \ + -a ./bin/ethos-u-object_detection.axf \ + -C mps3_board.v_path=./scripts/py/vsi +``` + +Note that VSI support is available in 11.22.35 of the FVP or above. Run the following to check the version: + +```log +./FVP_Corstone_SSE-300_Ethos-U65 --version + +Fast Models [11.22.35 (Aug 18 2023)] +Copyright 2000-2023 ARM Limited. +All Rights Reserved. + + +Info: /OSCI/SystemC: Simulation stopped by user. +``` + A log output appears on the terminal: ```log @@ -290,6 +345,23 @@ Choice: ``` +If `VSI_ENABLED` has been set, a sixth option will appear: + +```log +User input required +Enter option number from: + + 1. Run detection on next ifm + 2. Run detection ifm at chosen index + 3. Run detection on all ifm + 4. Show NN model info + 5. List ifm + 6. Run detection using VSI as input + +Choice: + +``` + What the preceding choices do: 1. Run detection on next ifm: Runs a single inference on the next in line image from the collection of the compiled images. @@ -351,6 +423,15 @@ What the preceding choices do: INFO - 3 => pitch_and_roll.bmp ``` +6. Run detection using VSI as input: this will begin to consume frames from the webcam connected to the host. + Preprocessing and inference will be run on each frame, which will then be displayed with a bounding box around any + detected face. The next frame will then be consumed and the process will repeat. + + Alternatively, if `VSI_IMAGE_INPUT` has been passed, the behaviour of this option will be similar to that of + option 1, with inference performed on a set of pre-defined images. + However, these images will be read from the local filesystem over VSI at runtime, + whereas option 1 continues to use images that have been pre-compiled into the application. + ### Running Object Detection Please select the first menu option to execute Object Detection. diff --git a/scripts/cmake/common_user_options.cmake b/scripts/cmake/common_user_options.cmake index 97e9b40..64b9c74 100644 --- a/scripts/cmake/common_user_options.cmake +++ b/scripts/cmake/common_user_options.cmake @@ -1,5 +1,5 @@ #---------------------------------------------------------------------------- -# SPDX-FileCopyrightText: Copyright 2021-2022 Arm Limited and/or its affiliates +# SPDX-FileCopyrightText: Copyright 2021-2022, 2024 Arm Limited and/or its affiliates # SPDX-License-Identifier: Apache-2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -81,6 +81,21 @@ USER_OPTION(USE_SINGLE_INPUT "Select if a use case should execute using a defaul OFF BOOL) +if (TARGET_PLATFORM STREQUAL mps3) + USER_OPTION(VSI_ENABLED + "Select to use VSI for video streaming" + OFF + BOOL) + + if (VSI_ENABLED) + USER_OPTION(VSI_IMAGE_INPUT + "Use images as input to VSI instead of video" + OFF + BOOL + ) + endif () +endif () + if (NOT TARGET_PLATFORM STREQUAL native) USER_OPTION(CMSIS_SRC_PATH diff --git a/scripts/cmake/platforms/mps3/sse-310/mps3-sse-310.sct b/scripts/cmake/platforms/mps3/sse-310/mps3-sse-310.sct index c49f628..691e6ee 100644 --- a/scripts/cmake/platforms/mps3/sse-310/mps3-sse-310.sct +++ b/scripts/cmake/platforms/mps3/sse-310/mps3-sse-310.sct @@ -1,4 +1,4 @@ -; SPDX-FileCopyrightText: Copyright 2021, 2023 Arm Limited and/or its affiliates +; SPDX-FileCopyrightText: Copyright 2021, 2023-2024 Arm Limited and/or its affiliates ; SPDX-License-Identifier: Apache-2.0 ; ; Licensed under the Apache License, Version 2.0 (the "License"); @@ -42,7 +42,7 @@ LOAD_REGION_0 0x11000000 0x00200000 * (InRoot$$Sections) ; Essentially only RO (code + data) - .ANY (+RO) + .ANY (+RO +RO-DATA) } ;----------------------------------------------------- @@ -51,9 +51,6 @@ LOAD_REGION_0 0x11000000 0x00200000 ;----------------------------------------------------- data.bin 0x110A0000 ALIGN 8 0x00060000 { - ; Any RO-DATA - .ANY (+RO-DATA) - ; Any R/W and/or zero initialised data .ANY(+RW +ZI) } diff --git a/scripts/cmake/source_gen_utils.cmake b/scripts/cmake/source_gen_utils.cmake index 6287cb6..6d65049 100644 --- a/scripts/cmake/source_gen_utils.cmake +++ b/scripts/cmake/source_gen_utils.cmake @@ -1,5 +1,5 @@ #---------------------------------------------------------------------------- -# SPDX-FileCopyrightText: Copyright 2021 Arm Limited and/or its affiliates +# SPDX-FileCopyrightText: Copyright 2021, 2024 Arm Limited and/or its affiliates # SPDX-License-Identifier: Apache-2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -20,12 +20,21 @@ set(SCRIPTS_DIR ${CMAKE_CURRENT_SOURCE_DIR}/scripts) # This function generates C++ files for images located in the directory it is # pointed at. NOTE: uses python ############################################################################## -function(generate_images_code input_dir src_out hdr_out img_size) +function(generate_images_code) + set(options GENERATE_FILE_PATHS) + set(oneValueArgs INPUT_DIR SRC_OUT HDR_OUT IMG_SIZE) + cmake_parse_arguments(PARSED "${options}" "${oneValueArgs}" "" ${ARGN}) # Absolute paths for passing into python script - get_filename_component(input_dir_abs ${input_dir} ABSOLUTE) - get_filename_component(src_out_abs ${src_out} ABSOLUTE) - get_filename_component(hdr_out_abs ${hdr_out} ABSOLUTE) + get_filename_component(input_dir_abs ${PARSED_INPUT_DIR} ABSOLUTE) + get_filename_component(src_out_abs ${PARSED_SRC_OUT} ABSOLUTE) + get_filename_component(hdr_out_abs ${PARSED_HDR_OUT} ABSOLUTE) + + if (${PARSED_GENERATE_FILE_PATHS}) + set(GENERATE_FILE_PATHS_ARG "--generate_file_paths") + else () + set(GENERATE_FILE_PATHS_ARG "") + endif () message(STATUS "Generating image files from ${input_dir_abs}") execute_process( @@ -33,7 +42,8 @@ function(generate_images_code input_dir src_out hdr_out img_size) --image_path ${input_dir_abs} --source_folder_path ${src_out_abs} --header_folder_path ${hdr_out_abs} - --image_size ${img_size} ${img_size} + --image_size ${PARSED_IMG_SIZE} ${PARSED_IMG_SIZE} + ${GENERATE_FILE_PATHS_ARG} RESULT_VARIABLE return_code ) if (NOT return_code EQUAL "0") diff --git a/scripts/py/gen_rgb_cpp.py b/scripts/py/gen_rgb_cpp.py index e1c93bb..f1200e6 100644 --- a/scripts/py/gen_rgb_cpp.py +++ b/scripts/py/gen_rgb_cpp.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: Copyright 2021-2023 Arm Limited and/or its affiliates +# SPDX-FileCopyrightText: Copyright 2021-2024 Arm Limited and/or its affiliates # SPDX-License-Identifier: Apache-2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -18,6 +18,7 @@ Utility script to convert a set of RGB images in a given location into corresponding cpp files and a single hpp file referencing the vectors from the cpp files. """ +import argparse import glob import math import typing @@ -59,6 +60,13 @@ parser.add_argument( help="Size (width and height) of the converted images." ) +parser.add_argument( + "--generate_file_paths", + type=bool, + action=argparse.BooleanOptionalAction, + help="Generate an array of file paths to the images as well as the images themselves." +) + parser.add_argument( "--license_template", type=str, @@ -85,11 +93,12 @@ class ImagesParams: image_filenames: typing.List[str] -def write_hpp_file( +def write_metadata_files( images_params: ImagesParams, header_file_path: Path, cc_file_path: Path, header_template_file: str, + source_directory: str = None ): """ Write Images.hpp and Images.cc @@ -98,6 +107,7 @@ def write_hpp_file( @param header_file_path: Images.hpp path @param cc_file_path: Images.cc path @param header_template_file: Header template file name + @param source_directory: Optional source directory of images """ print(f"++ Generating {header_file_path}") hdr = GenUtils.gen_header(env, header_template_file) @@ -109,14 +119,16 @@ def write_hpp_file( .stream(common_template_header=hdr, imgs_count=images_params.num_images, img_size=image_size, - var_names=images_params.image_array_names) \ + var_names=images_params.image_array_names, + source_directory=source_directory) \ .dump(str(header_file_path)) env \ .get_template('Images.cc.template') \ .stream(common_template_header=hdr, var_names=images_params.image_array_names, - img_names=images_params.image_filenames) \ + img_names=images_params.image_filenames, + source_directory=source_directory) \ .dump(str(cc_file_path)) @@ -196,9 +208,13 @@ def main(args): image_filenames = [] image_array_names = [] - if Path(args.image_path).is_dir(): + image_path = Path(args.image_path) + + if image_path.is_dir(): + image_directory = image_path filepaths = sorted(glob.glob(str(Path(args.image_path) / '**/*.*'), recursive=True)) - elif Path(args.image_path).is_file(): + elif image_path.is_file(): + image_directory = image_path.parent filepaths = [args.image_path] else: raise OSError("Directory or file does not exist.") @@ -228,13 +244,16 @@ def main(args): # Increment image index image_idx = image_idx + 1 - header_filepath = Path(args.header_folder_path) / "InputFiles.hpp" - common_cc_filepath = Path(args.source_folder_path) / "InputFiles.cc" - - images_params = ImagesParams(image_idx, args.image_size, image_array_names, image_filenames) - if len(image_filenames) > 0: - write_hpp_file(images_params, header_filepath, common_cc_filepath, args.license_template) + images_params = ImagesParams(image_idx, args.image_size, image_array_names, image_filenames) + + write_metadata_files( + images_params, + header_file_path=Path(args.header_folder_path) / "InputFiles.hpp", + cc_file_path=Path(args.source_folder_path) / "InputFiles.cc", + header_template_file=args.license_template, + source_directory=image_directory if args.generate_file_paths else None + ) else: raise FileNotFoundError("No valid images found.") diff --git a/scripts/py/templates/Images.cc.template b/scripts/py/templates/Images.cc.template index 2620ab4..c5b051a 100644 --- a/scripts/py/templates/Images.cc.template +++ b/scripts/py/templates/Images.cc.template @@ -1,5 +1,5 @@ {# - SPDX-FileCopyrightText: Copyright 2021 Arm Limited and/or its affiliates + SPDX-FileCopyrightText: Copyright 2021, 2024 Arm Limited and/or its affiliates SPDX-License-Identifier: Apache-2.0 Licensed under the Apache License, Version 2.0 (the "License"); @@ -24,6 +24,14 @@ static const char* imgFilenames[] = { {% endfor %} }; +{% if source_directory %} +static const char* imgFilePaths[] = { +{% for name in img_names %} + "{{source_directory}}/{{name}}", +{% endfor %} +}; +{% endif %} + static const uint8_t* imgArrays[] = { {{ var_names|join(',\n ') }} }; @@ -36,6 +44,16 @@ const char* GetFilename(const uint32_t idx) return nullptr; } +{% if source_directory %} +const char* GetFilePath(const uint32_t idx) +{ + if (idx < NUMBER_OF_FILES) { + return imgFilePaths[idx]; + } + return nullptr; +} +{% endif %} + const uint8_t* GetImgArray(const uint32_t idx) { if (idx < NUMBER_OF_FILES) { diff --git a/scripts/py/templates/Images.hpp.template b/scripts/py/templates/Images.hpp.template index d39fc49..1f0a70e 100644 --- a/scripts/py/templates/Images.hpp.template +++ b/scripts/py/templates/Images.hpp.template @@ -1,5 +1,5 @@ {# - SPDX-FileCopyrightText: Copyright 2021 Arm Limited and/or its affiliates + SPDX-FileCopyrightText: Copyright 2021, 2024 Arm Limited and/or its affiliates SPDX-License-Identifier: Apache-2.0 Licensed under the Apache License, Version 2.0 (the "License"); @@ -35,6 +35,15 @@ extern const uint8_t {{var_name}}[IMAGE_DATA_SIZE]; **/ const char* GetFilename(const uint32_t idx); +{% if source_directory %} +/** + * @brief Gets the file path for the image on the local filesystem + * @param[in] idx Index of the input. + * @return const C string pointer to the file path. + **/ +const char* GetFilePath(const uint32_t idx); +{% endif %} + /** * @brief Gets the pointer to image data. * @param[in] idx Index of the input. diff --git a/scripts/py/vsi/arm_vsi4.py b/scripts/py/vsi/arm_vsi4.py new file mode 100644 index 0000000..c903ab2 --- /dev/null +++ b/scripts/py/vsi/arm_vsi4.py @@ -0,0 +1,207 @@ +#!/usr/bin/env python3 +# SPDX-FileCopyrightText: Copyright 2024 Arm Limited and/or its affiliates +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import logging +import vsi_video + +## Set verbosity level +#verbosity = logging.DEBUG +verbosity = logging.ERROR + +# [debugging] Verbosity settings +level = { 10: "DEBUG", 20: "INFO", 30: "WARNING", 40: "ERROR" } +logging.basicConfig(format='Py: VSI4: [%(levelname)s]\t%(message)s', level = verbosity) +logging.info("Verbosity level is set to " + level[verbosity]) + + +# Video Server configuration +server_address = ('127.0.0.1', 6000) +server_authkey = 'vsi_video' + + +# IRQ registers +IRQ_Status = 0 + +# Timer registers +Timer_Control = 0 +Timer_Interval = 0 + +# Timer Control register definitions +Timer_Control_Run_Msk = 1<<0 +Timer_Control_Periodic_Msk = 1<<1 +Timer_Control_Trig_IRQ_Msk = 1<<2 +Timer_Control_Trig_DMA_Msk = 1<<3 + +# DMA registers +DMA_Control = 0 + +# DMA Control register definitions +DMA_Control_Enable_Msk = 1<<0 +DMA_Control_Direction_Msk = 1<<1 +DMA_Control_Direction_P2M = 0<<1 +DMA_Control_Direction_M2P = 1<<1 + +# User registers +Regs = [0] * 64 + +# Data buffer +Data = bytearray() + + +## Initialize +# @return None +def init(): + logging.info("Python function init() called") + vsi_video.init(server_address, server_authkey) + + +## Read interrupt request (the VSI IRQ Status Register) +# @return value value read (32-bit) +def rdIRQ(): + global IRQ_Status + logging.info("Python function rdIRQ() called") + + value = IRQ_Status + logging.debug("Read interrupt request: {}".format(value)) + + return value + + +## Write interrupt request (the VSI IRQ Status Register) +# @param value value to write (32-bit) +# @return value value written (32-bit) +def wrIRQ(value): + global IRQ_Status + logging.info("Python function wrIRQ() called") + + value = vsi_video.wrIRQ(IRQ_Status, value) + IRQ_Status = value + logging.debug("Write interrupt request: {}".format(value)) + + return value + + +## Write Timer registers (the VSI Timer Registers) +# @param index Timer register index (zero based) +# @param value value to write (32-bit) +# @return value value written (32-bit) +def wrTimer(index, value): + global Timer_Control, Timer_Interval + logging.info("Python function wrTimer() called") + + if index == 0: + Timer_Control = value + logging.debug("Write Timer_Control: {}".format(value)) + elif index == 1: + Timer_Interval = value + logging.debug("Write Timer_Interval: {}".format(value)) + + return value + + +## Timer event (called at Timer Overflow) +# @return None +def timerEvent(): + global IRQ_Status + + logging.info("Python function timerEvent() called") + + IRQ_Status = vsi_video.timerEvent(IRQ_Status) + + +## Write DMA registers (the VSI DMA Registers) +# @param index DMA register index (zero based) +# @param value value to write (32-bit) +# @return value value written (32-bit) +def wrDMA(index, value): + global DMA_Control + logging.info("Python function wrDMA() called") + + if index == 0: + DMA_Control = value + logging.debug("Write DMA_Control: {}".format(value)) + + return value + + +## Read data from peripheral for DMA P2M transfer (VSI DMA) +# @param size size of data to read (in bytes, multiple of 4) +# @return data data read (bytearray) +def rdDataDMA(size): + global Data + logging.info("Python function rdDataDMA() called") + + Data = vsi_video.rdDataDMA(size) + + n = min(len(Data), size) + data = bytearray(size) + data[0:n] = Data[0:n] + logging.debug("Read data ({} bytes)".format(size)) + + return data + + +## Write data to peripheral for DMA M2P transfer (VSI DMA) +# @param data data to write (bytearray) +# @param size size of data to write (in bytes, multiple of 4) +# @return None +def wrDataDMA(data, size): + global Data + logging.info("Python function wrDataDMA() called") + + Data = data + logging.debug("Write data ({} bytes)".format(size)) + + vsi_video.wrDataDMA(data, size) + + return + + +## Read user registers (the VSI User Registers) +# @param index user register index (zero based) +# @return value value read (32-bit) +def rdRegs(index): + global Regs + logging.info("Python function rdRegs() called") + + if index <= vsi_video.REG_IDX_MAX: + Regs[index] = vsi_video.rdRegs(index) + + value = Regs[index] + logging.debug("Read user register at index {}: {}".format(index, value)) + + return value + + +## Write user registers (the VSI User Registers) +# @param index user register index (zero based) +# @param value value to write (32-bit) +# @return value value written (32-bit) +def wrRegs(index, value): + global Regs + logging.info("Python function wrRegs() called") + + if index <= vsi_video.REG_IDX_MAX: + value = vsi_video.wrRegs(index, value) + + Regs[index] = value + logging.debug("Write user register at index {}: {}".format(index, value)) + + return value + + +## @} + diff --git a/scripts/py/vsi/arm_vsi5.py b/scripts/py/vsi/arm_vsi5.py new file mode 100644 index 0000000..8056096 --- /dev/null +++ b/scripts/py/vsi/arm_vsi5.py @@ -0,0 +1,207 @@ +#!/usr/bin/env python3 +# SPDX-FileCopyrightText: Copyright 2024 Arm Limited and/or its affiliates +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import logging +import vsi_video + +## Set verbosity level +#verbosity = logging.DEBUG +verbosity = logging.ERROR + +# [debugging] Verbosity settings +level = { 10: "DEBUG", 20: "INFO", 30: "WARNING", 40: "ERROR" } +logging.basicConfig(format='Py: VSI5: [%(levelname)s]\t%(message)s', level = verbosity) +logging.info("Verbosity level is set to " + level[verbosity]) + + +# Video Server configuration +server_address = ('127.0.0.1', 6001) +server_authkey = 'vsi_video' + + +# IRQ registers +IRQ_Status = 0 + +# Timer registers +Timer_Control = 0 +Timer_Interval = 0 + +# Timer Control register definitions +Timer_Control_Run_Msk = 1<<0 +Timer_Control_Periodic_Msk = 1<<1 +Timer_Control_Trig_IRQ_Msk = 1<<2 +Timer_Control_Trig_DMA_Msk = 1<<3 + +# DMA registers +DMA_Control = 0 + +# DMA Control register definitions +DMA_Control_Enable_Msk = 1<<0 +DMA_Control_Direction_Msk = 1<<1 +DMA_Control_Direction_P2M = 0<<1 +DMA_Control_Direction_M2P = 1<<1 + +# User registers +Regs = [0] * 64 + +# Data buffer +Data = bytearray() + + +## Initialize +# @return None +def init(): + logging.info("Python function init() called") + vsi_video.init(server_address, server_authkey) + + +## Read interrupt request (the VSI IRQ Status Register) +# @return value value read (32-bit) +def rdIRQ(): + global IRQ_Status + logging.info("Python function rdIRQ() called") + + value = IRQ_Status + logging.debug("Read interrupt request: {}".format(value)) + + return value + + +## Write interrupt request (the VSI IRQ Status Register) +# @param value value to write (32-bit) +# @return value value written (32-bit) +def wrIRQ(value): + global IRQ_Status + logging.info("Python function wrIRQ() called") + + value = vsi_video.wrIRQ(IRQ_Status, value) + IRQ_Status = value + logging.debug("Write interrupt request: {}".format(value)) + + return value + + +## Write Timer registers (the VSI Timer Registers) +# @param index Timer register index (zero based) +# @param value value to write (32-bit) +# @return value value written (32-bit) +def wrTimer(index, value): + global Timer_Control, Timer_Interval + logging.info("Python function wrTimer() called") + + if index == 0: + Timer_Control = value + logging.debug("Write Timer_Control: {}".format(value)) + elif index == 1: + Timer_Interval = value + logging.debug("Write Timer_Interval: {}".format(value)) + + return value + + +## Timer event (called at Timer Overflow) +# @return None +def timerEvent(): + global IRQ_Status + + logging.info("Python function timerEvent() called") + + IRQ_Status = vsi_video.timerEvent(IRQ_Status) + + +## Write DMA registers (the VSI DMA Registers) +# @param index DMA register index (zero based) +# @param value value to write (32-bit) +# @return value value written (32-bit) +def wrDMA(index, value): + global DMA_Control + logging.info("Python function wrDMA() called") + + if index == 0: + DMA_Control = value + logging.debug("Write DMA_Control: {}".format(value)) + + return value + + +## Read data from peripheral for DMA P2M transfer (VSI DMA) +# @param size size of data to read (in bytes, multiple of 4) +# @return data data read (bytearray) +def rdDataDMA(size): + global Data + logging.info("Python function rdDataDMA() called") + + Data = vsi_video.rdDataDMA(size) + + n = min(len(Data), size) + data = bytearray(size) + data[0:n] = Data[0:n] + logging.debug("Read data ({} bytes)".format(size)) + + return data + + +## Write data to peripheral for DMA M2P transfer (VSI DMA) +# @param data data to write (bytearray) +# @param size size of data to write (in bytes, multiple of 4) +# @return None +def wrDataDMA(data, size): + global Data + logging.info("Python function wrDataDMA() called") + + Data = data + logging.debug("Write data ({} bytes)".format(size)) + + vsi_video.wrDataDMA(data, size) + + return + + +## Read user registers (the VSI User Registers) +# @param index user register index (zero based) +# @return value value read (32-bit) +def rdRegs(index): + global Regs + logging.info("Python function rdRegs() called") + + if index <= vsi_video.REG_IDX_MAX: + Regs[index] = vsi_video.rdRegs(index) + + value = Regs[index] + logging.debug("Read user register at index {}: {}".format(index, value)) + + return value + + +## Write user registers (the VSI User Registers) +# @param index user register index (zero based) +# @param value value to write (32-bit) +# @return value value written (32-bit) +def wrRegs(index, value): + global Regs + logging.info("Python function wrRegs() called") + + if index <= vsi_video.REG_IDX_MAX: + value = vsi_video.wrRegs(index, value) + + Regs[index] = value + logging.debug("Write user register at index {}: {}".format(index, value)) + + return value + + +## @} + diff --git a/scripts/py/vsi/arm_vsi6.py b/scripts/py/vsi/arm_vsi6.py new file mode 100644 index 0000000..3d71562 --- /dev/null +++ b/scripts/py/vsi/arm_vsi6.py @@ -0,0 +1,207 @@ +#!/usr/bin/env python3 +# SPDX-FileCopyrightText: Copyright 2024 Arm Limited and/or its affiliates +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import logging +import vsi_video + +## Set verbosity level +#verbosity = logging.DEBUG +verbosity = logging.ERROR + +# [debugging] Verbosity settings +level = { 10: "DEBUG", 20: "INFO", 30: "WARNING", 40: "ERROR" } +logging.basicConfig(format='Py: VSI6: [%(levelname)s]\t%(message)s', level = verbosity) +logging.info("Verbosity level is set to " + level[verbosity]) + + +# Video Server configuration +server_address = ('127.0.0.1', 6002) +server_authkey = 'vsi_video' + + +# IRQ registers +IRQ_Status = 0 + +# Timer registers +Timer_Control = 0 +Timer_Interval = 0 + +# Timer Control register definitions +Timer_Control_Run_Msk = 1<<0 +Timer_Control_Periodic_Msk = 1<<1 +Timer_Control_Trig_IRQ_Msk = 1<<2 +Timer_Control_Trig_DMA_Msk = 1<<3 + +# DMA registers +DMA_Control = 0 + +# DMA Control register definitions +DMA_Control_Enable_Msk = 1<<0 +DMA_Control_Direction_Msk = 1<<1 +DMA_Control_Direction_P2M = 0<<1 +DMA_Control_Direction_M2P = 1<<1 + +# User registers +Regs = [0] * 64 + +# Data buffer +Data = bytearray() + + +## Initialize +# @return None +def init(): + logging.info("Python function init() called") + vsi_video.init(server_address, server_authkey) + + +## Read interrupt request (the VSI IRQ Status Register) +# @return value value read (32-bit) +def rdIRQ(): + global IRQ_Status + logging.info("Python function rdIRQ() called") + + value = IRQ_Status + logging.debug("Read interrupt request: {}".format(value)) + + return value + + +## Write interrupt request (the VSI IRQ Status Register) +# @param value value to write (32-bit) +# @return value value written (32-bit) +def wrIRQ(value): + global IRQ_Status + logging.info("Python function wrIRQ() called") + + value = vsi_video.wrIRQ(IRQ_Status, value) + IRQ_Status = value + logging.debug("Write interrupt request: {}".format(value)) + + return value + + +## Write Timer registers (the VSI Timer Registers) +# @param index Timer register index (zero based) +# @param value value to write (32-bit) +# @return value value written (32-bit) +def wrTimer(index, value): + global Timer_Control, Timer_Interval + logging.info("Python function wrTimer() called") + + if index == 0: + Timer_Control = value + logging.debug("Write Timer_Control: {}".format(value)) + elif index == 1: + Timer_Interval = value + logging.debug("Write Timer_Interval: {}".format(value)) + + return value + + +## Timer event (called at Timer Overflow) +# @return None +def timerEvent(): + global IRQ_Status + + logging.info("Python function timerEvent() called") + + IRQ_Status = vsi_video.timerEvent(IRQ_Status) + + +## Write DMA registers (the VSI DMA Registers) +# @param index DMA register index (zero based) +# @param value value to write (32-bit) +# @return value value written (32-bit) +def wrDMA(index, value): + global DMA_Control + logging.info("Python function wrDMA() called") + + if index == 0: + DMA_Control = value + logging.debug("Write DMA_Control: {}".format(value)) + + return value + + +## Read data from peripheral for DMA P2M transfer (VSI DMA) +# @param size size of data to read (in bytes, multiple of 4) +# @return data data read (bytearray) +def rdDataDMA(size): + global Data + logging.info("Python function rdDataDMA() called") + + Data = vsi_video.rdDataDMA(size) + + n = min(len(Data), size) + data = bytearray(size) + data[0:n] = Data[0:n] + logging.debug("Read data ({} bytes)".format(size)) + + return data + + +## Write data to peripheral for DMA M2P transfer (VSI DMA) +# @param data data to write (bytearray) +# @param size size of data to write (in bytes, multiple of 4) +# @return None +def wrDataDMA(data, size): + global Data + logging.info("Python function wrDataDMA() called") + + Data = data + logging.debug("Write data ({} bytes)".format(size)) + + vsi_video.wrDataDMA(data, size) + + return + + +## Read user registers (the VSI User Registers) +# @param index user register index (zero based) +# @return value value read (32-bit) +def rdRegs(index): + global Regs + logging.info("Python function rdRegs() called") + + if index <= vsi_video.REG_IDX_MAX: + Regs[index] = vsi_video.rdRegs(index) + + value = Regs[index] + logging.debug("Read user register at index {}: {}".format(index, value)) + + return value + + +## Write user registers (the VSI User Registers) +# @param index user register index (zero based) +# @param value value to write (32-bit) +# @return value value written (32-bit) +def wrRegs(index, value): + global Regs + logging.info("Python function wrRegs() called") + + if index <= vsi_video.REG_IDX_MAX: + value = vsi_video.wrRegs(index, value) + + Regs[index] = value + logging.debug("Write user register at index {}: {}".format(index, value)) + + return value + + +## @} + diff --git a/scripts/py/vsi/arm_vsi7.py b/scripts/py/vsi/arm_vsi7.py new file mode 100644 index 0000000..892433c --- /dev/null +++ b/scripts/py/vsi/arm_vsi7.py @@ -0,0 +1,207 @@ +#!/usr/bin/env python3 +# SPDX-FileCopyrightText: Copyright 2024 Arm Limited and/or its affiliates +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import logging +import vsi_video + +## Set verbosity level +#verbosity = logging.DEBUG +verbosity = logging.ERROR + +# [debugging] Verbosity settings +level = { 10: "DEBUG", 20: "INFO", 30: "WARNING", 40: "ERROR" } +logging.basicConfig(format='Py: VSI7: [%(levelname)s]\t%(message)s', level = verbosity) +logging.info("Verbosity level is set to " + level[verbosity]) + + +# Video Server configuration +server_address = ('127.0.0.1', 6003) +server_authkey = 'vsi_video' + + +# IRQ registers +IRQ_Status = 0 + +# Timer registers +Timer_Control = 0 +Timer_Interval = 0 + +# Timer Control register definitions +Timer_Control_Run_Msk = 1<<0 +Timer_Control_Periodic_Msk = 1<<1 +Timer_Control_Trig_IRQ_Msk = 1<<2 +Timer_Control_Trig_DMA_Msk = 1<<3 + +# DMA registers +DMA_Control = 0 + +# DMA Control register definitions +DMA_Control_Enable_Msk = 1<<0 +DMA_Control_Direction_Msk = 1<<1 +DMA_Control_Direction_P2M = 0<<1 +DMA_Control_Direction_M2P = 1<<1 + +# User registers +Regs = [0] * 64 + +# Data buffer +Data = bytearray() + + +## Initialize +# @return None +def init(): + logging.info("Python function init() called") + vsi_video.init(server_address, server_authkey) + + +## Read interrupt request (the VSI IRQ Status Register) +# @return value value read (32-bit) +def rdIRQ(): + global IRQ_Status + logging.info("Python function rdIRQ() called") + + value = IRQ_Status + logging.debug("Read interrupt request: {}".format(value)) + + return value + + +## Write interrupt request (the VSI IRQ Status Register) +# @param value value to write (32-bit) +# @return value value written (32-bit) +def wrIRQ(value): + global IRQ_Status + logging.info("Python function wrIRQ() called") + + value = vsi_video.wrIRQ(IRQ_Status, value) + IRQ_Status = value + logging.debug("Write interrupt request: {}".format(value)) + + return value + + +## Write Timer registers (the VSI Timer Registers) +# @param index Timer register index (zero based) +# @param value value to write (32-bit) +# @return value value written (32-bit) +def wrTimer(index, value): + global Timer_Control, Timer_Interval + logging.info("Python function wrTimer() called") + + if index == 0: + Timer_Control = value + logging.debug("Write Timer_Control: {}".format(value)) + elif index == 1: + Timer_Interval = value + logging.debug("Write Timer_Interval: {}".format(value)) + + return value + + +## Timer event (called at Timer Overflow) +# @return None +def timerEvent(): + global IRQ_Status + + logging.info("Python function timerEvent() called") + + IRQ_Status = vsi_video.timerEvent(IRQ_Status) + + +## Write DMA registers (the VSI DMA Registers) +# @param index DMA register index (zero based) +# @param value value to write (32-bit) +# @return value value written (32-bit) +def wrDMA(index, value): + global DMA_Control + logging.info("Python function wrDMA() called") + + if index == 0: + DMA_Control = value + logging.debug("Write DMA_Control: {}".format(value)) + + return value + + +## Read data from peripheral for DMA P2M transfer (VSI DMA) +# @param size size of data to read (in bytes, multiple of 4) +# @return data data read (bytearray) +def rdDataDMA(size): + global Data + logging.info("Python function rdDataDMA() called") + + Data = vsi_video.rdDataDMA(size) + + n = min(len(Data), size) + data = bytearray(size) + data[0:n] = Data[0:n] + logging.debug("Read data ({} bytes)".format(size)) + + return data + + +## Write data to peripheral for DMA M2P transfer (VSI DMA) +# @param data data to write (bytearray) +# @param size size of data to write (in bytes, multiple of 4) +# @return None +def wrDataDMA(data, size): + global Data + logging.info("Python function wrDataDMA() called") + + Data = data + logging.debug("Write data ({} bytes)".format(size)) + + vsi_video.wrDataDMA(data, size) + + return + + +## Read user registers (the VSI User Registers) +# @param index user register index (zero based) +# @return value value read (32-bit) +def rdRegs(index): + global Regs + logging.info("Python function rdRegs() called") + + if index <= vsi_video.REG_IDX_MAX: + Regs[index] = vsi_video.rdRegs(index) + + value = Regs[index] + logging.debug("Read user register at index {}: {}".format(index, value)) + + return value + + +## Write user registers (the VSI User Registers) +# @param index user register index (zero based) +# @param value value to write (32-bit) +# @return value value written (32-bit) +def wrRegs(index, value): + global Regs + logging.info("Python function wrRegs() called") + + if index <= vsi_video.REG_IDX_MAX: + value = vsi_video.wrRegs(index, value) + + Regs[index] = value + logging.debug("Write user register at index {}: {}".format(index, value)) + + return value + + +## @} + diff --git a/scripts/py/vsi/vsi_video.py b/scripts/py/vsi/vsi_video.py new file mode 100644 index 0000000..88f44fb --- /dev/null +++ b/scripts/py/vsi/vsi_video.py @@ -0,0 +1,461 @@ +#!/usr/bin/env python3 +# SPDX-FileCopyrightText: Copyright 2024 Arm Limited and/or its affiliates +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import time +import atexit +import logging +import subprocess +from multiprocessing.connection import Client, Connection +from os import path, getcwd +from os import name as os_name + + +class VideoClient: + def __init__(self): + # Server commands + self.SET_FILENAME = 1 + self.STREAM_CONFIGURE = 2 + self.STREAM_ENABLE = 3 + self.STREAM_DISABLE = 4 + self.FRAME_READ = 5 + self.FRAME_WRITE = 6 + self.CLOSE_SERVER = 7 + # Color space + self.GRAYSCALE8 = 1 + self.RGB888 = 2 + self.BGR565 = 3 + self.YUV420 = 4 + self.NV12 = 5 + self.NV21 = 6 + # Variables + self.conn = None + + def connectToServer(self, address, authkey): + for _ in range(50): + try: + self.conn = Client(address, authkey=authkey.encode('utf-8')) + if isinstance(self.conn, Connection): + break + else: + self.conn = None + except Exception: + self.conn = None + time.sleep(0.01) + + def setFilename(self, filename, mode): + self.conn.send([self.SET_FILENAME, getcwd(), filename, mode]) + filename_valid = self.conn.recv() + + return filename_valid + + def configureStream(self, frame_width, frame_height, color_format, frame_rate): + self.conn.send([self.STREAM_CONFIGURE, frame_width, frame_height, color_format, frame_rate]) + configuration_valid = self.conn.recv() + + return configuration_valid + + def enableStream(self, mode): + self.conn.send([self.STREAM_ENABLE, mode]) + stream_active = self.conn.recv() + + return stream_active + + def disableStream(self): + self.conn.send([self.STREAM_DISABLE]) + stream_active = self.conn.recv() + + return stream_active + + def readFrame(self): + self.conn.send([self.FRAME_READ]) + data = self.conn.recv_bytes() + eos = self.conn.recv() + + return data, eos + + def writeFrame(self, data): + self.conn.send([self.FRAME_WRITE]) + self.conn.send_bytes(data) + + def closeServer(self): + try: + if isinstance(self.conn, Connection): + self.conn.send([self.CLOSE_SERVER]) + self.conn.close() + except Exception as e: + logging.error(f'Exception occurred on cleanup: {e}') + + +# User registers +REG_IDX_MAX = 12 # Maximum user register index used in VSI +MODE = 0 # Regs[0] // Mode: 0=Input, 1=Output +CONTROL = 0 # Regs[1] // Control: enable, flush +STATUS = 0 # Regs[2] // Status: active, buf_empty, buf_full, overflow, underflow, eos +FILENAME_LEN = 0 # Regs[3] // Filename length +FILENAME_CHAR = 0 # Regs[4] // Filename character +FILENAME_VALID = 0 # Regs[5] // Filename valid flag +FRAME_WIDTH = 300 # Regs[6] // Requested frame width +FRAME_HEIGHT = 300 # Regs[7] // Requested frame height +COLOR_FORMAT = 0 # Regs[8] // Color format +FRAME_RATE = 0 # Regs[9] // Frame rate +FRAME_INDEX = 0 # Regs[10] // Frame index +FRAME_COUNT = 0 # Regs[11] // Frame count +FRAME_COUNT_MAX = 0 # Regs[12] // Frame count maximum + +# MODE register definitions +MODE_IO_Msk = 1<<0 +MODE_Input = 0<<0 +MODE_Output = 1<<0 + +# CONTROL register definitions +CONTROL_ENABLE_Msk = 1<<0 +CONTROL_CONTINUOS_Msk = 1<<1 +CONTROL_BUF_FLUSH_Msk = 1<<2 + +# STATUS register definitions +STATUS_ACTIVE_Msk = 1<<0 +STATUS_BUF_EMPTY_Msk = 1<<1 +STATUS_BUF_FULL_Msk = 1<<2 +STATUS_OVERFLOW_Msk = 1<<3 +STATUS_UNDERFLOW_Msk = 1<<4 +STATUS_EOS_Msk = 1<<5 + +# IRQ Status register definitions +IRQ_Status_FRAME_Msk = 1<<0 +IRQ_Status_OVERFLOW_Msk = 1<<1 +IRQ_Status_UNDERFLOW_Msk = 1<<2 +IRQ_Status_EOS_Msk = 1<<3 + +# Variables +Video = VideoClient() +Filename = "" +FilenameIdx = 0 + + +# Close VSI Video Server on exit +def cleanup(): + Video.closeServer() + + +# Client connection to VSI Video Server +def init(address, authkey): + global FILENAME_VALID + + base_dir = path.dirname(__file__) + server_path = path.join(base_dir, 'vsi_video_server.py') + + logging.info("Start video server") + if path.isfile(server_path): + # Start Video Server + if os_name == 'nt': + py_cmd = 'python' + else: + py_cmd = 'python3' + cmd = f"{py_cmd} {server_path} " \ + f"--ip {address[0]} " \ + f"--port {address[1]} " \ + f"--authkey {authkey}" + subprocess.Popen(cmd, shell=True) + # Connect to Video Server + Video.connectToServer(address, authkey) + if Video.conn == None: + logging.error("Server not connected") + + else: + logging.error(f"Server script not found: {server_path}") + + # Register clean-up function + atexit.register(cleanup) + + +## Flush Stream buffer +def flushBuffer(): + global STATUS, FRAME_INDEX, FRAME_COUNT + + STATUS |= STATUS_BUF_EMPTY_Msk + STATUS &= ~STATUS_BUF_FULL_Msk + + FRAME_INDEX = 0 + FRAME_COUNT = 0 + + +## VSI IRQ Status register +# @param IRQ_Status IRQ status register to update +# @param value status bits to clear +# @return IRQ_Status return updated register +def wrIRQ(IRQ_Status, value): + IRQ_Status_Clear = IRQ_Status & ~value + IRQ_Status &= ~IRQ_Status_Clear + + return IRQ_Status + + +## Timer Event +# @param IRQ_Status IRQ status register to update +# @return IRQ_Status return updated register +def timerEvent(IRQ_Status): + + IRQ_Status |= IRQ_Status_FRAME_Msk + + if (STATUS & STATUS_OVERFLOW_Msk) != 0: + IRQ_Status |= IRQ_Status_OVERFLOW_Msk + + if (STATUS & STATUS_UNDERFLOW_Msk) != 0: + IRQ_Status |= IRQ_Status_UNDERFLOW_Msk + + if (STATUS & STATUS_EOS_Msk) != 0: + IRQ_Status |= IRQ_Status_EOS_Msk + + if (CONTROL & CONTROL_CONTINUOS_Msk) == 0: + wrCONTROL(CONTROL & ~(CONTROL_ENABLE_Msk | CONTROL_CONTINUOS_Msk)) + + return IRQ_Status + + +## Read data from peripheral for DMA P2M transfer (VSI DMA) +# @param size size of data to read (in bytes, multiple of 4) +# @return data data read (bytearray) +def rdDataDMA(size): + global STATUS, FRAME_COUNT + + if (STATUS & STATUS_ACTIVE_Msk) != 0: + + if Video.conn != None: + data, eos = Video.readFrame() + if eos: + STATUS |= STATUS_EOS_Msk + if FRAME_COUNT < FRAME_COUNT_MAX: + FRAME_COUNT += 1 + else: + STATUS |= STATUS_OVERFLOW_Msk + if FRAME_COUNT == FRAME_COUNT_MAX: + STATUS |= STATUS_BUF_FULL_Msk + STATUS &= ~STATUS_BUF_EMPTY_Msk + else: + data = bytearray() + + else: + data = bytearray() + + return data + + +## Write data to peripheral for DMA M2P transfer (VSI DMA) +# @param data data to write (bytearray) +# @param size size of data to write (in bytes, multiple of 4) +def wrDataDMA(data, size): + global STATUS, FRAME_COUNT + + if (STATUS & STATUS_ACTIVE_Msk) != 0: + + if Video.conn != None: + Video.writeFrame(data) + if FRAME_COUNT > 0: + FRAME_COUNT -= 1 + else: + STATUS |= STATUS_UNDERFLOW_Msk + if FRAME_COUNT == 0: + STATUS |= STATUS_BUF_EMPTY_Msk + STATUS &= ~STATUS_BUF_FULL_Msk + + +## Write CONTROL register (user register) +# @param value value to write (32-bit) +def wrCONTROL(value): + global CONTROL, STATUS + + if ((value ^ CONTROL) & CONTROL_ENABLE_Msk) != 0: + STATUS &= ~STATUS_ACTIVE_Msk + if (value & CONTROL_ENABLE_Msk) != 0: + logging.info("Start video stream") + if Video.conn != None: + logging.info("Configure video stream") + configuration_valid = Video.configureStream(FRAME_WIDTH, FRAME_HEIGHT, COLOR_FORMAT, FRAME_RATE) + if configuration_valid: + logging.info("Enable video stream") + server_active = Video.enableStream(MODE) + if server_active: + STATUS |= STATUS_ACTIVE_Msk + STATUS &= ~(STATUS_OVERFLOW_Msk | STATUS_UNDERFLOW_Msk | STATUS_EOS_Msk) + else: + logging.error("Enable video stream failed") + else: + logging.error("Configure video stream failed") + else: + logging.error("Server not connected") + else: + logging.info("Stop video stream") + if Video.conn != None: + logging.info("Disable video stream") + Video.disableStream() + else: + logging.error("Server not connected") + + if (value & CONTROL_BUF_FLUSH_Msk) != 0: + value &= ~CONTROL_BUF_FLUSH_Msk + flushBuffer() + + CONTROL = value + + +## Read STATUS register (user register) +# @return status current STATUS User register (32-bit) +def rdSTATUS(): + global STATUS + + status = STATUS + STATUS &= ~(STATUS_OVERFLOW_Msk | STATUS_UNDERFLOW_Msk | STATUS_EOS_Msk) + + return status + + +## Write FILENAME_LEN register (user register) +# @param value value to write (32-bit) +def wrFILENAME_LEN(value): + global STATUS, FILENAME_LEN, FILENAME_VALID, Filename, FilenameIdx + + logging.info("Set new source name length and reset filename and valid flag") + FilenameIdx = 0 + Filename = "" + FILENAME_VALID = 0 + FILENAME_LEN = value + + +## Write FILENAME_CHAR register (user register) +# @param value value to write (32-bit) +def wrFILENAME_CHAR(value): + global FILENAME_VALID, Filename, FilenameIdx + + if FilenameIdx < FILENAME_LEN: + logging.info(f"Append {value} to filename") + Filename += f"{value}" + FilenameIdx += 1 + logging.debug(f"Received {FilenameIdx} of {FILENAME_LEN} characters") + + if FilenameIdx == FILENAME_LEN: + logging.info("Check if file exists on Server side and set VALID flag") + logging.debug(f"Filename: {Filename}") + + if Video.conn != None: + FILENAME_VALID = Video.setFilename(Filename, MODE) + else: + logging.error("Server not connected") + + logging.debug(f"Filename VALID: {FILENAME_VALID}") + + +## Write FRAME_INDEX register (user register) +# @param value value to write (32-bit) +# @return value value written (32-bit) +def wrFRAME_INDEX(value): + global STATUS, FRAME_INDEX, FRAME_COUNT + + FRAME_INDEX += 1 + if FRAME_INDEX == FRAME_COUNT_MAX: + FRAME_INDEX = 0 + + if (MODE & MODE_IO_Msk) == MODE_Input: + # Input + if FRAME_COUNT > 0: + FRAME_COUNT -= 1 + if FRAME_COUNT == 0: + STATUS |= STATUS_BUF_EMPTY_Msk + STATUS &= ~STATUS_BUF_FULL_Msk + else: + # Output + if FRAME_COUNT < FRAME_COUNT_MAX: + FRAME_COUNT += 1 + if FRAME_COUNT == FRAME_COUNT_MAX: + STATUS |= STATUS_BUF_FULL_Msk + STATUS &= ~STATUS_BUF_EMPTY_Msk + + return FRAME_INDEX + + +## Read user registers (the VSI User Registers) +# @param index user register index (zero based) +# @return value value read (32-bit) +def rdRegs(index): + value = 0 + + if index == 0: + value = MODE + elif index == 1: + value = CONTROL + elif index == 2: + value = rdSTATUS() + elif index == 3: + value = FILENAME_LEN + elif index == 4: + value = FILENAME_CHAR + elif index == 5: + value = FILENAME_VALID + elif index == 6: + value = FRAME_WIDTH + elif index == 7: + value = FRAME_HEIGHT + elif index == 8: + value = COLOR_FORMAT + elif index == 9: + value = FRAME_RATE + elif index == 10: + value = FRAME_INDEX + elif index == 11: + value = FRAME_COUNT + elif index == 12: + value = FRAME_COUNT_MAX + + return value + + +## Write user registers (the VSI User Registers) +# @param index user register index (zero based) +# @param value value to write (32-bit) +# @return value value written (32-bit) +def wrRegs(index, value): + global MODE, FRAME_WIDTH, FRAME_HEIGHT, COLOR_FORMAT, FRAME_RATE, FRAME_COUNT_MAX + + if index == 0: + MODE = value + elif index == 1: + wrCONTROL(value) + elif index == 2: + value = STATUS + elif index == 3: + wrFILENAME_LEN(value) + elif index == 4: + wrFILENAME_CHAR(chr(value)) + elif index == 5: + value = FILENAME_VALID + elif index == 6: + if value != 0: + FRAME_WIDTH = value + elif index == 7: + if value != 0: + FRAME_HEIGHT = value + elif index == 8: + COLOR_FORMAT = value + elif index == 9: + FRAME_RATE = value + elif index == 10: + value = wrFRAME_INDEX(value) + elif index == 11: + value = FRAME_COUNT + elif index == 12: + FRAME_COUNT_MAX = value + flushBuffer() + + return value diff --git a/scripts/py/vsi/vsi_video_server.py b/scripts/py/vsi/vsi_video_server.py new file mode 100644 index 0000000..f98b2ac --- /dev/null +++ b/scripts/py/vsi/vsi_video_server.py @@ -0,0 +1,447 @@ +#!/usr/bin/env python3 +# SPDX-FileCopyrightText: Copyright 2024 Arm Limited and/or its affiliates +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import argparse +import ipaddress +import logging +import os +from multiprocessing.connection import Listener + +import cv2 +import numpy as np + +## Set verbosity level +verbosity = logging.ERROR + +# [debugging] Verbosity settings +level = { 10: "DEBUG", 20: "INFO", 30: "WARNING", 40: "ERROR" } +logging.basicConfig(format='VSI Server: [%(levelname)s]\t%(message)s', level = verbosity) +logging.info("Verbosity level is set to " + level[verbosity]) + +# Default Server configuration +default_address = ('127.0.0.1', 6000) +default_authkey = 'vsi_video' + +# Supported file extensions +video_file_extensions = ('wmv', 'avi', 'mp4') +image_file_extensions = ('bmp', 'png', 'jpg') +video_fourcc = {'wmv' : 'WMV1', 'avi' : 'MJPG', 'mp4' : 'mp4v'} + +# Mode Input/Output +MODE_IO_Msk = 1<<0 +MODE_Input = 0<<0 +MODE_Output = 1<<0 + +class VideoServer: + def __init__(self, address, authkey): + # Server commands + self.SET_FILENAME = 1 + self.STREAM_CONFIGURE = 2 + self.STREAM_ENABLE = 3 + self.STREAM_DISABLE = 4 + self.FRAME_READ = 5 + self.FRAME_WRITE = 6 + self.CLOSE_SERVER = 7 + # Color space + self.GRAYSCALE8 = 1 + self.RGB888 = 2 + self.BGR565 = 3 + self.YUV420 = 4 + self.NV12 = 5 + self.NV21 = 6 + # Variables + self.listener = Listener(address, authkey=authkey.encode('utf-8')) + self.filename = "" + self.mode = None + self.active = False + self.video = True + self.stream = None + self.frame_ratio = 0 + self.frame_drop = 0 + self.frame_index = 0 + self.eos = False + # Stream configuration + self.resolution = (None, None) + self.color_format = None + self.frame_rate = None + + # Set filename + def _setFilename(self, base_dir, filename, mode): + filename_valid = False + + if self.active: + return filename_valid + + self.filename = "" + self.frame_index = 0 + + file_extension = str(filename).split('.')[-1].lower() + + if file_extension in video_file_extensions: + self.video = True + else: + self.video = False + + file_path = os.path.join(base_dir, filename) + logging.debug(f"File path: {file_path}") + + if (mode & MODE_IO_Msk) == MODE_Input: + self.mode = MODE_Input + if os.path.isfile(file_path): + if file_extension in (video_file_extensions + image_file_extensions): + self.filename = file_path + filename_valid = True + else: + self.mode = MODE_Output + if file_extension in (video_file_extensions + image_file_extensions): + if os.path.isfile(file_path): + os.remove(file_path) + self.filename = file_path + filename_valid = True + + return filename_valid + + # Configure video stream + def _configureStream(self, frame_width, frame_height, color_format, frame_rate): + if (frame_width == 0 or frame_height == 0 or frame_rate == 0): + return False + + self.resolution = (frame_width, frame_height) + self.color_format = color_format + self.frame_rate = frame_rate + + return True + + # Enable video stream + def _enableStream(self, mode): + if self.active: + return + + self.eos = False + self.frame_ratio = 0 + self.frame_drop = 0 + + if self.stream is not None: + self.stream.release() + self.stream = None + + if self.filename == "": + self.video = True + if (mode & MODE_IO_Msk) == MODE_Input: + # Device mode: camera + self.mode = MODE_Input + else: + # Device mode: display + self.mode = MODE_Output + + if self.video: + if self.mode == MODE_Input: + if self.filename == "": + self.stream = cv2.VideoCapture(0) + if not self.stream.isOpened(): + logging.error("Failed to open Camera interface") + return + else: + self.stream = cv2.VideoCapture(self.filename) + self.stream.set(cv2.CAP_PROP_POS_FRAMES, self.frame_index) + video_fps = self.stream.get(cv2.CAP_PROP_FPS) + if video_fps > self.frame_rate: + self.frame_ratio = video_fps / self.frame_rate + logging.debug(f"Frame ratio: {self.frame_ratio}") + else: + if self.filename != "": + extension = str(self.filename).split('.')[-1].lower() + fourcc = cv2.VideoWriter_fourcc(*f'{video_fourcc[extension]}') + + if os.path.isfile(self.filename) and (self.frame_index != 0): + tmp_filename = f'{self.filename.rstrip(f".{extension}")}_tmp.{extension}' + os.rename(self.filename, tmp_filename) + cap = cv2.VideoCapture(tmp_filename) + width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)) + height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT)) + self.resolution = (width, height) + self.frame_rate = cap.get(cv2.CAP_PROP_FPS) + self.stream = cv2.VideoWriter(self.filename, fourcc, self.frame_rate, self.resolution) + + while cap.isOpened(): + ret, frame = cap.read() + if not ret: + cap.release() + os.remove(tmp_filename) + break + self.stream.write(frame) + del frame + + else: + self.stream = cv2.VideoWriter(self.filename, fourcc, self.frame_rate, self.resolution) + + self.active = True + logging.info("Stream enabled") + + # Disable Video Server + def _disableStream(self): + self.active = False + if self.stream is not None: + if self.mode == MODE_Input: + self.frame_index = self.stream.get(cv2.CAP_PROP_POS_FRAMES) + self.stream.release() + self.stream = None + logging.info("Stream disabled") + + # Resize frame to requested resolution in pixels + def __resizeFrame(self, frame, resolution): + frame_h = frame.shape[0] + frame_w = frame.shape[1] + + # Calculate requested aspect ratio (width/height): + crop_aspect_ratio = resolution[0] / resolution[1] + + if crop_aspect_ratio != (frame_w / frame_h): + # Crop into image with resize aspect ratio + crop_w = int(frame_h * crop_aspect_ratio) + crop_h = int(frame_w / crop_aspect_ratio) + + if crop_w > frame_w: + # Crop top and bottom part of the image + top = (frame_h - crop_h) // 2 + bottom = top + crop_h + frame = frame[top : bottom, 0 : frame_w] + elif crop_h > frame_h: + # Crop left and right side of the image`` + left = (frame_w - crop_w) // 2 + right = left + crop_w + frame = frame[0 : frame_h, left : right] + else: + # Crop to the center of the image + left = (frame_w - crop_w) // 2 + right = left + crop_w + top = (frame_h - crop_h) // 2 + bottom = top + crop_h + frame = frame[top : bottom, left : right] + logging.debug(f"Frame cropped from ({frame_w}, {frame_h}) to ({frame.shape[1]}, {frame.shape[0]})") + + logging.debug(f"Resize frame from ({frame.shape[1]}, {frame.shape[0]}) to ({resolution[0]}, {resolution[1]})") + try: + frame = cv2.resize(frame, resolution) + except Exception as e: + logging.error(f"Error in resizeFrame(): {e}") + + return frame + + # Change color space of a frame from BGR to selected profile + def __changeColorSpace(self, frame, color_space): + color_format = None + + # Default OpenCV color profile: BGR + if self.mode == MODE_Input: + if color_space == self.GRAYSCALE8: + color_format = cv2.COLOR_BGR2GRAY + elif color_space == self.RGB888: + color_format = cv2.COLOR_BGR2RGB + elif color_space == self.BGR565: + color_format = cv2.COLOR_BGR2BGR565 + elif color_space == self.YUV420: + color_format = cv2.COLOR_BGR2YUV_I420 + elif color_space == self.NV12: + frame = self.__changeColorSpace(frame, self.YUV420) + color_format = cv2.COLOR_YUV2RGB_NV12 + elif color_space == self.NV21: + frame = self.__changeColorSpace(frame, self.YUV420) + color_format = cv2.COLOR_YUV2RGB_NV21 + + else: + if color_space == self.GRAYSCALE8: + color_format = cv2.COLOR_GRAY2BGR + elif color_space == self.RGB888: + color_format = cv2.COLOR_RGB2BGR + elif color_space == self.BGR565: + color_format = cv2.COLOR_BGR5652BGR + elif color_space == self.YUV420: + color_format = cv2.COLOR_YUV2BGR_I420 + elif color_space == self.NV12: + color_format = cv2.COLOR_YUV2BGR_I420 + elif color_space == self.NV21: + color_format = cv2.COLOR_YUV2BGR_I420 + + if color_format != None: + logging.debug(f"Change color space to {color_format}") + try: + frame = cv2.cvtColor(frame, color_format) + except Exception as e: + logging.error(f"Error in changeColorSpace(): {e}") + + return frame + + # Read frame from source + def _readFrame(self): + frame = bytearray() + + if not self.active: + return frame + + if self.eos: + return frame + + if self.video: + if self.frame_ratio > 1: + _, tmp_frame = self.stream.read() + self.frame_drop += (self.frame_ratio - 1) + if self.frame_drop > 1: + logging.debug(f"Frames to drop: {self.frame_drop}") + drop = int(self.frame_drop // 1) + for i in range(drop): + _, _ = self.stream.read() + logging.debug(f"Frames dropped: {drop}") + self.frame_drop -= drop + logging.debug(f"Frames left to drop: {self.frame_drop}") + else: + _, tmp_frame = self.stream.read() + if tmp_frame is None: + self.eos = True + logging.debug("End of stream.") + else: + tmp_frame = cv2.imread(self.filename) + self.eos = True + logging.debug("End of stream.") + + if tmp_frame is not None: + tmp_frame = self.__resizeFrame(tmp_frame, self.resolution) + tmp_frame = self.__changeColorSpace(tmp_frame, self.color_format) + frame = bytearray(tmp_frame.tobytes()) + + return frame + + # Write frame to destination + def _writeFrame(self, frame): + if not self.active: + return + + try: + decoded_frame = np.frombuffer(frame, dtype=np.uint8) + decoded_frame = decoded_frame.reshape((self.resolution[0], self.resolution[1], 3)) + bgr_frame = self.__changeColorSpace(decoded_frame, self.RGB888) + + if self.filename == "": + cv2.imshow(self.filename, bgr_frame) + cv2.waitKey(10) + else: + if self.video: + self.stream.write(np.uint8(bgr_frame)) + self.frame_index += 1 + else: + cv2.imwrite(self.filename, bgr_frame) + except Exception: + pass + + # Run Video Server + def run(self): + logging.info("Video server started") + + try: + conn = self.listener.accept() + logging.info(f'Connection accepted {self.listener.address}') + except Exception: + logging.error("Connection not accepted") + return + + while True: + try: + recv = conn.recv() + except EOFError: + return + + cmd = recv[0] # Command + payload = recv[1:] # Payload + + if cmd == self.SET_FILENAME: + logging.info("Set filename called") + filename_valid = self._setFilename(payload[0], payload[1], payload[2]) + conn.send(filename_valid) + + elif cmd == self.STREAM_CONFIGURE: + logging.info("Stream configure called") + configuration_valid = self._configureStream(payload[0], payload[1], payload[2], payload[3]) + conn.send(configuration_valid) + + elif cmd == self.STREAM_ENABLE: + logging.info("Enable stream called") + self._enableStream(payload[0]) + conn.send(self.active) + + elif cmd == self.STREAM_DISABLE: + logging.info("Disable stream called") + self._disableStream() + conn.send(self.active) + + elif cmd == self.FRAME_READ: + logging.info("Read frame called") + frame = self._readFrame() + conn.send_bytes(frame) + conn.send(self.eos) + + elif cmd == self.FRAME_WRITE: + logging.info("Write frame called") + frame = conn.recv_bytes() + self._writeFrame(frame) + + elif cmd == self.CLOSE_SERVER: + logging.info("Close server connection") + self.stop() + + # Stop Video Server + def stop(self): + self._disableStream() + if (self.mode == MODE_Output) and (self.filename == ""): + try: + cv2.destroyAllWindows() + except Exception: + pass + self.listener.close() + logging.info("Video server stopped") + + +# Validate IP address +def ip(ip): + try: + _ = ipaddress.ip_address(ip) + return ip + except: + raise argparse.ArgumentTypeError(f"Invalid IP address: {ip}!") + +def parse_arguments(): + formatter = lambda prog: argparse.HelpFormatter(prog, max_help_position=41) + parser = argparse.ArgumentParser(formatter_class=formatter, description="VSI Video Server") + + parser_optional = parser.add_argument_group("optional") + parser_optional.add_argument("--ip", dest="ip", metavar="", + help=f"Server IP address (default: {default_address[0]})", + type=ip, default=default_address[0]) + parser_optional.add_argument("--port", dest="port", metavar="", + help=f"TCP port (default: {default_address[1]})", + type=int, default=default_address[1]) + parser_optional.add_argument("--authkey", dest="authkey", metavar="", + help=f"Authorization key (default: {default_authkey})", + type=str, default=default_authkey) + + return parser.parse_args() + +if __name__ == '__main__': + args = parse_arguments() + Server = VideoServer((args.ip, args.port), args.authkey) + try: + Server.run() + except KeyboardInterrupt: + Server.stop() diff --git a/source/application/main/include/UseCaseCommonUtils.hpp b/source/application/main/include/UseCaseCommonUtils.hpp index 68ffe42..182aea6 100644 --- a/source/application/main/include/UseCaseCommonUtils.hpp +++ b/source/application/main/include/UseCaseCommonUtils.hpp @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: Copyright 2021-2022 Arm Limited and/or its affiliates + * SPDX-FileCopyrightText: Copyright 2021-2022, 2024 Arm Limited and/or its affiliates * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -56,12 +56,15 @@ bool SetAppCtxIfmIdx(arm::app::ApplicationContext& ctx, uint32_t idx, const std: namespace common { - enum OPCODES { - MENU_OPT_RUN_INF_NEXT = 1, /* Run on next vector. */ - MENU_OPT_RUN_INF_CHOSEN, /* Run on a user provided vector index. */ - MENU_OPT_RUN_INF_ALL, /* Run inference on all. */ - MENU_OPT_SHOW_MODEL_INFO, /* Show model info. */ - MENU_OPT_LIST_IFM /* List the current IFM. */ + enum OPCODES { + MENU_OPT_RUN_INF_NEXT = 1, /* Run on next vector. */ + MENU_OPT_RUN_INF_CHOSEN, /* Run on a user provided vector index. */ + MENU_OPT_RUN_INF_ALL, /* Run inference on all. */ + MENU_OPT_SHOW_MODEL_INFO, /* Show model info. */ + MENU_OPT_LIST_IFM, /* List the current IFM. */ +#if VSI_ENABLED + MENU_OPT_RUN_INF_VSI /* Run on input from VSI. */ +#endif }; } diff --git a/source/hal/source/components/vsi/CMakeLists.txt b/source/hal/source/components/vsi/CMakeLists.txt new file mode 100644 index 0000000..8d07459 --- /dev/null +++ b/source/hal/source/components/vsi/CMakeLists.txt @@ -0,0 +1,79 @@ +#---------------------------------------------------------------------------- +# SPDX-FileCopyrightText: Copyright 2024 Arm Limited and/or its affiliates +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +#---------------------------------------------------------------------------- + +########################################################## +# Arm Virtual Streaming Interface initialization library # +########################################################## + +# Arm Virtual Streaming Interface is only available on +# certain supported platforms. + +cmake_minimum_required(VERSION 3.21.0) +set(ARM_VSI_COMPONENT arm_vsi) +project(${ARM_VSI_COMPONENT} + DESCRIPTION "Arm Virtual Streaming Interface initialization library" + LANGUAGES C CXX ASM) + +## Logging utilities: +if (NOT TARGET log) + if (NOT DEFINED LOG_PROJECT_DIR) + message(FATAL_ERROR "LOG_PROJECT_DIR needs to be defined.") + endif() + add_subdirectory(${LOG_PROJECT_DIR} ${CMAKE_BINARY_DIR}/log) +endif() + +# Create static library +add_library(${ARM_VSI_COMPONENT} STATIC) + +## Include directories - public +target_include_directories(${ARM_VSI_COMPONENT} + PUBLIC + include) + +## Component sources +target_sources(${ARM_VSI_COMPONENT} + PUBLIC + source/video_drv.c) + +## If the rte_components target has been defined, include it as a dependency here. This component +## gives access to certain CPU related functions and definitions that should come from the CMSIS +## or custom system setup and boot implementation files. +## If the component is not defined as a target, a dependency for this target should be added by +## the project importing this one. +if (TARGET rte_components) + target_link_libraries(${ARM_VSI_COMPONENT} PUBLIC + rte_components) +else() + message(WARNING + "rte_components target not defined." + "${ARM_VSI_COMPONENT} will need to be provided access to" + "RTE_Components.h header to include CPU specific definitions.") +endif() +## Compile definitions +target_compile_definitions(${ARM_VSI_COMPONENT} + PUBLIC + VSI_ENABLED) + +## Add dependencies +target_link_libraries(${ARM_VSI_COMPONENT} PUBLIC + log) + +# Display status +message(STATUS "CMAKE_CURRENT_SOURCE_DIR: " ${CMAKE_CURRENT_SOURCE_DIR}) +message(STATUS "*******************************************************") +message(STATUS "Library : " ${ARM_VSI_COMPONENT}) +message(STATUS "*******************************************************") diff --git a/source/hal/source/components/vsi/include/arm_vsi.h b/source/hal/source/components/vsi/include/arm_vsi.h new file mode 100644 index 0000000..1d307d1 --- /dev/null +++ b/source/hal/source/components/vsi/include/arm_vsi.h @@ -0,0 +1,121 @@ +/* +* SPDX-FileCopyrightText: Copyright 2024 Arm Limited and/or its affiliates +* SPDX-License-Identifier: Apache-2.0 +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +/* + * Virtual Streaming Interface (VSI) + */ + +#ifndef __ARM_VSI_H +#define __ARM_VSI_H + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef __IM +#define __IM volatile const /*! Defines 'read only' structure member permissions */ +#endif +#ifndef __OM +#define __OM volatile /*! Defines 'write only' structure member permissions */ +#endif +#ifndef __IOM +#define __IOM volatile /*! Defines 'read/write' structure member permissions */ +#endif + +#include + +/* IRQ number assignment (should be moved to device header) */ +#define ARM_VSI0_IRQn 224 +#define ARM_VSI1_IRQn 225 +#define ARM_VSI2_IRQn 226 +#define ARM_VSI3_IRQn 227 +#define ARM_VSI4_IRQn 228 +#define ARM_VSI5_IRQn 229 +#define ARM_VSI6_IRQn 230 +#define ARM_VSI7_IRQn 231 + +/// Structure type to access the virtual streaming interface +typedef struct +{ + /// Interrupt Request (IRQ) + struct { + __IOM uint32_t Enable; /*!< (R/W) IRQ Enable */ + __OM uint32_t Set; /*!< (-/W) IRQ Set */ + __OM uint32_t Clear; /*!< (-/W) IRQ Clear */ + __IM uint32_t Status; /*!< (R/-) IRQ Status */ + } IRQ; + uint32_t reserved1[60]; + /// Time counter with 1MHz input frequency + struct { + __IOM uint32_t Control; /*!< (R/W) Timer Control */ + __IOM uint32_t Interval; /*!< (R/W) Timer Interval Value (in microseconds) */ + __IM uint32_t Count; /*!< (R/-) Timer Overflow Count */ + } Timer; + uint32_t reserved2[61]; + /// Direct Memory Access (DMA) Controller + struct { + __IOM uint32_t Control; /*!< (R/W) DMA Control */ + __IOM uint32_t Address; /*!< (R/W) DMA Memory Start Address */ + __IOM uint32_t BlockSize; /*!< (R/W) DMA Block Size (in bytes, multiple of 4) */ + __IOM uint32_t BlockNum; /*!< (R/W) DMA Number of Blocks (must be 2^n) */ + __IM uint32_t BlockIndex; /*!< (R/-) DMA Block Index */ + } DMA; + uint32_t reserved3[59]; + __IOM uint32_t Regs[64]; /*!< (R/W) User Registers */ +} ARM_VSI_Type; + +/* VSI Timer Control Definitions for Timer.Control register */ +#define ARM_VSI_Timer_Run_Pos 0U /*!< Timer Control: Run Position */ +#define ARM_VSI_Timer_Run_Msk (1UL << ARM_VSI_Timer_Run_Pos) /*!< Timer Control: Run Mask */ +#define ARM_VSI_Timer_Periodic_Pos 1U /*!< Timer Control: Periodic Position */ +#define ARM_VSI_Timer_Periodic_Msk (1UL << ARM_VSI_Timer_Periodic_Pos) /*!< Timer Control: Periodic Mask */ +#define ARM_VSI_Timer_Trig_IRQ_Pos 2U /*!< Timer Control: Trig_IRQ Position */ +#define ARM_VSI_Timer_Trig_IRQ_Msk (1UL << ARM_VSI_Timer_Trig_IRQ_Pos) /*!< Timer Control: Trig_IRQ Mask */ +#define ARM_VSI_Timer_Trig_DMA_Pos 3U /*!< Timer Control: Trig_DAM Position */ +#define ARM_VSI_Timer_Trig_DMA_Msk (1UL << ARM_VSI_Timer_Trig_DMA_Pos) /*!< Timer Control: Trig_DMA Mask */ + +/* VSI DMA Control Definitions for DMA.Control register */ +#define ARM_VSI_DMA_Enable_Pos 0U /*!< DMA Control: Enable Position */ +#define ARM_VSI_DMA_Enable_Msk (1UL << ARM_VSI_DMA_Enable_Pos) /*!< DMA Control: Enable Mask */ +#define ARM_VSI_DMA_Direction_Pos 1U /*!< DMA Control: Direction Position */ +#define ARM_VSI_DMA_Direction_Msk (1UL << ARM_VSI_DMA_Direction_Pos) /*!< DMA Control: Direction Mask */ +#define ARM_VSI_DMA_Direction_P2M (0UL*ARM_VSI_DMA_Direction_Msk) /*!< DMA Control: Direction P2M */ +#define ARM_VSI_DMA_Direction_M2P (1UL*ARM_VSI_DMA_Direction_Msk) /*!< DMA Control: Direction M2P */ + +/* Memory mapping of 8 VSI peripherals */ +#define ARM_VSI0_BASE (0x4FF00000UL) /*!< VSI 0 Base Address */ +#define ARM_VSI1_BASE (0x4FF10000UL) /*!< VSI 1 Base Address */ +#define ARM_VSI2_BASE (0x4FF20000UL) /*!< VSI 2 Base Address */ +#define ARM_VSI3_BASE (0x4FF30000UL) /*!< VSI 3 Base Address */ +#define ARM_VSI4_BASE (0x4FF40000UL) /*!< VSI 4 Base Address */ +#define ARM_VSI5_BASE (0x4FF50000UL) /*!< VSI 5 Base Address */ +#define ARM_VSI6_BASE (0x4FF60000UL) /*!< VSI 6 Base Address */ +#define ARM_VSI7_BASE (0x4FF70000UL) /*!< VSI 7 Base Address */ +#define ARM_VSI0 ((ARM_VSI_Type *)ARM_VSI0_BASE) /*!< VSI 0 struct */ +#define ARM_VSI1 ((ARM_VSI_Type *)ARM_VSI1_BASE) /*!< VSI 1 struct */ +#define ARM_VSI2 ((ARM_VSI_Type *)ARM_VSI2_BASE) /*!< VSI 2 struct */ +#define ARM_VSI3 ((ARM_VSI_Type *)ARM_VSI3_BASE) /*!< VSI 3 struct */ +#define ARM_VSI4 ((ARM_VSI_Type *)ARM_VSI4_BASE) /*!< VSI 4 struct */ +#define ARM_VSI5 ((ARM_VSI_Type *)ARM_VSI5_BASE) /*!< VSI 5 struct */ +#define ARM_VSI6 ((ARM_VSI_Type *)ARM_VSI6_BASE) /*!< VSI 6 struct */ +#define ARM_VSI7 ((ARM_VSI_Type *)ARM_VSI7_BASE) /*!< VSI 7 struct */ + +#ifdef __cplusplus +} +#endif + +#endif /* __ARM_VSI_H */ diff --git a/source/hal/source/components/vsi/include/video_drv.h b/source/hal/source/components/vsi/include/video_drv.h new file mode 100644 index 0000000..73a6ef7 --- /dev/null +++ b/source/hal/source/components/vsi/include/video_drv.h @@ -0,0 +1,142 @@ +/* +* SPDX-FileCopyrightText: Copyright 2024 Arm Limited and/or its affiliates +* SPDX-License-Identifier: Apache-2.0 +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +#ifndef VIDEO_DRV_H +#define VIDEO_DRV_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include + +/* Video Channel */ +#define VIDEO_DRV_IN0 (0UL) ///< Video Input channel 0 +#define VIDEO_DRV_OUT0 (1UL) ///< Video Output channel 0 +#define VIDEO_DRV_IN1 (2UL) ///< Video Input channel 1 +#define VIDEO_DRV_OUT1 (3UL) ///< Video Output channel 1 + +/* Video Event */ +#define VIDEO_DRV_EVENT_FRAME (1UL << 0) ///< Video frame received +#define VIDEO_DRV_EVENT_OVERFLOW (1UL << 1) ///< Video buffer overflow +#define VIDEO_DRV_EVENT_UNDERFLOW (1UL << 2) ///< Video buffer underflow +#define VIDEO_DRV_EVENT_EOS (1UL << 3) ///< Video end of stream + +/* Video Mode */ +#define VIDEO_DRV_MODE_SINGLE (0UL) ///< Single frame +#define VIDEO_DRV_MODE_CONTINUOS (1UL) ///< Continuos stream + +/* Return code */ +#define VIDEO_DRV_OK (0) ///< Operation succeeded +#define VIDEO_DRV_ERROR (-1) ///< Unspecified error +#define VIDEO_DRV_ERROR_PARAMETER (-2) ///< Parameter error + +/// Video Color Format +#define COLOR_FORMAT_BEGIN (0UL) +#define COLOR_GRAYSCALE8 (1UL) +#define COLOR_RGB888 (2UL) +#define COLOR_BGR565 (3UL) +#define COLOR_YUV420 (4UL) +#define COLOR_NV12 (5UL) +#define COLOR_NV21 (6UL) +#define COLOR_FORMAT_END (7UL) + +/// Video Status +typedef struct { + uint32_t active : 1; ///< Video stream active + uint32_t buf_empty : 1; ///< Video stream buffer empty + uint32_t buf_full : 1; ///< Video stream buffer full + uint32_t overflow : 1; ///< Video buffer overflow (cleared on GetStatus) + uint32_t underflow : 1; ///< Video buffer underflow (cleared on GetStatus) + uint32_t eos : 1; ///< Video end of stream (cleared on GetStatus) + uint32_t reserved : 26; +} VideoDrv_Status_t; + +/// \brief Video Events callback function type. +/// \param[in] channel channel number +/// \param[in] event events notification mask +/// \return none +typedef void (*VideoDrv_Event_t) (uint32_t channel, uint32_t event); + +/// \brief Initialize Video Interface. +/// \param[in] cb_event pointer to \ref VideoDrv_Event_t +/// \return return code +int32_t VideoDrv_Initialize (VideoDrv_Event_t cb_event); + +/// \brief De-initialize Video Interface. +/// \return return code +int32_t VideoDrv_Uninitialize (void); + +/// \brief Set Video Interface file. +/// \param[in] channel channel number +/// \param[in] name video filename (pointer to NULL terminated string) +/// \return return code +int32_t VideoDrv_SetFile (uint32_t channel, const char *name); + +/// \brief Configure Video Interface. +/// \param[in] channel channel number +/// \param[in] frame_width frame width in pixels +/// \param[in] frame_height frame height in pixels +/// \param[in] color_format pixel color format +/// \param[in] frame_rate frame rate (frames per second) +/// \return return code +int32_t VideoDrv_Configure (uint32_t channel, uint32_t frame_width, uint32_t frame_height, uint32_t color_format, uint32_t frame_rate); + +/// \brief Set Video Interface buffer. +/// \param[in] channel channel number +/// \param[in] buf pointer to buffer for video stream +/// \param[in] buf_size video stream buffer size in bytes +/// \return return code +int32_t VideoDrv_SetBuf (uint32_t channel, void *buf, uint32_t buf_size); + +/// \brief Flush Video Interface buffer. +/// \param[in] channel channel number +/// \return return code +int32_t VideoDrv_FlushBuf (uint32_t channel); + +/// \brief Start Stream on Video Interface. +/// \param[in] channel channel number +/// \param[in] mode stream mode +/// \return return code +int32_t VideoDrv_StreamStart (uint32_t channel, uint32_t mode); + +/// \brief Stop Stream on Video Interface. +/// \param[in] channel channel number +/// \return return code +int32_t VideoDrv_StreamStop (uint32_t channel); + +/// \brief Get Video Frame buffer. +/// \param[in] channel channel number +/// \return pointer to frame buffer +void *VideoDrv_GetFrameBuf (uint32_t channel); + +/// \brief Release Video Frame. +/// \param[in] channel channel number +/// \return return code +int32_t VideoDrv_ReleaseFrame (uint32_t channel); + +/// \brief Get Video Interface status. +/// \param[in] channel channel number +/// \return \ref VideoDrv_Status_t +VideoDrv_Status_t VideoDrv_GetStatus (uint32_t channel); + +#ifdef __cplusplus +} +#endif + +#endif /* VIDEO_DRV_H */ diff --git a/source/hal/source/components/vsi/source/video_drv.c b/source/hal/source/components/vsi/source/video_drv.c new file mode 100644 index 0000000..ab4e2e5 --- /dev/null +++ b/source/hal/source/components/vsi/source/video_drv.c @@ -0,0 +1,600 @@ +/* +* SPDX-FileCopyrightText: Copyright 2024 Arm Limited and/or its affiliates +* SPDX-License-Identifier: Apache-2.0 +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +#include +#include +#include "video_drv.h" + +#include "RTE_Components.h" +#include "arm_vsi.h" + +// Video channel definitions +#ifndef VIDEO_INPUT_CHANNELS +#define VIDEO_INPUT_CHANNELS 1 +#endif +#if (VIDEO_INPUT_CHANNELS > 2) +#error "Maximum 2 Video Input channels are supported!" +#endif +#ifndef VIDEO_OUTPUT_CHANNELS +#define VIDEO_OUTPUT_CHANNELS 1 +#endif +#if (VIDEO_OUTPUT_CHANNELS > 2) +#error "Maximum 2 Video Output channels are supported!" +#endif + + +// Video peripheral definitions +#define VideoI0 ARM_VSI4 // Video Input channel 0 access struct +#define VideoI0_IRQn ARM_VSI4_IRQn // Video Input channel 0 Interrupt number +#define VideoI0_Handler ARM_VSI4_Handler // Video Input channel 0 Interrupt handler +#define VideoI1 ARM_VSI5 // Video Input channel 1 access struct +#define VideoI1_IRQn ARM_VSI5_IRQn // Video Input channel 1 Interrupt number +#define VideoI1_Handler ARM_VSI5_Handler // Video Input channel 1 Interrupt handler +#define VideoO0 ARM_VSI6 // Video Output channel 0 access struct +#define VideoO0_IRQn ARM_VSI6_IRQn // Video Output channel 0 Interrupt number +#define VideoO0_Handler ARM_VSI6_Handler // Video Output channel 0 Interrupt handler +#define VideoO1 ARM_VSI7 // Video Output channel 1 access struct +#define VideoO1_IRQn ARM_VSI7_IRQn // Video Output channel 1 Interrupt number +#define VideoO1_Handler ARM_VSI7_Handler // Video Output channel 1 Interrupt handler + +// Video Peripheral registers +#define Reg_MODE Regs[0] // Mode: 0=Input, 1=Output +#define Reg_CONTROL Regs[1] // Control: enable, continuos, flush +#define Reg_STATUS Regs[2] // Status: active, buf_empty, buf_full, overflow, underflow, eos +#define Reg_FILENAME_LEN Regs[3] // Filename length +#define Reg_FILENAME_CHAR Regs[4] // Filename character +#define Reg_FILENAME_VALID Regs[5] // Filename valid flag +#define Reg_FRAME_WIDTH Regs[6] // Requested frame width +#define Reg_FRAME_HEIGHT Regs[7] // Requested frame height +#define Reg_COLOR_FORMAT Regs[8] // Color format +#define Reg_FRAME_RATE Regs[9] // Frame rate +#define Reg_FRAME_INDEX Regs[10] // Frame index +#define Reg_FRAME_COUNT Regs[11] // Frame count +#define Reg_FRAME_COUNT_MAX Regs[12] // Frame count maximum + +// Video MODE register defintions +#define Reg_MODE_IO_Pos 0U +#define Reg_MODE_IO_Msk (1UL << Reg_MODE_IO_Pos) +#define Reg_MODE_Input (0UL << Reg_MODE_IO_Pos) +#define Reg_MODE_Output (1UL << Reg_MODE_IO_Pos) + +// Video CONTROL register definitions +#define Reg_CONTROL_ENABLE_Pos 0U +#define Reg_CONTROL_ENABLE_Msk (1UL << Reg_CONTROL_ENABLE_Pos) +#define Reg_CONTROL_CONTINUOS_Pos 1U +#define Reg_CONTROL_CONTINUOS_Msk (1UL << Reg_CONTROL_CONTINUOS_Pos) +#define Reg_CONTROL_BUF_FLUSH_Pos 2U +#define Reg_CONTROL_BUF_FLUSH_Msk (1UL << Reg_CONTROL_BUF_FLUSH_Pos) + +// Video STATUS register definitions +#define Reg_STATUS_ACTIVE_Pos 0U +#define Reg_STATUS_ACTIVE_Msk (1UL << Reg_STATUS_ACTIVE_Pos) +#define Reg_STATUS_BUF_EMPTY_Pos 1U +#define Reg_STATUS_BUF_EMPTY_Msk (1UL << Reg_STATUS_BUF_EMPTY_Pos) +#define Reg_STATUS_BUF_FULL_Pos 2U +#define Reg_STATUS_BUF_FULL_Msk (1UL << Reg_STATUS_BUF_FULL_Pos) +#define Reg_STATUS_OVERFLOW_Pos 3U +#define Reg_STATUS_OVERFLOW_Msk (1UL << Reg_STATUS_OVERFLOW_Pos) +#define Reg_STATUS_UNDERFLOW_Pos 4U +#define Reg_STATUS_UNDERFLOW_Msk (1UL << Reg_STATUS_UNDERFLOW_Pos) +#define Reg_STATUS_EOS_Pos 5U +#define Reg_STATUS_EOS_Msk (1UL << Reg_STATUS_EOS_Pos) + +// IRQ Status register definitions +#define Reg_IRQ_Status_FRAME_Pos 0U +#define Reg_IRQ_Status_FRAME_Msk (1UL << Reg_IRQ_Status_FRAME_Pos) +#define Reg_IRQ_Status_OVERFLOW_Pos 1U +#define Reg_IRQ_Status_OVERFLOW_Msk (1UL << Reg_IRQ_Status_OVERFLOW_Pos) +#define Reg_IRQ_Status_UNDERFLOW_Pos 2U +#define Reg_IRQ_Status_UNDERFLOW_Msk (1UL << Reg_IRQ_Status_UNDERFLOW_Pos) +#define Reg_IRQ_Status_EOS_Pos 3U +#define Reg_IRQ_Status_EOS_Msk (1UL << Reg_IRQ_Status_EOS_Pos) + +#define Reg_IRQ_Status_Msk Reg_IRQ_Status_FRAME_Msk | \ + Reg_IRQ_Status_OVERFLOW_Msk | \ + Reg_IRQ_Status_UNDERFLOW_Msk | \ + Reg_IRQ_Status_EOS_Msk + +// Video peripheral access structure +static ARM_VSI_Type * const pVideo[4] = { VideoI0, VideoO0, VideoI1, VideoO1 }; + +// Driver State +static uint8_t Initialized = 0U; +static uint8_t Configured[4] = { 0U, 0U, 0U, 0U }; + +// Event Callback +static VideoDrv_Event_t CB_Event = NULL; + +// Video Interrupt Handler +static void Video_Handler (uint32_t channel) { + uint32_t irq_status; + uint32_t event; + + irq_status = pVideo[channel]->IRQ.Status; + pVideo[channel]->IRQ.Clear = irq_status; + __DSB(); + __ISB(); + + event = 0U; + if (irq_status & Reg_IRQ_Status_FRAME_Msk) { + event |= VIDEO_DRV_EVENT_FRAME; + } + if (irq_status & Reg_IRQ_Status_OVERFLOW_Msk) { + event |= VIDEO_DRV_EVENT_OVERFLOW; + } + if (irq_status & Reg_IRQ_Status_UNDERFLOW_Msk) { + event |= VIDEO_DRV_EVENT_UNDERFLOW; + } + if (irq_status & Reg_IRQ_Status_EOS_Msk) { + event |= VIDEO_DRV_EVENT_EOS; + } + + if (CB_Event != NULL) { + CB_Event(channel, event); + } +} + +// Video channel 0 Interrupt Handler +#if (VIDEO_INPUT_CHANNELS >= 1) +void VideoI0_Handler (void); +void VideoI0_Handler (void) { + Video_Handler(0U); +} +#endif + +// Video channel 1 Interrupt Handler +#if (VIDEO_OUTPUT_CHANNELS >= 1) +void VideoO0_Handler (void); +void VideoO0_Handler (void) { + Video_Handler(1U); +} +#endif + +// Video channel 2 Interrupt Handler +#if (VIDEO_INPUT_CHANNELS >= 2) +void VideoI1_Handler (void); +void VideoI1_Handler (void) { + Video_Handler(2U); +} +#endif + +// Video channel 3 Interrupt Handler +#if (VIDEO_OUTPUT_CHANNELS >= 2) +void VideoO1_Handler (void); +void VideoO1_Handler (void) { + Video_Handler(3U); +} +#endif + +// Initialize Video Interface +int32_t VideoDrv_Initialize (VideoDrv_Event_t cb_event) { + + CB_Event = cb_event; + + // Initialize Video Input channel 0 + #if (VIDEO_INPUT_CHANNELS >= 1) + VideoI0->Timer.Control = 0U; + VideoI0->DMA.Control = 0U; + VideoI0->IRQ.Clear = Reg_IRQ_Status_Msk; + VideoI0->IRQ.Enable = Reg_IRQ_Status_Msk; + VideoI0->Reg_MODE = Reg_MODE_Input; + VideoI0->Reg_CONTROL = 0U; +// NVIC_EnableIRQ(VideoI0_IRQn); + NVIC->ISER[(((uint32_t)VideoI0_IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)VideoI0_IRQn) & 0x1FUL)); + #endif + Configured[0] = 0U; + + // Initialize Video Output channel 0 + #if (VIDEO_OUTPUT_CHANNELS >= 1) + VideoO0->Timer.Control = 0U; + VideoO0->DMA.Control = 0U; + VideoO0->IRQ.Clear = Reg_IRQ_Status_Msk; + VideoO0->IRQ.Enable = Reg_IRQ_Status_Msk; + VideoO0->Reg_MODE = Reg_MODE_Output; + VideoO0->Reg_CONTROL = 0U; +// NVIC_EnableIRQ(VideoO0_IRQn); + NVIC->ISER[(((uint32_t)VideoO0_IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)VideoO0_IRQn) & 0x1FUL)); + #endif + Configured[1] = 0U; + + // Initialize Video Input channel 1 + #if (VIDEO_INPUT_CHANNELS >= 2) + VideoI1->Timer.Control = 0U; + VideoI1->DMA.Control = 0U; + VideoI1->IRQ.Clear = Reg_IRQ_Status_Msk; + VideoI1->IRQ.Enable = Reg_IRQ_Status_Msk; + VideoI1->Reg_MODE = Reg_MODE_Input; + VideoI1->Reg_CONTROL = 0U; +// NVIC_EnableIRQ(VideoI1_IRQn); + NVIC->ISER[(((uint32_t)VideoI1_IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)VideoI1_IRQn) & 0x1FUL)); + #endif + Configured[2] = 0U; + + // Initialize Video Output channel 1 + #if (VIDEO_OUTPUT_CHANNELS >= 2) + VideoO1->Timer.Control = 0U; + VideoO1->DMA.Control = 0U; + VideoO1->IRQ.Clear = Reg_IRQ_Status_Msk; + VideoO1->IRQ.Enable = Reg_IRQ_Status_Msk; + VideoO1->Reg_MODE = Reg_MODE_Output; + VideoO1->Reg_CONTROL = 0U; +// NVIC_EnableIRQ(VideoO1_IRQn); + NVIC->ISER[(((uint32_t)VideoO1_IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)VideoO1_IRQn) & 0x1FUL)); + #endif + Configured[3] = 0U; + + __DSB(); + __ISB(); + + Initialized = 1U; + + return VIDEO_DRV_OK; +} + +// De-initialize Video Interface +int32_t VideoDrv_Uninitialize (void) { + + // De-initialize Video Input channel 0 + #if (VIDEO_INPUT_CHANNELS >= 1) +// NVIC_DisableIRQ(VideoI0_IRQn); + NVIC->ICER[(((uint32_t)VideoI0_IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)VideoI0_IRQn) & 0x1FUL)); + VideoI0->Timer.Control = 0U; + VideoI0->DMA.Control = 0U; + VideoI0->IRQ.Clear = Reg_IRQ_Status_Msk; + VideoI0->IRQ.Enable = 0U; + VideoI0->Reg_CONTROL = 0U; + #endif + + // De-initialize Video Output channel 0 + #if (VIDEO_OUTPUT_CHANNELS >= 1) +// NVIC_DisableIRQ(VideoO0_IRQn); + NVIC->ICER[(((uint32_t)VideoO0_IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)VideoO0_IRQn) & 0x1FUL)); + VideoO0->Timer.Control = 0U; + VideoO0->DMA.Control = 0U; + VideoO0->IRQ.Clear = Reg_IRQ_Status_Msk; + VideoO0->IRQ.Enable = 0U; + VideoO0->Reg_CONTROL = 0U; + #endif + + // De-initialize Video Input channel 1 + #if (VIDEO_INPUT_CHANNELS >= 2) +// NVIC_DisableIRQ(VideoI1_IRQn); + NVIC->ICER[(((uint32_t)VideoI1_IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)VideoI1_IRQn) & 0x1FUL)); + VideoI1->Timer.Control = 0U; + VideoI1->DMA.Control = 0U; + VideoI1->IRQ.Clear = Reg_IRQ_Status_Msk; + VideoI1->IRQ.Enable = 0U; + VideoI1->Reg_CONTROL = 0U; + #endif + + // De-initialize Video Output channel 1 + #if (VIDEO_OUTPUT_CHANNELS >= 2) +// NVIC_DisableIRQ(VideoO1_IRQn); + NVIC->ICER[(((uint32_t)VideoO1_IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)VideoO1_IRQn) & 0x1FUL)); + VideoO1->Timer.Control = 0U; + VideoO1->DMA.Control = 0U; + VideoO1->IRQ.Clear = Reg_IRQ_Status_Msk; + VideoO1->IRQ.Enable = 0U; + VideoO1->Reg_CONTROL = 0U; + #endif + + __DSB(); + __ISB(); + + Initialized = 0U; + + return VIDEO_DRV_OK; +} + +// Set Video Interface file +int32_t VideoDrv_SetFile (uint32_t channel, const char *name) { + const char *p; + uint32_t n; + + if ((((channel & 1U) == 0U) && ((channel >> 1) >= VIDEO_INPUT_CHANNELS)) || + (((channel & 1U) != 0U) && ((channel >> 1) >= VIDEO_OUTPUT_CHANNELS)) || + (name == NULL)) { + return VIDEO_DRV_ERROR_PARAMETER; + } + + if (Initialized == 0U) { + return VIDEO_DRV_ERROR; + } + + if ((pVideo[channel]->Reg_STATUS & Reg_STATUS_ACTIVE_Msk) != 0U) { + return VIDEO_DRV_ERROR; + } + + // Register Video filename + n = strlen(name); + pVideo[channel]->Reg_FILENAME_LEN = n; + for (p = name; n != 0U; n--) { + pVideo[channel]->Reg_FILENAME_CHAR = *p++; + } + if (pVideo[channel]->Reg_FILENAME_VALID == 0U) { + return VIDEO_DRV_ERROR; + } + + return VIDEO_DRV_OK; +} + +// Configure Video Interface +int32_t VideoDrv_Configure (uint32_t channel, uint32_t frame_width, uint32_t frame_height, uint32_t color_format, uint32_t frame_rate) { + uint32_t pixel_size; + uint32_t block_size; + + if ((((channel & 1U) == 0U) && ((channel >> 1) >= VIDEO_INPUT_CHANNELS)) || + (((channel & 1U) != 0U) && ((channel >> 1) >= VIDEO_OUTPUT_CHANNELS)) || + (frame_width == 0U) || + (frame_height == 0U) || + (frame_rate == 0U) || + (color_format <= COLOR_FORMAT_BEGIN) || + (color_format >= COLOR_FORMAT_END)) { + return VIDEO_DRV_ERROR_PARAMETER; + } + + switch (color_format) { + case COLOR_GRAYSCALE8: + pixel_size = 8U; + break; + case COLOR_YUV420: + pixel_size = 12U; + break; + case COLOR_BGR565: + pixel_size = 16U; + break; + case COLOR_RGB888: + case COLOR_NV12: + case COLOR_NV21: + pixel_size = 24U; + break; + default: + return VIDEO_DRV_ERROR_PARAMETER; + } + + block_size = (((frame_width * frame_height) * pixel_size) + 7U) / 8U; + block_size = (block_size + 3U) & ~3U; + + if (Initialized == 0U) { + return VIDEO_DRV_ERROR; + } + + if ((pVideo[channel]->Reg_STATUS & Reg_STATUS_ACTIVE_Msk) != 0U) { + return VIDEO_DRV_ERROR; + } + + pVideo[channel]->Reg_FRAME_WIDTH = frame_width; + pVideo[channel]->Reg_FRAME_HEIGHT = frame_height; + pVideo[channel]->Reg_COLOR_FORMAT = color_format; + pVideo[channel]->Reg_FRAME_RATE = frame_rate; + pVideo[channel]->Timer.Interval = 1000000U / frame_rate; + pVideo[channel]->DMA.BlockSize = block_size; + + Configured[channel] = 1U; + + return VIDEO_DRV_OK; +} + +// Set Video Interface buffer +int32_t VideoDrv_SetBuf (uint32_t channel, void *buf, uint32_t buf_size) { + uint32_t block_num; + + if ((((channel & 1U) == 0U) && ((channel >> 1) >= VIDEO_INPUT_CHANNELS)) || + (((channel & 1U) != 0U) && ((channel >> 1) >= VIDEO_OUTPUT_CHANNELS)) || + (buf == NULL) || + (buf_size == 0U)) { + return VIDEO_DRV_ERROR_PARAMETER; + } + + if ((Initialized == 0U) || + (Configured[channel] == 0U)) { + return VIDEO_DRV_ERROR; + } + + if ((pVideo[channel]->Reg_STATUS & Reg_STATUS_ACTIVE_Msk) != 0U) { + return VIDEO_DRV_ERROR; + } + + block_num = buf_size / pVideo[channel]->DMA.BlockSize; + if (block_num == 0U) { + return VIDEO_DRV_ERROR; + } + + pVideo[channel]->Reg_FRAME_COUNT_MAX = block_num; + pVideo[channel]->DMA.BlockNum = block_num; + + pVideo[channel]->DMA.Address = (uint32_t)buf; + + Configured[channel] = 2U; + + return VIDEO_DRV_OK; +} + +// Flush Video Interface buffer +int32_t VideoDrv_FlushBuf (uint32_t channel) { + + if ((((channel & 1U) == 0U) && ((channel >> 1) >= VIDEO_INPUT_CHANNELS)) || + (((channel & 1U) != 0U) && ((channel >> 1) >= VIDEO_OUTPUT_CHANNELS))) { + return VIDEO_DRV_ERROR_PARAMETER; + } + + if (Initialized == 0U) { + return VIDEO_DRV_ERROR; + } + + if ((pVideo[channel]->Reg_STATUS & Reg_STATUS_ACTIVE_Msk) != 0U) { + return VIDEO_DRV_ERROR; + } + + pVideo[channel]->Reg_CONTROL = Reg_CONTROL_BUF_FLUSH_Msk; + + return VIDEO_DRV_OK; +} + +// Start Stream on Video Interface +int32_t VideoDrv_StreamStart (uint32_t channel, uint32_t mode) { + uint32_t control; + + if ((((channel & 1U) == 0U) && ((channel >> 1) >= VIDEO_INPUT_CHANNELS)) || + (((channel & 1U) != 0U) && ((channel >> 1) >= VIDEO_OUTPUT_CHANNELS)) || + (mode > VIDEO_DRV_MODE_CONTINUOS)) { + return VIDEO_DRV_ERROR_PARAMETER; + } + + if ((Initialized == 0U) || + (Configured[channel] < 2U)) { + return VIDEO_DRV_ERROR; + } + + if ((pVideo[channel]->Reg_STATUS & Reg_STATUS_ACTIVE_Msk) != 0U) { + return VIDEO_DRV_OK; + } + + control = Reg_CONTROL_ENABLE_Msk; + if (mode == VIDEO_DRV_MODE_CONTINUOS) { + control |= Reg_CONTROL_CONTINUOS_Msk; + } + pVideo[channel]->Reg_CONTROL = control; + + + if ((pVideo[channel]->Reg_STATUS & Reg_STATUS_ACTIVE_Msk) == 0U) { + return VIDEO_DRV_ERROR; + } + + control = ARM_VSI_DMA_Enable_Msk; + if ((channel & 1U) == 0U) { + control |= ARM_VSI_DMA_Direction_P2M; + } else { + control |= ARM_VSI_DMA_Direction_M2P; + } + pVideo[channel]->DMA.Control = control; + + control = ARM_VSI_Timer_Run_Msk | + ARM_VSI_Timer_Trig_DMA_Msk | + ARM_VSI_Timer_Trig_IRQ_Msk; + if (mode == VIDEO_DRV_MODE_CONTINUOS) { + control |= ARM_VSI_Timer_Periodic_Msk; + } + pVideo[channel]->Timer.Control = control; + + return VIDEO_DRV_OK; +} + +// Stop Stream on Video Interface +int32_t VideoDrv_StreamStop (uint32_t channel) { + + if ((((channel & 1U) == 0U) && ((channel >> 1) >= VIDEO_INPUT_CHANNELS)) || + (((channel & 1U) != 0U) && ((channel >> 1) >= VIDEO_OUTPUT_CHANNELS))) { + return VIDEO_DRV_ERROR_PARAMETER; + } + + if ((Initialized == 0U) || + (Configured[channel] < 2U)) { + return VIDEO_DRV_ERROR; + } + + if ((pVideo[channel]->Reg_STATUS & Reg_STATUS_ACTIVE_Msk) == 0U) { + return VIDEO_DRV_OK; + } + + pVideo[channel]->Timer.Control = 0U; + pVideo[channel]->DMA.Control = 0U; + pVideo[channel]->Reg_CONTROL = 0U; + + return VIDEO_DRV_OK; +} + +// Get Video Frame buffer +void *VideoDrv_GetFrameBuf (uint32_t channel) { + void *frame = NULL; + + if ((((channel & 1U) == 0U) && ((channel >> 1) >= VIDEO_INPUT_CHANNELS)) || + (((channel & 1U) != 0U) && ((channel >> 1) >= VIDEO_OUTPUT_CHANNELS))) { + return NULL; + } + + if ((Initialized == 0U) || + (Configured[channel] < 2U)) { + return NULL; + } + + if ((pVideo[channel]->Reg_MODE & Reg_MODE_IO_Msk) == Reg_MODE_Input) { + // Input + if ((pVideo[channel]->Reg_STATUS & Reg_STATUS_BUF_EMPTY_Msk) != 0U) { + return NULL; + } + } else { + // Output + if ((pVideo[channel]->Reg_STATUS & Reg_STATUS_BUF_FULL_Msk) != 0U) { + return NULL; + } + } + + frame = (void *)(pVideo[channel]->DMA.Address + (pVideo[channel]->Reg_FRAME_INDEX * pVideo[channel]->DMA.BlockSize)); + + return frame; +} + +// Release Video Frame +int32_t VideoDrv_ReleaseFrame (uint32_t channel) { + + if ((((channel & 1U) == 0U) && ((channel >> 1) >= VIDEO_INPUT_CHANNELS)) || + (((channel & 1U) != 0U) && ((channel >> 1) >= VIDEO_OUTPUT_CHANNELS))) { + return VIDEO_DRV_ERROR_PARAMETER; + } + + if ((Initialized == 0U) || + (Configured[channel] < 2U)) { + return VIDEO_DRV_ERROR; + } + + if ((pVideo[channel]->Reg_MODE & Reg_MODE_IO_Msk) == Reg_MODE_Input) { + // Input + if ((pVideo[channel]->Reg_STATUS & Reg_STATUS_BUF_EMPTY_Msk) != 0U) { + return VIDEO_DRV_ERROR; + } + } else { + // Output + if ((pVideo[channel]->Reg_STATUS & Reg_STATUS_BUF_FULL_Msk) != 0U) { + return VIDEO_DRV_ERROR; + } + } + + pVideo[channel]->Reg_FRAME_INDEX = 0U; + + return VIDEO_DRV_OK; +} + + +// Get Video Interface status +VideoDrv_Status_t VideoDrv_GetStatus (uint32_t channel) { + VideoDrv_Status_t status = { 0U, 0U, 0U, 0U, 0U, 0U, 0U }; + uint32_t status_reg; + + if ((((channel & 1U) == 0U) && ((channel >> 1) < VIDEO_INPUT_CHANNELS)) || + (((channel & 1U) != 0U) && ((channel >> 1) < VIDEO_OUTPUT_CHANNELS))) { + status_reg = pVideo[channel]->Reg_STATUS; + status.active = (status_reg & Reg_STATUS_ACTIVE_Msk) >> Reg_STATUS_ACTIVE_Pos; + status.buf_empty = (status_reg & Reg_STATUS_BUF_EMPTY_Msk) >> Reg_STATUS_BUF_EMPTY_Pos; + status.buf_full = (status_reg & Reg_STATUS_BUF_FULL_Msk) >> Reg_STATUS_BUF_FULL_Pos; + status.overflow = (status_reg & Reg_STATUS_OVERFLOW_Msk) >> Reg_STATUS_OVERFLOW_Pos; + status.underflow = (status_reg & Reg_STATUS_UNDERFLOW_Msk) >> Reg_STATUS_UNDERFLOW_Pos; + status.eos = (status_reg & Reg_STATUS_EOS_Msk) >> Reg_STATUS_EOS_Pos; + } + + return status; +} diff --git a/source/hal/source/platform/mps3/CMakeLists.txt b/source/hal/source/platform/mps3/CMakeLists.txt index 5008f0b..2791150 100644 --- a/source/hal/source/platform/mps3/CMakeLists.txt +++ b/source/hal/source/platform/mps3/CMakeLists.txt @@ -1,5 +1,5 @@ #---------------------------------------------------------------------------- -# SPDX-FileCopyrightText: Copyright 2022 Arm Limited and/or its affiliates +# SPDX-FileCopyrightText: Copyright 2022, 2024 Arm Limited and/or its affiliates # SPDX-License-Identifier: Apache-2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -133,6 +133,18 @@ if (CPU_PROFILE_ENABLED) target_compile_definitions(${PLATFORM_DRIVERS_TARGET} PUBLIC CPU_PROFILE_ENABLED) endif() +# If Virtual Streaming Interface is enabled, we need to link the vsi driver +if (VSI_ENABLED) + target_sources(${PLATFORM_DRIVERS_TARGET} + PRIVATE + source/vsi_mps3.c) + add_subdirectory(${COMPONENTS_DIR}/vsi ${CMAKE_BINARY_DIR}/vsi) + + target_link_libraries(${PLATFORM_DRIVERS_TARGET} + PUBLIC + arm_vsi) +endif () + # If Ethos-U is enabled, we need the driver library too if (ETHOS_U_NPU_ENABLED) diff --git a/source/hal/source/platform/mps3/include/sse-300/peripheral_irqs.h b/source/hal/source/platform/mps3/include/sse-300/peripheral_irqs.h index 2085c6a..9fb7b9e 100644 --- a/source/hal/source/platform/mps3/include/sse-300/peripheral_irqs.h +++ b/source/hal/source/platform/mps3/include/sse-300/peripheral_irqs.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: Copyright 2022 Arm Limited and/or its affiliates + * SPDX-FileCopyrightText: Copyright 2022, 2024 Arm Limited and/or its affiliates * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -131,6 +131,14 @@ #define UARTRX5_IRQn (125) /* UART 5 RX Interrupt */ #define UARTTX5_IRQn (126) /* UART 5 TX Interrupt */ #define UART5_IRQn (127) /* UART 5 combined Interrupt */ +#define ARM_VSI0_IRQn (224) /* 224: VSI 0 */ +#define ARM_VSI1_IRQn (225) /* 225: VSI 1 */ +#define ARM_VSI2_IRQn (226) /* 226: VSI 2 */ +#define ARM_VSI3_IRQn (227) /* 227: VSI 3 */ +#define ARM_VSI4_IRQn (228) /* 228: VSI 4 */ +#define ARM_VSI5_IRQn (229) /* 229: VSI 5 */ +#define ARM_VSI6_IRQn (230) /* 230: VSI 6 */ +#define ARM_VSI7_IRQn (231) /* 231: VSI 7 */ /* #undef HDCLCD_IRQn */ #endif /* PERIPHERAL_IRQS_H */ diff --git a/source/hal/source/platform/mps3/include/sse-310/peripheral_irqs.h b/source/hal/source/platform/mps3/include/sse-310/peripheral_irqs.h index 95b62c7..0414f63 100644 --- a/source/hal/source/platform/mps3/include/sse-310/peripheral_irqs.h +++ b/source/hal/source/platform/mps3/include/sse-310/peripheral_irqs.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: Copyright 2022 Arm Limited and/or its affiliates + * SPDX-FileCopyrightText: Copyright 2022, 2024 Arm Limited and/or its affiliates * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -129,6 +129,14 @@ #define UARTRX5_IRQn (125) /* UART 5 RX Interrupt */ #define UARTTX5_IRQn (126) /* UART 5 TX Interrupt */ #define UART5_IRQn (127) /* UART 5 combined Interrupt */ +#define ARM_VSI0_IRQn (224) /* 224: VSI 0 */ +#define ARM_VSI1_IRQn (225) /* 225: VSI 1 */ +#define ARM_VSI2_IRQn (226) /* 226: VSI 2 */ +#define ARM_VSI3_IRQn (227) /* 227: VSI 3 */ +#define ARM_VSI4_IRQn (228) /* 228: VSI 4 */ +#define ARM_VSI5_IRQn (229) /* 229: VSI 5 */ +#define ARM_VSI6_IRQn (230) /* 230: VSI 6 */ +#define ARM_VSI7_IRQn (231) /* 231: VSI 7 */ /* #undef HDCLCD_IRQn */ #endif /* PERIPHERAL_IRQS_H */ diff --git a/source/hal/source/platform/mps3/include/vsi_mps3.h b/source/hal/source/platform/mps3/include/vsi_mps3.h new file mode 100644 index 0000000..a8895ea --- /dev/null +++ b/source/hal/source/platform/mps3/include/vsi_mps3.h @@ -0,0 +1,115 @@ +/* + * SPDX-FileCopyrightText: Copyright 2024 Arm Limited and/or its affiliates + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef VSI_MPS3_H +#define VSI_MPS3_H + +#include +#include + +#include "peripheral_irqs.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef __IM +#define __IM volatile const /*! Defines 'read only' structure member permissions */ +#endif +#ifndef __OM +#define __OM volatile /*! Defines 'write only' structure member permissions */ +#endif +#ifndef __IOM +#define __IOM volatile /*! Defines 'read/write' structure member permissions */ +#endif + +/// Structure type to access the virtual streaming interface +typedef struct +{ + /// Interrupt Request (IRQ) + struct { + __IOM uint32_t Enable; /*!< (R/W) IRQ Enable */ + __OM uint32_t Set; /*!< (-/W) IRQ Set */ + __OM uint32_t Clear; /*!< (-/W) IRQ Clear */ + __IM uint32_t Status; /*!< (R/-) IRQ Status */ + } IRQ; + uint32_t reserved1[60]; + /// Time counter with 1MHz input frequency + struct { + __IOM uint32_t Control; /*!< (R/W) Timer Control */ + __IOM uint32_t Interval; /*!< (R/W) Timer Interval Value (in microseconds) */ + __IM uint32_t Count; /*!< (R/-) Timer Overflow Count */ + } Timer; + uint32_t reserved2[61]; + /// Direct Memory Access (DMA) Controller + struct { + __IOM uint32_t Control; /*!< (R/W) DMA Control */ + __IOM uint32_t Address; /*!< (R/W) DMA Memory Start Address */ + __IOM uint32_t BlockSize; /*!< (R/W) DMA Block Size (in bytes, multiple of 4) */ + __IOM uint32_t BlockNum; /*!< (R/W) DMA Number of Blocks (must be 2^n) */ + __IM uint32_t BlockIndex; /*!< (R/-) DMA Block Index */ + } DMA; + uint32_t reserved3[59]; + __IOM uint32_t Regs[64]; /*!< (R/W) User Registers */ +} ARM_VSI_Type; + +/* VSI Timer Control Definitions for Timer.Control register */ +#define ARM_VSI_Timer_Run_Pos 0U /*!< Timer Control: Run Position */ +#define ARM_VSI_Timer_Run_Msk (1UL << ARM_VSI_Timer_Run_Pos) /*!< Timer Control: Run Mask */ +#define ARM_VSI_Timer_Periodic_Pos 1U /*!< Timer Control: Periodic Position */ +#define ARM_VSI_Timer_Periodic_Msk (1UL << ARM_VSI_Timer_Periodic_Pos) /*!< Timer Control: Periodic Mask */ +#define ARM_VSI_Timer_Trig_IRQ_Pos 2U /*!< Timer Control: Trig_IRQ Position */ +#define ARM_VSI_Timer_Trig_IRQ_Msk (1UL << ARM_VSI_Timer_Trig_IRQ_Pos) /*!< Timer Control: Trig_IRQ Mask */ +#define ARM_VSI_Timer_Trig_DMA_Pos 3U /*!< Timer Control: Trig_DAM Position */ +#define ARM_VSI_Timer_Trig_DMA_Msk (1UL << ARM_VSI_Timer_Trig_DMA_Pos) /*!< Timer Control: Trig_DMA Mask */ + +/* VSI DMA Control Definitions for DMA.Control register */ +#define ARM_VSI_DMA_Enable_Pos 0U /*!< DMA Control: Enable Position */ +#define ARM_VSI_DMA_Enable_Msk (1UL << ARM_VSI_DMA_Enable_Pos) /*!< DMA Control: Enable Mask */ +#define ARM_VSI_DMA_Direction_Pos 1U /*!< DMA Control: Direction Position */ +#define ARM_VSI_DMA_Direction_Msk (1UL << ARM_VSI_DMA_Direction_Pos) /*!< DMA Control: Direction Mask */ +#define ARM_VSI_DMA_Direction_P2M (0UL*ARM_VSI_DMA_Direction_Msk) /*!< DMA Control: Direction P2M */ +#define ARM_VSI_DMA_Direction_M2P (1UL*ARM_VSI_DMA_Direction_Msk) /*!< DMA Control: Direction M2P */ + +/* Memory mapping of 8 VSI peripherals */ +#define ARM_VSI0_BASE (0x4FF00000UL) /*!< VSI 0 Base Address */ +#define ARM_VSI1_BASE (0x4FF10000UL) /*!< VSI 1 Base Address */ +#define ARM_VSI2_BASE (0x4FF20000UL) /*!< VSI 2 Base Address */ +#define ARM_VSI3_BASE (0x4FF30000UL) /*!< VSI 3 Base Address */ +#define ARM_VSI4_BASE (0x4FF40000UL) /*!< VSI 4 Base Address */ +#define ARM_VSI5_BASE (0x4FF50000UL) /*!< VSI 5 Base Address */ +#define ARM_VSI6_BASE (0x4FF60000UL) /*!< VSI 6 Base Address */ +#define ARM_VSI7_BASE (0x4FF70000UL) /*!< VSI 7 Base Address */ +#define ARM_VSI0 ((ARM_VSI_Type *)ARM_VSI0_BASE) /*!< VSI 0 struct */ +#define ARM_VSI1 ((ARM_VSI_Type *)ARM_VSI1_BASE) /*!< VSI 1 struct */ +#define ARM_VSI2 ((ARM_VSI_Type *)ARM_VSI2_BASE) /*!< VSI 2 struct */ +#define ARM_VSI3 ((ARM_VSI_Type *)ARM_VSI3_BASE) /*!< VSI 3 struct */ +#define ARM_VSI4 ((ARM_VSI_Type *)ARM_VSI4_BASE) /*!< VSI 4 struct */ +#define ARM_VSI5 ((ARM_VSI_Type *)ARM_VSI5_BASE) /*!< VSI 5 struct */ +#define ARM_VSI6 ((ARM_VSI_Type *)ARM_VSI6_BASE) /*!< VSI 6 struct */ +#define ARM_VSI7 ((ARM_VSI_Type *)ARM_VSI7_BASE) /*!< VSI 7 struct */ + +/** + * @brief Initialises the VSI + * @return 0 if successful, error code otherwise + **/ +int vsi_init(void); + +#ifdef __cplusplus +} +#endif + +#endif /* VSI_MPS3_H */ diff --git a/source/hal/source/platform/mps3/source/platform_drivers.c b/source/hal/source/platform/mps3/source/platform_drivers.c index 73b388b..1f55bb9 100644 --- a/source/hal/source/platform/mps3/source/platform_drivers.c +++ b/source/hal/source/platform/mps3/source/platform_drivers.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: Copyright 2022-2023 Arm Limited and/or its affiliates + * SPDX-FileCopyrightText: Copyright 2022-2024 Arm Limited and/or its affiliates * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -40,6 +40,10 @@ #endif /* ARM_NPU */ +#if VSI_ENABLED +#include "vsi_mps3.h" +#endif /* VSI_ENABLED */ + /** * @brief Checks if the platform is valid by checking * the CPU ID for the FPGA implementation against @@ -94,6 +98,12 @@ int platform_init(void) #endif /* ARM_NPU */ +#if VSI_ENABLED + if (0 != (err = vsi_init())) { + return err; + } +#endif /* VSI_ENABLED */ + /* Print target design info */ info("Target system design: %s\n", s_platform_name); return 0; diff --git a/source/hal/source/platform/mps3/source/vsi_mps3.c b/source/hal/source/platform/mps3/source/vsi_mps3.c new file mode 100644 index 0000000..8516c1c --- /dev/null +++ b/source/hal/source/platform/mps3/source/vsi_mps3.c @@ -0,0 +1,62 @@ +/* + * SPDX-FileCopyrightText: Copyright 2024 Arm Limited and/or its affiliates + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "vsi_mps3.h" + +#include "log_macros.h" +#include "RTE_Components.h" +#include "peripheral_irqs.h" + +#include "video_drv.h" + +/* Exception / Interrupt Handler */ +#define DEFAULT_IRQ_HANDLER(handler_name) \ +void __WEAK __NO_RETURN handler_name(void); \ +void handler_name(void) { \ + while(1); \ +} + +DEFAULT_IRQ_HANDLER(ARM_VSI0_Handler) +DEFAULT_IRQ_HANDLER(ARM_VSI1_Handler) +DEFAULT_IRQ_HANDLER(ARM_VSI2_Handler) +DEFAULT_IRQ_HANDLER(ARM_VSI3_Handler) +DEFAULT_IRQ_HANDLER(ARM_VSI4_Handler) +DEFAULT_IRQ_HANDLER(ARM_VSI5_Handler) +DEFAULT_IRQ_HANDLER(ARM_VSI6_Handler) +DEFAULT_IRQ_HANDLER(ARM_VSI7_Handler) + +int vsi_init(void) +{ + /* Register the VSI handlers in our vector table. */ + NVIC_SetVector(ARM_VSI0_IRQn, (uint32_t)ARM_VSI0_Handler); + NVIC_SetVector(ARM_VSI1_IRQn, (uint32_t)ARM_VSI1_Handler); + NVIC_SetVector(ARM_VSI2_IRQn, (uint32_t)ARM_VSI2_Handler); + NVIC_SetVector(ARM_VSI3_IRQn, (uint32_t)ARM_VSI3_Handler); + NVIC_SetVector(ARM_VSI4_IRQn, (uint32_t)ARM_VSI4_Handler); + NVIC_SetVector(ARM_VSI5_IRQn, (uint32_t)ARM_VSI5_Handler); + NVIC_SetVector(ARM_VSI6_IRQn, (uint32_t)ARM_VSI6_Handler); + NVIC_SetVector(ARM_VSI7_IRQn, (uint32_t)ARM_VSI7_Handler); + + /* Initialize Video VSI. */ + if (VideoDrv_Initialize(NULL) != VIDEO_DRV_OK) { + printf_err("Failed to initialise video driver\n"); + return 1; + } + + info("VSI device initialised\n"); + + return 0; +} diff --git a/source/use_case/img_class/usecase.cmake b/source/use_case/img_class/usecase.cmake index 4c25989..f18522c 100644 --- a/source/use_case/img_class/usecase.cmake +++ b/source/use_case/img_class/usecase.cmake @@ -1,5 +1,5 @@ #---------------------------------------------------------------------------- -# SPDX-FileCopyrightText: Copyright 2021 Arm Limited and/or its affiliates +# SPDX-FileCopyrightText: Copyright 2021, 2024 Arm Limited and/or its affiliates # SPDX-License-Identifier: Apache-2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -28,10 +28,12 @@ USER_OPTION(${use_case}_LABELS_TXT_FILE "Labels' txt file for the chosen model" FILEPATH) # Generate input files -generate_images_code("${${use_case}_FILE_PATH}" - ${SRC_GEN_DIR} - ${INC_GEN_DIR} - "${${use_case}_IMAGE_SIZE}") +generate_images_code( + INPUT_DIR "${${use_case}_FILE_PATH}" + SRC_OUT ${SRC_GEN_DIR} + HDR_OUT ${INC_GEN_DIR} + IMG_SIZE "${${use_case}_IMAGE_SIZE}" +) # Generate labels file set(${use_case}_LABELS_CPP_FILE Labels) diff --git a/source/use_case/object_detection/include/UseCaseHandler.hpp b/source/use_case/object_detection/include/UseCaseHandler.hpp index 9fe716f..43fb6bf 100644 --- a/source/use_case/object_detection/include/UseCaseHandler.hpp +++ b/source/use_case/object_detection/include/UseCaseHandler.hpp @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: Copyright 2022 Arm Limited and/or its affiliates + * SPDX-FileCopyrightText: Copyright 2022, 2024 Arm Limited and/or its affiliates * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -17,11 +17,39 @@ #ifndef OBJ_DET_HANDLER_HPP #define OBJ_DET_HANDLER_HPP +#if VSI_ENABLED +#include "DetectionResult.hpp" +#include +#endif + +#include #include "AppContext.hpp" namespace arm { namespace app { +#if VSI_ENABLED + typedef object_detection::DetectionResult OdResults; + + /** + * @brief Draw boxe(s) detected by the model. + * @param[in] image Pointer to the image. + * @param[in] imageWidth Image width. + * @param[in] imageHeight Image height. + * @param[in] results A vector of detection results. + **/ + void DrawDetectionBoxesVsi(uint8_t* image, + const uint32_t imageWidth, + const uint32_t imageHeight, + const std::vector& results); + /** + * @brief Handles the inference event when using VSI. + * @param[in] ctx Pointer to the application context. + * @return true or false based on execution success. + **/ + bool ObjectDetectionHandlerVsi(ApplicationContext& ctx); +#endif + /** * @brief Handles the inference event. * @param[in] ctx Pointer to the application context. diff --git a/source/use_case/object_detection/src/MainLoop.cc b/source/use_case/object_detection/src/MainLoop.cc index 4735bcb..fe21de6 100644 --- a/source/use_case/object_detection/src/MainLoop.cc +++ b/source/use_case/object_detection/src/MainLoop.cc @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: Copyright 2022 Arm Limited and/or its affiliates + * SPDX-FileCopyrightText: Copyright 2022, 2024 Arm Limited and/or its affiliates * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -41,7 +41,12 @@ static void DisplayDetectionMenu() printf(" %u. Run detection ifm at chosen index\n", common::MENU_OPT_RUN_INF_CHOSEN); printf(" %u. Run detection on all ifm\n", common::MENU_OPT_RUN_INF_ALL); printf(" %u. Show NN model info\n", common::MENU_OPT_SHOW_MODEL_INFO); - printf(" %u. List ifm\n\n", common::MENU_OPT_LIST_IFM); + printf(" %u. List ifm\n", common::MENU_OPT_LIST_IFM); +#if VSI_ENABLED + fflush(stdout); + printf(" %u. Run detection using VSI as input\n", common::MENU_OPT_RUN_INF_VSI); +#endif + printf("\n"); printf(" Choice: "); fflush(stdout); } @@ -99,6 +104,11 @@ void main_loop() case common::MENU_OPT_LIST_IFM: executionSuccessful = ListFilesHandler(caseContext); break; +#if VSI_ENABLED + case common::MENU_OPT_RUN_INF_VSI: + executionSuccessful = ObjectDetectionHandlerVsi(caseContext); + break; +#endif default: printf("Incorrect choice, try again."); break; diff --git a/source/use_case/object_detection/src/UseCaseHandler.cc b/source/use_case/object_detection/src/UseCaseHandler.cc index 9330187..1a20db5 100644 --- a/source/use_case/object_detection/src/UseCaseHandler.cc +++ b/source/use_case/object_detection/src/UseCaseHandler.cc @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: Copyright 2022 Arm Limited and/or its affiliates + * SPDX-FileCopyrightText: Copyright 2022, 2024 Arm Limited and/or its affiliates * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -28,6 +28,235 @@ namespace arm { namespace app { +#if VSI_ENABLED +#include "video_drv.h" /* Video Driver API. */ + + /** + * @brief Draws a box in the image using the object detection result object. + * + * @param[out] imageData Pointer to the start of the image. + * @param[in] width Image width. + * @param[in] height Image height. + * @param[in] result Object detection result. + */ + static void DrawBox(uint8_t* imageData, + const uint32_t width, + const uint32_t height, + const OdResults& result) + { + UNUSED(height); + const auto x = result.m_x0; + const auto y = result.m_y0; + const auto w = result.m_w; + const auto h = result.m_h; + + const uint32_t step = width * 3; + uint8_t* const imStart = imageData + (y * step) + (x * 3); + + uint8_t* dst_0 = imStart; + uint8_t* dst_1 = imStart + (h * step); + + for (uint32_t i = 0; i < static_cast(w); ++i) { + *dst_0 = 255; + *dst_1 = 255; + + dst_0 += 3; + dst_1 += 3; + } + + dst_0 = imStart; + dst_1 = imStart + (w * 3); + + for (uint32_t j = 0; j < static_cast(h); ++j) { + *dst_0 = 255; + *dst_1 = 255; + + dst_0 += step; + dst_1 += step; + } + } + + void DrawDetectionBoxesVsi(uint8_t* image, + const uint32_t imageWidth, + const uint32_t imageHeight, + const std::vector& results) + { + for (const auto& result : results) { + DrawBox(image, imageWidth, imageHeight, result); + printf("Detection :: [%d" ", %d" + ", %d" ", %d" "]\n", + result.m_x0, + result.m_y0, + result.m_w, + result.m_h); + } + } + + /* Object detection VSI inference handler. */ + bool ObjectDetectionHandlerVsi(ApplicationContext& ctx) + { + /* Image buffer. */ + static uint8_t ImageBuf[IMAGE_DATA_SIZE]; + static uint8_t ImageOut[IMAGE_DATA_SIZE]; + + /* Model object creation and initialisation. */ + auto& model = ctx.Get("model"); + + TfLiteTensor* inputTensor = model.GetInputTensor(0); + TfLiteTensor* outputTensor0 = model.GetOutputTensor(0); + TfLiteTensor* outputTensor1 = model.GetOutputTensor(1); + + if (!inputTensor->dims) { + printf_err("Invalid input tensor dims\n"); + return false; + } else if (inputTensor->dims->size < 3) { + printf_err("Input tensor dimension should be >= 3\n"); + return false; + } + + TfLiteIntArray* inputShape = model.GetInputShape(0); + const int inputImgCols = inputShape->data[arm::app::YoloFastestModel::ms_inputColsIdx]; + const int inputImgRows = inputShape->data[arm::app::YoloFastestModel::ms_inputRowsIdx]; + + /* Set up pre- and post-processing. */ + arm::app::DetectorPreProcess preProcess = + arm::app::DetectorPreProcess(inputTensor, true, model.IsDataSigned()); + + std::vector results; + const arm::app::object_detection::PostProcessParams postProcessParams{ + inputImgRows, + inputImgCols, + arm::app::object_detection::originalImageSize, + arm::app::object_detection::anchor1, + arm::app::object_detection::anchor2}; + arm::app::DetectorPostProcess postProcess = + arm::app::DetectorPostProcess(outputTensor0, outputTensor1, results, postProcessParams); + + const size_t imgSz = inputTensor->bytes < IMAGE_DATA_SIZE ? + inputTensor->bytes : IMAGE_DATA_SIZE; + + if (sizeof(ImageBuf) < imgSz) { + printf_err("Image buffer is insufficient\n"); + return false; + } + + /* Configure Input Video. */ + if (VideoDrv_Configure(VIDEO_DRV_IN0, + arm::app::object_detection::originalImageSize, + arm::app::object_detection::originalImageSize, + COLOR_RGB888, 24U) != VIDEO_DRV_OK) { + printf_err("Failed to configure video input\n"); + return false; + } + + /* Set Input Video buffer. */ + if (VideoDrv_SetBuf(VIDEO_DRV_IN0, ImageBuf, IMAGE_DATA_SIZE) != VIDEO_DRV_OK) { + printf_err("Failed to set buffer for video input\n"); + return false; + } + + /* Set Output Video file (only when using AVH - default: Display) */ + // if (VideoDrv_SetFile(VIDEO_DRV_OUT0, "output_image.png") != VIDEO_DRV_OK) { + // printf_err("Failed to set filename for video output\n"); + // return 1; + // } + /* Configure Output Video. */ + if (VideoDrv_Configure(VIDEO_DRV_OUT0, + arm::app::object_detection::originalImageSize, + arm::app::object_detection::originalImageSize, + COLOR_RGB888, 24U) != VIDEO_DRV_OK) { + printf_err("Failed to configure video output\n"); + return false; + } + + /* Set Output Video buffer. */ + if (VideoDrv_SetBuf(VIDEO_DRV_OUT0, ImageOut, IMAGE_DATA_SIZE) != VIDEO_DRV_OK) { + printf_err("Failed to set buffer for video output\n"); + return false; + } + + auto imgCount = ctx.Get("imgIndex"); + void* imgFrame = nullptr; + void* outFrame = nullptr; + + while (true) { +#if VSI_IMAGE_INPUT + if (VideoDrv_SetFile(VIDEO_DRV_IN0, GetFilePath(imgCount)) != VIDEO_DRV_OK) { + printf_err("Failed to set filename for video input\n"); + return false; + } +#endif + + VideoDrv_Status_t status; + + results.clear(); + + /* Start video capture (single frame). */ + if (VideoDrv_StreamStart(VIDEO_DRV_IN0, VIDEO_DRV_MODE_SINGLE) != VIDEO_DRV_OK) { + printf_err("Failed to start video capture\n"); + return false; + } + + /* Wait for video input frame. */ + do { + status = VideoDrv_GetStatus(VIDEO_DRV_IN0); + } while (status.buf_empty != 0U); + + /* Get input video frame buffer. */ + imgFrame = VideoDrv_GetFrameBuf(VIDEO_DRV_IN0); + + /* Run the pre-processing, inference and post-processing. */ + if (!preProcess.DoPreProcess(imgFrame, imgSz)) { + printf_err("Pre-processing failed.\n"); + return false; + } + + /* Run inference over this image. */ + printf("\rImage %" PRIu32 "; ", ++imgCount); + + if (!model.RunInference()) { + printf_err("Inference failed.\n"); + return false; + } + + if (!postProcess.DoPostProcess()) { + printf_err("Post-processing failed.\n"); + return false; + } + + /* Release input frame. */ + VideoDrv_ReleaseFrame(VIDEO_DRV_IN0); + + arm::app::DrawDetectionBoxesVsi(static_cast(imgFrame), inputImgCols, inputImgRows, results); + + /* Get output video frame buffer. */ + outFrame = VideoDrv_GetFrameBuf(VIDEO_DRV_OUT0); + + /* Copy image frame with detection boxes to output frame buffer. */ + memcpy(outFrame, imgFrame, IMAGE_DATA_SIZE); + + /* Release output frame. */ + VideoDrv_ReleaseFrame(VIDEO_DRV_OUT0); + + /* Start video output (single frame). */ + VideoDrv_StreamStart(VIDEO_DRV_OUT0, VIDEO_DRV_MODE_SINGLE); + + /* Check for end of stream (when using AVH with file as Video input). */ + if (status.eos != 0U) { + while (VideoDrv_GetStatus(VIDEO_DRV_OUT0).buf_empty == 0U); + break; + } + } + + IncrementAppCtxIfmIdx(ctx, "imgIndex"); + + /* De-initialize Video Interface. */ + //VideoDrv_Uninitialize(); + return true; + } + +#endif + /** * @brief Presents inference results along using the data presentation * object. diff --git a/source/use_case/object_detection/usecase.cmake b/source/use_case/object_detection/usecase.cmake index a36d3e0..cb0236b 100644 --- a/source/use_case/object_detection/usecase.cmake +++ b/source/use_case/object_detection/usecase.cmake @@ -1,5 +1,5 @@ #---------------------------------------------------------------------------- -# SPDX-FileCopyrightText: Copyright 2022 Arm Limited and/or its affiliates +# SPDX-FileCopyrightText: Copyright 2022, 2024 Arm Limited and/or its affiliates # SPDX-License-Identifier: Apache-2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -36,10 +36,24 @@ USER_OPTION(${use_case}_CHANNELS_IMAGE_DISPLAYED "Channels' image displayed on t BOOL) # Generate input files -generate_images_code("${${use_case}_FILE_PATH}" - ${SRC_GEN_DIR} - ${INC_GEN_DIR} - "${${use_case}_IMAGE_SIZE}") +if (VSI_IMAGE_INPUT) + set(${use_case}_COMPILE_DEFS "VSI_IMAGE_INPUT") + + generate_images_code( + INPUT_DIR "${${use_case}_FILE_PATH}" + SRC_OUT ${SRC_GEN_DIR} + HDR_OUT ${INC_GEN_DIR} + IMG_SIZE "${${use_case}_IMAGE_SIZE}" + GENERATE_FILE_PATHS + ) +else () + generate_images_code( + INPUT_DIR "${${use_case}_FILE_PATH}" + SRC_OUT ${SRC_GEN_DIR} + HDR_OUT ${INC_GEN_DIR} + IMG_SIZE "${${use_case}_IMAGE_SIZE}" + ) +endif () USER_OPTION(${use_case}_ACTIVATION_BUF_SZ "Activation buffer size for the chosen model" 0x00082000 diff --git a/source/use_case/vww/usecase.cmake b/source/use_case/vww/usecase.cmake index 7a4d876..99580cc 100644 --- a/source/use_case/vww/usecase.cmake +++ b/source/use_case/vww/usecase.cmake @@ -1,5 +1,5 @@ #---------------------------------------------------------------------------- -# SPDX-FileCopyrightText: Copyright 2021 Arm Limited and/or its affiliates +# SPDX-FileCopyrightText: Copyright 2021, 2024 Arm Limited and/or its affiliates # SPDX-License-Identifier: Apache-2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -58,7 +58,9 @@ generate_labels_code( ) # Generate input files -generate_images_code("${${use_case}_FILE_PATH}" - ${SRC_GEN_DIR} - ${INC_GEN_DIR} - "${${use_case}_IMAGE_SIZE}") +generate_images_code( + INPUT_DIR "${${use_case}_FILE_PATH}" + SRC_OUT ${SRC_GEN_DIR} + HDR_OUT ${INC_GEN_DIR} + IMG_SIZE "${${use_case}_IMAGE_SIZE}" +) -- cgit v1.2.1