From 1122680e56ad2740ce70488eed69830b4e94a8ef Mon Sep 17 00:00:00 2001 From: Artur Tynecki <77382963+ATmobica@users.noreply.github.com> Date: Tue, 16 May 2023 04:56:13 +0200 Subject: [PATCH] [OIS] CI improvments and stabilization (#25866) * [OIS] PlatformManagerImpl improvements Improve CHIP stack initialization in PlatformManager - add stack initialization flag. Check initialization stack state in event loop functions. Initialize the CHIP stack in timer starting and event posting. Dispatch all events in queue. Signed-off-by: ATmobica * [OIS] Improve unit tests Remove CHIP stack initialization in unit-tests application main. Restore CHIP stack init and shutdown in unit tests context. Skip TestEventLogging due to lack of real-time clock support. Skip TestTLVPacketBufferBackingStore due to LwIP buffers usage in SystemPacketBuffer. Signed-off-by: ATmobica * [Fix] Fix LwIP structure initialization in unit-tests Zero-initialization of ip6_addr_t structure in TestInetAddress. That protects against error that may occur when the memory is aligned to multiples of 4 and LWIP_IPV6_SCOPES is enabled. In this case, the ip6_addr_t contains 16 bytes array of address and 1 byte of zone but the structure length is 20. If we don't set the initial value for ip_addr_1 and ip_addr_2 the memcmp returns mismatch. Signed-off-by: ATmobica * [Fix] Fix PlatformMgr unit test - event loop stopping Add sleep after stopping event loop task. In OIS platform implementations the event loop thread is self-terminating. We need time to process the stopping event inside event loop. Signed-off-by: ATmobica * [OIS] Improve build/run script Update Matter Python builder. Check if FVP process run properly and wait for the connection port log. Validate if FVP starts properly. Wait for the FVP stop. Move Pigweed env activation to a common point. Create supported apps list and use it. Fixing the optimization issue for mcu-driver-hal target in the non-debug build. Signed-off-by: ATmobica * [OIS] Improve internal cmake function Use target name from function argument instead of APP_TARGET variable in linker.cmake and sdk.cmake. Add custom command to sign the TF-M non-secure image. Signed-off-by: ATmobica * [OIS] Integration test suite improvements Common: Improve commissioning function to return commission error. Check if FVP process run properly and wait for the connection port log. Set verbose for wait_for_output() function. The default value is false - not print waiting log. Fix telnet connection readline function, buffering output date and processes only entire lines (ending with "\n"). Increase wait for lock-app application run timeout. Increase wait for setup QR code timeout. Add "verbose" state setting in the device class. Improve read/write ZCL attributes and send ZCL command functions. Improve controller device - add controllerConfig fixture, the dictionary with settings, remove persistent storage file at the end, change fixture scope to session. Lock test: Apply common changes. Shell test: Change the shell prompt to "Enter command:\r\n". It should be terminated by a newline character. Add waiting for the prompt before sending the shell command. Update file header. Signed-off-by: ATmobica * [OIS] Extend RAM size for TF-M app For unit-test applications, 1MB ISRAM0 is not enough. Extend it by adding ISRAM1, then 2MB of memory is available. Change TF-M linker script and AN552 target configuration. Signed-off-by: ATmobica * [OIS] Add TF-M support to unit-tests Adapt unit-tests project to TF-M support. Change unit-tests debug Vscode task. Increase application run timeout for unit-tests integration test. Signed-off-by: ATmobica * [OIS] Move unit-test validation to pyTest integration tests Create unit-tests Python integration test that checks and control time execution of OIS unit-tests. Change run script to only run single test on FVP. Add testing of unit-test apps to test script and Vscode tasks and CI. Signed-off-by: ATmobica * [OIS] Improve LwIP configuration Open IoT SDK version udpate. Move LwIP opts settings to sdk.cmake. As default the config/openiotsdk/lwip/user_lwipopts.h file is used. You can provide your settings with define LWIP_PROJECT_OPTS_DIR with path to custom file. Add CONFIG_CHIP_OPEN_IOT_SDK_LWIP_DEBUG variable to enable LwIP debug logs. It's disabled by default. Add LwIP debug logs option to build script and VScode tasks. Create common LwIP options for examples and unit-tests. Signed-off-by: ATmobica * [OIS] TF-M settings improvements Enable TFM fault trace support - application hangs instead of reset in case of fault. Force secure partition isolation to level 1 - this speed up processing which allows slow Github runners to pass the tests. Signed-off-by: Vincent Coubard * [OIS] Rework application startup and serial output Add startup library This library setup the C runtime: - Initialize stdio - Start the kernel - Create an run the main thread The main thread setups TFM and the locks used by the libc before entering main. Additionaly, lock support has been added to malloc/free, operator new and delete have been overriden to print errors and sbrk is overriden to print heap segment overallocation. The startup also rework stdio: - Override iotsdk-serial-retarget - Lock stream at the libc level - Buffer bytes on the RX path Add option to set serial baud rate from Cmake level. Decrease serial baudrate for shell examples to 9600. Create ois_logging_init() function and move setting log filter for non-debug mode to it. Co-authored-by: ATmobica Signed-off-by: Vincent Coubard * [OIS] Fix TDB key-value store string writing Add correct condition for nullptr string argument. Signed-off-by: ATmobica * [OIS] Restore testing steps in the CI workflow Adjust timeouts for job and steps. Signed-off-by: ATmobica --------- Signed-off-by: ATmobica Signed-off-by: Vincent Coubard Co-authored-by: Vincent Coubard --- .github/workflows/examples-openiotsdk.yaml | 21 +- .vscode/launch.json | 8 +- .vscode/tasks.json | 33 +- config/openiotsdk/cmake/linker.cmake | 6 +- config/openiotsdk/cmake/sdk.cmake | 111 ++-- config/openiotsdk/ld/cs300_gcc_tfm.ld | 2 +- config/openiotsdk/lwip/user_lwipopts.h | 33 +- examples/lock-app/openiotsdk/CMakeLists.txt | 19 +- examples/lock-app/openiotsdk/main/main_ns.cpp | 53 +- .../platform/openiotsdk/app/CMakeLists.txt | 33 +- .../openiotsdk/app/openiotsdk_platform.cpp | 47 +- .../openiotsdk/app/openiotsdk_startup_gcc.cpp | 573 ++++++++++++++++++ .../targets/an552/partition/region_defs.h | 4 +- .../tf-m/targets/an552/target_cfg.c | 30 +- examples/shell/openiotsdk/CMakeLists.txt | 29 +- .../main/include/CHIPProjectConfig.h | 2 + examples/shell/openiotsdk/main/main_ns.cpp | 55 +- scripts/build/builders/openiotsdk.py | 1 + .../testdata/dry_run_openiotsdk-lock.txt | 2 +- .../testdata/dry_run_openiotsdk-shell.txt | 2 +- scripts/examples/openiotsdk_example.sh | 230 +++---- src/app/tests/BUILD.gn | 5 +- src/app/tests/TestFailSafeContext.cpp | 4 - src/inet/tests/TestInetAddress.cpp | 2 +- src/platform/openiotsdk/BUILD.gn | 1 + .../openiotsdk/KVBlockDeviceStore.cpp | 4 +- src/platform/openiotsdk/KVPsaPsStore.cpp | 2 +- src/platform/openiotsdk/Logging.cpp | 21 +- .../openiotsdk/Logging.h} | 22 +- .../openiotsdk/PlatformManagerImpl.cpp | 68 ++- src/platform/openiotsdk/PlatformManagerImpl.h | 1 + src/platform/tests/TestPlatformMgr.cpp | 6 + src/system/tests/BUILD.gn | 5 +- .../openiotsdk/integration-tests/.gitignore | 2 +- .../integration-tests/common/device.py | 8 +- .../integration-tests/common/fixtures.py | 29 +- .../integration-tests/common/fvp_device.py | 34 +- .../common/telnet_connection.py | 15 +- .../integration-tests/common/utils.py | 159 +++-- .../integration-tests/lock-app/test_app.py | 107 ++-- .../integration-tests/shell/test_app.py | 54 +- .../integration-tests/unit-tests/__init__.py | 0 .../integration-tests/unit-tests/test_app.py | 45 ++ .../openiotsdk/unit-tests/.gitignore | 1 - .../openiotsdk/unit-tests/CMakeLists.txt | 40 +- .../freertos-config/FreeRTOSConfig.h | 8 +- .../unit-tests/main/{main.cpp => main_ns.cpp} | 27 +- third_party/open-iot-sdk/sdk | 2 +- 48 files changed, 1369 insertions(+), 597 deletions(-) create mode 100644 examples/platform/openiotsdk/app/openiotsdk_startup_gcc.cpp rename src/{test_driver/openiotsdk/unit-tests/lwip-config/user_lwipopts.h => platform/openiotsdk/Logging.h} (67%) create mode 100644 src/test_driver/openiotsdk/integration-tests/unit-tests/__init__.py create mode 100644 src/test_driver/openiotsdk/integration-tests/unit-tests/test_app.py rename src/test_driver/openiotsdk/unit-tests/main/{main.cpp => main_ns.cpp} (73%) diff --git a/.github/workflows/examples-openiotsdk.yaml b/.github/workflows/examples-openiotsdk.yaml index e34b079f2b81d7..d2f2e904c70827 100644 --- a/.github/workflows/examples-openiotsdk.yaml +++ b/.github/workflows/examples-openiotsdk.yaml @@ -32,7 +32,7 @@ env: jobs: openiotsdk: name: Open IoT SDK examples building - timeout-minutes: 140 + timeout-minutes: 90 env: TEST_NETWORK_NAME: OIStest @@ -107,25 +107,22 @@ jobs: run: | scripts/examples/openiotsdk_example.sh unit-tests - - name: Test shell example - # Temporarily disable test due to performance issue with FVP - if: false #steps.build_shell.outcome == 'success' + - name: "Test: shell example" + if: steps.build_shell.outcome == 'success' timeout-minutes: 5 run: | scripts/examples/openiotsdk_example.sh -C test shell - - name: Test lock-app example - # Temporarily disable test due to performance issue with FVP - if: false #steps.build_lock_app.outcome == 'success' + - name: "Test: lock-app example" + if: steps.build_lock_app.outcome == 'success' timeout-minutes: 5 run: | scripts/setup/openiotsdk/network_setup.sh -n $TEST_NETWORK_NAME up scripts/run_in_ns.sh ${TEST_NETWORK_NAME}ns scripts/examples/openiotsdk_example.sh -C test -n ${TEST_NETWORK_NAME}tap lock-app scripts/setup/openiotsdk/network_setup.sh -n $TEST_NETWORK_NAME down - - name: Run unit tests - # Temporarily disable test due to performance issue with FVP - if: false #steps.build_unit_tests.outcome == 'success' && github.event_name == 'pull_request' - timeout-minutes: 90 + - name: "Test: unit-tests" + if: steps.build_unit_tests.outcome == 'success' + timeout-minutes: 40 run: | - scripts/examples/openiotsdk_example.sh -C run unit-tests + scripts/examples/openiotsdk_example.sh -C test unit-tests diff --git a/.vscode/launch.json b/.vscode/launch.json index e68b78cbd4e57a..83ce3d5fe494bd 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -455,7 +455,13 @@ "armToolchainPath": "${env:PW_ENVIRONMENT_ROOT}/cipd/packages/arm/bin/", "servertype": "external", "gdbTarget": ":31627", //GDBserver port on FVP - "overrideLaunchCommands": ["-enable-pretty-printing"], + "overrideLaunchCommands": [ + "-enable-pretty-printing", + "add-symbol-file ./build/bl2.elf 0x10000000", + "add-symbol-file ./build/tfm_s.elf 0x38000400", + "add-symbol-file ./build/${input:openiotsdkUnittest}_ns.elf 0x28060400", + "break main_ns.cpp:main" + ], "runToEntryPoint": "main", "preLaunchTask": "Debug Open IoT SDK unit-tests", "showDevDebugOutput": "parsed" diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 2e495a9536fd2c..d0e5fbfae284c4 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -250,6 +250,7 @@ "args": [ "-Cbuild", "-d${input:openiotsdkDebugMode}", + "-l${input:openiotsdkLwipDebug}", "${input:openiotsdkExample}" ], "group": "build", @@ -266,7 +267,12 @@ "label": "Build Open IoT SDK unit-tests", "type": "shell", "command": "scripts/examples/openiotsdk_example.sh", - "args": ["-Cbuild", "-d${input:openiotsdkDebugMode}", "unit-tests"], + "args": [ + "-Cbuild", + "-d${input:openiotsdkDebugMode}", + "-l${input:openiotsdkLwipDebug}", + "unit-tests" + ], "group": "build", "problemMatcher": { "pattern": { @@ -334,6 +340,21 @@ } } }, + { + "label": "Test Open IoT SDK unit-tests", + "type": "shell", + "command": "scripts/examples/openiotsdk_example.sh", + "args": ["-Ctest", "unit-tests", "${input:openiotsdkUnitTest}"], + "group": "test", + "problemMatcher": { + "pattern": { + "regexp": "^(.*):(\\d+):(\\d+):\\s+(warning|error):\\s+(.*)$", + "file": 1, + "line": 2, + "message": 5 + } + } + }, { "label": "Debug Open IoT SDK example", "type": "shell", @@ -439,6 +460,13 @@ "options": ["false", "true"], "default": "false" }, + { + "type": "pickString", + "id": "openiotsdkLwipDebug", + "description": "Do you want to use LwIP debug logs?", + "options": ["false", "true"], + "default": "false" + }, { "type": "pickString", "id": "openiotsdkExample", @@ -451,7 +479,6 @@ "id": "openiotsdkUnitTest", "description": "What unit test do you want to use?", "options": [ - "all", "accesstest", "AppTests", "ASN1Tests", @@ -478,7 +505,7 @@ "TransportLayerTests", "UserDirectedCommissioningTests" ], - "default": "all" + "default": "accesstest" }, { "type": "promptString", diff --git a/config/openiotsdk/cmake/linker.cmake b/config/openiotsdk/cmake/linker.cmake index 032b95f54afd60..fc3429ffd78656 100644 --- a/config/openiotsdk/cmake/linker.cmake +++ b/config/openiotsdk/cmake/linker.cmake @@ -27,12 +27,12 @@ function(set_target_link target) if (NOT LINKER_SCRIPT) set(LINKER_SCRIPT ${OPEN_IOT_SDK_CONFIG}/ld/cs300_gcc.ld) endif() - target_link_options(${APP_TARGET} PRIVATE -T ${LINKER_SCRIPT}) - set_target_properties(${APP_TARGET} PROPERTIES LINK_DEPENDS ${LINKER_SCRIPT}) + target_link_options(${target} PRIVATE -T ${LINKER_SCRIPT}) + set_target_properties(${target} PROPERTIES LINK_DEPENDS ${LINKER_SCRIPT}) target_link_options(${target} PRIVATE - "-Wl,-Map=${APP_TARGET}.map" + "-Wl,-Map=${target}.map" ) endfunction() diff --git a/config/openiotsdk/cmake/sdk.cmake b/config/openiotsdk/cmake/sdk.cmake index dbe54d4d122c37..7fbc7efa9561bc 100644 --- a/config/openiotsdk/cmake/sdk.cmake +++ b/config/openiotsdk/cmake/sdk.cmake @@ -1,5 +1,5 @@ # -# Copyright (c) 2022 Project CHIP Authors +# Copyright (c) 2022-2023 Project CHIP Authors # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -30,6 +30,12 @@ list(APPEND CONFIG_CHIP_EXTERNAL_TARGETS) # Additional Open IoT SDK build configuration set(TFM_SUPPORT NO CACHE BOOL "Add Trusted Firmware-M (TF-M) support to application") set(TFM_NS_APP_VERSION "0.0.0" CACHE STRING "TF-M non-secure application version (in the x.x.x format)") +set(CONFIG_CHIP_OPEN_IOT_SDK_LWIP_DEBUG NO CACHE BOOL "Enable LwIP debug logs") + +# Default LwIP options directory (should contain user_lwipopts.h file) +if (NOT LWIP_PROJECT_OPTS_DIR) + set(LWIP_PROJECT_OPTS_DIR ${OPEN_IOT_SDK_CONFIG}/lwip) +endif() # Overwrite versions of Open IoT SDK components @@ -76,11 +82,11 @@ if(TFM_SUPPORT) set(TFM_PLATFORM ${OPEN_IOT_SDK_EXAMPLE_COMMON}/tf-m/targets/an552) set(TFM_PSA_FIRMWARE_UPDATE ON) set(MCUBOOT_IMAGE_VERSION_NS ${TFM_NS_APP_VERSION}) - set(TFM_CMAKE_ARGS "-DCONFIG_TFM_ENABLE_FP=ON;-DTFM_PROFILE=profile_medium") + set(TFM_CMAKE_ARGS "-DCONFIG_TFM_ENABLE_FP=ON;-DTFM_PROFILE=profile_medium;-DTFM_EXCEPTION_INFO_DUMP=ON;-DCONFIG_TFM_HALT_ON_CORE_PANIC=ON;-DTFM_ISOLATION_LEVEL=1") if ("${CMAKE_BUILD_TYPE}" STREQUAL "Debug") - set(TFM_CMAKE_ARGS "${TFM_CMAKE_ARGS};-DMCUBOOT_LOG_LEVEL=INFO;-DTFM_SPM_LOG_LEVEL=TFM_SPM_LOG_LEVEL_INFO;-DTFM_PARTITION_LOG_LEVEL=TFM_PARTITION_LOG_LEVEL_INFO") + set(TFM_CMAKE_ARGS "${TFM_CMAKE_ARGS};-DMCUBOOT_LOG_LEVEL=INFO;-DTFM_SPM_LOG_LEVEL=TFM_SPM_LOG_LEVEL_DEBUG;-DTFM_PARTITION_LOG_LEVEL=TFM_PARTITION_LOG_LEVEL_INFO") else() - set(TFM_CMAKE_ARGS "${TFM_CMAKE_ARGS};-DMCUBOOT_LOG_LEVEL=ERROR;-DTFM_SPM_LOG_LEVEL=TFM_SPM_LOG_LEVEL_ERROR;-DTFM_PARTITION_LOG_LEVEL=TFM_PARTITION_LOG_LEVEL_ERROR") + set(TFM_CMAKE_ARGS "${TFM_CMAKE_ARGS};-DMCUBOOT_LOG_LEVEL=ERROR;-DTFM_SPM_LOG_LEVEL=TFM_SPM_LOG_LEVEL_DEBUG;-DTFM_PARTITION_LOG_LEVEL=TFM_PARTITION_LOG_LEVEL_ERROR") endif() if(TFM_PROJECT_CONFIG_HEADER_FILE) set(TFM_CMAKE_ARGS "${TFM_CMAKE_ARGS};-DPROJECT_CONFIG_HEADER_FILE=${TFM_PROJECT_CONFIG_HEADER_FILE}") @@ -135,7 +141,6 @@ if(TARGET cmsis-rtos-api) target_link_libraries(cmsis-rtos-api PUBLIC freertos-cmsis-rtos - freertos-kernel-heap-3 ) target_compile_definitions(cmsis-rtos-api @@ -162,12 +167,17 @@ if(TARGET lwip-cmsis-port) target_compile_definitions(lwipopts INTERFACE DEBUG_PRINT=printf + $<$:LWIP_DEBUG> + $<$:CHIP_LIB_TESTS> ) - if(TARGET lwip-cmsis-port) - # Link the emac factory to LwIP port - target_link_libraries(lwip-cmsis-port PUBLIC iotsdk-emac-factory) - endif() + target_include_directories(lwipopts + INTERFACE + ${LWIP_PROJECT_OPTS_DIR} + ) + + # Link the emac factory to LwIP port + target_link_libraries(lwip-cmsis-port PUBLIC iotsdk-emac-factory) endif() # MDH configuration @@ -183,6 +193,15 @@ if(TARGET mcu-driver-hal) INTERFACE DOMAIN_NS=$,1,0> ) + + # Fixing the optimization issue for mcu-driver-hal target in the no-debug build. + # The default -Og optimization causes performance issues for the application. + # We need to replace it with -O2 which is suitable for performance. + # This fix can be removed in the future when the issue will be fixed in SDK directly. + if (NOT "${CMAKE_BUILD_TYPE}" STREQUAL "Debug") + target_compile_options(mcu-driver-hal INTERFACE $<$:-O2>) + target_compile_options(mcu-driver-hal INTERFACE $<$:-O2>) + endif() endif() # Mbedtls config @@ -279,66 +298,84 @@ list(APPEND CONFIG_CHIP_EXTERNAL_TARGETS ) function(sdk_post_build target) - string(REPLACE "_ns" "" APP_NAME ${APP_TARGET}) + string(REPLACE "_ns" "" APP_NAME ${target}) if(TFM_SUPPORT) include(ConvertElfToBin) include(SignTfmImage) - target_elf_to_bin(${APP_TARGET}) - iotsdk_tf_m_sign_image(${APP_TARGET}) - iotsdk_tf_m_merge_images(${APP_TARGET} 0x10000000 0x38000000 0x28060000) ExternalProject_Get_Property(trusted-firmware-m-build BINARY_DIR) + target_elf_to_bin(${target}) + add_custom_command( + TARGET + ${target} + POST_BUILD + DEPENDS + $/${target}.bin + COMMAND + # Sign the non-secure (application) image for TF-M bootloader (BL2)" + python3 ${BINARY_DIR}/install/image_signing/scripts/wrapper/wrapper.py + --layout ${BINARY_DIR}/install/image_signing/layout_files/signing_layout_ns.o + -v ${MCUBOOT_IMAGE_VERSION_NS} + -k ${BINARY_DIR}/install/image_signing/keys/root-RSA-3072_1.pem + --public-key-format full + --align 1 --pad --pad-header -H 0x400 -s auto -d "(0, 0.0.0+0)" + $/${target}.bin + --overwrite-only + --measured-boot-record + $/${target}_signed.bin + VERBATIM + ) + iotsdk_tf_m_merge_images(${target} 0x10000000 0x38000000 0x28060000) # Cleanup add_custom_command( TARGET - ${APP_TARGET} + ${target} POST_BUILD DEPENDS - $/tfm_s_signed.bin - $/${APP_TARGET}.bin - $/${APP_TARGET}_signed.bin - $/${APP_TARGET}_merged.hex - $/${APP_TARGET}_merged.elf + $/${target}.bin + $/${target}_signed.bin + $/${target}_merged.hex + $/${target}_merged.elf COMMAND - # Copy the TF-M secure elf image + # Copy the bootloader and TF-M secure image for debugging purposes ${CMAKE_COMMAND} -E copy + ${BINARY_DIR}/install/outputs/bl2.elf ${BINARY_DIR}/install/outputs/tfm_s.elf - $/ + $/ COMMAND # Rename output file ${CMAKE_COMMAND} -E copy - $/${APP_TARGET}_merged.elf - $/${APP_NAME}.elf + $/${target}_merged.elf + $/${APP_NAME}.elf COMMAND rm ARGS -Rf - $/tfm_s_signed.bin - $/${APP_TARGET}.bin - $/${APP_TARGET}_signed.bin - $/${APP_TARGET}_merged.hex - $/${APP_TARGET}_merged.elf + $/${target}.bin + $/${target}_signed.bin + $/${target}_merged.hex + $/${target}_merged.elf VERBATIM ) else() add_custom_command( TARGET - ${APP_TARGET} + ${target} POST_BUILD DEPENDS - $/${APP_TARGET}.elf - $/${APP_TARGET}.map + $/${target}.elf + $/${target}.map COMMAND # Rename output elf file ${CMAKE_COMMAND} -E copy - $/${APP_TARGET}.elf - $/${APP_NAME}.elf + $/${target}.elf + $/${APP_NAME}.elf COMMAND # Rename output map file ${CMAKE_COMMAND} -E copy - $/${APP_TARGET}.map - $/${APP_NAME}.map + $/${target}.map + $/${APP_NAME}.map COMMAND rm ARGS -Rf - $/${APP_TARGET}.elf - $/${APP_TARGET}.map + $/${target}.elf + $/${target}.map VERBATIM ) endif() #TFM_SUPPORT diff --git a/config/openiotsdk/ld/cs300_gcc_tfm.ld b/config/openiotsdk/ld/cs300_gcc_tfm.ld index 523c96e8efb77c..fd1d951258d917 100644 --- a/config/openiotsdk/ld/cs300_gcc_tfm.ld +++ b/config/openiotsdk/ld/cs300_gcc_tfm.ld @@ -18,7 +18,7 @@ MEMORY { FLASH (rx) : ORIGIN = ((0x28000000) + (0x60000) + (0x400)), LENGTH = ((0x200000) - (0x400) - (0x800)) - RAM (rwx) : ORIGIN = 0x21000000, LENGTH = 0x100000 + RAM (rwx) : ORIGIN = 0x21000000, LENGTH = 0x200000 } __stack_size__ = 0x1000; diff --git a/config/openiotsdk/lwip/user_lwipopts.h b/config/openiotsdk/lwip/user_lwipopts.h index 8bf38b1129fc56..48d8d4aee81b21 100644 --- a/config/openiotsdk/lwip/user_lwipopts.h +++ b/config/openiotsdk/lwip/user_lwipopts.h @@ -1,6 +1,6 @@ /* * - * Copyright (c) 2022 Project CHIP Authors + * Copyright (c) 2022-2023 Project CHIP Authors * All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -19,12 +19,35 @@ #ifndef USER_LWIPOPTS_H #define USER_LWIPOPTS_H +/** + * MEM_LIBC_MALLOC: use malloc/free/realloc provided by your C-library + * instead of the lwip internal allocator. + */ +#define MEM_LIBC_MALLOC (1) + +/** + * MEMP_NUM_NETBUF: the number of struct netbufs. + */ +#define MEMP_NUM_NETBUF 16 + +/** + * PBUF_POOL_SIZE: the number of buffers in the pbuf pool. + * + * Some unit tests required more pbuf buffers. + */ +#ifdef CHIP_LIB_TESTS +#define PBUF_POOL_SIZE (1001) +#endif // CHIP_LIB_TESTS + +/** + * LWIP_STATS : Turn off statistics gathering + */ #define LWIP_STATS (0) -#define LWIP_IGMP (1) -#define LWIP_RAW (1) -#define MEM_LIBC_MALLOC (1) -#define MEM_USE_POOLS (0) +/** + * LWIP_RAW: Enable application layer to hook into the IP layer itself. + */ +#define LWIP_RAW (1) #ifdef LWIP_DEBUG diff --git a/examples/lock-app/openiotsdk/CMakeLists.txt b/examples/lock-app/openiotsdk/CMakeLists.txt index fca486fed81d09..289ecbc893fc54 100644 --- a/examples/lock-app/openiotsdk/CMakeLists.txt +++ b/examples/lock-app/openiotsdk/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright (c) 2022 Project CHIP Authors +# Copyright (c) 2022-2023 Project CHIP Authors # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -38,22 +38,6 @@ project(${APP_TARGET} LANGUAGES C CXX ASM) include(sdk) -# LwIP configuration -if(TARGET lwip-cmsis-port) - # lwip requires user_lwipopts.h, we use the custom settings - target_include_directories(lwipopts - INTERFACE - ${OPEN_IOT_SDK_CONFIG}/lwip - ) - - if (${CMAKE_BUILD_TYPE} STREQUAL "Debug") - target_compile_definitions(lwipopts - INTERFACE - LWIP_DEBUG - ) - endif() -endif() - add_executable(${APP_TARGET}) # Application CHIP build configuration @@ -80,6 +64,7 @@ target_sources(${APP_TARGET} ) target_link_libraries(${APP_TARGET} + openiotsdk-startup openiotsdk-app ) diff --git a/examples/lock-app/openiotsdk/main/main_ns.cpp b/examples/lock-app/openiotsdk/main/main_ns.cpp index 2b6484b05180c2..aea75e3c58ac7a 100644 --- a/examples/lock-app/openiotsdk/main/main_ns.cpp +++ b/examples/lock-app/openiotsdk/main/main_ns.cpp @@ -22,38 +22,8 @@ #include "openiotsdk_platform.h" -static void app_thread(void * argument) -{ - if (openiotsdk_network_init(true)) - { - ChipLogError(NotSpecified, "Network initialization failed"); - goto exit; - } - - if (openiotsdk_chip_run()) - { - ChipLogError(NotSpecified, "CHIP stack run failed"); - goto exit; - } - - ChipLogProgress(NotSpecified, "Open IoT SDK lock-app example application run"); - - while (true) - { - // Add forever delay to ensure proper workload for this thread - osDelay(osWaitForever); - } - - openiotsdk_chip_shutdown(); - -exit: - osThreadTerminate(osThreadGetId()); -} - int main() { - ChipLogProgress(NotSpecified, "Open IoT SDK lock-app example application start"); - if (openiotsdk_platform_init()) { ChipLogError(NotSpecified, "Open IoT SDK platform initialization failed"); @@ -66,22 +36,29 @@ int main() return EXIT_FAILURE; } - static const osThreadAttr_t thread_attr = { - .stack_size = 16 * 1024 // Allocate enough stack for app thread - }; + ChipLogProgress(NotSpecified, "Open IoT SDK lock-app example application start"); - osThreadId_t appThread = osThreadNew(app_thread, NULL, &thread_attr); - if (appThread == NULL) + if (openiotsdk_network_init(true)) { - ChipLogError(NotSpecified, "Failed to create app thread"); + ChipLogError(NotSpecified, "Network initialization failed"); return EXIT_FAILURE; } - if (openiotsdk_platform_run()) + if (openiotsdk_chip_run()) { - ChipLogError(NotSpecified, "Open IoT SDK platform run failed"); + ChipLogError(NotSpecified, "CHIP stack run failed"); return EXIT_FAILURE; } + ChipLogProgress(NotSpecified, "Open IoT SDK lock-app example application run"); + + while (true) + { + // Add forever delay to ensure proper workload for this thread + osDelay(osWaitForever); + } + + openiotsdk_chip_shutdown(); + return EXIT_SUCCESS; } diff --git a/examples/platform/openiotsdk/app/CMakeLists.txt b/examples/platform/openiotsdk/app/CMakeLists.txt index 4d566fa2e37cd6..c9939ea6a74fcd 100644 --- a/examples/platform/openiotsdk/app/CMakeLists.txt +++ b/examples/platform/openiotsdk/app/CMakeLists.txt @@ -16,6 +16,38 @@ cmake_minimum_required(VERSION 3.21) +# Declare Open IoT SDK startup target +add_library(openiotsdk-startup + OBJECT + openiotsdk_startup_gcc.cpp +) + +target_link_libraries(openiotsdk-startup + PUBLIC + # iotsdk-serial-retarget contains the UART but we don't need the retarget part + $,EXCLUDE,.*gcc_retarget.*> + cmsis-rtos-api + mcu-driver-hal +) + +target_link_options(openiotsdk-startup + PUBLIC + "-Wl,--wrap,__malloc_lock" + "-Wl,--wrap,__malloc_unlock" +) + +if(TFM_SUPPORT) + target_compile_definitions(openiotsdk-startup + PUBLIC + TFM_SUPPORT + ) + + target_link_libraries(openiotsdk-startup + PUBLIC + tfm-ns-interface + ) +endif() + # Declare Open IoT SDK app interface target add_library(openiotsdk-app openiotsdk_platform.cpp @@ -31,7 +63,6 @@ target_include_directories(openiotsdk-app target_link_libraries(openiotsdk-app PUBLIC chip - $ ) if(TFM_SUPPORT) diff --git a/examples/platform/openiotsdk/app/openiotsdk_platform.cpp b/examples/platform/openiotsdk/app/openiotsdk_platform.cpp index 829980d31f2392..28b6da91f5c176 100644 --- a/examples/platform/openiotsdk/app/openiotsdk_platform.cpp +++ b/examples/platform/openiotsdk/app/openiotsdk_platform.cpp @@ -1,5 +1,4 @@ /* - * * Copyright (c) 2022 Project CHIP Authors * All rights reserved. * @@ -31,6 +30,7 @@ #include #include #include +#include #include #include @@ -53,6 +53,7 @@ using namespace ::chip; using namespace ::chip::Platform; using namespace ::chip::DeviceLayer; +using namespace ::chip::Logging::Platform; constexpr EndpointId kNetworkCommissioningEndpointSecondary = 0xFFFE; @@ -168,7 +169,8 @@ static int get_psa_images_details() int openiotsdk_platform_init(void) { int ret; - osKernelState_t state; + + ois_logging_init(); ret = mbedtls_platform_setup(NULL); if (ret) @@ -178,13 +180,6 @@ int openiotsdk_platform_init(void) } #ifdef TFM_SUPPORT - ret = tfm_ns_interface_init(); - if (ret != 0) - { - ChipLogError(NotSpecified, "TF-M initialization failed: %d", ret); - return EXIT_FAILURE; - } - ret = get_psa_images_details(); if (ret != 0) { @@ -193,20 +188,6 @@ int openiotsdk_platform_init(void) } #endif // TFM_SUPPORT - ret = osKernelInitialize(); - if (ret != osOK) - { - ChipLogError(NotSpecified, "osKernelInitialize failed: %d", ret); - return EXIT_FAILURE; - } - - state = osKernelGetState(); - if (state != osKernelReady) - { - ChipLogError(NotSpecified, "Kernel not ready: %d", state); - return EXIT_FAILURE; - } - return EXIT_SUCCESS; } @@ -214,10 +195,6 @@ int openiotsdk_chip_init(void) { CHIP_ERROR err; -#if NDEBUG - chip::Logging::SetLogFilter(chip::Logging::LogCategory::kLogCategory_Progress); -#endif - err = MemoryInit(); if (err != CHIP_NO_ERROR) { @@ -244,18 +221,6 @@ int openiotsdk_chip_init(void) return EXIT_SUCCESS; } -int openiotsdk_platform_run(void) -{ - int ret = osKernelStart(); - if (ret != osOK) - { - ChipLogError(NotSpecified, "Failed to start kernel: %d", ret); - return EXIT_FAILURE; - } - - return EXIT_SUCCESS; -} - int openiotsdk_network_init(bool wait) { int ret; @@ -289,12 +254,10 @@ int openiotsdk_network_init(bool wait) int openiotsdk_chip_run(void) { - CHIP_ERROR err; - #ifdef USE_CHIP_DATA_MODEL // Init ZCL Data Model and start server static chip::CommonCaseDeviceServerInitParams initParams; - err = initParams.InitializeStaticResourcesBeforeServerInit(); + CHIP_ERROR err = initParams.InitializeStaticResourcesBeforeServerInit(); if (err != CHIP_NO_ERROR) { ChipLogError(NotSpecified, "Initialize static resources before server init failed: %s", err.AsString()); diff --git a/examples/platform/openiotsdk/app/openiotsdk_startup_gcc.cpp b/examples/platform/openiotsdk/app/openiotsdk_startup_gcc.cpp new file mode 100644 index 00000000000000..bf143db5d32877 --- /dev/null +++ b/examples/platform/openiotsdk/app/openiotsdk_startup_gcc.cpp @@ -0,0 +1,573 @@ +/* + * Copyright (c) 2006-2022 ARM Limited + * Copyright (c) 2023 Project CHIP Authors + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "bootstrap/mbed_critical.h" +#include "cmsis_os2.h" +extern "C" { +#include "hal/serial_api.h" +} + +#ifdef TFM_SUPPORT +extern "C" uint32_t tfm_ns_interface_init(void); +#endif // TFM_SUPPORT + +#define CALLER_ADDR() __builtin_extract_return_addr(__builtin_return_address(0)) + +// Consider reducing the baudrate if the serial is used as input and characters are lost +extern "C" mdh_serial_t * get_example_serial(); +#ifndef IOT_SDK_APP_SERIAL_BAUDRATE +#define IOT_SDK_APP_SERIAL_BAUDRATE 921600 +#endif + +// main thread declaration +// The thread object and associated stack are statically allocated +#ifndef IOT_SDK_APP_MAIN_STACK_SIZE +#define IOT_SDK_APP_MAIN_STACK_SIZE 16 * 1024 +#endif +static void main_thread(void * argument); +alignas(8) static char main_thread_stack[IOT_SDK_APP_MAIN_STACK_SIZE]; +alignas(8) static uint8_t main_thread_storage[100] __attribute__((section(".bss.os.thread.cb"))); + +// malloc mutex declaration +static osMutexId_t malloc_mutex; +alignas(8) static uint8_t malloc_mutex_obj[80]; + +// C runtime import: constructor initialization and main +extern "C" void __libc_init_array(void); +extern "C" int main(void); + +// IOT SDK serial declarations +#define STDIN_FILENO 0 +#define STDOUT_FILENO 1 +#define STDERR_FILENO 2 + +static mdh_serial_t * serial; + +// stdout tx mutex declaration +static osMutexId_t tx_mutex; +alignas(8) static uint8_t tx_mutex_obj[80]; + +static int serial_out(const char * str, size_t len) +{ + if (str == NULL) + { + return -1; + } + + if (len == 0) + { + return 0; + } + + size_t written = 0; + while (written++ < len) + { + mdh_serial_put_data(serial, *str++); + } + + return len; +} + +// prints while printf is not usable yet +static void bare_metal_print(const char * str) +{ + serial_out(str, strlen(str)); +} + +static void serial_irq_cb(void * p_instance, mdh_serial_irq_type_t event); + +struct CriticalSection +{ + CriticalSection() { core_util_critical_section_enter(); } + CriticalSection(const CriticalSection &) = delete; + CriticalSection & operator=(const CriticalSection &) = delete; + ~CriticalSection() { core_util_critical_section_exit(); } +}; + +// RX buffer +template +struct RingBuffer +{ + static_assert(Size > 0); + RingBuffer() : head_(0), tail_(0), full_(false) {} + void put(uint8_t data); + bool get(uint8_t * data); + size_t get(uint8_t * data, size_t size); + bool empty() const; + bool full() const; + size_t size() const; + +private: + uint8_t buffer_[Size]; + size_t head_ = 0; + size_t tail_ = 0; + bool full_ = false; + + void increment(size_t & val); + void increment(size_t & val, size_t incr); + bool empty_() const; + size_t size_() const; +}; + +// Buffer that receive data from serial +static RingBuffer<128> rx_buffer; + +/* + * This function override startup sequence. Instead of releasing control to the C runtime + * the following operations are performed: + * + * - initialize the serial (low level) + * - initialize RTOS + * - Start the RTOS with the main thread + */ +extern "C" void mbed_sdk_init(void) +{ + serial = get_example_serial(); + mdh_serial_set_baud(serial, IOT_SDK_APP_SERIAL_BAUDRATE); + + int ret = osKernelInitialize(); + if (ret != osOK) + { + bare_metal_print("osKernelInitialize failed\r\n"); + abort(); + } + + // Create main thread used to run the application + { + osThreadAttr_t main_thread_attr = { + .name = "main", + .cb_mem = &main_thread_storage, + .cb_size = sizeof(main_thread_storage), + .stack_mem = main_thread_stack, + .stack_size = sizeof(main_thread_stack), + .priority = osPriorityNormal, + }; + + osThreadId_t main_thread_id = osThreadNew(main_thread, NULL, &main_thread_attr); + if (main_thread_id == NULL) + { + bare_metal_print("Main thread creation failed\r\n"); + abort(); + } + } + + ret = osKernelStart(); + // Note osKernelStart should never return + bare_metal_print("Kernel failed to start\r\n"); + abort(); +} + +/** + * Main thread + * - Initialize TF-M + * - Initialize the toolchain: + * - Setup mutexes for malloc and environment + * - Construct global objects + * - Run the main + */ +static void main_thread(void * argument) +{ + // Create Malloc mutex + { + osMutexAttr_t malloc_mutex_attr = { .name = "malloc_mutex", + .attr_bits = osMutexRecursive | osMutexPrioInherit, + .cb_mem = &malloc_mutex_obj, + .cb_size = sizeof(malloc_mutex_obj) }; + + malloc_mutex = osMutexNew(&malloc_mutex_attr); + if (malloc_mutex == NULL) + { + bare_metal_print("Failed to initialize malloc mutex\r\n"); + abort(); + } + } + + // Create stdout TX mutex + { + osMutexAttr_t tx_mutex_attr = { + .name = "tx_mutex", .attr_bits = osMutexPrioInherit, .cb_mem = &tx_mutex_obj, .cb_size = sizeof(tx_mutex_obj) + }; + + tx_mutex = osMutexNew(&tx_mutex_attr); + if (tx_mutex == NULL) + { + bare_metal_print("Failed to initialize tx mutex\r\n"); + abort(); + } + } + + // Disable buffering to let write and fwrite call straight into _write + setvbuf(stdout, /* buffer */ NULL, _IONBF, /* size */ 0); + setvbuf(stderr, /* buffer */ NULL, _IONBF, /* size */ 0); + setvbuf(stdin, /* buffer */ NULL, _IONBF, /* size */ 0); + + // It is safe to use printf from this point + +#ifdef TFM_SUPPORT + { + int ret = tfm_ns_interface_init(); + if (ret != 0) + { + bare_metal_print("TF-M initialization failed\r\n"); + abort(); + } + } +#endif + + /* Run the C++ global object constructors */ + __libc_init_array(); + + // Note: Reception on the serial port is buffered to mitigate bytes lost + mdh_serial_set_irq_callback(serial, serial_irq_cb, serial); + mdh_serial_set_irq_availability(serial, MDH_SERIAL_IRQ_TYPE_RX, true); + + // It is safe to receive data on serial from this point + + int return_code = main(); + + exit(return_code); +} + +/* + * Override of lock/unlock functions for malloc. + */ +extern "C" void __wrap___malloc_lock(struct _reent * reent) +{ + osMutexAcquire(malloc_mutex, osWaitForever); +} + +extern "C" void __wrap___malloc_unlock(struct _reent * reent) +{ + osMutexRelease(malloc_mutex); +} + +/* + * Override of new/delete operators. + * The override add a trace when a non-throwing new fails. + */ + +void * operator new(std::size_t count) +{ + void * buffer = malloc(count); + if (!buffer) + { + printf("operator new failure from %p\r\n", CALLER_ADDR()); + abort(); + } + return buffer; +} + +void * operator new[](std::size_t count) +{ + void * buffer = malloc(count); + if (!buffer) + { + printf("operator new[] failure from %p\r\n", CALLER_ADDR()); + abort(); + } + return buffer; +} + +void * operator new(std::size_t count, const std::nothrow_t & tag) +{ + return malloc(count); +} + +void * operator new[](std::size_t count, const std::nothrow_t & tag) +{ + return malloc(count); +} + +void operator delete(void * ptr) +{ + free(ptr); +} + +void operator delete(void * ptr, std::size_t) +{ + free(ptr); +} + +void operator delete[](void * ptr) +{ + free(ptr); +} + +void operator delete[](void * ptr, std::size_t) +{ + free(ptr); +} + +/* + * Override of _sbrk + * It prints an error when the system runs out of memory in the heap segment. + */ + +#undef errno +extern "C" int errno; + +extern "C" char __end__; +extern "C" char __HeapLimit; + +extern "C" void * _sbrk(int incr) +{ + static uint32_t heap = (uint32_t) &__end__; + uint32_t prev_heap = heap; + uint32_t new_heap = heap + incr; + + /* __HeapLimit is end of heap section */ + if (new_heap > (uint32_t) &__HeapLimit) + { + printf("_sbrk failure, incr = %d, new_heap = 0x%08lX\r\n", incr, new_heap); + errno = ENOMEM; + return (void *) -1; + } + + heap = new_heap; + return (void *) prev_heap; +} + +// Override exit +extern "C" void _exit(int return_code) +{ + // display exit reason + if (return_code) + { + printf("Application exited with %d\r\n", return_code); + } + + // flush stdio + fflush(stdout); + fflush(stderr); + + // lock the kernel and go to sleep forever + osKernelLock(); + while (1) + { + __WFE(); + } +} + +// Calling a FreeRTOS API is illegal while scheduler is suspended. +// Therefore we provide this custom implementation which relies on underlying +// safety of malloc. +extern "C" void * pvPortMalloc(size_t size) +{ + return malloc(size); +} + +extern "C" void vPortFree(void * ptr) +{ + free(ptr); +} + +// Retarget of low level read and write + +extern "C" int _write(int fd, const char * str, size_t len) +{ + if (fd != STDOUT_FILENO && fd != STDERR_FILENO) + { + return -1; + } + + osMutexAcquire(tx_mutex, osWaitForever); + int len_written = serial_out(str, len); + osMutexRelease(tx_mutex); + + return len_written; +} + +extern "C" int _read(int fd, char * str, size_t len) +{ + if (fd != STDIN_FILENO) + { + return -1; + } + + return rx_buffer.get((uint8_t *) str, len); +} + +static void serial_irq_cb(void * p_instance, mdh_serial_irq_type_t event) +{ + if ((event != MDH_SERIAL_IRQ_TYPE_RX) || !p_instance) + { + return; + } + + mdh_serial_t * serial = reinterpret_cast(p_instance); + + if (event == MDH_SERIAL_IRQ_TYPE_RX) + { + while (mdh_serial_is_readable(serial)) + { + // Gather as much as possible data bytes + rx_buffer.put(mdh_serial_get_data(serial)); + } + } +} + +// Ring buffer implementation +template +void RingBuffer::put(uint8_t data) +{ + CriticalSection _; + buffer_[head_] = data; + increment(head_); + + if (full_) + { + tail_ = head_; + } + else if (head_ == tail_) + { + full_ = true; + } +} + +template +bool RingBuffer::get(uint8_t * data) +{ + CriticalSection _; + if (empty_()) + { + return false; + } + + *data = buffer_[tail_]; + increment(tail_); + full_ = false; + + return true; +} + +template +size_t RingBuffer::get(uint8_t * data, size_t size) +{ + if (size == 0) + { + return 0; + } + + if (size == 1) + { + return get(data) ? 1 : 0; + } + + CriticalSection _; + if (empty_()) + { + return 0; + } + + // resize dest to fit with available data + size = std::min(size_(), size); + + if (tail_ + size > Size) + { + auto it = std::copy(buffer_ + tail_, buffer_ + Size, data); + // set to the future tail + tail_ = size - (Size - tail_); + std::copy(buffer_, buffer_ + tail_, it); + } + else + { + std::copy(buffer_ + tail_, buffer_ + size, data); + increment(tail_, size); + } + + full_ = false; + + return size; +} + +template +bool RingBuffer::empty() const +{ + CriticalSection _; + return empty_(); +} + +template +bool RingBuffer::full() const +{ + CriticalSection _; + return bool(full_); +} + +template +size_t RingBuffer::size() const +{ + CriticalSection _; + return size_(); +} + +template +void RingBuffer::increment(size_t & val) +{ + ++val; + assert(val <= Size); + + if (val == Size) + { + val = 0; + } +} + +template +void RingBuffer::increment(size_t & val, size_t incr) +{ + val += incr; + + if (val >= Size) + { + val = val - Size; + } +} + +template +bool RingBuffer::empty_() const +{ + return head_ == tail_ && !bool(full_); +} + +template +size_t RingBuffer::size_() const +{ + if (full_) + { + return Size; + } + else if (head_ < tail_) + { + return Size - (tail_ - head_); + } + else + { + return head_ - tail_; + }; +} diff --git a/examples/platform/openiotsdk/tf-m/targets/an552/partition/region_defs.h b/examples/platform/openiotsdk/tf-m/targets/an552/partition/region_defs.h index d124a00450785e..682f0f50747373 100755 --- a/examples/platform/openiotsdk/tf-m/targets/an552/partition/region_defs.h +++ b/examples/platform/openiotsdk/tf-m/targets/an552/partition/region_defs.h @@ -101,9 +101,9 @@ #define NS_CODE_SIZE (IMAGE_NS_CODE_SIZE) #define NS_CODE_LIMIT (NS_CODE_START + NS_CODE_SIZE - 1) -/* Non-Secure Data stored in ISRAM0 */ +/* Non-Secure Data stored in ISRAM0 + ISRAM1 */ #define NS_DATA_START (ISRAM0_BASE_NS) -#define NS_DATA_SIZE (ISRAM0_SIZE) +#define NS_DATA_SIZE (ISRAM0_SIZE + ISRAM1_SIZE) #define NS_DATA_LIMIT (NS_DATA_START + NS_DATA_SIZE - 1) /* NS partition information is used for MPC and SAU configuration */ diff --git a/examples/platform/openiotsdk/tf-m/targets/an552/target_cfg.c b/examples/platform/openiotsdk/tf-m/targets/an552/target_cfg.c index 9b0338b38c8532..0358426779cafc 100755 --- a/examples/platform/openiotsdk/tf-m/targets/an552/target_cfg.c +++ b/examples/platform/openiotsdk/tf-m/targets/an552/target_cfg.c @@ -171,6 +171,13 @@ enum tfm_plat_err_t nvic_interrupt_enable(void) return TFM_PLAT_ERR_SYSTEM_ERR; } + ret = Driver_ISRAM1_MPC.EnableInterrupt(); + if (ret != ARM_DRIVER_OK) + { + ERROR_MSG("Failed to Enable MPC interrupt for ISRAM1!"); + return TFM_PLAT_ERR_SYSTEM_ERR; + } + ret = Driver_SRAM_MPC.EnableInterrupt(); if (ret != ARM_DRIVER_OK) { @@ -302,6 +309,21 @@ enum tfm_plat_err_t mpc_init_cfg(void) return TFM_PLAT_ERR_SYSTEM_ERR; } + /* ISRAM1 is allocated for NS data, so whole range is set to non-secure + * accesible. */ + ret = Driver_ISRAM1_MPC.Initialize(); + if (ret != ARM_DRIVER_OK) + { + ERROR_MSG("Failed to Initialize MPC for ISRAM1!"); + return TFM_PLAT_ERR_SYSTEM_ERR; + } + ret = Driver_ISRAM1_MPC.ConfigRegion(MPC_ISRAM1_RANGE_BASE_NS, MPC_ISRAM1_RANGE_LIMIT_NS, ARM_MPC_ATTR_NONSECURE); + if (ret != ARM_DRIVER_OK) + { + ERROR_MSG("Failed to Configure MPC for ISRAM1!"); + return TFM_PLAT_ERR_SYSTEM_ERR; + } + /* Configuring additional flash partition. */ ret = Driver_SRAM_MPC.Initialize(); if (ret != ARM_DRIVER_OK) @@ -331,10 +353,7 @@ enum tfm_plat_err_t mpc_init_cfg(void) return TFM_PLAT_ERR_SYSTEM_ERR; } - /* Lock down not used MPC's */ - Driver_ISRAM1_MPC.LockDown(); - - /* SRAM and ISRAM0 MPCs left unlocked as they are not reset if NVIC system + /* SRAM, ISRAM0 and ISRAM1 MPCs left unlocked as they are not reset if NVIC system * reset asserted. */ @@ -351,6 +370,8 @@ void mpc_revert_non_secure_to_secure_cfg(void) { Driver_ISRAM0_MPC.ConfigRegion(MPC_ISRAM0_RANGE_BASE_S, MPC_ISRAM0_RANGE_LIMIT_S, ARM_MPC_ATTR_SECURE); + Driver_ISRAM1_MPC.ConfigRegion(MPC_ISRAM1_RANGE_BASE_S, MPC_ISRAM1_RANGE_LIMIT_S, ARM_MPC_ATTR_SECURE); + Driver_SRAM_MPC.ConfigRegion(MPC_SRAM_RANGE_BASE_S, MPC_SRAM_RANGE_LIMIT_S, ARM_MPC_ATTR_SECURE); Driver_QSPI_MPC.ConfigRegion(MPC_QSPI_RANGE_BASE_S, MPC_QSPI_RANGE_LIMIT_S, ARM_MPC_ATTR_SECURE); @@ -365,6 +386,7 @@ void mpc_revert_non_secure_to_secure_cfg(void) void mpc_clear_irq(void) { Driver_ISRAM0_MPC.ClearInterrupt(); + Driver_ISRAM1_MPC.ClearInterrupt(); Driver_SRAM_MPC.ClearInterrupt(); Driver_QSPI_MPC.ClearInterrupt(); } diff --git a/examples/shell/openiotsdk/CMakeLists.txt b/examples/shell/openiotsdk/CMakeLists.txt index d17908bf6021e7..13ea384b261ff6 100644 --- a/examples/shell/openiotsdk/CMakeLists.txt +++ b/examples/shell/openiotsdk/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright (c) 2022 Project CHIP Authors +# Copyright (c) 2022-2023 Project CHIP Authors # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -35,22 +35,6 @@ project(${APP_TARGET} LANGUAGES C CXX ASM) include(sdk) -# LwIP configuration -if(TARGET lwip-cmsis-port) - # lwip requires user_lwipopts.h, we use the custom settings - target_include_directories(lwipopts - INTERFACE - ${OPEN_IOT_SDK_CONFIG}/lwip - ) - - if (${CMAKE_BUILD_TYPE} STREQUAL "Debug") - target_compile_definitions(lwipopts - INTERFACE - LWIP_DEBUG - ) - endif() -endif() - add_executable(${APP_TARGET}) # Application CHIP build configuration @@ -59,6 +43,16 @@ include(chip) add_subdirectory(${OPEN_IOT_SDK_EXAMPLE_COMMON}/app ./app_build) +target_compile_definitions(openiotsdk-startup + PRIVATE + IOT_SDK_APP_SERIAL_BAUDRATE=9600 +) + +target_compile_definitions(openiotsdk-startup + PRIVATE + IOT_SDK_APP_MAIN_STACK_SIZE=8192 +) + target_include_directories(${APP_TARGET} PRIVATE main/include @@ -73,6 +67,7 @@ target_sources(${APP_TARGET} ) target_link_libraries(${APP_TARGET} + openiotsdk-startup openiotsdk-app ) diff --git a/examples/shell/openiotsdk/main/include/CHIPProjectConfig.h b/examples/shell/openiotsdk/main/include/CHIPProjectConfig.h index 7089ae47a8038f..1d5b781c19e7ef 100644 --- a/examples/shell/openiotsdk/main/include/CHIPProjectConfig.h +++ b/examples/shell/openiotsdk/main/include/CHIPProjectConfig.h @@ -28,3 +28,5 @@ #define CHIP_DEVICE_CONFIG_USE_TEST_SETUP_DISCRIMINATOR 0xF00 #define CHIP_DISPATCH_EVENT_LONG_DISPATCH_TIME_WARNING_THRESHOLD_MS 500 + +#define CHIP_SHELL_PROMPT "Enter command:\r\n" diff --git a/examples/shell/openiotsdk/main/main_ns.cpp b/examples/shell/openiotsdk/main/main_ns.cpp index c832b2df4e49cf..056ab2c66f62ff 100644 --- a/examples/shell/openiotsdk/main/main_ns.cpp +++ b/examples/shell/openiotsdk/main/main_ns.cpp @@ -29,41 +29,19 @@ using namespace ::chip; using namespace ::chip::Shell; -static void app_thread(void * argument) +int main() { - int ret; - - if (openiotsdk_network_init(true)) - { - ChipLogError(Shell, "Network initialization failed"); - goto exit; - } - - // Initialize the default streamer that was linked. - ret = Engine::Root().Init(); - if (ret) + if (openiotsdk_platform_init()) { - ChipLogError(Shell, "Streamer initialization failed [%d]", ret); - goto exit; + ChipLogError(Shell, "Open IoT SDK platform initialization failed"); + return EXIT_FAILURE; } - cmd_misc_init(); - - ChipLogProgress(Shell, "Open IoT SDK shell example application run"); - - Engine::Root().RunMainLoop(); - -exit: - osThreadTerminate(osThreadGetId()); -} - -int main() -{ ChipLogProgress(Shell, "Open IoT SDK shell example application start"); - if (openiotsdk_platform_init()) + if (openiotsdk_network_init(true)) { - ChipLogError(Shell, "Open IoT SDK platform initialization failed"); + ChipLogError(Shell, "Network initialization failed"); return EXIT_FAILURE; } @@ -73,22 +51,19 @@ int main() return EXIT_FAILURE; } - static const osThreadAttr_t thread_attr = { - .stack_size = 8 * 1024 // Allocate enough stack for app thread - }; - - osThreadId_t appThread = osThreadNew(app_thread, NULL, &thread_attr); - if (appThread == NULL) + // Initialize the default streamer that was linked. + int ret = Engine::Root().Init(); + if (ret) { - ChipLogError(Shell, "Failed to create app thread"); + ChipLogError(Shell, "Streamer initialization failed [%d]", ret); return EXIT_FAILURE; } - if (openiotsdk_platform_run()) - { - ChipLogError(Shell, "Open IoT SDK platform run failed"); - return EXIT_FAILURE; - } + cmd_misc_init(); + + ChipLogProgress(Shell, "Open IoT SDK shell example application run"); + + Engine::Root().RunMainLoop(); return EXIT_SUCCESS; } diff --git a/scripts/build/builders/openiotsdk.py b/scripts/build/builders/openiotsdk.py index 7de737e3ea60a4..7f3cd682d86ba3 100644 --- a/scripts/build/builders/openiotsdk.py +++ b/scripts/build/builders/openiotsdk.py @@ -64,6 +64,7 @@ def generate(self): shlex.quote(self.toolchain_path)), '-DCMAKE_SYSTEM_PROCESSOR={}'.format( self.system_processor), + '-DCMAKE_BUILD_TYPE=Release', ], title='Generating ' + self.identifier) def _build(self): diff --git a/scripts/build/testdata/dry_run_openiotsdk-lock.txt b/scripts/build/testdata/dry_run_openiotsdk-lock.txt index bbe17977600ec6..a0c36ee27f53ad 100644 --- a/scripts/build/testdata/dry_run_openiotsdk-lock.txt +++ b/scripts/build/testdata/dry_run_openiotsdk-lock.txt @@ -2,7 +2,7 @@ cd "{root}" # Generating openiotsdk-lock -cmake -GNinja -S {root}/examples/lock-app/openiotsdk -B {out}/openiotsdk-lock --toolchain=toolchains/toolchain-arm-none-eabi-gcc.cmake -DCMAKE_SYSTEM_PROCESSOR=cortex-m55 +cmake -GNinja -S {root}/examples/lock-app/openiotsdk -B {out}/openiotsdk-lock --toolchain=toolchains/toolchain-arm-none-eabi-gcc.cmake -DCMAKE_SYSTEM_PROCESSOR=cortex-m55 -DCMAKE_BUILD_TYPE=Release # Building openiotsdk-lock cmake --build {out}/openiotsdk-lock diff --git a/scripts/build/testdata/dry_run_openiotsdk-shell.txt b/scripts/build/testdata/dry_run_openiotsdk-shell.txt index ef1a848a14ac88..6c7c63befb4e7a 100644 --- a/scripts/build/testdata/dry_run_openiotsdk-shell.txt +++ b/scripts/build/testdata/dry_run_openiotsdk-shell.txt @@ -2,7 +2,7 @@ cd "{root}" # Generating openiotsdk-shell -cmake -GNinja -S {root}/examples/shell/openiotsdk -B {out}/openiotsdk-shell --toolchain=toolchains/toolchain-arm-none-eabi-gcc.cmake -DCMAKE_SYSTEM_PROCESSOR=cortex-m55 +cmake -GNinja -S {root}/examples/shell/openiotsdk -B {out}/openiotsdk-shell --toolchain=toolchains/toolchain-arm-none-eabi-gcc.cmake -DCMAKE_SYSTEM_PROCESSOR=cortex-m55 -DCMAKE_BUILD_TYPE=Release # Building openiotsdk-shell cmake --build {out}/openiotsdk-shell diff --git a/scripts/examples/openiotsdk_example.sh b/scripts/examples/openiotsdk_example.sh index fbba7a343f0c7c..11aeffbe08601f 100755 --- a/scripts/examples/openiotsdk_example.sh +++ b/scripts/examples/openiotsdk_example.sh @@ -1,7 +1,7 @@ #!/bin/bash # -# Copyright (c) 2020 Project CHIP Authors +# Copyright (c) 2022-2023 Project CHIP Authors # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -18,7 +18,6 @@ # Build and/or run Open IoT SDK examples. -IS_TEST=0 NAME="$(basename "$0")" HERE="$(dirname "$0")" CHIP_ROOT="$(realpath "$HERE"/../..)" @@ -30,6 +29,7 @@ EXAMPLE_PATH="" BUILD_PATH="" TOOLCHAIN=arm-none-eabi-gcc DEBUG=false +LWIP_DEBUG=false EXAMPLE="" FVP_BIN=FVP_Corstone_SSE-300_Ethos-U55 GDB_PLUGIN="$FAST_MODEL_PLUGINS_PATH/GDBRemoteConnection.so" @@ -37,11 +37,18 @@ OIS_CONFIG="$CHIP_ROOT/config/openiotsdk" FVP_CONFIG_FILE="$OIS_CONFIG/fvp/cs300.conf" EXAMPLE_TEST_PATH="$CHIP_ROOT/src/test_driver/openiotsdk/integration-tests" TELNET_TERMINAL_PORT=5000 +TELNET_CONNECTION_PORT="" FAILED_TESTS=0 +IS_UNIT_TEST=0 FVP_NETWORK="user" readarray -t TEST_NAMES <"$CHIP_ROOT"/src/test_driver/openiotsdk/unit-tests/testnames.txt +declare -a SUPPORTED_APP_NAMES +SUPPORTED_APP_NAMES+=("shell") +SUPPORTED_APP_NAMES+=("lock-app") +SUPPORTED_APP_NAMES+=("unit-tests") + function show_usage() { cat < Action to execute - -d,--debug Build in debug mode - -p,--path Build path - -n,--network FVP network interface name + -h,--help Show this help + -c,--clean Clean target build + -s,--scratch Remove build directory at all before building + -C,--command Action to execute + -d,--debug Build in debug mode + -l,--lwipdebug Build with LwIP debug logs support + -p,--path Build path + -n,--network FVP network interface name Examples: - shell - lock-app - unit-tests +EOF + + for app in "${SUPPORTED_APP_NAMES[@]}"; do + echo " $app" + done + + cat < parameter as [test_name] - -The "test" command can be used for all supported examples expect the unit-tests. +Use "test" command without a specific test name, runs all supported unit tests. EOF } @@ -104,8 +113,9 @@ function build_with_cmake() { BUILD_OPTIONS+=(-DCMAKE_BUILD_TYPE=Debug) fi - # Activate Matter environment - source "$CHIP_ROOT"/scripts/activate.sh + if "$LWIP_DEBUG"; then + BUILD_OPTIONS+=(-DCONFIG_CHIP_OPEN_IOT_SDK_LWIP_DEBUG=YES) + fi cmake -G Ninja -S "$EXAMPLE_PATH" -B "$BUILD_PATH" --toolchain="$TOOLCHAIN_PATH" "${BUILD_OPTIONS[@]}" cmake --build "$BUILD_PATH" @@ -121,18 +131,18 @@ function run_fvp() { exit 1 fi - if [[ $IS_TEST -eq 0 ]]; then - EXAMPLE_EXE_PATH="$BUILD_PATH/chip-openiotsdk-$EXAMPLE-example.elf" - else - EXAMPLE_EXE_PATH="$BUILD_PATH/$EXAMPLE.elf" - fi - # Check if executable file exists if ! [ -f "$EXAMPLE_EXE_PATH" ]; then echo "Error: $EXAMPLE_EXE_PATH does not exist." >&2 exit 1 fi + # Check if FVP GDB plugin file exists + if "$DEBUG" && ! [ -f "$GDB_PLUGIN" ]; then + echo "Error: $GDB_PLUGIN does not exist. Ensure Fast Model extensions are mounted." >&2 + exit 1 + fi + RUN_OPTIONS=(-C mps3_board.telnetterminal0.start_port="$TELNET_TERMINAL_PORT") RUN_OPTIONS+=(--quantum=25) @@ -148,39 +158,46 @@ function run_fvp() { echo "Running $EXAMPLE_EXE_PATH with options: ${RUN_OPTIONS[@]}" - "$FVP_BIN" "${RUN_OPTIONS[@]}" -f "$FVP_CONFIG_FILE" --application "$EXAMPLE_EXE_PATH" >/dev/null 2>&1 & + # Run the FVP + "$FVP_BIN" "${RUN_OPTIONS[@]}" -f "$FVP_CONFIG_FILE" --application "$EXAMPLE_EXE_PATH" 2>&1 >/tmp/FVP_run_$$ & FVP_PID=$! - sleep 1 - - if [[ $IS_TEST -eq 1 ]]; then - set +e - expect <"$EXAMPLE_PATH"/test_report.json + + # Wait for FVP to start and exist the output file + timeout=0 + while [ ! -e /tmp/FVP_run_$$ ]; do + timeout=$((timeout + 1)) + if [ "$timeout" -ge 5 ]; then + echo "Error: FVP start failed" >&2 + break + fi + sleep 1 + done + + while IFS= read -t 5 -r line; do + if [[ $line == *"Listening for serial connection on port"* ]]; then + TELNET_CONNECTION_PORT="${line##* }" + break + fi + done &2 fi - # stop the fvp - kill -9 "$FVP_PID" || true - set -e - sleep 1 + # Stop the FVP + kill -SIGTERM "$FVP_PID" + # Wait for the FVP stop + while kill -0 "$FVP_PID"; do + sleep 1 + done + rm -rf /tmp/FVP_run_$$ } function run_test() { - EXAMPLE_EXE_PATH="$BUILD_PATH/chip-openiotsdk-$EXAMPLE-example.elf" # Check if executable file exists if ! [ -f "$EXAMPLE_EXE_PATH" ]; then echo "Error: $EXAMPLE_EXE_PATH does not exist." >&2 @@ -193,9 +210,6 @@ function run_test() { exit 1 fi - # Activate Matter environment with pytest - source "$CHIP_ROOT"/scripts/activate.sh - # Check if pytest exists if ! [ -x "$(command -v pytest)" ]; then echo "Error: pytest not installed." >&2 @@ -208,25 +222,25 @@ function run_test() { TEST_OPTIONS+=(--networkInterface="$FVP_NETWORK") fi - if [[ -f $EXAMPLE_TEST_PATH/$EXAMPLE/test_report.json ]]; then - rm -rf "$EXAMPLE_TEST_PATH/$EXAMPLE"/test_report.json + if [[ -f $EXAMPLE_TEST_PATH/test_report_$EXAMPLE.json ]]; then + rm -rf "$EXAMPLE_TEST_PATH/test_report_$EXAMPLE".json fi set +e - pytest --json-report --json-report-summary --json-report-file="$EXAMPLE_TEST_PATH/$EXAMPLE"/test_report.json --binaryPath="$EXAMPLE_EXE_PATH" --fvp="$FVP_BIN" --fvpConfig="$FVP_CONFIG_FILE" "${TEST_OPTIONS[@]}" "$EXAMPLE_TEST_PATH/$EXAMPLE"/test_app.py + pytest --json-report --json-report-summary --json-report-file="$EXAMPLE_TEST_PATH"/test_report_"$EXAMPLE".json --binaryPath="$EXAMPLE_EXE_PATH" --fvp="$FVP_BIN" --fvpConfig="$FVP_CONFIG_FILE" "${TEST_OPTIONS[@]}" "$EXAMPLE_TEST_PATH"/test_app.py set -e - if [[ ! -f $EXAMPLE_TEST_PATH/$EXAMPLE/test_report.json ]]; then + if [[ ! -f $EXAMPLE_TEST_PATH/test_report_$EXAMPLE.json ]]; then exit 1 else - if [[ $(jq '.summary | has("failed")' $EXAMPLE_TEST_PATH/$EXAMPLE/test_report.json) == true ]]; then - FAILED_TESTS=$(jq '.summary.failed' "$EXAMPLE_TEST_PATH/$EXAMPLE"/test_report.json) + if [[ $(jq '.summary | has("failed")' $EXAMPLE_TEST_PATH/test_report_$EXAMPLE.json) == true ]]; then + FAILED_TESTS=$((FAILED_TESTS + $(jq '.summary.failed' "$EXAMPLE_TEST_PATH"/test_report_"$EXAMPLE".json))) fi fi } -SHORT=C:,p:,d:,n:,c,s,h -LONG=command:,path:,debug:,network:,clean,scratch,help +SHORT=C:,p:,d:,l:,n:,c,s,h +LONG=command:,path:,debug:,lwipdebug:,network:,clean,scratch,help OPTS=$(getopt -n build --options "$SHORT" --longoptions "$LONG" -- "$@") eval set -- "$OPTS" @@ -253,6 +267,10 @@ while :; do DEBUG=$2 shift 2 ;; + -l | --lwipdebug) + LWIP_DEBUG=$2 + shift 2 + ;; -p | --path) BUILD_PATH=$CHIP_ROOT/$2 shift 2 @@ -278,12 +296,18 @@ if [[ $# -lt 1 ]]; then exit 1 fi -case "$1" in - shell | unit-tests | lock-app) - EXAMPLE=$1 - ;; +EXAMPLE=$1 + +if [[ ! " ${SUPPORTED_APP_NAMES[@]} " =~ " ${EXAMPLE} " ]]; then + echo "Wrong example name" + show_usage + exit 2 +fi + +case "$COMMAND" in + build | run | test | build-run) ;; *) - echo "Wrong example name" + echo "Wrong command definition" show_usage exit 2 ;; @@ -292,13 +316,8 @@ esac if [[ "$EXAMPLE" == "unit-tests" ]]; then if [ ! -z "$2" ]; then if [[ " ${TEST_NAMES[*]} " =~ " $2 " ]]; then - if [[ "$COMMAND" != *"run"* ]]; then - echo "Test suites can only accept --command run" - show_usage - exit 2 - fi EXAMPLE=$2 - echo "Run specific unit test $EXAMPLE" + echo "Use specific unit test $EXAMPLE" elif [[ "$2" == "all" ]]; then echo "Use all unit tests" else @@ -309,54 +328,38 @@ if [[ "$EXAMPLE" == "unit-tests" ]]; then else echo "Use all unit tests" fi - IS_TEST=1 + EXAMPLE_PATH="$CHIP_ROOT/src/test_driver/openiotsdk/unit-tests" + IS_UNIT_TEST=1 +else + EXAMPLE_PATH="$CHIP_ROOT/examples/$EXAMPLE/openiotsdk" fi -case "$COMMAND" in - build | run | test | build-run) ;; - *) - echo "Wrong command definition" - show_usage - exit 2 - ;; -esac - TOOLCHAIN_PATH="toolchains/toolchain-$TOOLCHAIN.cmake" -if [[ $IS_TEST -eq 0 ]]; then - EXAMPLE_PATH="$CHIP_ROOT/examples/$EXAMPLE/openiotsdk" -else - EXAMPLE_PATH="$CHIP_ROOT/src/test_driver/openiotsdk/unit-tests" - if [[ -f $EXAMPLE_PATH/test_report.json ]]; then - rm -rf "$EXAMPLE_PATH"/test_report.json - fi - echo "{}" >"$EXAMPLE_PATH"/test_report.json -fi - if [ -z "$BUILD_PATH" ]; then BUILD_PATH="$EXAMPLE_PATH/build" fi +# Activate Matter environment +source "$CHIP_ROOT"/scripts/activate.sh + +if [[ $IS_UNIT_TEST -eq 0 ]]; then + EXAMPLE_EXE_PATH="$BUILD_PATH/chip-openiotsdk-$EXAMPLE-example.elf" + EXAMPLE_TEST_PATH+="/$EXAMPLE" +else + EXAMPLE_EXE_PATH="$BUILD_PATH/$EXAMPLE.elf" + EXAMPLE_TEST_PATH+="/unit-tests" +fi + if [[ "$COMMAND" == *"build"* ]]; then build_with_cmake fi if [[ "$COMMAND" == *"run"* ]]; then - # If user wants to run unit-tests we need to loop through all test names if [[ "$EXAMPLE" == "unit-tests" ]]; then - if "$DEBUG"; then - echo "You have to specify the test suites to run in debug mode" - show_usage - exit 2 - else - for NAME in "${TEST_NAMES[@]}"; do - EXAMPLE=$NAME - echo "$EXAMPLE_PATH" - echo "Run specific unit test $EXAMPLE" - run_fvp - done - echo "Failed tests total: $FAILED_TESTS" - fi + echo "You have to specify the test suites to run" + show_usage + exit 2 else run_fvp fi @@ -364,15 +367,16 @@ fi if [[ "$COMMAND" == *"test"* ]]; then if [[ "$EXAMPLE" == "unit-tests" ]]; then - echo "The test command can not be applied to the unit-tests example" - show_usage - exit 2 + for NAME in "${TEST_NAMES[@]}"; do + EXAMPLE=$NAME + EXAMPLE_EXE_PATH="$BUILD_PATH/$EXAMPLE.elf" + echo "Test specific unit test $EXAMPLE" + run_test + done + else - IS_TEST=1 run_test fi -fi - -if [[ $IS_TEST -eq 1 ]]; then + echo "Failed tests total: $FAILED_TESTS" exit "$FAILED_TESTS" fi diff --git a/src/app/tests/BUILD.gn b/src/app/tests/BUILD.gn index b4da7f9f6af015..49657c9d3798c8 100644 --- a/src/app/tests/BUILD.gn +++ b/src/app/tests/BUILD.gn @@ -150,11 +150,12 @@ chip_test_suite("tests") { test_sources += [ "TestClusterStateCache.cpp" ] } - # On NRF and fake platforms we do not have a realtime clock available, so + # On NRF, Open IoT SDK and fake platforms we do not have a realtime clock available, so # TestEventLogging.cpp would be testing the same thing as # TestEventLoggingNoUTCTime, but it's not set up to deal with the timestamps # being that low. - if (chip_device_platform != "nrfconnect" && chip_device_platform != "fake") { + if (chip_device_platform != "nrfconnect" && + chip_device_platform != "openiotsdk" && chip_device_platform != "fake") { test_sources += [ "TestEventLogging.cpp" ] } diff --git a/src/app/tests/TestFailSafeContext.cpp b/src/app/tests/TestFailSafeContext.cpp index f0631cd7a92386..9e5d11dc320b73 100644 --- a/src/app/tests/TestFailSafeContext.cpp +++ b/src/app/tests/TestFailSafeContext.cpp @@ -50,10 +50,8 @@ constexpr FabricIndex kTestAccessingFabricIndex2 = 2; static void TestPlatformMgr_Init(nlTestSuite * inSuite, void * inContext) { -#if !defined(CHIP_DEVICE_LAYER_TARGET_OPEN_IOT_SDK) CHIP_ERROR err = PlatformMgr().InitChipStack(); NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); -#endif // !defined(CHIP_DEVICE_LAYER_TARGET_OPEN_IOT_SDK) } static void TestFailSafeContext_ArmFailSafe(nlTestSuite * inSuite, void * inContext) @@ -123,9 +121,7 @@ int TestFailSafeContext_Setup(void * inContext) */ int TestFailSafeContext_Teardown(void * inContext) { -#if !defined(CHIP_DEVICE_LAYER_TARGET_OPEN_IOT_SDK) PlatformMgr().Shutdown(); -#endif // !defined(CHIP_DEVICE_LAYER_TARGET_OPEN_IOT_SDK) chip::Platform::MemoryShutdown(); return SUCCESS; } diff --git a/src/inet/tests/TestInetAddress.cpp b/src/inet/tests/TestInetAddress.cpp index 4df87b1e752e59..a31bbb878efee9 100644 --- a/src/inet/tests/TestInetAddress.cpp +++ b/src/inet/tests/TestInetAddress.cpp @@ -1009,7 +1009,7 @@ void CheckToIPv6(nlTestSuite * inSuite, void * inContext) SetupIPAddress(test_addr, lCurrent); #if CHIP_SYSTEM_CONFIG_USE_LWIP - ip6_addr_t ip_addr_1, ip_addr_2; + ip6_addr_t ip_addr_1 = { 0 }, ip_addr_2 = { 0 }; memcpy(ip_addr_1.addr, addr, sizeof(addr)); #if LWIP_IPV6_SCOPES ip_addr_1.zone = 0; diff --git a/src/platform/openiotsdk/BUILD.gn b/src/platform/openiotsdk/BUILD.gn index 726b2face0b36e..984b4ff28b157f 100644 --- a/src/platform/openiotsdk/BUILD.gn +++ b/src/platform/openiotsdk/BUILD.gn @@ -68,6 +68,7 @@ static_library("openiotsdk") { "KeyValueStoreManagerImpl.cpp", "KeyValueStoreManagerImpl.h", "Logging.cpp", + "Logging.h", "NetworkCommissioningDriver.h", "NetworkCommissioningEthernetDriver.cpp", "OpenIoTSDKArchUtils.c", diff --git a/src/platform/openiotsdk/KVBlockDeviceStore.cpp b/src/platform/openiotsdk/KVBlockDeviceStore.cpp index f42d76300e7efb..3a235a0d99c61a 100644 --- a/src/platform/openiotsdk/KVBlockDeviceStore.cpp +++ b/src/platform/openiotsdk/KVBlockDeviceStore.cpp @@ -300,12 +300,12 @@ CHIP_ERROR KVBlockDeviceStore::WriteConfigValue(Key key, uint64_t val) CHIP_ERROR KVBlockDeviceStore::WriteConfigValueStr(Key key, const char * str) { - return WriteConfigValueBin(key, reinterpret_cast(str), strlen(str)); + return WriteConfigValueBin(key, reinterpret_cast(str), (str != nullptr) ? strlen(str) : 0); } CHIP_ERROR KVBlockDeviceStore::WriteConfigValueStr(Key key, const char * str, size_t strLen) { - return WriteConfigValueBin(key, reinterpret_cast(str), strLen); + return WriteConfigValueBin(key, reinterpret_cast(str), (strLen > 0) ? strLen : 1); } CHIP_ERROR KVBlockDeviceStore::WriteConfigValueBin(Key key, const uint8_t * data, size_t dataLen) diff --git a/src/platform/openiotsdk/KVPsaPsStore.cpp b/src/platform/openiotsdk/KVPsaPsStore.cpp index 0e46cec64d5919..523cb37b536c2a 100644 --- a/src/platform/openiotsdk/KVPsaPsStore.cpp +++ b/src/platform/openiotsdk/KVPsaPsStore.cpp @@ -303,7 +303,7 @@ CHIP_ERROR KVPsaPsStore::WriteConfigValue(Key key, uint64_t val) CHIP_ERROR KVPsaPsStore::WriteConfigValueStr(Key key, const char * str) { - return WriteConfigValueBin(key, reinterpret_cast(str), (str != NULL) ? strlen(str) : 0); + return WriteConfigValueBin(key, reinterpret_cast(str), (str != nullptr) ? strlen(str) : 0); } CHIP_ERROR KVPsaPsStore::WriteConfigValueStr(Key key, const char * str, size_t strLen) diff --git a/src/platform/openiotsdk/Logging.cpp b/src/platform/openiotsdk/Logging.cpp index 2a3ecfa7e64e7b..bc51e4e44d6570 100644 --- a/src/platform/openiotsdk/Logging.cpp +++ b/src/platform/openiotsdk/Logging.cpp @@ -47,19 +47,12 @@ void __attribute__((weak)) OnLogOutput() {} namespace Logging { namespace Platform { -/** - * Logging static buffer - */ -namespace { -char logMsgBuffer[CHIP_CONFIG_LOG_MESSAGE_MAX_SIZE]; -} - /** * CHIP log output functions. */ void ENFORCE_FORMAT(3, 0) LogV(const char * module, uint8_t category, const char * msg, va_list v) { - vsnprintf(logMsgBuffer, sizeof(logMsgBuffer), msg, v); + char logMsgBuffer[CHIP_CONFIG_LOG_MESSAGE_MAX_SIZE]; const char * category_prefix; switch (category) @@ -79,12 +72,22 @@ void ENFORCE_FORMAT(3, 0) LogV(const char * module, uint8_t category, const char break; } - printf("%s [%s] %s\r\n", category_prefix, module, logMsgBuffer); + int header_len = snprintf(logMsgBuffer, sizeof(logMsgBuffer), "%s [%s] ", category_prefix, module); + int content_len = vsnprintf(logMsgBuffer + header_len, sizeof(logMsgBuffer) - header_len, msg, v); + int trailer_len = snprintf(logMsgBuffer + header_len + content_len, sizeof(logMsgBuffer) - header_len - content_len, "\r\n"); + fwrite(logMsgBuffer, header_len + content_len + trailer_len, 1, stdout); // Let the application know that a log message has been emitted. DeviceLayer::OnLogOutput(); } +void ois_logging_init() +{ +#if defined(NDEBUG) && CHIP_CONFIG_TEST == 0 + SetLogFilter(LogCategory::kLogCategory_Progress); +#endif +} + } // namespace Platform } // namespace Logging } // namespace chip diff --git a/src/test_driver/openiotsdk/unit-tests/lwip-config/user_lwipopts.h b/src/platform/openiotsdk/Logging.h similarity index 67% rename from src/test_driver/openiotsdk/unit-tests/lwip-config/user_lwipopts.h rename to src/platform/openiotsdk/Logging.h index 59ed1720c7e36c..cd34e5206cd58a 100644 --- a/src/test_driver/openiotsdk/unit-tests/lwip-config/user_lwipopts.h +++ b/src/platform/openiotsdk/Logging.h @@ -16,10 +16,22 @@ * limitations under the License. */ -#ifndef USER_LWIPOPTS_H -#define USER_LWIPOPTS_H +/** + * @file + * Provides an implementation of logging support + * for Open IOT SDK platform. + */ + +#pragma once + +#include + +namespace chip { +namespace Logging { +namespace Platform { -#define LWIP_STATS (0) -#define PBUF_POOL_SIZE (1001) +void ois_logging_init(void); -#endif /* USER_LWIPOPTS_H */ +} // namespace Platform +} // namespace Logging +} // namespace chip diff --git a/src/platform/openiotsdk/PlatformManagerImpl.cpp b/src/platform/openiotsdk/PlatformManagerImpl.cpp index 47b8a5293dafdf..579eb5e7f10429 100644 --- a/src/platform/openiotsdk/PlatformManagerImpl.cpp +++ b/src/platform/openiotsdk/PlatformManagerImpl.cpp @@ -40,6 +40,18 @@ namespace DeviceLayer { CHIP_ERROR PlatformManagerImpl::_InitChipStack(void) { + if (mInitialized) + { + return CHIP_NO_ERROR; + } + + // Call up to the base class _InitChipStack() to perform the bulk of the initialization. + CHIP_ERROR err = GenericPlatformManagerImpl::_InitChipStack(); + if (err != CHIP_NO_ERROR) + { + return err; + } + // Members are initialized by the stack osMutexAttr_t mut_att = { .attr_bits = osMutexRecursive }; @@ -62,15 +74,12 @@ CHIP_ERROR PlatformManagerImpl::_InitChipStack(void) return CHIP_ERROR_INTERNAL; } + mInitialized = true; + SetConfigurationMgr(&ConfigurationManagerImpl::GetDefaultInstance()); SetDiagnosticDataProvider(&DiagnosticDataProviderImpl::GetDefaultInstance()); - // Call up to the base class _InitChipStack() to perform the bulk of the initialization. - CHIP_ERROR err = GenericPlatformManagerImpl::_InitChipStack(); - SuccessOrExit(err); - -exit: - return err; + return CHIP_NO_ERROR; } void PlatformManagerImpl::_LockChipStack() @@ -97,6 +106,9 @@ void PlatformManagerImpl::_UnlockChipStack() CHIP_ERROR PlatformManagerImpl::_PostEvent(const ChipDeviceEvent * eventPtr) { + // The post event requires event queue from stack initialization + ReturnLogErrorOnFailure(_InitChipStack()); + osStatus_t status = osMessageQueuePut(mQueue, eventPtr, 0, 0); CHIP_ERROR ret = (status == osOK) ? CHIP_NO_ERROR : CHIP_ERROR_INTERNAL; osEventFlagsSet(mPlatformFlags, kPostEventFlag); @@ -107,12 +119,18 @@ void PlatformManagerImpl::HandlePostEvent() { /* handle an event */ ChipDeviceEvent event; - osStatus_t status = osMessageQueueGet(mQueue, &event, nullptr, 0); - if (status == osOK) + uint32_t count = osMessageQueueGetCount(mQueue); + while (count) { + if (osMessageQueueGet(mQueue, &event, nullptr, 0) != osOK) + { + break; + } + LockChipStack(); DispatchEvent(&event); UnlockChipStack(); + count--; } } @@ -154,12 +172,8 @@ void PlatformManagerImpl::RunEventLoopInternal() if (flags & kPostEventFlag) { + osEventFlagsClear(mPlatformFlags, kPostEventFlag); HandlePostEvent(); - - if (!osMessageQueueGetCount(mQueue)) - { - osEventFlagsClear(mPlatformFlags, kPostEventFlag); - } } if ((flags & kTaskRunningEventFlag) == 0) @@ -171,6 +185,12 @@ void PlatformManagerImpl::RunEventLoopInternal() void PlatformManagerImpl::_RunEventLoop() { + if (!mInitialized) + { + ChipLogError(DeviceLayer, "_RunEventLoop: stack not initialized"); + return; + } + osEventFlagsSet(mPlatformFlags, kTaskRunningEventFlag); RunEventLoopInternal(); @@ -185,6 +205,12 @@ void PlatformManagerImpl::EventLoopTask(void * arg) CHIP_ERROR PlatformManagerImpl::_StartEventLoopTask() { + if (!mInitialized) + { + ChipLogError(DeviceLayer, "_StartEventLoopTask: stack not initialized"); + return CHIP_ERROR_INCORRECT_STATE; + } + // this mutex only needed to guard against multiple launches { osMutexAcquire(mEventTaskMutex, osWaitForever); @@ -221,6 +247,12 @@ CHIP_ERROR PlatformManagerImpl::_StartEventLoopTask() CHIP_ERROR PlatformManagerImpl::_StopEventLoopTask() { + if (!mInitialized) + { + ChipLogError(DeviceLayer, "_StopEventLoopTask: stack not initialized"); + return CHIP_ERROR_INCORRECT_STATE; + } + // this mutex only needed to guard against multiple calls to stop { osMutexAcquire(mEventTaskMutex, osWaitForever); @@ -258,6 +290,9 @@ void PlatformManagerImpl::TimerCallback(void * arg) CHIP_ERROR PlatformManagerImpl::_StartChipTimer(System::Clock::Timeout duration) { + // The timer requires event queue from stack initialization + ReturnLogErrorOnFailure(_InitChipStack()); + if (duration.count() == 0) { TimerCallback(0); @@ -276,6 +311,12 @@ CHIP_ERROR PlatformManagerImpl::_StartChipTimer(System::Clock::Timeout duration) void PlatformManagerImpl::_Shutdown() { + if (!mInitialized) + { + ChipLogError(DeviceLayer, "_Shutdown: stack not initialized"); + return; + } + // // Call up to the base class _Shutdown() to perform the actual stack de-initialization // and clean-up @@ -304,6 +345,7 @@ void PlatformManagerImpl::_Shutdown() mEventTaskMutex = nullptr; mQueue = nullptr; mTimer = nullptr; + mInitialized = false; GenericPlatformManagerImpl::_Shutdown(); } diff --git a/src/platform/openiotsdk/PlatformManagerImpl.h b/src/platform/openiotsdk/PlatformManagerImpl.h index 70664944002143..2a5e86e8341e5d 100644 --- a/src/platform/openiotsdk/PlatformManagerImpl.h +++ b/src/platform/openiotsdk/PlatformManagerImpl.h @@ -100,6 +100,7 @@ class PlatformManagerImpl final : public PlatformManager, public Internal::Gener osMutexId_t mEventTaskMutex = nullptr; osMessageQueueId_t mQueue = nullptr; osTimerId_t mTimer = nullptr; + bool mInitialized = false; }; /** diff --git a/src/platform/tests/TestPlatformMgr.cpp b/src/platform/tests/TestPlatformMgr.cpp index 239817d9417465..09acb8a9d046f8 100644 --- a/src/platform/tests/TestPlatformMgr.cpp +++ b/src/platform/tests/TestPlatformMgr.cpp @@ -100,6 +100,12 @@ static void TestPlatformMgr_BasicEventLoopTask(nlTestSuite * inSuite, void * inC err = PlatformMgr().StopEventLoopTask(); NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); + + // Sleep for a short time to allow the event loop to stop. + // Note, in some platform implementations the event loop thread + // is self-terminating. We need time to process the stopping event + // inside event loop. + chip::test_utils::SleepMillis(10); } NL_TEST_ASSERT(inSuite, counterRun == (3 * 2)); diff --git a/src/system/tests/BUILD.gn b/src/system/tests/BUILD.gn index 3c90b372df69f3..1fb0a8f708b7a9 100644 --- a/src/system/tests/BUILD.gn +++ b/src/system/tests/BUILD.gn @@ -35,10 +35,11 @@ chip_test_suite("tests") { test_sources += [ "TestSystemScheduleWork.cpp" ] } - # SystemPacketBuffer on nrfconnect uses LwIP buffers, which ignore the + # SystemPacketBuffer on nrfconnect and openiotsdk uses LwIP buffers, which ignore the # requested allocation size and always allocate at max-size. So our test, # which tries to size-limit the buffers, does not work correctly there. - if (chip_device_platform != "nrfconnect") { + if (chip_device_platform != "nrfconnect" && + chip_device_platform != "openiotsdk") { test_sources += [ "TestTLVPacketBufferBackingStore.cpp" ] } diff --git a/src/test_driver/openiotsdk/integration-tests/.gitignore b/src/test_driver/openiotsdk/integration-tests/.gitignore index 9e76edca71065a..4b72c4651b729f 100644 --- a/src/test_driver/openiotsdk/integration-tests/.gitignore +++ b/src/test_driver/openiotsdk/integration-tests/.gitignore @@ -1 +1 @@ -/**/test_report.json +/**/test_report*.json diff --git a/src/test_driver/openiotsdk/integration-tests/common/device.py b/src/test_driver/openiotsdk/integration-tests/common/device.py index 51e939e135f3b3..c88564ee699676 100644 --- a/src/test_driver/openiotsdk/integration-tests/common/device.py +++ b/src/test_driver/openiotsdk/integration-tests/common/device.py @@ -30,6 +30,7 @@ def __init__(self, name: Optional[str] = None): Base Device runner class containing device handling functions and logging :param name: Logging name for the client """ + self.verbose = True self.iq = queue.Queue() self.oq = queue.Queue() if name is None: @@ -72,7 +73,7 @@ def flush(self, timeout: float = 0) -> [str]: except queue.Empty: return lines - def wait_for_output(self, search: str, timeout: float = 10, assert_timeout: bool = True) -> [str]: + def wait_for_output(self, search: str, timeout: float = 10, assert_timeout: bool = True, verbose: bool = False) -> [str]: """ Wait for expected output response :param search: Expected response string @@ -108,7 +109,7 @@ def wait_for_output(self, search: str, timeout: float = 10, assert_timeout: bool else: log.warning(timeout_error_msg) return [] - if now - last > 1: + if verbose and (now - last > 1): log.info('{}: Waiting for "{}" string... Timeout in {:.0f} s'.format(self.name, search, abs(now - start - timeout))) @@ -117,3 +118,6 @@ def _write(self, data): def _read_line(self, timeout): return self.iq.get(timeout=timeout) + + def set_verbose(self, state: bool): + self.verbose = state diff --git a/src/test_driver/openiotsdk/integration-tests/common/fixtures.py b/src/test_driver/openiotsdk/integration-tests/common/fixtures.py index 04d1927ae77f2b..1bbb75d9e9eab6 100644 --- a/src/test_driver/openiotsdk/integration-tests/common/fixtures.py +++ b/src/test_driver/openiotsdk/integration-tests/common/fixtures.py @@ -75,34 +75,20 @@ def device(fvp, fvpConfig, binaryPath, telnetPort, networkInterface): @pytest.fixture(scope="session") -def vendor_id(): - return 0xFFF1 - - -@pytest.fixture(scope="session") -def fabric_id(): - return 1 - - -@pytest.fixture(scope="session") -def node_id(): - return 1 - - -@pytest.fixture(scope="function") -def controller(vendor_id, fabric_id, node_id): +def controller(controllerConfig): try: chip.native.Init() chipStack = chip.ChipStack.ChipStack( - persistentStoragePath='/tmp/openiotsdk-test-storage.json', enableServerInteractions=False) + persistentStoragePath=controllerConfig['persistentStoragePath'], enableServerInteractions=False) certificateAuthorityManager = chip.CertificateAuthority.CertificateAuthorityManager( chipStack, chipStack.GetStorageManager()) certificateAuthorityManager.LoadAuthoritiesFromStorage() if (len(certificateAuthorityManager.activeCaList) == 0): ca = certificateAuthorityManager.NewCertificateAuthority() - ca.NewFabricAdmin(vendorId=vendor_id, fabricId=fabric_id) + ca.NewFabricAdmin(vendorId=controllerConfig['vendorId'], fabricId=controllerConfig['fabricId']) elif (len(certificateAuthorityManager.activeCaList[0].adminList) == 0): - certificateAuthorityManager.activeCaList[0].NewFabricAdmin(vendorId=vendor_id, fabricId=fabric_id) + certificateAuthorityManager.activeCaList[0].NewFabricAdmin( + vendorId=controllerConfig['vendorId'], fabricId=controllerConfig['fabricId']) caList = certificateAuthorityManager.activeCaList @@ -116,3 +102,8 @@ def controller(vendor_id, fabric_id, node_id): return None yield devCtrl + + devCtrl.Shutdown() + certificateAuthorityManager.Shutdown() + chipStack.Shutdown() + os.remove(controllerConfig['persistentStoragePath']) diff --git a/src/test_driver/openiotsdk/integration-tests/common/fvp_device.py b/src/test_driver/openiotsdk/integration-tests/common/fvp_device.py index 9fd1233be2e818..d84d9d00dc1931 100644 --- a/src/test_driver/openiotsdk/integration-tests/common/fvp_device.py +++ b/src/test_driver/openiotsdk/integration-tests/common/fvp_device.py @@ -16,9 +16,10 @@ # import logging +import re import subprocess import threading -from time import sleep +import time from .device import Device @@ -60,9 +61,30 @@ def start(self): """ log.info('Starting "{}" runner...'.format(self.name)) - self.proc = subprocess.Popen(self.fvp_cmd) - sleep(3) + self.proc = subprocess.Popen(self.fvp_cmd, stdout=subprocess.PIPE) + timeout = time.time() + 10 # 10s timeout + # Check if FVP process run properly and wait for the connection port log + while True: + if time.time() >= timeout: + raise Exception("FVP start failed") + else: + # Readline from process output + output = self.proc.stdout.readline() + + # Check if process still running + if output == '' and self.proc.poll() is not None: + raise Exception("FVP process has stopped") + else: + line = output.decode().strip() + if re.match(".*Listening for serial connection on port .*", line): + connection_port = int(line.split("port", 1)[1]) + break + time.sleep(0.5) + + if self.connection_channel.get_port() != connection_port: + self.connection_channel.set_port(connection_port) self.connection_channel.open() + self.run = True self.it.start() self.ot.start() @@ -86,7 +108,8 @@ def _input_thread(self): while self.run: line = self.connection_channel.readline() if line: - log.info('<--|{}| {}'.format(self.name, line.strip())) + if self.verbose: + log.info('<--|{}| {}'.format(self.name, line.strip())) self.iq.put(line) else: pass @@ -95,7 +118,8 @@ def _output_thread(self): while self.run: line = self.oq.get() if line: - log.info('-->|{}| {}'.format(self.name, line.strip())) + if self.verbose: + log.info('-->|{}| {}'.format(self.name, line.strip())) self.connection_channel.write(line) else: log.debug('Nothing sent') diff --git a/src/test_driver/openiotsdk/integration-tests/common/telnet_connection.py b/src/test_driver/openiotsdk/integration-tests/common/telnet_connection.py index 66a509e2627e6b..cc1c42c3d5c1d9 100644 --- a/src/test_driver/openiotsdk/integration-tests/common/telnet_connection.py +++ b/src/test_driver/openiotsdk/integration-tests/common/telnet_connection.py @@ -33,6 +33,7 @@ def __init__(self, host=None, port=0): self.host = host self.port = port self.is_open = False + self.output_line = bytearray() def open(self): """ @@ -85,11 +86,15 @@ def readline(self): if not self.is_open: return None try: - output = self.telnet.read_until(b"\n", 1) - return self.__formatline(output) + self.output_line.extend(self.telnet.read_until(b"\n", 1)) + if b"\n" in self.output_line: + output = self.__formatline(self.output_line) + self.output_line.clear() + return output except Exception as e: log.error('Telnet read failed {}'.format(e)) return None + return None def write(self, data): """ @@ -114,6 +119,12 @@ def close(self): self.telnet.close() self.is_open = False + def set_port(self, port): + """ + set port number of telnet connection + """ + self.port = port + def get_port(self): """ Get port number of telnet connection diff --git a/src/test_driver/openiotsdk/integration-tests/common/utils.py b/src/test_driver/openiotsdk/integration-tests/common/utils.py index 905876f36057da..52b52839a6b21b 100644 --- a/src/test_driver/openiotsdk/integration-tests/common/utils.py +++ b/src/test_driver/openiotsdk/integration-tests/common/utils.py @@ -19,10 +19,11 @@ import logging import random import re -import shlex from chip import discovery, exceptions +from chip.clusters import Attribute as ClusterAttribute from chip.clusters import Objects as GeneratedObjects +from chip.interaction_model import delegate as IM from chip.setup_payload import SetupPayload log = logging.getLogger(__name__) @@ -36,7 +37,7 @@ def get_setup_payload(device): :param device: serial device instance :return: setup payload or None """ - ret = device.wait_for_output("SetupQRCode") + ret = device.wait_for_output("SetupQRCode", timeout=30) if ret is None or len(ret) < 2: return None @@ -74,9 +75,10 @@ def discover_device(devCtrl, setupPayload): return res[0] -def connect_device(setupPayload, commissionableDevice, nodeId=None): +def connect_device(devCtrl, setupPayload, commissionableDevice, nodeId=None): """ Connect to Matter discovered device on network + :param devCtrl: device controller instance :param setupPayload: device setup payload :param commissionableDevice: CommissionableNode object with discovered device :param nodeId: device node ID @@ -87,10 +89,14 @@ def connect_device(setupPayload, commissionableDevice, nodeId=None): pincode = int(setupPayload.attributes['SetUpPINCode']) try: - commissionableDevice.Commission(nodeId, pincode) + res = devCtrl.CommissionOnNetwork( + nodeId, pincode, filterType=discovery.FilterType.INSTANCE_NAME, filter=commissionableDevice.instanceName) except exceptions.ChipStackError as ex: log.error("Commission discovered device failed {}".format(str(ex))) return None + if not res: + log.info("Commission discovered device failed") + return None return nodeId @@ -109,89 +115,28 @@ def disconnect_device(devCtrl, nodeId): return True -class ParsingError(exceptions.ChipStackException): - def __init__(self, msg=None): - self.msg = "Parsing Error: " + msg - - def __str__(self): - return self.msg - - -def ParseEncodedString(value): - if value.find(":") < 0: - raise ParsingError( - "Value should be encoded in encoding:encodedvalue format") - enc, encValue = value.split(":", 1) - if enc == "str": - return encValue.encode("utf-8") + b'\x00' - elif enc == "hex": - return bytes.fromhex(encValue) - raise ParsingError("Only str and hex encoding is supported") - - -def ParseValueWithType(value, type): - if type == 'int': - return int(value) - elif type == 'str': - return value - elif type == 'bytes': - return ParseEncodedString(value) - elif type == 'bool': - return (value.upper() not in ['F', 'FALSE', '0']) - else: - raise ParsingError('Cannot recognize type: {}'.format(type)) - - -def ParseValueWithStruct(value, cluster): - return eval(f"GeneratedObjects.{cluster}.Structs.{value}") - - -def ParseValue(value, valueType, cluster): - if valueType: - return ParseValueWithType(value, valueType) - elif value.find(":") > 0 and value.split(":", 1)[0] == "struct": - return ParseValueWithStruct(value.split(":", 1)[1], cluster) - else: - raise ParsingError('Cannot parse value: {}'.format(value)) - - -def FormatZCLArguments(cluster, args, cmdArgsWithType): - cmdArgsDict = {} - for kvPair in args: - if kvPair.find("=") < 0: - raise ParsingError("Argument should in key=value format") - key, value = kvPair.split("=", 1) - valueType = cmdArgsWithType.get(key, None) - cmdArgsDict[key] = ParseValue(value, valueType, cluster) - return cmdArgsDict - - -def send_zcl_command(devCtrl, line, requestTimeoutMs: int = None): +def send_zcl_command(devCtrl, cluster: str, command: str, nodeId: int, endpoint: int, args, requestTimeoutMs: int = None): """ - Format and send ZCL message to device. + Send ZCL command to device. :param devCtrl: device controller instance - :param line: command line + :param cluster: cluster name + :param command: command name + :param nodeId: device node ID + :param endpoint: device endpoint + :parma args: command argument in dictionary format :param requestTimeoutMs: command request timeout in ms :return: error code and command response """ res = None err = 0 try: - args = shlex.split(line) - if len(args) < 4: - raise exceptions.InvalidArgumentCount(4, len(args)) - - cluster, command, nodeId, endpoint = args[0:4] - cmdArgsLine = args[4:] allCommands = devCtrl.ZCLCommandList() if cluster not in allCommands: raise exceptions.UnknownCluster(cluster) - cmdArgsWithType = allCommands.get(cluster).get(command, None) - # When command takes no arguments, (not command) is True - if command is None: + cmd = allCommands.get(cluster).get(command) + if cmd is None: raise exceptions.UnknownCommand(cluster, command) - args = FormatZCLArguments(cluster, cmdArgsLine, cmdArgsWithType) clusterObj = getattr(GeneratedObjects, cluster) commandObj = getattr(clusterObj.Commands, command) req = commandObj(**args) @@ -208,32 +153,76 @@ def send_zcl_command(devCtrl, line, requestTimeoutMs: int = None): return (err, res) -def read_zcl_attribute(devCtrl, line): +def write_zcl_attribute(devCtrl, cluster: str, attribute: str, nodeId: int, endpoint: int, value): """ - Read ZCL attribute from device: - + Write ZCL attribute to device. :param devCtrl: device controller instance - :param line: command line + :param cluster: cluster name + :param attribute: attribute name + :param nodeId: device node ID + :param endpoint: device endpoint + :parma value: attribute value to write :return: error code and attribute response """ res = None err = 0 try: - args = shlex.split(line) - if len(args) < 4: - raise exceptions.InvalidArgumentCount(4, len(args)) + allAttrs = devCtrl.ZCLAttributeList() + if cluster not in allAttrs: + raise exceptions.UnknownCluster(cluster) + + attrDetails = allAttrs.get(cluster).get(attribute) + if attrDetails is None: + raise exceptions.UnknownAttribute(cluster, attribute) - cluster, attribute, nodeId, endpoint = args[0:4] + clusterObj = getattr(GeneratedObjects, cluster) + attributeObj = getattr(clusterObj.Attributes, attribute) + req = attributeObj(value) + + res = asyncio.run(devCtrl.WriteAttribute(nodeId, [(endpoint, req)])) + + except exceptions.ChipStackException as ex: + log.error("An exception occurred during processing ZCL attribute: {}".format(str(ex))) + err = -1 + except Exception as ex: + log.error("An exception occurred during processing input: {}".format(str(ex))) + err = -1 + + return (err, res) + + +def read_zcl_attribute(devCtrl, cluster: str, attribute: str, nodeId: int, endpoint: int): + """ + Read ZCL attribute from device. + :param devCtrl: device controller instance + :param cluster: cluster name + :param attribute: attribute name + :param nodeId: device node ID + :param endpoint: device endpoint + :return: error code and attribute response + """ + res = None + err = 0 + try: allAttrs = devCtrl.ZCLAttributeList() if cluster not in allAttrs: raise exceptions.UnknownCluster(cluster) - attrDetails = allAttrs.get(cluster).get(attribute, None) + attrDetails = allAttrs.get(cluster).get(attribute) if attrDetails is None: raise exceptions.UnknownAttribute(cluster, attribute) - res = devCtrl.ZCLReadAttribute(cluster, attribute, int( - nodeId), int(endpoint), 0) + clusterObj = getattr(GeneratedObjects, cluster) + attributeObj = getattr(clusterObj.Attributes, attribute) + + result = asyncio.run(devCtrl.ReadAttribute(nodeId, [(endpoint, attributeObj)])) + + path = ClusterAttribute.AttributePath( + EndpointId=endpoint, Attribute=attributeObj) + + res = IM.AttributeReadResult(path=IM.AttributePath(nodeId=nodeId, endpointId=path.EndpointId, clusterId=path.ClusterId, + attributeId=path.AttributeId), status=0, value=result[endpoint][clusterObj][attributeObj]) + except exceptions.ChipStackException as ex: log.error("An exception occurred during processing ZCL attribute: {}".format(str(ex))) err = -1 diff --git a/src/test_driver/openiotsdk/integration-tests/lock-app/test_app.py b/src/test_driver/openiotsdk/integration-tests/lock-app/test_app.py index 72ff8fdd78d8c1..8969dcb6ef2165 100644 --- a/src/test_driver/openiotsdk/integration-tests/lock-app/test_app.py +++ b/src/test_driver/openiotsdk/integration-tests/lock-app/test_app.py @@ -20,6 +20,7 @@ import pytest from chip.clusters.Objects import DoorLock +from chip.clusters.Types import NullValue from common.utils import connect_device, disconnect_device, discover_device, get_setup_payload, read_zcl_attribute, send_zcl_command log = logging.getLogger(__name__) @@ -33,11 +34,21 @@ def binaryPath(request, rootDir): return os.path.join(rootDir, 'examples/lock-app/openiotsdk/build/chip-openiotsdk-lock-app-example.elf') +@pytest.fixture(scope="session") +def controllerConfig(request): + config = { + 'vendorId': 0xFFF1, + 'fabricId': 1, + 'persistentStoragePath': '/tmp/openiotsdk-test-storage.json' + } + return config + + @pytest.mark.smoketest def test_smoke_test(device): ret = device.wait_for_output("Open IoT SDK lock-app example application start") assert ret is not None and len(ret) > 0 - ret = device.wait_for_output("Open IoT SDK lock-app example application run") + ret = device.wait_for_output("Open IoT SDK lock-app example application run", timeout=30) assert ret is not None and len(ret) > 0 @@ -46,6 +57,9 @@ def test_commissioning(device, controller): assert controller is not None devCtrl = controller + ret = device.wait_for_output("Open IoT SDK lock-app example application start") + assert ret is not None and len(ret) > 0 + setupPayload = get_setup_payload(device) assert setupPayload is not None @@ -56,7 +70,7 @@ def test_commissioning(device, controller): assert commissionable_device.productId == int(setupPayload.attributes['ProductID']) assert commissionable_device.addresses[0] is not None - nodeId = connect_device(setupPayload, commissionable_device) + nodeId = connect_device(devCtrl, setupPayload, commissionable_device) assert nodeId is not None log.info("Device {} connected".format(commissionable_device.addresses[0])) @@ -66,7 +80,7 @@ def test_commissioning(device, controller): assert disconnect_device(devCtrl, nodeId) -LOCK_CTRL_TEST_PIN_CODE = 12345 +LOCK_CTRL_TEST_PIN_CODE = b"123456" LOCK_CTRL_TEST_USER_INDEX = 1 LOCK_CTRL_TEST_ENDPOINT_ID = 1 LOCK_CTRL_TEST_USER_NAME = 'testUser' @@ -78,28 +92,30 @@ def test_lock_ctrl(device, controller): assert controller is not None devCtrl = controller + ret = device.wait_for_output("Open IoT SDK lock-app example application start") + assert ret is not None and len(ret) > 0 + setupPayload = get_setup_payload(device) assert setupPayload is not None commissionable_device = discover_device(devCtrl, setupPayload) assert commissionable_device is not None - nodeId = connect_device(setupPayload, commissionable_device) + nodeId = connect_device(devCtrl, setupPayload, commissionable_device) assert nodeId is not None ret = device.wait_for_output("Commissioning completed successfully", timeout=30) assert ret is not None and len(ret) > 0 - err, res = send_zcl_command( - devCtrl, "DoorLock SetUser {} {} operationType={} userIndex={} userName={} userUniqueId={} " - "userStatus={} userType={} credentialRule={}".format(nodeId, LOCK_CTRL_TEST_ENDPOINT_ID, - DoorLock.Enums.DlDataOperationType.kAdd, - LOCK_CTRL_TEST_USER_INDEX, - LOCK_CTRL_TEST_USER_NAME, - LOCK_CTRL_TEST_USER_INDEX, - DoorLock.Enums.DlUserStatus.kOccupiedEnabled, - DoorLock.Enums.DlUserType.kUnrestrictedUser, - DoorLock.Enums.DlCredentialRule.kSingle), requestTimeoutMs=1000) + err, res = send_zcl_command(devCtrl, "DoorLock", "SetUser", nodeId, LOCK_CTRL_TEST_ENDPOINT_ID, + dict(operationType=DoorLock.Enums.DataOperationTypeEnum.kAdd, + userIndex=LOCK_CTRL_TEST_USER_INDEX, + userName=LOCK_CTRL_TEST_USER_NAME, + userUniqueID=LOCK_CTRL_TEST_USER_INDEX, + userStatus=DoorLock.Enums.UserStatusEnum.kOccupiedEnabled, + userType=DoorLock.Enums.UserTypeEnum.kUnrestrictedUser, + credentialRule=DoorLock.Enums.CredentialRuleEnum.kSingle), + requestTimeoutMs=1000) assert err == 0 ret = device.wait_for_output("Successfully set the user [mEndpointId={},index={},adjustedIndex=0]".format( @@ -107,28 +123,26 @@ def test_lock_ctrl(device, controller): LOCK_CTRL_TEST_USER_INDEX)) assert ret is not None and len(ret) > 0 - err, res = send_zcl_command( - devCtrl, "DoorLock GetUser {} {} userIndex={}".format(nodeId, LOCK_CTRL_TEST_ENDPOINT_ID, - LOCK_CTRL_TEST_USER_INDEX)) + err, res = send_zcl_command(devCtrl, "DoorLock", "GetUser", nodeId, LOCK_CTRL_TEST_ENDPOINT_ID, + dict(userIndex=LOCK_CTRL_TEST_USER_INDEX), + requestTimeoutMs=1000) assert err == 0 assert res.userIndex == LOCK_CTRL_TEST_USER_INDEX assert res.userName == LOCK_CTRL_TEST_USER_NAME assert res.userUniqueID == LOCK_CTRL_TEST_USER_INDEX - assert res.userStatus == DoorLock.Enums.DlUserStatus.kOccupiedEnabled - assert res.userType == DoorLock.Enums.DlUserType.kUnrestrictedUser - assert res.credentialRule == DoorLock.Enums.DlCredentialRule.kSingle - - err, res = send_zcl_command( - devCtrl, "DoorLock SetCredential {} {} operationType={} " - "credential=struct:DlCredential(credentialType={},credentialIndex={}) credentialData=str:{} " - "userIndex={} userStatus={} userType={}".format(nodeId, LOCK_CTRL_TEST_ENDPOINT_ID, - DoorLock.Enums.DlDataOperationType.kAdd, - DoorLock.Enums.DlCredentialType.kPin, - LOCK_CTRL_TEST_CREDENTIAL_INDEX, - LOCK_CTRL_TEST_PIN_CODE, - LOCK_CTRL_TEST_USER_INDEX, - DoorLock.Enums.DlUserStatus.kOccupiedEnabled, - DoorLock.Enums.DlUserType.kUnrestrictedUser), requestTimeoutMs=1000) + assert res.userStatus == DoorLock.Enums.UserStatusEnum.kOccupiedEnabled + assert res.userType == DoorLock.Enums.UserTypeEnum.kUnrestrictedUser + assert res.credentialRule == DoorLock.Enums.CredentialRuleEnum.kSingle + + err, res = send_zcl_command(devCtrl, "DoorLock", "SetCredential", nodeId, LOCK_CTRL_TEST_ENDPOINT_ID, + dict(operationType=DoorLock.Enums.DataOperationTypeEnum.kAdd, + credential=DoorLock.Structs.CredentialStruct( + credentialType=DoorLock.Enums.CredentialTypeEnum.kPin, credentialIndex=LOCK_CTRL_TEST_CREDENTIAL_INDEX), + credentialData=LOCK_CTRL_TEST_PIN_CODE, + userIndex=LOCK_CTRL_TEST_USER_INDEX, + userStatus=NullValue, + userType=NullValue), + requestTimeoutMs=1000) assert err == 0 assert res.status == DoorLock.Enums.DlStatus.kSuccess @@ -136,42 +150,39 @@ def test_lock_ctrl(device, controller): "credentialType={},creator=1,modifier=1]".format( LOCK_CTRL_TEST_ENDPOINT_ID, LOCK_CTRL_TEST_USER_INDEX, - DoorLock.Enums.DlCredentialType.kPin + DoorLock.Enums.CredentialTypeEnum.kPin )) assert ret is not None and len(ret) > 0 - err, res = send_zcl_command( - devCtrl, "DoorLock GetCredentialStatus {} {} credential=struct:DlCredential(credentialType={}," - "credentialIndex={})".format(nodeId, LOCK_CTRL_TEST_ENDPOINT_ID, - DoorLock.Enums.DlCredentialType.kPin, - LOCK_CTRL_TEST_CREDENTIAL_INDEX), requestTimeoutMs=1000) + err, res = send_zcl_command(devCtrl, "DoorLock", "GetCredentialStatus", nodeId, LOCK_CTRL_TEST_ENDPOINT_ID, + dict(credential=DoorLock.Structs.CredentialStruct( + credentialType=DoorLock.Enums.CredentialTypeEnum.kPin, credentialIndex=LOCK_CTRL_TEST_CREDENTIAL_INDEX)), + requestTimeoutMs=1000) assert err == 0 assert res.credentialExists assert res.userIndex == LOCK_CTRL_TEST_USER_INDEX - err, res = send_zcl_command( - devCtrl, "DoorLock LockDoor {} {} pinCode=str:{}".format(nodeId, LOCK_CTRL_TEST_ENDPOINT_ID, - LOCK_CTRL_TEST_PIN_CODE), requestTimeoutMs=1000) + err, res = send_zcl_command(devCtrl, "DoorLock", "LockDoor", nodeId, LOCK_CTRL_TEST_ENDPOINT_ID, + dict(PINCode=LOCK_CTRL_TEST_PIN_CODE), + requestTimeoutMs=1000) assert err == 0 ret = device.wait_for_output("setting door lock state to \"Locked\"") assert ret is not None and len(ret) > 0 - err, res = read_zcl_attribute( - devCtrl, "DoorLock LockState {} {}".format(nodeId, LOCK_CTRL_TEST_ENDPOINT_ID)) + err, res = read_zcl_attribute(devCtrl, "DoorLock", "LockState", nodeId, LOCK_CTRL_TEST_ENDPOINT_ID) assert err == 0 assert res.value == DoorLock.Enums.DlLockState.kLocked - err, res = send_zcl_command( - devCtrl, "DoorLock UnlockDoor {} {} pinCode=str:{}".format(nodeId, LOCK_CTRL_TEST_ENDPOINT_ID, - LOCK_CTRL_TEST_PIN_CODE), requestTimeoutMs=1000) + err, res = send_zcl_command(devCtrl, "DoorLock", "UnlockDoor", nodeId, LOCK_CTRL_TEST_ENDPOINT_ID, + dict(PINCode=LOCK_CTRL_TEST_PIN_CODE), + requestTimeoutMs=1000) assert err == 0 ret = device.wait_for_output("setting door lock state to \"Unlocked\"") assert ret is not None and len(ret) > 0 - err, res = read_zcl_attribute( - devCtrl, "DoorLock LockState {} {}".format(nodeId, LOCK_CTRL_TEST_ENDPOINT_ID)) + err, res = read_zcl_attribute(devCtrl, "DoorLock", "LockState", nodeId, LOCK_CTRL_TEST_ENDPOINT_ID) assert err == 0 assert res.value == DoorLock.Enums.DlLockState.kUnlocked diff --git a/src/test_driver/openiotsdk/integration-tests/shell/test_app.py b/src/test_driver/openiotsdk/integration-tests/shell/test_app.py index a10989b7f9379d..529436c7c79671 100644 --- a/src/test_driver/openiotsdk/integration-tests/shell/test_app.py +++ b/src/test_driver/openiotsdk/integration-tests/shell/test_app.py @@ -1,17 +1,19 @@ -# Copyright (c) 2009-2021 Arm Limited -# 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 +# Copyright (c) 2022 Project CHIP Authors +# All rights reserved. # -# http://www.apache.org/licenses/LICENSE-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. # -# 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 os @@ -91,6 +93,10 @@ def test_command_check(device): ret = device.wait_for_output("Open IoT SDK shell example application run") assert ret is not None and len(ret) > 0 + # Wait for printing prompt + ret = device.wait_for_output("Enter command") + assert ret is not None and len(ret) > 0 + # Help ret = device.send(command="help", expected_output="Done") assert ret is not None and len(ret) > 1 @@ -98,32 +104,44 @@ def test_command_check(device): assert set(SHELL_COMMAND_NAME) == set(shell_commands) # Echo + ret = device.wait_for_output("Enter command") + assert ret is not None and len(ret) > 0 ret = device.send(command="echo Hello", expected_output="Done") assert ret is not None and len(ret) > 1 assert "Hello" in ret[-2] # Log + ret = device.wait_for_output("Enter command") + assert ret is not None and len(ret) > 0 ret = device.send(command="log Hello", expected_output="Done") assert ret is not None and len(ret) > 1 assert "[INF] [TOO] Hello" in ret[-2] # Rand + ret = device.wait_for_output("Enter command") + assert ret is not None and len(ret) > 0 ret = device.send(command="rand", expected_output="Done") assert ret is not None and len(ret) > 1 assert ret[-2].rstrip().isdigit() # Base64 + ret = device.wait_for_output("Enter command") + assert ret is not None and len(ret) > 0 hex_string = "1234" ret = device.send(command="base64 encode {}".format( hex_string), expected_output="Done") assert ret is not None and len(ret) > 1 base64code = ret[-2] + ret = device.wait_for_output("Enter command") + assert ret is not None and len(ret) > 0 ret = device.send(command="base64 decode {}".format( base64code), expected_output="Done") assert ret is not None and len(ret) > 1 assert ret[-2].rstrip() == hex_string # Version + ret = device.wait_for_output("Enter command") + assert ret is not None and len(ret) > 0 ret = device.send(command="version", expected_output="Done") assert ret is not None and len(ret) > 1 assert "CHIP" in ret[-2].split()[0] @@ -131,12 +149,16 @@ def test_command_check(device): assert isinstance(version.parse(app_version), version.Version) # Config + ret = device.wait_for_output("Enter command") + assert ret is not None and len(ret) > 0 ret = device.send(command="config", expected_output="Done") assert ret is not None and len(ret) > 2 config = parse_config_response(ret[1:-1]) for param_name, value in config.items(): + ret = device.wait_for_output("Enter command") + assert ret is not None and len(ret) > 0 ret = device.send(command="config {}".format( param_name), expected_output="Done") assert ret is not None and len(ret) > 1 @@ -145,23 +167,31 @@ def test_command_check(device): else: assert int(ret[-2].split()[0]) == value + ret = device.wait_for_output("Enter command") + assert ret is not None and len(ret) > 0 new_value = int(config['discriminator']) + 1 ret = device.send(command="config discriminator {}".format( new_value), expected_output="Done") assert ret is not None and len(ret) > 1 assert "Setup discriminator set to: {}".format(new_value) in ret[-2] + ret = device.wait_for_output("Enter command") + assert ret is not None and len(ret) > 0 ret = device.send(command="config discriminator", expected_output="Done") assert ret is not None and len(ret) > 1 assert int(ret[-2].split()[0], 16) == new_value # Onboardingcodes + ret = device.wait_for_output("Enter command") + assert ret is not None and len(ret) > 0 ret = device.send(command="onboardingcodes none", expected_output="Done") assert ret is not None and len(ret) > 2 boarding_codes = parse_boarding_codes_response(ret[1:-1]) for param, value in boarding_codes.items(): + ret = device.wait_for_output("Enter command") + assert ret is not None and len(ret) > 0 ret = device.send(command="onboardingcodes none {}".format( param), expected_output="Done") assert ret is not None and len(ret) > 1 @@ -184,5 +214,7 @@ def test_command_check(device): assert device_details is not None and len(device_details) != 0 # Exit - should be the last check + ret = device.wait_for_output("Enter command") + assert ret is not None and len(ret) > 0 ret = device.send(command="exit", expected_output="Goodbye") assert ret is not None and len(ret) > 0 diff --git a/src/test_driver/openiotsdk/integration-tests/unit-tests/__init__.py b/src/test_driver/openiotsdk/integration-tests/unit-tests/__init__.py new file mode 100644 index 00000000000000..e69de29bb2d1d6 diff --git a/src/test_driver/openiotsdk/integration-tests/unit-tests/test_app.py b/src/test_driver/openiotsdk/integration-tests/unit-tests/test_app.py new file mode 100644 index 00000000000000..c232eddba54d93 --- /dev/null +++ b/src/test_driver/openiotsdk/integration-tests/unit-tests/test_app.py @@ -0,0 +1,45 @@ +# +# Copyright (c) 2022 Project CHIP Authors +# All rights reserved. +# +# 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 re + +import pytest + +log = logging.getLogger(__name__) + + +@pytest.fixture(scope="session") +def binaryPath(request, rootDir): + if request.config.getoption('binaryPath'): + return request.config.getoption('binaryPath') + else: + assert False + + +def test_unit_tests(device): + ret = device.wait_for_output("Open IoT SDK unit-tests start", timeout=30) + assert ret is not None and len(ret) > 0 + ret = device.wait_for_output("Open IoT SDK unit-tests run", timeout=30) + assert ret is not None and len(ret) > 0 + + ret = device.wait_for_output("Test status:", 1200) + # Get test status + test_status = ret[-1] + result = re.findall(r'\d+', test_status) + assert len(result) == 1 + assert int(result[0]) == 0 diff --git a/src/test_driver/openiotsdk/unit-tests/.gitignore b/src/test_driver/openiotsdk/unit-tests/.gitignore index bcd261e241a6c2..567609b1234a9b 100644 --- a/src/test_driver/openiotsdk/unit-tests/.gitignore +++ b/src/test_driver/openiotsdk/unit-tests/.gitignore @@ -1,2 +1 @@ build/ -test_report.json diff --git a/src/test_driver/openiotsdk/unit-tests/CMakeLists.txt b/src/test_driver/openiotsdk/unit-tests/CMakeLists.txt index e9c0f48953cfde..15bf03373d5b46 100644 --- a/src/test_driver/openiotsdk/unit-tests/CMakeLists.txt +++ b/src/test_driver/openiotsdk/unit-tests/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright (c) 2022 Project CHIP Authors +# Copyright (c) 2022-2023 Project CHIP Authors # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -22,6 +22,8 @@ get_filename_component(OPEN_IOT_SDK_EXAMPLE_COMMON ${CHIP_ROOT}/examples/platfor list(APPEND CMAKE_MODULE_PATH ${OPEN_IOT_SDK_CONFIG}/cmake) +set(TFM_SUPPORT YES) + # Application CHIP build configuration set(CONFIG_CHIP_LIB_TESTS YES) set(CONFIG_CHIP_DETAIL_LOGGING NO) @@ -35,48 +37,44 @@ include(toolchain) project(chip-open-iot-sdk-unit-tests LANGUAGES C CXX ASM) include(sdk) -include(linker) - -# LwIP configuration -if(TARGET lwip-cmsis-port) - # lwip requires user_lwipopts.h, we use the custom settings - target_include_directories(lwipopts - INTERFACE - lwip-config - ) -endif() - include(chip) +include(linker) add_subdirectory(${OPEN_IOT_SDK_EXAMPLE_COMMON}/app ./app_build) file(STRINGS testnames.txt TEST_NAMES_FROM_FILE) STRING(REGEX REPLACE "\n" ";" TEST_NAMES_FROM_FILE "${TEST_NAMES_FROM_FILE}") +target_compile_definitions(openiotsdk-startup + PRIVATE + IOT_SDK_APP_MAIN_STACK_SIZE=20480 +) + foreach(TEST_NAME IN LISTS TEST_NAMES_FROM_FILE) - add_executable(${TEST_NAME}) - target_include_directories(${TEST_NAME} + set(APP_TARGET ${TEST_NAME}_ns) + add_executable(${APP_TARGET}) + target_include_directories(${APP_TARGET} PRIVATE main/include ${CHIP_ROOT}/third_party/nlunit-test/repo/src ) - target_sources(${TEST_NAME} + target_sources(${APP_TARGET} PRIVATE - main/main.cpp + main/main_ns.cpp ) - target_link_libraries(${TEST_NAME} + target_link_libraries(${APP_TARGET} + openiotsdk-startup openiotsdk-app ) # Link the *whole-archives* to keep the static test objects. - target_link_options(${TEST_NAME} + target_link_options(${APP_TARGET} PUBLIC -Wl,--whole-archive "${CMAKE_CURRENT_BINARY_DIR}/chip_build/lib/lib${TEST_NAME}.a" -Wl,--no-whole-archive) - # set_target_link requires APP_TARGET to be defined - set(APP_TARGET ${TEST_NAME}) - set_target_link(${TEST_NAME}) + set_target_link(${APP_TARGET}) + sdk_post_build(${APP_TARGET}) endforeach() diff --git a/src/test_driver/openiotsdk/unit-tests/freertos-config/FreeRTOSConfig.h b/src/test_driver/openiotsdk/unit-tests/freertos-config/FreeRTOSConfig.h index 6011c1e9d6bf79..0e306efb13d231 100644 --- a/src/test_driver/openiotsdk/unit-tests/freertos-config/FreeRTOSConfig.h +++ b/src/test_driver/openiotsdk/unit-tests/freertos-config/FreeRTOSConfig.h @@ -39,13 +39,13 @@ extern uint32_t SystemCoreClock; // Minimal stack size [words] <0-65535> // Stack for idle task and default task stack in words. -// Default: 128 +// Default: 4kB #define configMINIMAL_STACK_SIZE ((uint16_t)(4 * 1024)) // Total heap size [bytes] <0-0xFFFFFFFF> // Heap memory size in bytes. -// Default: 8192 -#define configTOTAL_HEAP_SIZE ((size_t) 8192) +// Default: 8kB +#define configTOTAL_HEAP_SIZE ((size_t)(8 * 1024)) // Kernel tick frequency [Hz] <0-0xFFFFFFFF> // Kernel tick rate in Hz. @@ -190,7 +190,7 @@ extern uint32_t SystemCoreClock; // Use TrustZone Secure Side Only // This settings prevents FreeRTOS contex switch to Non-Secure side. // Enable this setting when FreeRTOS runs on the Secure side only. -#define configRUN_FREERTOS_SECURE_ONLY 1 +#define configRUN_FREERTOS_SECURE_ONLY CONFIG_RUN_FREERTOS_SECURE_ONLY // Use TrustZone Security Extension // Using TrustZone affects context handling. diff --git a/src/test_driver/openiotsdk/unit-tests/main/main.cpp b/src/test_driver/openiotsdk/unit-tests/main/main_ns.cpp similarity index 73% rename from src/test_driver/openiotsdk/unit-tests/main/main.cpp rename to src/test_driver/openiotsdk/unit-tests/main/main_ns.cpp index 947e2b9f55137a..e9c8bbd208cf48 100644 --- a/src/test_driver/openiotsdk/unit-tests/main/main.cpp +++ b/src/test_driver/openiotsdk/unit-tests/main/main_ns.cpp @@ -42,13 +42,6 @@ static void test_thread(void * argument) goto exit; } - err = DeviceLayer::PlatformMgr().InitChipStack(); - if (err != CHIP_NO_ERROR) - { - ChipLogAutomation("Chip stack initialization failed: %s", err.AsString()); - goto exit; - } - ChipLogAutomation("Open IoT SDK unit-tests run..."); status = RunRegisteredUnitTests(); ChipLogAutomation("Test status: %d", status); @@ -59,8 +52,6 @@ static void test_thread(void * argument) int main() { - ChipLogAutomation("Open IoT SDK unit-tests start"); - if (openiotsdk_platform_init()) { ChipLogAutomation("ERROR: Open IoT SDK platform initialization failed"); @@ -69,22 +60,18 @@ int main() nlTestSetLogger(&NlTestLogger::nl_test_logger); - static const osThreadAttr_t thread_attr = { - .stack_size = 20 * 1024 // Allocate enough stack for app thread - }; + ChipLogAutomation("Open IoT SDK unit-tests start"); - osThreadId_t testThread = osThreadNew(test_thread, NULL, &thread_attr); - if (testThread == NULL) + if (openiotsdk_network_init(true)) { - ChipLogAutomation("ERROR: Failed to create app thread"); + ChipLogAutomation("ERROR: Network initialization failed"); return EXIT_FAILURE; } - if (openiotsdk_platform_run()) - { - ChipLogAutomation("ERROR: Open IoT SDK platform run failed"); - return EXIT_FAILURE; - } + ChipLogAutomation("Open IoT SDK unit-tests run..."); + int status = RunRegisteredUnitTests(); + ChipLogAutomation("Test status: %d", status); + ChipLogAutomation("Open IoT SDK unit-tests completed"); return EXIT_SUCCESS; } diff --git a/third_party/open-iot-sdk/sdk b/third_party/open-iot-sdk/sdk index f2ffb845311828..152061529ebeca 160000 --- a/third_party/open-iot-sdk/sdk +++ b/third_party/open-iot-sdk/sdk @@ -1 +1 @@ -Subproject commit f2ffb845311828af0c5a8ecdbacdc104cb03c703 +Subproject commit 152061529ebeca4488c81f2562a0db9c60b7b325