From e50a923671905f59bd34bd2146e0cfee477242df Mon Sep 17 00:00:00 2001 From: Christopher Sidebottom Date: Thu, 10 Feb 2022 09:11:58 +0000 Subject: [PATCH] Add FreeRTOS variant of NPU demo (#10004) * Add FreeRTOS variant of NPU demo This adds an extra flag to the existing NPU demo that runs it using the FreeRTOS kernel task scheduling and queues. * Add FreeRTOS notes to tutorial * Update FreeRTOS demo to run in demo script Also, minor text fixes. * Minor text fixes * Fix docs formatting --- apps/microtvm/ethosu/.gitignore | 3 + apps/microtvm/ethosu/Makefile | 21 ++- apps/microtvm/ethosu/README.md | 7 +- apps/microtvm/ethosu/include/FreeRTOSConfig.h | 112 +++++++++++++++ apps/microtvm/ethosu/include/tvm_runtime.h | 2 +- apps/microtvm/ethosu/run_demo.sh | 14 +- .../ethosu/src/{demo.c => demo_bare_metal.c} | 0 apps/microtvm/ethosu/src/demo_freertos.c | 131 ++++++++++++++++++ apps/microtvm/zephyr_cmsisnn/run_demo.sh | 1 - .../how_to/work_with_microtvm/micro_ethosu.py | 13 ++ tests/scripts/task_cpp_unittest.sh | 14 -- tests/scripts/task_demo_microtvm.sh | 9 ++ 12 files changed, 307 insertions(+), 20 deletions(-) create mode 100644 apps/microtvm/ethosu/.gitignore create mode 100644 apps/microtvm/ethosu/include/FreeRTOSConfig.h rename apps/microtvm/ethosu/src/{demo.c => demo_bare_metal.c} (100%) create mode 100644 apps/microtvm/ethosu/src/demo_freertos.c diff --git a/apps/microtvm/ethosu/.gitignore b/apps/microtvm/ethosu/.gitignore new file mode 100644 index 000000000000..3ef8f08db900 --- /dev/null +++ b/apps/microtvm/ethosu/.gitignore @@ -0,0 +1,3 @@ +include/inputs.h +include/outputs.h +include/labels.h diff --git a/apps/microtvm/ethosu/Makefile b/apps/microtvm/ethosu/Makefile index d76bf9058a1d..1c4af7774451 100644 --- a/apps/microtvm/ethosu/Makefile +++ b/apps/microtvm/ethosu/Makefile @@ -58,6 +58,23 @@ $(else) QUIET ?= @ $(endif) +ifdef FREERTOS_PATH +DEMO_MAIN = src/demo_freertos.c +FREERTOS_KERNEL = $(FREERTOS_PATH)/FreeRTOS/Source +FREERTOS_FLAGS = -I$(FREERTOS_KERNEL)/include/ \ + -I$(FREERTOS_KERNEL)/portable/GCC/ARM_CM33_NTZ/non_secure/ +FREERTOS_SOURCES = $(FREERTOS_KERNEL)/portable/GCC/ARM_CM33_NTZ/non_secure/port.c \ + $(FREERTOS_KERNEL)/portable/GCC/ARM_CM33_NTZ/non_secure/portasm.c \ + $(FREERTOS_KERNEL)/tasks.c \ + $(FREERTOS_KERNEL)/list.c \ + $(FREERTOS_KERNEL)/queue.c \ + $(FREERTOS_KERNEL)/timers.c \ + $(FREERTOS_KERNEL)/event_groups.c \ + $(FREERTOS_KERNEL)/portable/MemMang/heap_3.c +else +DEMO_MAIN = src/demo_bare_metal.c +endif + CODEGEN_SRCS = $(wildcard $(abspath $(BUILD_DIR))/codegen/host/src/*.c) CODEGEN_OBJS = $(subst .c,.o,$(CODEGEN_SRCS)) CMSIS_STARTUP_SRCS = $(wildcard ${CMSIS_PATH}/Device/ARM/${ARM_CPU}/Source/*.c) @@ -99,9 +116,9 @@ ${BUILD_DIR}/cmsis_nn/Source/SoftmaxFunctions/libCMSISNNSoftmax.a: $(QUIET)cd $(abspath $(BUILD_DIR)/cmsis_nn) && $(MAKE) CMSISNNSoftmax # Build demo application -$(BUILD_DIR)/demo: src/demo.c src/tvm_ethosu_runtime.c $(UART_SRCS) $(BUILD_DIR)/stack_allocator.o $(BUILD_DIR)/crt_backend_api.o ${BUILD_DIR}/libcodegen.a ${BUILD_DIR}/libcmsis_startup.a ${BUILD_DIR}/ethosu_core_driver/libethosu_core_driver.a ${BUILD_DIR}/cmsis_nn/Source/SoftmaxFunctions/libCMSISNNSoftmax.a +$(BUILD_DIR)/demo: $(DEMO_MAIN) src/tvm_ethosu_runtime.c $(FREERTOS_SOURCES) $(UART_SRCS) $(BUILD_DIR)/stack_allocator.o $(BUILD_DIR)/crt_backend_api.o ${BUILD_DIR}/libcodegen.a ${BUILD_DIR}/libcmsis_startup.a ${BUILD_DIR}/ethosu_core_driver/libethosu_core_driver.a ${BUILD_DIR}/cmsis_nn/Source/SoftmaxFunctions/libCMSISNNSoftmax.a $(QUIET)mkdir -p $(@D) - $(QUIET)$(CC) $(PKG_CFLAGS) -o $@ $^ $(PKG_LDFLAGS) + $(QUIET)$(CC) $(PKG_CFLAGS) $(FREERTOS_FLAGS) -o $@ $^ $(PKG_LDFLAGS) clean: $(QUIET)rm -rf $(BUILD_DIR)/codegen diff --git a/apps/microtvm/ethosu/README.md b/apps/microtvm/ethosu/README.md index e681b29b6b30..1f08928a6ee4 100644 --- a/apps/microtvm/ethosu/README.md +++ b/apps/microtvm/ethosu/README.md @@ -58,12 +58,17 @@ export PATH=/opt/arm/FVP_Corstone_SSE-300/models/Linux64_GCC-6.4:/opt/arm/cmake/ Running the demo application ---------------------------- -Type the following command to run the demo application: +Type the following command to run the bare metal demo application ([src/demo_bare_metal.c](./src/demo_bare_metal.c)): ```bash ./run_demo.sh ``` +To run the demo using FreeRTOS task scheduling and queues ([src/demo_freertos.c](./src/demo_freertos.c)), specify the path to FreeRTOS using `--freertos_path`, for example: +``` +./run_demo.sh --freertos_path /opt/freertos/FreeRTOSv202112.00/ +``` + If the Ethos(TM)-U driver and/or CMSIS have not been installed in /opt/arm/ethosu then the locations for these can be specified as arguments to run_demo.sh, for example: diff --git a/apps/microtvm/ethosu/include/FreeRTOSConfig.h b/apps/microtvm/ethosu/include/FreeRTOSConfig.h new file mode 100644 index 000000000000..a123581d3b77 --- /dev/null +++ b/apps/microtvm/ethosu/include/FreeRTOSConfig.h @@ -0,0 +1,112 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +/* Please refer to http://www.freertos.org/a00110.html for refernce. */ +#ifndef FREERTOS_CONFIG_H +#define FREERTOS_CONFIG_H +/****************************************************************************** + * Defines + **********SYSTEM_CORE_CLOCK********************************************************************/ +/* Hardware features */ +#define configENABLE_MPU 0 +#define configENABLE_FPU 0 +#define configENABLE_TRUSTZONE 0 +/* Scheduling */ +#define configCPU_CLOCK_HZ 25000000 +#define configUSE_PORT_OPTIMISED_TASK_SELECTION 0 +#define configUSE_PREEMPTION 1 +#define configUSE_TIME_SLICING 0 +#define configMAX_PRIORITIES 5 +#define configIDLE_SHOULD_YIELD 1 +#define configUSE_16_BIT_TICKS 0 +#define configRUN_FREERTOS_SECURE_ONLY 1 +/* Stack and heap */ +#define configMINIMAL_STACK_SIZE (uint16_t)128 +#define configMINIMAL_SECURE_STACK_SIZE 1024 +#define configTOTAL_HEAP_SIZE (size_t)(50 * 1024) +#define configMAX_TASK_NAME_LEN 12 +/* OS features */ +#define configUSE_MUTEXES 1 +#define configUSE_TICKLESS_IDLE 1 +#define configUSE_APPLICATION_TASK_TAG 0 +#define configUSE_NEWLIB_REENTRANT 0 +#define configUSE_CO_ROUTINES 0 +#define configUSE_COUNTING_SEMAPHORES 1 +#define configUSE_RECURSIVE_MUTEXES 1 +#define configUSE_QUEUE_SETS 0 +#define configUSE_TASK_NOTIFICATIONS 1 +#define configUSE_TRACE_FACILITY 1 +/* Hooks */ +#define configUSE_IDLE_HOOK 0 +#define configUSE_TICK_HOOK 0 +#define configUSE_MALLOC_FAILED_HOOK 0 +/* Debug features */ +#define configCHECK_FOR_STACK_OVERFLOW 0 +#define configASSERT(x) \ + if ((x) == 0) { \ + taskDISABLE_INTERRUPTS(); \ + for (;;) \ + ; \ + } +#define configQUEUE_REGISTRY_SIZE 0 +/* Timers and queues */ +#define configUSE_TIMERS 1 +#define configTIMER_TASK_PRIORITY (configMAX_PRIORITIES - 1) +#define configTIMER_TASK_STACK_DEPTH configMINIMAL_STACK_SIZE +#define configTIMER_QUEUE_LENGTH 5 +/* Task settings */ +#define INCLUDE_vTaskPrioritySet 1 +#define INCLUDE_uxTaskPriorityGet 1 +#define INCLUDE_vTaskDelete 1 +#define INCLUDE_vTaskCleanUpResources 0 +#define INCLUDE_vTaskSuspend 1 +#define INCLUDE_vTaskDelayUntil 1 +#define INCLUDE_vTaskDelay 1 +#define INCLUDE_uxTaskGetStackHighWaterMark 0 +#define INCLUDE_xTaskGetIdleTaskHandle 0 +#define INCLUDE_eTaskGetState 1 +#define INCLUDE_xTaskResumeFromISR 0 +#define INCLUDE_xTaskGetCurrentTaskHandle 1 +#define INCLUDE_xTaskGetSchedulerState 0 +#define INCLUDE_xSemaphoreGetMutexHolder 0 +#define INCLUDE_xTimerPendFunctionCall 1 +#define configUSE_STATS_FORMATTING_FUNCTIONS 1 +#define configCOMMAND_INT_MAX_OUTPUT_SIZE 2048 +#ifdef __NVIC_PRIO_BITS +#define configPRIO_BITS __NVIC_PRIO_BITS +#else +#define configPRIO_BITS 3 +#endif +/* Interrupt settings */ +#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY 0x07 +#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 5 +#define configKERNEL_INTERRUPT_PRIORITY \ + (configLIBRARY_LOWEST_INTERRUPT_PRIORITY << (8 - configPRIO_BITS)) +#define configMAX_SYSCALL_INTERRUPT_PRIORITY \ + (configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY << (8 - configPRIO_BITS)) +#ifndef __IASMARM__ +#define configGENERATE_RUN_TIME_STATS 0 +#define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() +#define portGET_RUN_TIME_COUNTER_VALUE() 0 +#define configTICK_RATE_HZ (TickType_t)1000 +#endif /* __IASMARM__ */ +#define xPortPendSVHandler PendSV_Handler +#define vPortSVCHandler SVC_Handler +#define xPortSysTickHandler SysTick_Handler +#endif /* FREERTOS_CONFIG_H */ diff --git a/apps/microtvm/ethosu/include/tvm_runtime.h b/apps/microtvm/ethosu/include/tvm_runtime.h index 09d766ef6a29..3ab99817136e 100644 --- a/apps/microtvm/ethosu/include/tvm_runtime.h +++ b/apps/microtvm/ethosu/include/tvm_runtime.h @@ -57,4 +57,4 @@ TVM_DLL int TVMFuncRegisterGlobal(const char* name, TVMFunctionHandle f, int ove #ifdef __cplusplus } -#endif \ No newline at end of file +#endif diff --git a/apps/microtvm/ethosu/run_demo.sh b/apps/microtvm/ethosu/run_demo.sh index 7c2bb09a60f0..18d82f7ead41 100755 --- a/apps/microtvm/ethosu/run_demo.sh +++ b/apps/microtvm/ethosu/run_demo.sh @@ -71,6 +71,18 @@ while (( $# )); do fi ;; + --freertos_path) + if [ $# -gt 1 ] + then + export FREERTOS_PATH="$2" + shift 2 + else + echo 'ERROR: --freertos_path requires a non-empty argument' >&2 + show_usage >&2 + exit 1 + fi + ;; + --ethosu_platform_path) if [ $# -gt 1 ] then @@ -105,7 +117,7 @@ while (( $# )); do show_usage >&2 exit 1 fi - ;; + ;; -*|--*) echo "Error: Unknown flag: $1" >&2 diff --git a/apps/microtvm/ethosu/src/demo.c b/apps/microtvm/ethosu/src/demo_bare_metal.c similarity index 100% rename from apps/microtvm/ethosu/src/demo.c rename to apps/microtvm/ethosu/src/demo_bare_metal.c diff --git a/apps/microtvm/ethosu/src/demo_freertos.c b/apps/microtvm/ethosu/src/demo_freertos.c new file mode 100644 index 000000000000..cd5b76b15f49 --- /dev/null +++ b/apps/microtvm/ethosu/src/demo_freertos.c @@ -0,0 +1,131 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include +#include +#include +#include +#include + +#include "ethosu_mod.h" +#include "uart.h" + +// Header files generated by convert_image.py and convert_labels.py +#include "inputs.h" +#include "labels.h" +#include "outputs.h" + +static void prvInferenceTask(void* pvParameters); +static void prvDataCollectionTask(void* pvParameters); + +#define mainQUEUE_INFERENCE_TASK_PRIORITY (tskIDLE_PRIORITY + 3) +#define mainQUEUE_INFERENCE_TASK_STACK_SIZE 4096 +#define mainQUEUE_DATA_TASK_PRIORITY (tskIDLE_PRIORITY + 2) +#define mainQUEUE_DATA_TASK_STACK_SIZE configMINIMAL_STACK_SIZE +#define mainQUEUE_LENGTH (1) +#define mainQUEUE_SEND_FREQUENCY_MS (100 / portTICK_PERIOD_MS) + +/* The queue used to pass data to run through our model */ +static QueueHandle_t xQueue = NULL; + +int main(void) { + // Platform UART + uart_init(); + // NPU + EthosuInit(); + // TVM Memory Manager + StackMemoryManager_Init(&app_workspace, g_aot_memory, WORKSPACE_SIZE); + + // Queue for inferences + xQueue = xQueueCreate(mainQUEUE_LENGTH, sizeof(uint8_t*)); + + if (xQueue != NULL) { + // Inference task + xTaskCreate(prvInferenceTask, "Inference", mainQUEUE_INFERENCE_TASK_STACK_SIZE, NULL, + mainQUEUE_INFERENCE_TASK_PRIORITY, NULL); + + // Data collector task + xTaskCreate(prvDataCollectionTask, "Data", mainQUEUE_DATA_TASK_STACK_SIZE, NULL, + mainQUEUE_DATA_TASK_PRIORITY, NULL); + + // Start the task scheduling + vTaskStartScheduler(); + } + + // The task scheduler should take over before this is reached + printf("Unreachable code reached!\n"); +} + +/* + * This task emulates collection of data and sending it to another inference task + * for processing + */ +static void prvDataCollectionTask(void* pvParameters) { + // Unused + (void)pvParameters; + + // Working + vTaskDelay(mainQUEUE_SEND_FREQUENCY_MS); + + // Construct pointer to copy to queue + uint8_t** pucInputData = &input; + xQueueSend(xQueue, &pucInputData, 0U); +} + +/* + * This task emulates the inference of data sent by the collector task + */ +static void prvInferenceTask(void* pvParameters) { + uint8_t* pucReceivedData; + + // Unused + (void)pvParameters; + + // Wait for data collection + xQueueReceive(xQueue, &pucReceivedData, portMAX_DELAY); + + // Print output of inference and exit task + printf("Running inference\n"); + struct tvmgen_default_inputs xInputs = { + .tfl_quantize = pucReceivedData, + }; + struct tvmgen_default_outputs xOutputs = { + .output = output, + }; + struct ethosu_driver* xDriver = ethosu_reserve_driver(); + struct tvmgen_default_devices xDevices = { + .ethos_u = xDriver, + }; + tvmgen_default_run(&xInputs, &xOutputs, &xDevices); + ethosu_release_driver(xDriver); + + // Calculate index of max value + int8_t ucMaxValue = -128; + int32_t lMaxIndex = -1; + for (unsigned int i = 0; i < output_len; ++i) { + if (output[i] > ucMaxValue) { + ucMaxValue = output[i]; + lMaxIndex = i; + } + } + printf("The image has been classified as '%s'\n", labels[lMaxIndex]); + + // The FVP will shut down when it receives "EXITTHESIM" on the UART + printf("EXITTHESIM\n"); +} diff --git a/apps/microtvm/zephyr_cmsisnn/run_demo.sh b/apps/microtvm/zephyr_cmsisnn/run_demo.sh index 13d163b5f7c2..6588b561743b 100755 --- a/apps/microtvm/zephyr_cmsisnn/run_demo.sh +++ b/apps/microtvm/zephyr_cmsisnn/run_demo.sh @@ -45,7 +45,6 @@ export ARMFVP_BIN_PATH=/opt/arm/FVP_Corstone_SSE-300/models/Linux64_GCC-6.4/ west zephyr-export west build west build -t run &> ${LOGDIR}/west.log & -WEST_PID=$! # Wait for "exit" keyword until grep -m 1 "exit" ${LOGDIR}/west.log; do sleep 1 ; done diff --git a/gallery/how_to/work_with_microtvm/micro_ethosu.py b/gallery/how_to/work_with_microtvm/micro_ethosu.py index 848db2e9dda1..ef34c7d7ccb5 100644 --- a/gallery/how_to/work_with_microtvm/micro_ethosu.py +++ b/gallery/how_to/work_with_microtvm/micro_ethosu.py @@ -411,6 +411,12 @@ # # `include files `_ +################################################################################ +# .. note:: +# +# If you'd like to use FreeRTOS for task scheduling and queues, a sample application can be found here +# `demo_freertos.c ` + ################################################################################ # Creating the linker script # -------------------------- @@ -453,6 +459,13 @@ # An example Makefile can be found here: # `Makefile `_ +################################################################################ +# .. note:: +# +# If you're using FreeRTOS, the Makefile builds it from the specified FREERTOS_PATH: +# ``make FREERTOS_PATH=`` +# + ################################################################################ # Running the demo application # ---------------------------- diff --git a/tests/scripts/task_cpp_unittest.sh b/tests/scripts/task_cpp_unittest.sh index e6c3669c7605..3df7b580d79d 100755 --- a/tests/scripts/task_cpp_unittest.sh +++ b/tests/scripts/task_cpp_unittest.sh @@ -45,17 +45,3 @@ cd apps/bundle_deploy rm -rf build make test_dynamic test_static cd ../.. - -# Test Arm(R) Cortex(R)-M55 CPU and Ethos(TM)-U55 NPU demo app -FVP_PATH="/opt/arm/FVP_Corstone_SSE-300" - -# TODO(@grant-arm): Remove once ci_cpu docker image has been updated to FVP_Corstone_SSE -if test ! -d $FVP_PATH; then - FVP_PATH="/opt/arm/FVP_Corstone_SSE-300_Ethos-U55" -fi - -if test -d $FVP_PATH && pip3 list | grep vela; then - cd apps/microtvm/ethosu - ./run_demo.sh --fvp_path $FVP_PATH --cmake_path /opt/arm/cmake/bin/cmake - cd ../../.. -fi diff --git a/tests/scripts/task_demo_microtvm.sh b/tests/scripts/task_demo_microtvm.sh index 383e69374b51..c569f6ce686c 100755 --- a/tests/scripts/task_demo_microtvm.sh +++ b/tests/scripts/task_demo_microtvm.sh @@ -21,3 +21,12 @@ set -euxo pipefail pushd apps/microtvm/zephyr_cmsisnn ./run_demo.sh popd + +pushd apps/microtvm/ethosu +FVP_PATH="/opt/arm/FVP_Corstone_SSE-300_Ethos-U55" +CMAKE_PATH="/opt/arm/cmake/bin/cmake" +FREERTOS_PATH="/opt/freertos/FreeRTOSv202112.00" + +./run_demo.sh --fvp_path $FVP_PATH --cmake_path $CMAKE_PATH +./run_demo.sh --fvp_path $FVP_PATH --cmake_path $CMAKE_PATH --freertos_path $FREERTOS_PATH +popd