summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRichard Burton <richard.burton@arm.com>2023-12-06 17:13:10 +0000
committeralex.tawse <alex.tawse@arm.com>2024-01-23 17:26:45 +0000
commitcefc7e1cacdd3028b46325b3a1f6c15416914b2f (patch)
treed0353f7775d18a207d8a9fd352a3b716ea7b9773
parent001a8ff315dc766a206b05a3e00e831e3f972b0d (diff)
downloadml-embedded-evaluation-kit-experimental/vsi.tar.gz
MLECO-4503: Adding video VSI for object detectionexperimental/vsi
* 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 <idriss.chaouch@arm.com> Signed-off-by: Richard Burton <richard.burton@arm.com> Change-Id: Ie59ae955d4d85f672a49c63733052624542aec85
-rw-r--r--.pylintrc6
-rwxr-xr-xbuild_default.py4
-rw-r--r--docs/sections/building.md10
-rw-r--r--docs/use_cases/object_detection.md81
-rw-r--r--scripts/cmake/common_user_options.cmake17
-rw-r--r--scripts/cmake/platforms/mps3/sse-310/mps3-sse-310.sct7
-rw-r--r--scripts/cmake/source_gen_utils.cmake22
-rw-r--r--scripts/py/gen_rgb_cpp.py43
-rw-r--r--scripts/py/templates/Images.cc.template20
-rw-r--r--scripts/py/templates/Images.hpp.template11
-rw-r--r--scripts/py/vsi/arm_vsi4.py207
-rw-r--r--scripts/py/vsi/arm_vsi5.py207
-rw-r--r--scripts/py/vsi/arm_vsi6.py207
-rw-r--r--scripts/py/vsi/arm_vsi7.py207
-rw-r--r--scripts/py/vsi/vsi_video.py461
-rw-r--r--scripts/py/vsi/vsi_video_server.py447
-rw-r--r--source/application/main/include/UseCaseCommonUtils.hpp17
-rw-r--r--source/hal/source/components/vsi/CMakeLists.txt79
-rw-r--r--source/hal/source/components/vsi/include/arm_vsi.h121
-rw-r--r--source/hal/source/components/vsi/include/video_drv.h142
-rw-r--r--source/hal/source/components/vsi/source/video_drv.c600
-rw-r--r--source/hal/source/platform/mps3/CMakeLists.txt14
-rw-r--r--source/hal/source/platform/mps3/include/sse-300/peripheral_irqs.h10
-rw-r--r--source/hal/source/platform/mps3/include/sse-310/peripheral_irqs.h10
-rw-r--r--source/hal/source/platform/mps3/include/vsi_mps3.h115
-rw-r--r--source/hal/source/platform/mps3/source/platform_drivers.c12
-rw-r--r--source/hal/source/platform/mps3/source/vsi_mps3.c62
-rw-r--r--source/use_case/img_class/usecase.cmake12
-rw-r--r--source/use_case/object_detection/include/UseCaseHandler.hpp30
-rw-r--r--source/use_case/object_detection/src/MainLoop.cc14
-rw-r--r--source/use_case/object_detection/src/UseCaseHandler.cc231
-rw-r--r--source/use_case/object_detection/usecase.cmake24
-rw-r--r--source/use_case/vww/usecase.cmake12
33 files changed, 3398 insertions, 64 deletions
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 <open-source-office@arm.com>
+# SPDX-FileCopyrightText: Copyright 2023-2024 Arm Limited and/or its affiliates <open-source-office@arm.com>
# 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 <open-source-office@arm.com>
+# SPDX-FileCopyrightText: Copyright 2021-2024 Arm Limited and/or its affiliates <open-source-office@arm.com>
# 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 <open-source-office@arm.com>
+# SPDX-FileCopyrightText: Copyright 2021-2022, 2024 Arm Limited and/or its affiliates <open-source-office@arm.com>
# 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 <open-source-office@arm.com>
+; SPDX-FileCopyrightText: Copyright 2021, 2023-2024 Arm Limited and/or its affiliates <open-source-office@arm.com>
; 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 <open-source-office@arm.com>
+# SPDX-FileCopyrightText: Copyright 2021, 2024 Arm Limited and/or its affiliates <open-source-office@arm.com>
# 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 <open-source-office@arm.com>
+# SPDX-FileCopyrightText: Copyright 2021-2024 Arm Limited and/or its affiliates <open-source-office@arm.com>
# 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
@@ -60,6 +61,13 @@ parser.add_argument(
)
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,
help="Header template file",
@@ -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 <open-source-office@arm.com>
+ SPDX-FileCopyrightText: Copyright 2021, 2024 Arm Limited and/or its affiliates <open-source-office@arm.com>
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 <open-source-office@arm.com>
+ SPDX-FileCopyrightText: Copyright 2021, 2024 Arm Limited and/or its affiliates <open-source-office@arm.com>
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 <open-source-office@arm.com>
+# 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 <open-source-office@arm.com>
+# 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 <open-source-office@arm.com>
+# 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 <open-source-office@arm.com>
+# 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 <open-source-office@arm.com>
+# 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 <open-source-office@arm.com>
+# 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="<IP>",
+ help=f"Server IP address (default: {default_address[0]})",
+ type=ip, default=default_address[0])
+ parser_optional.add_argument("--port", dest="port", metavar="<TCP Port>",
+ help=f"TCP port (default: {default_address[1]})",
+ type=int, default=default_address[1])
+ parser_optional.add_argument("--authkey", dest="authkey", metavar="<Auth Key>",
+ 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 <open-source-office@arm.com>
+ * SPDX-FileCopyrightText: Copyright 2021-2022, 2024 Arm Limited and/or its affiliates <open-source-office@arm.com>
* 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 <open-source-office@arm.com>
+# 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 <open-source-office@arm.com>
+* 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 <stdint.h>
+
+/* 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 <open-source-office@arm.com>
+* 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 <stdint.h>
+
+/* 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 <open-source-office@arm.com>
+* 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 <stdint.h>
+#include <string.h>
+#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 <open-source-office@arm.com>
+# SPDX-FileCopyrightText: Copyright 2022, 2024 Arm Limited and/or its affiliates <open-source-office@arm.com>
# 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 <open-source-office@arm.com>
+ * SPDX-FileCopyrightText: Copyright 2022, 2024 Arm Limited and/or its affiliates <open-source-office@arm.com>
* 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 <open-source-office@arm.com>
+ * SPDX-FileCopyrightText: Copyright 2022, 2024 Arm Limited and/or its affiliates <open-source-office@arm.com>
* 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 <open-source-office@arm.com>
+ * 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 <stdint.h>
+#include <stdbool.h>
+
+#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 <open-source-office@arm.com>
+ * SPDX-FileCopyrightText: Copyright 2022-2024 Arm Limited and/or its affiliates <open-source-office@arm.com>
* 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 <open-source-office@arm.com>
+ * 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 <open-source-office@arm.com>
+# SPDX-FileCopyrightText: Copyright 2021, 2024 Arm Limited and/or its affiliates <open-source-office@arm.com>
# 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 <open-source-office@arm.com>
+ * SPDX-FileCopyrightText: Copyright 2022, 2024 Arm Limited and/or its affiliates <open-source-office@arm.com>
* 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 <vector>
+#endif
+
+#include <cstdint>
#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<OdResults>& 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 <open-source-office@arm.com>
+ * SPDX-FileCopyrightText: Copyright 2022, 2024 Arm Limited and/or its affiliates <open-source-office@arm.com>
* 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
* <open-source-office@arm.com> 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<uint32_t>(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<uint32_t>(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<OdResults>& 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&>("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<arm::app::OdResults> 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<uint32_t>("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<uint8_t*>(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 <open-source-office@arm.com>
+# SPDX-FileCopyrightText: Copyright 2022, 2024 Arm Limited and/or its affiliates <open-source-office@arm.com>
# 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 <open-source-office@arm.com>
+# SPDX-FileCopyrightText: Copyright 2021, 2024 Arm Limited and/or its affiliates <open-source-office@arm.com>
# 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}"
+)