diff --git a/TESTS/mbed_drivers/reset_reason/main.cpp b/TESTS/mbed_drivers/reset_reason/main.cpp index 4b1eaa8e3a..a72796f879 100644 --- a/TESTS/mbed_drivers/reset_reason/main.cpp +++ b/TESTS/mbed_drivers/reset_reason/main.cpp @@ -25,7 +25,7 @@ #include "mbed.h" #if DEVICE_WATCHDOG -#include "drivers/Watchdog.h" +#include "hal/watchdog_api.h" #define MSG_VALUE_WATCHDOG_STATUS "wdg_present" #define WDG_TIMEOUT_MS 50UL @@ -101,9 +101,9 @@ static cmd_status_t handle_command(const char *key, const char *value) if (strcmp(key, MSG_KEY_DEVICE_RESET) == 0 && strcmp(value, MSG_VALUE_DEVICE_RESET_WATCHDOG) == 0) { greentea_send_kv(MSG_KEY_DEVICE_RESET, MSG_VALUE_DEVICE_RESET_ACK); wait_ms(10); // Wait for the serial buffers to flush. - Watchdog watchdog; - if (watchdog.start(WDG_TIMEOUT_MS) != WATCHDOG_STATUS_OK) { - TEST_ASSERT_MESSAGE(0, "watchdog.start() error."); + watchdog_config_t config = { .timeout_ms = WDG_TIMEOUT_MS }; + if (hal_watchdog_init(&config) != WATCHDOG_STATUS_OK) { + TEST_ASSERT_MESSAGE(0, "hal_watchdog_init() error."); return CMD_STATUS_ERROR; } wait_ms(WDG_TIMEOUT_MS + WDG_TIMEOUT_DELTA_MS); diff --git a/TESTS/mbed_drivers/watchdog/Watchdog_tests.h b/TESTS/mbed_drivers/watchdog/Watchdog_tests.h deleted file mode 100644 index 86f20fc84e..0000000000 --- a/TESTS/mbed_drivers/watchdog/Watchdog_tests.h +++ /dev/null @@ -1,114 +0,0 @@ -/* Mbed Microcontroller Library - * Copyright (c) 2018 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 - * - * 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. - */ - -/** - * @addtogroup drivers_watchdog_tests - * @{ - */ - -#ifndef MBED_DRIVERS_WATCHDOG_TESTS_H -#define MBED_DRIVERS_WATCHDOG_TESTS_H - -#if DEVICE_WATCHDOG - -/** Test Watchdog max_timeout validity - * - * Given a device supporting Watchdog driver API, - * when @a Watchdog::max_timeout() is called, - * then the returned value is greater than 1. - */ -void test_max_timeout_is_valid(); - -/** Test Watchdog stop - * - * Given the Watchdog is *NOT* running, - * when @a Watchdog::stop() is called, - * then WATCHDOG_STATUS_OK is returned. - * - * Given the Watchdog is running, - * when @a Watchdog::stop() is called before the timeout expires, - * then WATCHDOG_STATUS_OK is returned and the device is not restarted. - * - * Given the Watchdog is *NOT* running (it has already been stopped), - * when @a Watchdog::stop() is called, - * then WATCHDOG_STATUS_OK is returned. - */ -void test_stop(); - -/** Test Watchdog start multiple times - * - * Given @a max_timeout value returned by @a Watchdog::max_timeout(), - * - * when @a Watchdog::start(max_timeout - delta) is called, - * then @a WATCHDOG_STATUS_OK is returned - * and @a Watchdog::reload_value() returns max_timeout - delta; - * - * when @a Watchdog::start(max_timeout) is called, - * then @a WATCHDOG_STATUS_OK is returned - * and @a Watchdog::reload_value() returns max_timeout; - * - * when @a Watchdog::start(max_timeout + delta) is called, - * then @a WATCHDOG_STATUS_INVALID_ARGUMENT is returned - * and @a Watchdog::reload_value() returns previously set value (max_timeout); - * - * when @a Watchdog::start(0) is called, - * then @a WATCHDOG_STATUS_INVALID_ARGUMENT is returned - * and @a Watchdog::reload_value() returns previously set value (max_timeout). - */ -void test_restart(); - -/** Test Watchdog start with 0 ms timeout - * - * Given a device supporting Watchdog driver API, - * when @a Watchdog::start() is called with @a timeout set to 0 ms, - * then @a WATCHDOG_STATUS_INVALID_ARGUMENT is returned. - */ -void test_start_zero(); - -/** Test Watchdog start - * - * Given a value of X ms which is within supported Watchdog timeout range, - * when @a Watchdog::start() is called with @a timeout set to X ms, - * then @a WATCHDOG_STATUS_OK is returned - * and @a Watchdog::reload_value() returns X. - */ -template -void test_start(); - -/** Test Watchdog start with max_timeout - * - * Given @a max_timeout value returned by @a Watchdog::max_timeout(), - * when @a Watchdog::start() is called with @a timeout set to max_timeout, - * then @a WATCHDOG_STATUS_OK is returned - * and @a Watchdog::reload_value() returns max_timeout. - */ -void test_start_max_timeout(); - -/** Test Watchdog start with a timeout value greater than max_timeout - * - * Given @a max_timeout value returned by @a Watchdog::max_timeout(), - * when @a Watchdog::start() is called with @a timeout set to max_timeout + 1, - * then @a WATCHDOG_STATUS_INVALID_ARGUMENT is retuned. - */ -void test_start_max_timeout_exceeded(); - -#endif - -#endif - -/** @}*/ - diff --git a/TESTS/mbed_drivers/watchdog/main.cpp b/TESTS/mbed_drivers/watchdog/main.cpp deleted file mode 100644 index d9ed790055..0000000000 --- a/TESTS/mbed_drivers/watchdog/main.cpp +++ /dev/null @@ -1,227 +0,0 @@ -/* Mbed Microcontroller Library - * Copyright (c) 2018 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 - * - * 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. - */ -#if !DEVICE_WATCHDOG -#error [NOT_SUPPORTED] Watchdog not supported for this target -#endif - -#define __STDC_LIMIT_MACROS -#include "drivers/Watchdog.h" -#include "greentea-client/test_env.h" -#include "mbed_wait_api.h" -#include "unity/unity.h" -#include "utest/utest.h" -#include "Watchdog_tests.h" - -#include -#include - -/* This is platform specific and depends on the watchdog timer implementation, - * e.g. STM32F4 uses 32kHz internal RC oscillator to clock the IWDG, so - * when the prescaler divider is set to max value of 256 the resolution - * drops to 8 ms. - */ -#define WORST_TIMEOUT_RESOLUTION_MS 8UL - -#define TIMEOUT_DELTA_MS (WORST_TIMEOUT_RESOLUTION_MS) -#define WDG_TIMEOUT_MS 500UL - -#define MSG_VALUE_DUMMY "0" -#define MSG_VALUE_LEN 24 -#define MSG_KEY_LEN 24 - -#define MSG_KEY_DEVICE_READY "ready" -#define MSG_KEY_START_CASE "start_case" -#define MSG_KEY_DEVICE_RESET "reset_on_case_teardown" - -int CASE_INDEX_START; -int CASE_INDEX_CURRENT; - -using utest::v1::Case; -using utest::v1::Specification; -using utest::v1::Harness; - -using namespace mbed; - -void test_max_timeout_is_valid() -{ - Watchdog watchdog; - TEST_ASSERT(watchdog.max_timeout() > 1UL); -} - -void test_stop() -{ - Watchdog watchdog; - if (watchdog.stop() == WATCHDOG_STATUS_NOT_SUPPORTED) { - TEST_IGNORE_MESSAGE("Disabling watchdog not supported for this platform"); - return; - } - - TEST_ASSERT_EQUAL(WATCHDOG_STATUS_OK, watchdog.stop()); - - TEST_ASSERT_EQUAL(WATCHDOG_STATUS_OK, watchdog.start(WDG_TIMEOUT_MS)); - TEST_ASSERT_EQUAL(WATCHDOG_STATUS_OK, watchdog.stop()); - // Make sure that a disabled watchdog does not reset the core. - wait_ms(WDG_TIMEOUT_MS + TIMEOUT_DELTA_MS); - - TEST_ASSERT_EQUAL(WATCHDOG_STATUS_OK, watchdog.stop()); -} - -void test_restart() -{ - Watchdog watchdog; - watchdog.start(watchdog.max_timeout()); - uint64_t timeout = watchdog.max_timeout() - 2ULL * WORST_TIMEOUT_RESOLUTION_MS; - watchdog_status_t status = watchdog.start(timeout); - - if (status == WATCHDOG_STATUS_NOT_SUPPORTED) { - TEST_IGNORE_MESSAGE("Updating watchdog config not supported for this platform"); - return; - } - - TEST_ASSERT_EQUAL(WATCHDOG_STATUS_OK, status); - TEST_ASSERT_UINT32_WITHIN(WORST_TIMEOUT_RESOLUTION_MS, timeout, watchdog.reload_value()); - - TEST_ASSERT_EQUAL(WATCHDOG_STATUS_OK, watchdog.start(watchdog.max_timeout())); - TEST_ASSERT_UINT32_WITHIN(WORST_TIMEOUT_RESOLUTION_MS, watchdog.max_timeout(), watchdog.reload_value()); - - timeout = watchdog.max_timeout() + 2ULL * WORST_TIMEOUT_RESOLUTION_MS; - // Make sure requested timeout does not overflow uint32_t. - if (timeout <= UINT32_MAX) { - TEST_ASSERT_EQUAL(WATCHDOG_STATUS_INVALID_ARGUMENT, watchdog.start(timeout)); - TEST_ASSERT_UINT32_WITHIN(WORST_TIMEOUT_RESOLUTION_MS, watchdog.max_timeout(), watchdog.reload_value()); - } - - TEST_ASSERT_EQUAL(WATCHDOG_STATUS_INVALID_ARGUMENT, watchdog.start(0UL)); - TEST_ASSERT_UINT32_WITHIN(WORST_TIMEOUT_RESOLUTION_MS, watchdog.max_timeout(), watchdog.reload_value()); -} - -utest::v1::status_t case_setup_sync_on_reset(const Case *const source, const size_t index_of_case) -{ - CASE_INDEX_CURRENT = index_of_case; - return utest::v1::greentea_case_setup_handler(source, index_of_case); -} - -utest::v1::status_t case_teardown_sync_on_reset(const Case *const source, const size_t passed, const size_t failed, - const utest::v1::failure_t failure) -{ - utest::v1::status_t status = utest::v1::greentea_case_teardown_handler(source, passed, failed, failure); - if (failed) { - /* Return immediately and skip the device reset, if the test case failed. - * Provided that the device won't be restarted by other means (i.e. watchdog timer), - * this should allow the test suite to finish in a defined manner - * and report failure to host. - * In case of watchdog reset during test suite teardown, the loss of serial - * connection is possible, so the host-test-runner may return 'TIMEOUT' - * instead of 'FAIL'. - */ - return status; - } - greentea_send_kv(MSG_KEY_DEVICE_RESET, CASE_INDEX_START + CASE_INDEX_CURRENT); - utest_printf("The device will now restart.\n"); - wait_ms(10); // Wait for the serial buffers to flush. - NVIC_SystemReset(); - return status; // Reset is instant so this line won't be reached. -} - -void test_start_zero() -{ - Watchdog watchdog; - TEST_ASSERT_EQUAL(WATCHDOG_STATUS_INVALID_ARGUMENT, watchdog.start(0UL)); -} - -template -void test_start() -{ - Watchdog watchdog; - TEST_ASSERT_EQUAL(WATCHDOG_STATUS_OK, watchdog.start(timeout_ms)); - TEST_ASSERT_UINT32_WITHIN(WORST_TIMEOUT_RESOLUTION_MS, timeout_ms, watchdog.reload_value()); -} - -void test_start_max_timeout() -{ - Watchdog watchdog; - TEST_ASSERT_EQUAL(WATCHDOG_STATUS_OK, watchdog.start(watchdog.max_timeout())); - TEST_ASSERT_UINT32_WITHIN(WORST_TIMEOUT_RESOLUTION_MS, watchdog.max_timeout(), watchdog.reload_value()); -} - -void test_start_max_timeout_exceeded() -{ - Watchdog watchdog; - uint64_t timeout = watchdog.max_timeout() + 2ULL * WORST_TIMEOUT_RESOLUTION_MS; - // Make sure requested timeout does not overflow uint32_t. - if (timeout > UINT32_MAX) { - TEST_IGNORE_MESSAGE("Requested timeout overflows uint32_t -- ignoring test case."); - return; - } - TEST_ASSERT_EQUAL(WATCHDOG_STATUS_INVALID_ARGUMENT, watchdog.start(timeout)); -} - -int testsuite_setup_sync_on_reset(const size_t number_of_cases) -{ - GREENTEA_SETUP(45, "sync_on_reset"); - utest::v1::status_t status = utest::v1::greentea_test_setup_handler(number_of_cases); - if (status != utest::v1::STATUS_CONTINUE) { - return status; - } - - char key[MSG_KEY_LEN + 1] = { }; - char value[MSG_VALUE_LEN + 1] = { }; - - greentea_send_kv(MSG_KEY_DEVICE_READY, MSG_VALUE_DUMMY); - greentea_parse_kv(key, value, MSG_KEY_LEN, MSG_VALUE_LEN); - - if (strcmp(key, MSG_KEY_START_CASE) != 0) { - utest_printf("Invalid message key.\n"); - return utest::v1::STATUS_ABORT; - } - - char *tailptr = NULL; - CASE_INDEX_START = (int) strtol(value, &tailptr, 10); - if (*tailptr != '\0' || CASE_INDEX_START < 0) { - utest_printf("Invalid start case index received from host\n"); - return utest::v1::STATUS_ABORT; - } - - utest_printf("Starting with test case index %i of all %i defined test cases.\n", CASE_INDEX_START, number_of_cases); - return CASE_INDEX_START; -} - -Case cases[] = { - Case("max_timeout is valid", test_max_timeout_is_valid), - Case("Stop", test_stop), - Case("Restart multiple times", (utest::v1::case_setup_handler_t) case_setup_sync_on_reset, - test_restart, (utest::v1::case_teardown_handler_t) case_teardown_sync_on_reset), - - // Do not set watchdog timeout shorter than 500 ms as it may cause the - // host-test-runner return 'TIMEOUT' instead of 'FAIL' / 'PASS' if watchdog - // performs reset during test suite teardown. - Case("Start, 500 ms", (utest::v1::case_setup_handler_t) case_setup_sync_on_reset, - test_start<500UL>, (utest::v1::case_teardown_handler_t) case_teardown_sync_on_reset), - Case("Start, max_timeout", (utest::v1::case_setup_handler_t) case_setup_sync_on_reset, - test_start_max_timeout, (utest::v1::case_teardown_handler_t) case_teardown_sync_on_reset), - - Case("Start, 0 ms", test_start_zero), - Case("Start, max_timeout exceeded", test_start_max_timeout_exceeded), -}; - -Specification specification((utest::v1::test_setup_handler_t) testsuite_setup_sync_on_reset, cases); - -int main() -{ - // Harness will start with a test case index provided by host script. - return !Harness::run(specification); -} diff --git a/TESTS/mbed_drivers/watchdog_reset/main.cpp b/TESTS/mbed_platform/watchdog_mgr_reset/main.cpp similarity index 51% rename from TESTS/mbed_drivers/watchdog_reset/main.cpp rename to TESTS/mbed_platform/watchdog_mgr_reset/main.cpp index 2ce3c84564..df6e5b211a 100644 --- a/TESTS/mbed_drivers/watchdog_reset/main.cpp +++ b/TESTS/mbed_platform/watchdog_mgr_reset/main.cpp @@ -21,11 +21,11 @@ #include "greentea-client/test_env.h" #include "utest/utest.h" #include "unity/unity.h" -#include "drivers/Watchdog.h" -#include "Watchdog_reset_tests.h" +#include "platform/mbed_watchdog_mgr.h" +#include "watchdog_api.h" +#include "watchdog_mgr_reset_tests.h" #include "mbed.h" -#define TIMEOUT_MS 500UL #define TIMEOUT_DELTA_MS 50UL #define MSG_VALUE_DUMMY "0" @@ -80,97 +80,27 @@ void test_simple_reset() // Phase 1. -- run the test code. // Init the watchdog and wait for a device reset. - Watchdog watchdog; - if (send_reset_notification(¤t_case, TIMEOUT_MS + TIMEOUT_DELTA_MS) == false) { + if (send_reset_notification(¤t_case, 2 * HW_WATCHDOG_TIMEOUT) == false) { TEST_ASSERT_MESSAGE(0, "Dev-host communication error."); return; } - TEST_ASSERT_EQUAL(WATCHDOG_STATUS_OK, watchdog.start(TIMEOUT_MS)); - wait_ms(TIMEOUT_MS + TIMEOUT_DELTA_MS); // Device reset expected. + TEST_ASSERT_TRUE(mbed_wdog_manager_start()); + // Block interrupts, including the one from the wdog_manager maintenance ticker. + core_util_critical_section_enter(); + // Watchdog should fire before twice the timeout value. + wait((2 * HW_WATCHDOG_TIMEOUT) / 1000.0); // Device reset expected. - // Watchdog reset should have occurred during wait_ms() above; + // Watchdog reset should have occurred during wait() above; - watchdog.kick(); // Just to buy some time for testsuite failure handling. + core_util_critical_section_exit(); TEST_ASSERT_MESSAGE(0, "Watchdog did not reset the device as expected."); } -#if DEVICE_SLEEP -void test_sleep_reset() -{ - // Phase 2. -- verify the test results. - if (current_case.received_data != CASE_DATA_INVALID) { - TEST_ASSERT_EQUAL(CASE_DATA_PHASE2_OK, current_case.received_data); - current_case.received_data = CASE_DATA_INVALID; - return; - } - - // Phase 1. -- run the test code. - Watchdog watchdog; - Semaphore sem(0, 1); - Timeout timeout; - if (send_reset_notification(¤t_case, TIMEOUT_MS + TIMEOUT_DELTA_MS) == false) { - TEST_ASSERT_MESSAGE(0, "Dev-host communication error."); - return; - } - TEST_ASSERT_EQUAL(WATCHDOG_STATUS_OK, watchdog.start(TIMEOUT_MS)); - sleep_manager_lock_deep_sleep(); - timeout.attach_us(mbed::callback(release_sem, &sem), 1000ULL * (TIMEOUT_MS + TIMEOUT_DELTA_MS)); - if (sleep_manager_can_deep_sleep()) { - TEST_ASSERT_MESSAGE(0, "Deepsleep should be disallowed."); - return; - } - while (sem.wait(0) != 1) { - sleep(); // Device reset expected. - } - sleep_manager_unlock_deep_sleep(); - - // Watchdog reset should have occurred during sleep() above; - - watchdog.kick(); // Just to buy some time for testsuite failure handling. - TEST_ASSERT_MESSAGE(0, "Watchdog did not reset the device as expected."); -} - -#if DEVICE_LOWPOWERTIMER -void test_deepsleep_reset() -{ - // Phase 2. -- verify the test results. - if (current_case.received_data != CASE_DATA_INVALID) { - TEST_ASSERT_EQUAL(CASE_DATA_PHASE2_OK, current_case.received_data); - current_case.received_data = CASE_DATA_INVALID; - return; - } - - // Phase 1. -- run the test code. - Watchdog watchdog; - Semaphore sem(0, 1); - LowPowerTimeout lp_timeout; - if (send_reset_notification(¤t_case, TIMEOUT_MS + TIMEOUT_DELTA_MS) == false) { - TEST_ASSERT_MESSAGE(0, "Dev-host communication error."); - return; - } - TEST_ASSERT_EQUAL(WATCHDOG_STATUS_OK, watchdog.start(TIMEOUT_MS)); - lp_timeout.attach_us(mbed::callback(release_sem, &sem), 1000ULL * (TIMEOUT_MS + TIMEOUT_DELTA_MS)); - wait_ms(10); // Wait for the serial buffers to flush. - if (!sleep_manager_can_deep_sleep()) { - TEST_ASSERT_MESSAGE(0, "Deepsleep should be allowed."); - } - while (sem.wait(0) != 1) { - sleep(); // Device reset expected. - } - - // Watchdog reset should have occurred during that sleep() above; - - watchdog.kick(); // Just to buy some time for testsuite failure handling. - TEST_ASSERT_MESSAGE(0, "Watchdog did not reset the device as expected."); -} -#endif -#endif - void test_restart_reset() { - Watchdog watchdog; - if (watchdog.stop() == WATCHDOG_STATUS_NOT_SUPPORTED) { - TEST_IGNORE_MESSAGE("Disabling watchdog not supported for this platform"); + watchdog_features_t features = hal_watchdog_get_platform_features(); + if (!features.disable_watchdog) { + TEST_IGNORE_MESSAGE("Disabling Watchdog not supported for this platform"); return; } @@ -182,22 +112,31 @@ void test_restart_reset() } // Phase 1. -- run the test code. - TEST_ASSERT_EQUAL(WATCHDOG_STATUS_OK, watchdog.start(TIMEOUT_MS)); - wait_ms(TIMEOUT_MS / 2UL); - TEST_ASSERT_EQUAL(WATCHDOG_STATUS_OK, watchdog.stop()); - // Check that stopping the watchdog prevents a device reset. - wait_ms(TIMEOUT_MS / 2UL + TIMEOUT_DELTA_MS); + TEST_ASSERT_TRUE(mbed_wdog_manager_start()); + // The Watchdog Manager maintenance ticker has a period equal to a half of + // Watchdog timeout. Wait shorter than that and stop the Watchdog Manager + // before the Watchdog is kicked by the ticker callback. + wait((HW_WATCHDOG_TIMEOUT / 4UL) / 1000.0); + TEST_ASSERT_TRUE(mbed_wdog_manager_stop()); + // Block interrupts, including the one from the wdog_manager maintenance ticker. + core_util_critical_section_enter(); + // Check that stopping the Watchdog Manager prevents a device reset. + wait((2 * HW_WATCHDOG_TIMEOUT) / 1000.0); + core_util_critical_section_exit(); - if (send_reset_notification(¤t_case, TIMEOUT_MS + TIMEOUT_DELTA_MS) == false) { + if (send_reset_notification(¤t_case, 2 * HW_WATCHDOG_TIMEOUT) == false) { TEST_ASSERT_MESSAGE(0, "Dev-host communication error."); return; } - TEST_ASSERT_EQUAL(WATCHDOG_STATUS_OK, watchdog.start(TIMEOUT_MS)); - wait_ms(TIMEOUT_MS + TIMEOUT_DELTA_MS); // Device reset expected. + TEST_ASSERT_TRUE(mbed_wdog_manager_start()); + // Block interrupts, including the one from the wdog_manager maintenance ticker. + core_util_critical_section_enter(); + // Watchdog should fire before twice the timeout value. + wait((2 * HW_WATCHDOG_TIMEOUT) / 1000.0); // Device reset expected. - // Watchdog reset should have occurred during that wait() above; + // Watchdog reset should have occurred during wait() above; - watchdog.kick(); // Just to buy some time for testsuite failure handling. + core_util_critical_section_exit(); TEST_ASSERT_MESSAGE(0, "Watchdog did not reset the device as expected."); } @@ -211,21 +150,21 @@ void test_kick_reset() } // Phase 1. -- run the test code. - Watchdog watchdog; - TEST_ASSERT_EQUAL(WATCHDOG_STATUS_OK, watchdog.start(TIMEOUT_MS)); - for (int i = 3; i; i--) { - wait_ms(TIMEOUT_MS / 2UL); - watchdog.kick(); - } - if (send_reset_notification(¤t_case, TIMEOUT_MS + TIMEOUT_DELTA_MS) == false) { + TEST_ASSERT_TRUE(mbed_wdog_manager_start()); + wait((2 * HW_WATCHDOG_TIMEOUT) / 1000.0); // Device reset expected. + + if (send_reset_notification(¤t_case, 2 * HW_WATCHDOG_TIMEOUT) == false) { TEST_ASSERT_MESSAGE(0, "Dev-host communication error."); return; } - wait_ms(TIMEOUT_MS + TIMEOUT_DELTA_MS); // Device reset expected. + // Block interrupts, including the one from the wdog_manager maintenance ticker. + core_util_critical_section_enter(); + // Watchdog should fire before twice the timeout value. + wait((2 * HW_WATCHDOG_TIMEOUT) / 1000.0); // Device reset expected. - // Watchdog reset should have occurred during that wait() above; + // Watchdog reset should have occurred during wait() above; - watchdog.kick(); // Just to buy some time for testsuite failure handling. + core_util_critical_section_exit(); TEST_ASSERT_MESSAGE(0, "Watchdog did not reset the device as expected."); } @@ -266,15 +205,9 @@ int testsuite_setup(const size_t number_of_cases) } Case cases[] = { - Case("Watchdog reset", case_setup, test_simple_reset), -#if DEVICE_SLEEP - Case("Watchdog reset in sleep mode", case_setup, test_sleep_reset), -#if DEVICE_LOWPOWERTIMER - Case("Watchdog reset in deepsleep mode", case_setup, test_deepsleep_reset), -#endif -#endif - Case("Watchdog started again", case_setup, test_restart_reset), - Case("Kicking the watchdog prevents reset", case_setup, test_kick_reset), + Case("Watchdog Manager reset", case_setup, test_simple_reset), + Case("Watchdog Manager started again", case_setup, test_restart_reset), + Case("Watchdog Manager's ticker prevents reset", case_setup, test_kick_reset), }; Specification specification((utest::v1::test_setup_handler_t) testsuite_setup, cases); diff --git a/TESTS/mbed_drivers/watchdog_reset/Watchdog_reset_tests.h b/TESTS/mbed_platform/watchdog_mgr_reset/watchdog_mgr_reset_tests.h similarity index 54% rename from TESTS/mbed_drivers/watchdog_reset/Watchdog_reset_tests.h rename to TESTS/mbed_platform/watchdog_mgr_reset/watchdog_mgr_reset_tests.h index 548dea1c2a..c1b55d6b2d 100644 --- a/TESTS/mbed_drivers/watchdog_reset/Watchdog_reset_tests.h +++ b/TESTS/mbed_platform/watchdog_mgr_reset/watchdog_mgr_reset_tests.h @@ -16,52 +16,38 @@ */ /** - * @addtogroup drivers_watchdog_tests + * @addtogroup platform_watchdog_mgr_tests * @{ */ -#ifndef MBED_DRIVERS_WATCHDOG_RESET_TESTS_H -#define MBED_DRIVERS_WATCHDOG_RESET_TESTS_H +#ifndef MBED_WATCHDOG_MGR_RESET_TESTS_H +#define MBED_WATCHDOG_MGR_RESET_TESTS_H #if DEVICE_WATCHDOG -/** Test Watchdog reset +/** Test Watchdog Manager reset * - * Given a device with a Watchdog started, - * when the Watchdog timeout expires, + * Given a device with a Watchdog Manager started, + * when the Watchdog Manager maintenance ticker interrupt is blocked longer + * than the Watchdog timeout, * then the device is restarted. */ void test_simple_reset(); -/** Test Watchdog reset in sleep mode +/** Test Watchdog Manager reset after Watchdog Manager restart * - * Given a device with a Watchdog started, - * when the Watchdog timeout expires while the device is in sleep mode, - * then the device is restarted. - */ -void test_sleep_reset(); - -/** Test Watchdog reset in deepsleep mode - * - * Given a device with a Watchdog started, - * when the Watchdog timeout expires while the device is in deepsleep mode, - * then the device is restarted. - */ -void test_deepsleep_reset(); - -/** Test Watchdog reset after Watchdog restart - * - * Given a device with a Watchdog started, - * when the Watchdog is stopped before its timeout expires, + * Given a device with a Watchdog Manager started, + * when the Watchdog Manager is stopped before its timeout expires, * then the device is not restarted. - * When the Watchdog is started again and its timeout expires, + * When the Watchdog Manager is started again and the Watchdog Manager + * maintenance ticker interrupt is blocked longer than the Watchdog timeout, * then the device is restarted. */ void test_restart_reset(); -/** Test Watchdog kick +/** Test Watchdog Manager kick * - * Given a device with a Watchdog started, + * Given a device with a Watchdog Manager started, * when the Watchdog is kicked before its timeout expires, * then the device restart is prevented. * When the Watchdog is *NOT* kicked again before next timeout expires, diff --git a/UNITTESTS/CMakeLists.txt b/UNITTESTS/CMakeLists.txt index 112aa373ea..95806c5b7c 100644 --- a/UNITTESTS/CMakeLists.txt +++ b/UNITTESTS/CMakeLists.txt @@ -92,6 +92,7 @@ set(unittest-includes-base "${PROJECT_SOURCE_DIR}/target_h/events" "${PROJECT_SOURCE_DIR}/target_h/events/equeue" "${PROJECT_SOURCE_DIR}/target_h/platform" + "${PROJECT_SOURCE_DIR}/target_h/drivers" "${PROJECT_SOURCE_DIR}/stubs" "${PROJECT_SOURCE_DIR}/.." "${PROJECT_SOURCE_DIR}/../features" diff --git a/UNITTESTS/drivers/Watchdog/test_watchdog.cpp b/UNITTESTS/drivers/Watchdog/test_watchdog.cpp new file mode 100644 index 0000000000..d31477e1a8 --- /dev/null +++ b/UNITTESTS/drivers/Watchdog/test_watchdog.cpp @@ -0,0 +1,143 @@ +/* + * Copyright (c) 2018, Arm Limited and affiliates. + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "gtest/gtest.h" +#include "gmock/gmock.h" +#include "drivers/Watchdog.h" + +class TestWatchdog : public testing::Test { +public: + static uint32_t expect_assert_count; + static uint32_t expect_reset_count; +protected: + virtual void SetUp() {} + virtual void TearDown() {} +}; + +uint32_t TestWatchdog::expect_assert_count = 0; +uint32_t TestWatchdog::expect_reset_count = 0; + +void mbed_assert_internal(const char *expr, const char *file, int line) +{ + TestWatchdog::expect_assert_count++; +} + +void mock_system_reset() +{ + TestWatchdog::expect_reset_count++; +} + +TEST_F(TestWatchdog, wdog_constructor) +{ + EXPECT_LE(sizeof(mbed::Watchdog), 1024); + mbed::Watchdog watchdog(500, "watchdog_unittest"); +} + +TEST_F(TestWatchdog, wdog_constructor_with_default_value) +{ + mbed::Watchdog watchdog; +} + +TEST_F(TestWatchdog, wdog_start_pass) +{ + mbed::Watchdog watchdog(500, "watchdog_unittest"); + watchdog.start(); + watchdog.stop(); + EXPECT_EQ(0, TestWatchdog::expect_assert_count); +} + +TEST_F(TestWatchdog, wdog_kick_pass) +{ + mbed::Watchdog watchdog(500, "watchdog_unittest"); + watchdog.start(); + watchdog.kick(); + watchdog.stop(); + EXPECT_EQ(0, TestWatchdog::expect_assert_count); +} + +TEST_F(TestWatchdog, wdog_stop_fail) +{ + mbed::Watchdog watchdog(500, "watchdog_unittest"); + watchdog.start(); + watchdog.stop(); + watchdog.stop(); + EXPECT_EQ(1, TestWatchdog::expect_assert_count); + TestWatchdog::expect_assert_count = 0; +} +TEST_F(TestWatchdog, wdog_kick_fail) +{ + mbed::Watchdog watchdog(500, "watchdog_unittest"); + watchdog.kick(); + EXPECT_EQ(1, TestWatchdog::expect_assert_count); + TestWatchdog::expect_assert_count = 0; +} + +TEST_F(TestWatchdog, wdog_start_kick_pass) +{ + mbed::Watchdog watchdog(500, "watchdog_unittest"); + mbed::Watchdog watchdog1(600, "watchdog_unittest_1"); + mbed::Watchdog watchdog2(700, "watchdog_unittest_2"); + watchdog.start(); + watchdog1.start(); + watchdog2.start(); + watchdog1.kick(); + watchdog.kick(); + watchdog2.kick(); + watchdog1.stop(); + watchdog.stop(); + watchdog2.stop(); + EXPECT_EQ(0, TestWatchdog::expect_assert_count); + EXPECT_EQ(0, TestWatchdog::expect_reset_count); +} + +TEST_F(TestWatchdog, wdog_start_process_pass) +{ + mbed::Watchdog watchdog(500, "watchdog_unittest"); + watchdog.start(); + watchdog.kick(); + watchdog.process((HW_WATCHDOG_TIMEOUT / 2)); + watchdog.stop(); + EXPECT_EQ(0, TestWatchdog::expect_assert_count); + EXPECT_EQ(0, TestWatchdog::expect_reset_count); +} + +TEST_F(TestWatchdog, wdog_start_process_fail) +{ + mbed::Watchdog watchdog(500, "watchdog_unittest"); + mbed::Watchdog watchdog1(500, "watchdog_unittest-1"); + watchdog.start(); + watchdog1.start(); + watchdog.process((HW_WATCHDOG_TIMEOUT / 2)); + watchdog.process((HW_WATCHDOG_TIMEOUT / 2)); + watchdog1.kick(); + watchdog.process((HW_WATCHDOG_TIMEOUT / 2)); + watchdog.process((HW_WATCHDOG_TIMEOUT / 2)); + watchdog.stop(); + watchdog1.stop(); + EXPECT_EQ(0, TestWatchdog::expect_assert_count); + EXPECT_EQ(1, TestWatchdog::expect_reset_count); + TestWatchdog::expect_reset_count = 0; +} + +TEST_F(TestWatchdog, wdog_start_fail) +{ + mbed::Watchdog watchdog(500, "watchdog_unittest"); + watchdog.start(); + watchdog.start(); + watchdog.stop(); + EXPECT_EQ(1, TestWatchdog::expect_assert_count); + TestWatchdog::expect_assert_count = 0; +} diff --git a/UNITTESTS/drivers/Watchdog/unittest.cmake b/UNITTESTS/drivers/Watchdog/unittest.cmake new file mode 100644 index 0000000000..517701e9ea --- /dev/null +++ b/UNITTESTS/drivers/Watchdog/unittest.cmake @@ -0,0 +1,27 @@ + +#################### +# UNIT TESTS +#################### +set(TEST_SUITE_NAME "Watchdog") + +# Add test specific include paths +set(unittest-includes ${unittest-includes} + . + ../hal +) + +# Source files +set(unittest-sources + ../drivers/Watchdog.cpp + +) + +# Test files +set(unittest-test-sources + drivers/Watchdog/test_watchdog.cpp + stubs/mbed_critical_stub.c +) + +# defines +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DDEVICE_WATCHDOG -DMBED_WDOG_ASSERT=1") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DDEVICE_WATCHDOG -DMBED_WDOG_ASSERT=1") diff --git a/UNITTESTS/features/lorawan/loramaccrypto/unittest.cmake b/UNITTESTS/features/lorawan/loramaccrypto/unittest.cmake index a2740df449..2001f8e9d7 100644 --- a/UNITTESTS/features/lorawan/loramaccrypto/unittest.cmake +++ b/UNITTESTS/features/lorawan/loramaccrypto/unittest.cmake @@ -35,6 +35,7 @@ set(unittest-test-sources stubs/cipher_stub.c stubs/aes_stub.c stubs/cmac_stub.c + stubs/mbed_assert_stub.c ../features/nanostack/coap-service/test/coap-service/unittest/stub/mbedtls_stub.c ) diff --git a/UNITTESTS/features/netsocket/cellular/CellularNonIPSocket/unittest.cmake b/UNITTESTS/features/netsocket/cellular/CellularNonIPSocket/unittest.cmake index 3dc6d8d78f..57ace7017d 100644 --- a/UNITTESTS/features/netsocket/cellular/CellularNonIPSocket/unittest.cmake +++ b/UNITTESTS/features/netsocket/cellular/CellularNonIPSocket/unittest.cmake @@ -20,4 +20,5 @@ set(unittest-test-sources stubs/EventFlags_stub.cpp stubs/Mutex_stub.cpp stubs/CellularContext_stub.cpp + stubs/mbed_assert_stub.c ) diff --git a/UNITTESTS/platform/mbed_watchdog_mgr/Watchdog.cpp b/UNITTESTS/platform/mbed_watchdog_mgr/Watchdog.cpp new file mode 100644 index 0000000000..33ba1e9146 --- /dev/null +++ b/UNITTESTS/platform/mbed_watchdog_mgr/Watchdog.cpp @@ -0,0 +1,57 @@ +/* mbed Microcontroller Library + * Copyright (c) 2018 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 + * + * 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. + */ +#ifdef DEVICE_WATCHDOG + +#include "Watchdog.h" + +namespace mbed { + +Watchdog *Watchdog::_first; + +Watchdog::Watchdog(uint32_t timeout, const char *const str): _name(str) +{ + +} + +void Watchdog::start() +{ + +} + +void Watchdog::kick() +{ + +} + +void Watchdog::stop() +{ + +} + +void Watchdog::process(uint32_t elapsed_ms) +{ + +} + +Watchdog::~Watchdog() +{ + +} + +} // namespace mbed + +#endif // DEVICE_WATCHDOG diff --git a/UNITTESTS/platform/mbed_watchdog_mgr/test_mbed_watchdog_mgr.cpp b/UNITTESTS/platform/mbed_watchdog_mgr/test_mbed_watchdog_mgr.cpp new file mode 100644 index 0000000000..f65765a075 --- /dev/null +++ b/UNITTESTS/platform/mbed_watchdog_mgr/test_mbed_watchdog_mgr.cpp @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2018, Arm Limited and affiliates. + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "gtest/gtest.h" +#include "gmock/gmock.h" +#include "mbed_watchdog_mgr.h" + +using namespace mbed; +extern bool testcase; +// AStyle ignored as the definition is not clear due to preprocessor usage +// *INDENT-OFF* +class TestMbedWatchdogMgr : public testing::Test { +protected: + + void SetUp() + { + } + + void TearDown() + { + } +}; +// *INDENT-ON* + +TEST_F(TestMbedWatchdogMgr, test_mbed_watchdog_mgr_start_pass) +{ + EXPECT_TRUE(mbed_wdog_manager_start()); +} + +TEST_F(TestMbedWatchdogMgr, test_mbed_watchdog_mgr_start_fail) +{ + EXPECT_FALSE(mbed_wdog_manager_start()); +} + +TEST_F(TestMbedWatchdogMgr, test_mbed_watchdog_mgr_stop_pass) +{ + EXPECT_TRUE(mbed_wdog_manager_stop()); +} + +TEST_F(TestMbedWatchdogMgr, test_mbed_watchdog_mgr_stop_fail) +{ + EXPECT_FALSE(mbed_wdog_manager_stop()); +} + +TEST_F(TestMbedWatchdogMgr, test_mbed_wdog_manager_get_max_timeout) +{ + EXPECT_EQ(0xFFFFFFFF, mbed_wdog_manager_get_max_timeout()); +} + + +TEST_F(TestMbedWatchdogMgr, test_mbed_wdog_manager_get_timeout) +{ + EXPECT_EQ(500, mbed_wdog_manager_get_timeout()); +} + diff --git a/UNITTESTS/platform/mbed_watchdog_mgr/unittest.cmake b/UNITTESTS/platform/mbed_watchdog_mgr/unittest.cmake new file mode 100644 index 0000000000..2b466aa804 --- /dev/null +++ b/UNITTESTS/platform/mbed_watchdog_mgr/unittest.cmake @@ -0,0 +1,29 @@ + +#################### +# UNIT TESTS +#################### +set(TEST_SUITE_NAME "mbed_watchdog_mgr") +# Add test specific include paths +set(unittest-includes ${unittest-includes} + . + ../hal +) + +# Source files +set(unittest-sources + ../platform/mbed_watchdog_mgr.cpp + +) + +# Test files +set(unittest-test-sources + platform/mbed_watchdog_mgr/test_mbed_watchdog_mgr.cpp + platform/mbed_watchdog_mgr/Watchdog.cpp + stubs/mbed_assert_stub.c + stubs/mbed_critical_stub.c + stubs/watchdog_api_stub.c +) + +# defines +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DDEVICE_WATCHDOG -DHW_WATCHDOG_TIMEOUT=500") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DDEVICE_WATCHDOG -DHW_WATCHDOG_TIMEOUT=500") diff --git a/UNITTESTS/stubs/watchdog_api_stub.c b/UNITTESTS/stubs/watchdog_api_stub.c new file mode 100644 index 0000000000..d04a84394a --- /dev/null +++ b/UNITTESTS/stubs/watchdog_api_stub.c @@ -0,0 +1,48 @@ +/* mbed Microcontroller Library + * Copyright (c) 2017 ARM Limited + * + * 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 "watchdog_api.h" + +#if DEVICE_WATCHDOG + +watchdog_status_t hal_watchdog_init(const watchdog_config_t *config) +{ + return WATCHDOG_STATUS_OK; +} + +void hal_watchdog_kick(void) +{ + +} + +watchdog_status_t hal_watchdog_stop(void) +{ + return WATCHDOG_STATUS_OK; +} + +uint32_t hal_watchdog_get_reload_value(void) +{ + return (500); +} + + +watchdog_features_t hal_watchdog_get_platform_features(void) +{ + watchdog_features_t features; + features.max_timeout = 0xFFFFFFFF; + return features; +} + +#endif // DEVICE_WATCHDOG diff --git a/UNITTESTS/target_h/drivers/LowPowerTicker.h b/UNITTESTS/target_h/drivers/LowPowerTicker.h new file mode 100644 index 0000000000..8762133892 --- /dev/null +++ b/UNITTESTS/target_h/drivers/LowPowerTicker.h @@ -0,0 +1,54 @@ +/* mbed Microcontroller Library + * Copyright (c) 2015 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef MBED_LOWPOWERTICKER_H +#define MBED_LOWPOWERTICKER_H + +#include "hal/ticker_api.h" +#include "Callback.h" + +namespace mbed { +/** \addtogroup drivers */ + +/** mock Low Power Ticker + * + */ +class LowPowerTicker { + +public: + LowPowerTicker() + { + } + + virtual ~LowPowerTicker() + { + } + + void attach_us(Callback func, us_timestamp_t t) + { + + } + void detach() + { + + } +}; + +} // namespace mbed + +#endif + + diff --git a/UNITTESTS/target_h/drivers/Ticker.h b/UNITTESTS/target_h/drivers/Ticker.h new file mode 100644 index 0000000000..9749c0820f --- /dev/null +++ b/UNITTESTS/target_h/drivers/Ticker.h @@ -0,0 +1,52 @@ +/* mbed Microcontroller Library + * Copyright (c) 2006-2013 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef MBED_TICKER_H +#define MBED_TICKER_H +#include "drivers/TimerEvent.h" +#include "platform/Callback.h" + +namespace mbed { +/** \addtogroup drivers */ + +/** mock Ticker + * + */ +class Ticker { + +public: + Ticker() + { + } + + void attach_us(Callback func, us_timestamp_t t) + { + + } + + void detach() + { + + } + + ~Ticker() + { + } +}; + +} // namespace mbed + +#endif diff --git a/UNITTESTS/target_h/platform/mbed_assert.h b/UNITTESTS/target_h/platform/mbed_assert.h new file mode 100644 index 0000000000..1616fb0f85 --- /dev/null +++ b/UNITTESTS/target_h/platform/mbed_assert.h @@ -0,0 +1,135 @@ + +/** \addtogroup platform */ +/** @{*/ +/** + * \defgroup platform_Assert Assert macros + * @{ + */ +/* mbed Microcontroller Library + * Copyright (c) 2006-2013 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef MBED_ASSERT_H +#define MBED_ASSERT_H + +#include "mbed_preprocessor.h" +#include "mbed_toolchain.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** Internal mbed assert function which is invoked when MBED_ASSERT macro fails. + * This function is active only if NDEBUG is not defined prior to including this + * assert header file. + * In case of MBED_ASSERT failing condition, error() is called with the assertation message. + * @param expr Expression to be checked. + * @param file File where assertation failed. + * @param line Failing assertation line number. + */ +MBED_NORETURN void mbed_assert_internal(const char *expr, const char *file, int line); + +#ifdef __cplusplus +} +#endif + +/** MBED_ASSERT + * Declare runtime assertions: results in runtime error if condition is false + * + * @note + * Use of MBED_ASSERT is limited to Debug and Develop builds. + * + * @code + * + * int Configure(serial_t *obj) { + * MBED_ASSERT(obj); + * } + * @endcode + */ +#if defined( NDEBUG ) && !defined (MBED_WDOG_ASSERT) +#define MBED_ASSERT(expr) ((void)0) +#else +#define MBED_ASSERT(expr) \ +do { \ + if (!(expr)) { \ + mbed_assert_internal(#expr, __FILE__, __LINE__); \ + } \ +} while (0) +#endif + + + +/** MBED_STATIC_ASSERT + * Declare compile-time assertions, results in compile-time error if condition is false + * + * The assertion acts as a declaration that can be placed at file scope, in a + * code block (except after a label), or as a member of a C++ class/struct/union. + * + * @note + * Use of MBED_STATIC_ASSERT as a member of a struct/union is limited: + * - In C++, MBED_STATIC_ASSERT is valid in class/struct/union scope. + * - In C, MBED_STATIC_ASSERT is not valid in struct/union scope, and + * MBED_STRUCT_STATIC_ASSERT is provided as an alternative that is valid + * in C and C++ class/struct/union scope. + * + * @code + * MBED_STATIC_ASSERT(MBED_LIBRARY_VERSION >= 120, + * "The mbed library must be at least version 120"); + * + * int main() { + * MBED_STATIC_ASSERT(sizeof(int) >= sizeof(char), + * "An int must be larger than a char"); + * } + * @endcode + */ +#if defined(__cplusplus) && (__cplusplus >= 201103L || __cpp_static_assert >= 200410L) +#define MBED_STATIC_ASSERT(expr, msg) static_assert(expr, msg) +#elif !defined(__cplusplus) && __STDC_VERSION__ >= 201112L +#define MBED_STATIC_ASSERT(expr, msg) _Static_assert(expr, msg) +#elif defined(__cplusplus) && defined(__GNUC__) && defined(__GXX_EXPERIMENTAL_CXX0X__) \ + && (__GNUC__*100 + __GNUC_MINOR__) > 403L +#define MBED_STATIC_ASSERT(expr, msg) __extension__ static_assert(expr, msg) +#elif !defined(__cplusplus) && defined(__GNUC__) && !defined(__CC_ARM) \ + && (__GNUC__*100 + __GNUC_MINOR__) > 406L +#define MBED_STATIC_ASSERT(expr, msg) __extension__ _Static_assert(expr, msg) +#elif defined(__ICCARM__) +#define MBED_STATIC_ASSERT(expr, msg) static_assert(expr, msg) +#else +#define MBED_STATIC_ASSERT(expr, msg) \ + enum {MBED_CONCAT(MBED_ASSERTION_AT_, __LINE__) = sizeof(char[(expr) ? 1 : -1])} +#endif + +/** MBED_STRUCT_STATIC_ASSERT + * Declare compile-time assertions, results in compile-time error if condition is false + * + * Unlike MBED_STATIC_ASSERT, MBED_STRUCT_STATIC_ASSERT can and must be used + * as a member of a C/C++ class/struct/union. + * + * @code + * struct thing { + * MBED_STATIC_ASSERT(2 + 2 == 4, + * "Hopefully the universe is mathematically consistent"); + * }; + * @endcode + */ +#define MBED_STRUCT_STATIC_ASSERT(expr, msg) int : (expr) ? 0 : -1 + + +#endif + +/**@}*/ + +/**@}*/ + diff --git a/UNITTESTS/target_h/platform/mbed_power_mgmt.h b/UNITTESTS/target_h/platform/mbed_power_mgmt.h index 2810b22dae..d0475a10d7 100644 --- a/UNITTESTS/target_h/platform/mbed_power_mgmt.h +++ b/UNITTESTS/target_h/platform/mbed_power_mgmt.h @@ -15,3 +15,18 @@ * limitations under the License. */ +/** Resets the processor and most of the sub-system + * + * @note Does not affect the debug sub-system + */ +#ifndef MBED_POWER_MGMT_H +#define MBED_POWER_MGMT_H +extern void mock_system_reset(); + +MBED_NORETURN static inline void system_reset(void) +{ + mock_system_reset(); +} + +#endif + diff --git a/drivers/Watchdog.cpp b/drivers/Watchdog.cpp index 0214173e45..6be0e0f7d0 100644 --- a/drivers/Watchdog.cpp +++ b/drivers/Watchdog.cpp @@ -20,46 +20,89 @@ namespace mbed { -watchdog_status_t Watchdog::start(const uint32_t timeout) +Watchdog *Watchdog::_first = NULL; + +Watchdog::Watchdog(uint32_t timeout, const char *const str): _name(str) { - if (timeout == 0) { - return WATCHDOG_STATUS_INVALID_ARGUMENT; - } + _current_count = 0; + _is_initialized = false; + _next = NULL; + _max_timeout = timeout; +} - if (timeout > max_timeout()) { - return WATCHDOG_STATUS_INVALID_ARGUMENT; - } +void Watchdog::add_to_list() +{ + this->_next = _first; + _first = this; + _is_initialized = true; +} - watchdog_config_t config; - config.timeout_ms = timeout; - - return hal_watchdog_init(&config); +void Watchdog::start() +{ + MBED_ASSERT(!_is_initialized); + core_util_critical_section_enter(); + add_to_list(); + core_util_critical_section_exit(); } void Watchdog::kick() { - hal_watchdog_kick(); + MBED_ASSERT(_is_initialized); + core_util_critical_section_enter(); + _current_count = 0; + core_util_critical_section_exit(); } - -watchdog_status_t Watchdog::stop() +void Watchdog::stop() { - return hal_watchdog_stop(); + MBED_ASSERT(_is_initialized); + core_util_critical_section_enter(); + remove_from_list(); + core_util_critical_section_exit(); } - -uint32_t Watchdog::reload_value() const +void Watchdog::remove_from_list() { - return hal_watchdog_get_reload_value(); + Watchdog *cur_ptr = _first, + *prev_ptr = NULL; + while (cur_ptr != NULL) { + if (cur_ptr == this) { + if (cur_ptr == _first) { + prev_ptr = _first; + _first = cur_ptr->_next; + prev_ptr->_next = NULL; + } else { + prev_ptr->_next = cur_ptr->_next; + cur_ptr->_next = NULL; + } + _is_initialized = false; + break; + } else { + prev_ptr = cur_ptr; + cur_ptr = cur_ptr->_next; + } + } } - -uint32_t Watchdog::max_timeout() +void Watchdog::process(uint32_t elapsed_ms) { - const watchdog_features_t features = hal_watchdog_get_platform_features(); + Watchdog *cur_ptr = _first; + while (cur_ptr != NULL) { + if (cur_ptr->_current_count > cur_ptr->_max_timeout) { + system_reset(); + } else { + cur_ptr->_current_count += elapsed_ms; + } + cur_ptr = cur_ptr->_next; + } +} - return features.max_timeout; +Watchdog::~Watchdog() +{ + if (_is_initialized) { + stop(); + } } } // namespace mbed diff --git a/drivers/Watchdog.h b/drivers/Watchdog.h index 24f029d449..ce3a58c35d 100644 --- a/drivers/Watchdog.h +++ b/drivers/Watchdog.h @@ -20,21 +20,25 @@ #ifdef DEVICE_WATCHDOG -#include "watchdog_api.h" - #include - +#include "mbed_error.h" +#include "platform/mbed_critical.h" +#include "platform/mbed_power_mgmt.h" +#include "mbed_assert.h" namespace mbed { + /** \addtogroup drivers */ -/** A system timer that will reset the system in the case of system failures or +/** A software watchdog gets used by services(wifi,tls etc.,) which needs monitor of non-block periodic behavior or * malfunctions. + * Normally one instance of SW Watchdog gets created in HW Watchdog startup to allow access this SW Watchdog, + * HW Watchdog periodically calls "process" method of the services to check non-block periodic behavior. * * Example: * @code * - * Watchdog watchdog = Watchdog(); - * watchdog.start(2000); + * Watchdog watchdog(300,"Software Watchdog"); + * watchdog.start(); * * while (true) { * watchdog.kick(); @@ -46,21 +50,28 @@ namespace mbed { */ class Watchdog { public: - Watchdog() {} + /** Constructor configured with timeout and name for this software watchdog instance + * + */ + Watchdog(uint32_t timeout = 1, const char *const str = NULL); + ~Watchdog(); public: + /** Start an independent watchdog timer with specified parameters * - * @param timeout Timeout of the watchdog in milliseconds - * - * @return status WATCHDOG_STATUS_OK if the watchdog timer was started - * successfully. WATCHDOG_INVALID_ARGUMENT if one of the input - * parameters is out of range for the current platform. - * WATCHDOG_NOT_SUPPORTED if one of the enabled input - * parameters is not supported by the current platform. + * Assert for multiple calls of start */ - watchdog_status_t start(const uint32_t timeout); + void start(); + /** Stops the watchdog timer + * + * Calling this function will attempt to disable any currently running + * watchdog timers if supported by the current platform. + * + * Assert with out called start + */ + void stop(); /** Refreshes the watchdog timer. * @@ -71,35 +82,36 @@ public: */ void kick(); - - /** Stops the watchdog timer + /** mbed_watchdog_manager(runs by periodic call from ticker) used this API interface + * to go through all the registered user/threads of watchdog. * - * Calling this function will attempt to disable any currently running - * watchdog timers if supported by the current platform. + * @param elapsed_ms completed ticker callback elapsed milliseconds * - * @return Returns WATCHDOG_STATUS_OK if the watchdog timer was successfully - * stopped, or if the timer was never started. Returns - * WATCHDOG_STATUS_NOT_SUPPORTED if the watchdog cannot be disabled - * on the current platform. + * This function should be called from mbed_watchdog_manager_kick to monitor all the + * user/threads alive state. + * + * Otherwise, the system is reset. */ - watchdog_status_t stop(); + static void process(uint32_t elapsed_ms); +protected : + /** add_to_list is used to store the registered user into List. + * This API is only used to call from start. + */ + void add_to_list(); - /** Get the watchdog timer refresh value - * - * This function returns the refresh timeout of the watchdog timer. - * - * @return Reload value for the watchdog timer in milliseconds. - */ - uint32_t reload_value() const; - - - /** Get the maximum refresh value for the current platform in milliseconds - * - * @return Maximum refresh value supported by the watchdog for the current - * platform in milliseconds - */ - static uint32_t max_timeout(); + /** Remove from list is used to remove the entry from the list. + * This API is only used to call from stop. + * + */ + void remove_from_list(); +private: + uint32_t _max_timeout; //_max_timeout initialized via constructor while creating instance of this class + const char *_name; //To store the details of user + uint32_t _current_count; //this parameter is used to reset everytime threads/user calls kick + bool _is_initialized; //To control start and stop functionality + static Watchdog *_first; //List to store the user/threads who called start + Watchdog *_next; }; } // namespace mbed diff --git a/mbed.h b/mbed.h index 3272ca56d6..97cdcccc94 100644 --- a/mbed.h +++ b/mbed.h @@ -77,7 +77,7 @@ // mbed Internal components #include "drivers/ResetReason.h" -#include "drivers/Watchdog.h" +#include "platform/mbed_watchdog_mgr.h" #include "drivers/Timer.h" #include "drivers/Ticker.h" #include "drivers/Timeout.h" diff --git a/platform/mbed_watchdog_mgr.cpp b/platform/mbed_watchdog_mgr.cpp new file mode 100644 index 0000000000..ed297fb450 --- /dev/null +++ b/platform/mbed_watchdog_mgr.cpp @@ -0,0 +1,110 @@ + +/* mbed Microcontroller Library + * Copyright (c) 2018 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 + * + * 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. + */ +#ifdef DEVICE_WATCHDOG + +#include "mbed_watchdog_mgr.h" + +static bool is_watchdog_started = false; //boolean to control watchdog start and stop +#define MS_TO_US(x) ((x) * 1000) //macro to convert millisecond to microsecond +static uint32_t elapsed_ms = (HW_WATCHDOG_TIMEOUT / 2); +MBED_STATIC_ASSERT((HW_WATCHDOG_TIMEOUT > 0), "Timeout must be greater than zero"); + +#if DEVICE_LPTICKER +/** Create singleton instance of LowPowerTicker for watchdog periodic call back of kick. + */ +static SingletonPtr _ticker; +#else +/** Create singleton instance of Ticker for watchdog periodic call back of kick. + */ +static SingletonPtr _ticker; +#endif + +/** Refreshes the watchdog timer. + * + * This function should be called periodically before the watchdog times out. + * Otherwise, the system is reset. + * + * If the watchdog timer is not currently running this function does nothing + */ +static void mbed_wdog_manager_kick() +{ + core_util_critical_section_enter(); + hal_watchdog_kick(); + // mbed watchdog manager will access the watchdog process method to verify + // all registered users/threads in alive state */ + mbed::Watchdog::process(((elapsed_ms <= 0) ? 1 : elapsed_ms)); + core_util_critical_section_exit(); +} + +uint32_t mbed_wdog_manager_get_max_timeout() +{ + const watchdog_features_t features = hal_watchdog_get_platform_features(); + return features.max_timeout; +} + +bool mbed_wdog_manager_start() +{ + watchdog_status_t sts; + MBED_ASSERT(HW_WATCHDOG_TIMEOUT < mbed_wdog_manager_get_max_timeout()); + core_util_critical_section_enter(); + if (is_watchdog_started) { + core_util_critical_section_exit(); + return false; + } + watchdog_config_t config; + config.timeout_ms = HW_WATCHDOG_TIMEOUT; + sts = hal_watchdog_init(&config); + if (sts == WATCHDOG_STATUS_OK) { + is_watchdog_started = true; + } + core_util_critical_section_exit(); + if (is_watchdog_started) { + us_timestamp_t timeout = (MS_TO_US(((elapsed_ms <= 0) ? 1 : elapsed_ms))); + _ticker->attach_us(mbed::callback(&mbed_wdog_manager_kick), timeout); + } + return is_watchdog_started; +} + +bool mbed_wdog_manager_stop() +{ + watchdog_status_t sts; + bool msts = true; + core_util_critical_section_enter(); + if (is_watchdog_started) { + sts = hal_watchdog_stop(); + if (sts != WATCHDOG_STATUS_OK) { + msts = false; + } else { + _ticker->detach(); + is_watchdog_started = false; + } + + } else { + msts = false; + } + core_util_critical_section_exit(); + return msts; +} + +uint32_t mbed_wdog_manager_get_timeout() +{ + return hal_watchdog_get_reload_value(); +} + + +#endif // DEVICE_WATCHDOG diff --git a/platform/mbed_watchdog_mgr.h b/platform/mbed_watchdog_mgr.h new file mode 100644 index 0000000000..6d06dda0fb --- /dev/null +++ b/platform/mbed_watchdog_mgr.h @@ -0,0 +1,97 @@ +/* mbed Microcontroller Library + * Copyright (c) 2018 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MBED_WATCHDOG_MGR_H +#define MBED_WATCHDOG_MGR_H + +#ifdef DEVICE_WATCHDOG + +#include "watchdog_api.h" +#include "mbed_error.h" +#include "platform/Callback.h" +#include "platform/mbed_critical.h" +#include "platform/SingletonPtr.h" +#if DEVICE_LPTICKER +#include "LowPowerTicker.h" +#else +#include "Ticker.h" +#endif +#include "Watchdog.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** \addtogroup platform */ +/** A system timer that will reset the system in the case of system failures or + * malfunctions. + * + * Example: + * @code + * + * mbed_wdog_manager_start(); + * + * while (true) { + * wait(0.3); + * + * } + * @endcode + * @ingroup platform + */ + +/** Start an independent watchdog timer + * + * + * @return status true if the watchdog timer was started + * successfully. assert if one of the input parameters is out of range for the current platform. + * false if watchdog timer was not started + */ +bool mbed_wdog_manager_start(); + +/** Stops the watchdog timer + * + * Calling this function will attempt to disable any currently running + * watchdog timers if supported by the current platform. + * + * @return Returns true if the watchdog timer was successfully + * stopped, Returns false if the watchdog cannot be disabled + * on the current platform or if the timer was never started. + */ +bool mbed_wdog_manager_stop(); + + +/** Get the watchdog timer refresh value + * + * This function returns the refresh timeout of the watchdog timer. + * + * @return Reload value for the watchdog timer in milliseconds. + */ +uint32_t mbed_wdog_manager_get_timeout(); + + +/** Get the maximum refresh value for the current platform in milliseconds + * + * @return Maximum refresh value supported by the watchdog for the current + * platform in milliseconds + */ +uint32_t mbed_wdog_manager_get_max_timeout(); + +#ifdef __cplusplus +} +#endif +#endif // DEVICE_WATCHDOG +#endif // MBED_WATCHDOG_H diff --git a/targets/targets.json b/targets/targets.json index 221d9c20d9..1900777305 100644 --- a/targets/targets.json +++ b/targets/targets.json @@ -47,6 +47,11 @@ "tickless-from-us-ticker": { "help": "Run tickless from the microsecond ticker rather than the low power ticker. Running tickless off of the microsecond ticker improves interrupt latency on targets which use lpticker_delay_ticks", "value": false + }, + "hw-watchdog_timeout": { + "help": "Define the timeout in ms value LowPowerTicker to do HW kick", + "value": "800", + "macro_name": "HW_WATCHDOG_TIMEOUT" } } },