diff --git a/TESTS/host_tests/reset_reason.py b/TESTS/host_tests/reset_reason.py new file mode 100644 index 00000000000..c8769e22a20 --- /dev/null +++ b/TESTS/host_tests/reset_reason.py @@ -0,0 +1,163 @@ +""" +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. +""" +import time +from mbed_host_tests import BaseHostTest +from mbed_host_tests.host_tests_runner.host_test_default import DefaultTestSelector + +DEFAULT_SYNC_DELAY = 4.0 + +MSG_VALUE_WATCHDOG_PRESENT = 'wdg_present' +MSG_VALUE_DUMMY = '0' +MSG_VALUE_RESET_REASON_GET = 'get' +MSG_VALUE_RESET_REASON_CLEAR = 'clear' +MSG_VALUE_DEVICE_RESET_NVIC = 'nvic' +MSG_VALUE_DEVICE_RESET_WATCHDOG = 'watchdog' + +MSG_KEY_DEVICE_READY = 'ready' +MSG_KEY_RESET_REASON_RAW = 'reason_raw' +MSG_KEY_RESET_REASON = 'reason' +MSG_KEY_DEVICE_RESET = 'reset' +MSG_KEY_SYNC = '__sync' + +RESET_REASONS = { + 'POWER_ON': '0', + 'PIN_RESET': '1', + 'BROWN_OUT': '2', + 'SOFTWARE': '3', + 'WATCHDOG': '4', + 'LOCKUP': '5', + 'WAKE_LOW_POWER': '6', + 'ACCESS_ERROR': '7', + 'BOOT_ERROR': '8', + 'MULTIPLE': '9', + 'PLATFORM': '10', + 'UNKNOWN': '11' +} + + +def raise_if_different(expected, actual, text=''): + """Raise a RuntimeError if actual is different than expected.""" + if expected != actual: + raise RuntimeError('{}Got {!r}, expected {!r}' + .format(text, actual, expected)) + + +class ResetReasonTest(BaseHostTest): + """Test for the Reset Reason HAL API. + + Given a device supporting a Reset Reason API. + When the device is restarted using various methods. + Then the device returns a correct reset reason for every restart. + """ + + def __init__(self): + super(ResetReasonTest, self).__init__() + self.device_has_watchdog = None + self.raw_reset_reasons = set() + self.sync_delay = DEFAULT_SYNC_DELAY + self.test_steps_sequence = self.test_steps() + # Advance the coroutine to it's first yield statement. + self.test_steps_sequence.send(None) + + def setup(self): + sync_delay = self.get_config_item('forced_reset_timeout') + self.sync_delay = sync_delay if sync_delay is not None else DEFAULT_SYNC_DELAY + self.register_callback(MSG_KEY_DEVICE_READY, self.cb_device_ready) + self.register_callback(MSG_KEY_RESET_REASON_RAW, self.cb_reset_reason_raw) + self.register_callback(MSG_KEY_RESET_REASON, self.cb_reset_reason) + self.register_callback(MSG_KEY_DEVICE_RESET, self.cb_reset_reason) + + def cb_device_ready(self, key, value, timestamp): + """Request a raw value of the reset_reason register. + + Additionally, save the device's watchdog status on the first call. + """ + if self.device_has_watchdog is None: + self.device_has_watchdog = (value == MSG_VALUE_WATCHDOG_PRESENT) + self.send_kv(MSG_KEY_RESET_REASON_RAW, MSG_VALUE_RESET_REASON_GET) + + def cb_reset_reason_raw(self, key, value, timestamp): + """Verify that the raw reset_reason register value is unique. + + Fail the test suite if the raw reset_reason value is not unique. + Request a platform independent reset_reason otherwise. + """ + if value in self.raw_reset_reasons: + self.log('TEST FAILED: The raw reset reason is not unique. ' + '{!r} is already present in {!r}.' + .format(value, self.raw_reset_reasons)) + self.notify_complete(False) + else: + self.raw_reset_reasons.add(value) + self.send_kv(MSG_KEY_RESET_REASON, MSG_VALUE_RESET_REASON_GET) + + def cb_reset_reason(self, key, value, timestamp): + """Feed the test_steps coroutine with reset_reason value. + + Pass the test suite if the coroutine yields True. + Fail the test suite if the iterator stops or raises a RuntimeError. + """ + try: + if self.test_steps_sequence.send(value): + self.notify_complete(True) + except (StopIteration, RuntimeError) as exc: + self.log('TEST FAILED: {}'.format(exc)) + self.notify_complete(False) + + def test_steps(self): + """Generate a sequence of test steps. + + This coroutine calls yield to wait for the input from the device + (the reset_reason). If the device gives the wrong response, the + generator raises a RuntimeError exception and fails the test. + """ + # Ignore the first reason. + __ignored_reset_reason = yield + self.raw_reset_reasons.clear() + self.send_kv(MSG_KEY_RESET_REASON, MSG_VALUE_RESET_REASON_CLEAR) + __ignored_clear_ack = yield + + # Request a NVIC_SystemReset() call. + self.send_kv(MSG_KEY_DEVICE_RESET, MSG_VALUE_DEVICE_RESET_NVIC) + __ignored_reset_ack = yield + time.sleep(self.sync_delay) + self.send_kv(MSG_KEY_SYNC, MSG_VALUE_DUMMY) + reset_reason = yield + raise_if_different(RESET_REASONS['SOFTWARE'], reset_reason, 'Wrong reset reason. ') + self.send_kv(MSG_KEY_RESET_REASON, MSG_VALUE_RESET_REASON_CLEAR) + __ignored_clear_ack = yield + + # Reset the device using DAP. + self.reset_dut(DefaultTestSelector.RESET_TYPE_SW_RST) + reset_reason = yield + raise_if_different(RESET_REASONS['PIN_RESET'], reset_reason, 'Wrong reset reason. ') + self.send_kv(MSG_KEY_RESET_REASON, MSG_VALUE_RESET_REASON_CLEAR) + __ignored_clear_ack = yield + + # Start a watchdog timer and wait for it to reset the device. + if not self.device_has_watchdog: + self.log('DUT does not have a watchdog. Skipping this reset reason.') + else: + self.send_kv(MSG_KEY_DEVICE_RESET, MSG_VALUE_DEVICE_RESET_WATCHDOG) + __ignored_reset_ack = yield + time.sleep(self.sync_delay) + self.send_kv(MSG_KEY_SYNC, MSG_VALUE_DUMMY) + reset_reason = yield + raise_if_different(RESET_REASONS['WATCHDOG'], reset_reason, 'Wrong reset reason. ') + + # The sequence is correct -- test passed. + yield True diff --git a/TESTS/host_tests/sync_on_reset.py b/TESTS/host_tests/sync_on_reset.py new file mode 100644 index 00000000000..de3cbc0ae35 --- /dev/null +++ b/TESTS/host_tests/sync_on_reset.py @@ -0,0 +1,73 @@ +""" +mbed SDK +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. +""" +import time +from mbed_host_tests import BaseHostTest + +DEFAULT_SYNC_DELAY = 4.0 + +MSG_VALUE_DUMMY = '0' +MSG_KEY_DEVICE_READY = 'ready' +MSG_KEY_START_CASE = 'start_case' +MSG_KEY_DEVICE_RESET = 'reset_on_case_teardown' +MSG_KEY_SYNC = '__sync' + + +class SyncOnReset(BaseHostTest): + """Host side test that handles device reset during case teardown. + + Given a device that performs a reset during a test case teardown. + When the device notifies the host about the reset. + Then the host: + * keeps track of the test case index of the current test suite, + * performs a dev-host handshake, + * advances the test suite to next test case. + + Note: + Developed for a watchdog test, so that it can be run on devices that + do not support watchdog timeout updates after the initial setup. + As a solution, after testing watchdog with one set of settings, the + device performs a reset and notifies the host with the test case number, + so that the test suite may be advanced once the device boots again. + """ + + def __init__(self): + super(SyncOnReset, self).__init__() + self.test_case_num = 0 + self.sync_delay = DEFAULT_SYNC_DELAY + + def setup(self): + sync_delay = self.get_config_item('forced_reset_timeout') + self.sync_delay = sync_delay if sync_delay is not None else DEFAULT_SYNC_DELAY + self.register_callback(MSG_KEY_DEVICE_READY, self.cb_device_ready) + self.register_callback(MSG_KEY_DEVICE_RESET, self.cb_device_reset) + + def cb_device_ready(self, key, value, timestamp): + """Advance the device test suite to the next test case.""" + self.send_kv(MSG_KEY_START_CASE, self.test_case_num) + + def cb_device_reset(self, key, value, timestamp): + """Wait for the device to boot and perform a handshake. + + Additionally, keep track of the last test case number. + """ + try: + self.test_case_num = int(value) + except ValueError: + pass + self.test_case_num += 1 + time.sleep(self.sync_delay) + self.send_kv(MSG_KEY_SYNC, MSG_VALUE_DUMMY) diff --git a/TESTS/host_tests/watchdog_reset.py b/TESTS/host_tests/watchdog_reset.py new file mode 100644 index 00000000000..4f12d420f82 --- /dev/null +++ b/TESTS/host_tests/watchdog_reset.py @@ -0,0 +1,145 @@ +""" +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. +""" +import collections +import threading +from mbed_host_tests import BaseHostTest + +TestCaseData = collections.namedtuple('TestCaseData', ['index', 'data_to_send']) + +DEFAULT_SYNC_DELAY = 4.0 +MAX_HB_PERIOD = 2.5 # [s] Max expected heartbeat period. + +MSG_VALUE_DUMMY = '0' +CASE_DATA_INVALID = 0xffffffff +CASE_DATA_PHASE2_OK = 0xfffffffe +CASE_DATA_INSUFF_HB = 0x0 + +MSG_KEY_SYNC = '__sync' +MSG_KEY_DEVICE_READY = 'ready' +MSG_KEY_START_CASE = 'start_case' +MSG_KEY_DEVICE_RESET = 'dev_reset' +MSG_KEY_HEARTBEAT = 'hb' + + +class WatchdogReset(BaseHostTest): + """Host side test that handles device reset. + + Given a device with a watchdog timer started. + When the device notifies the host about an incoming reset. + Then the host: + * keeps track of the test case index of the current test suite, + * performs a dev-host handshake. + """ + + def __init__(self): + super(WatchdogReset, self).__init__() + self.current_case = TestCaseData(0, CASE_DATA_INVALID) + self.__handshake_timer = None + self.sync_delay = DEFAULT_SYNC_DELAY + self.drop_heartbeat_messages = True + self.hb_timestamps_us = [] + + def handshake_timer_start(self, seconds=1.0, pre_sync_fun=None): + """Start a new handshake timer.""" + + def timer_handler(): + """Perform a dev-host handshake by sending a sync message.""" + if pre_sync_fun is not None: + pre_sync_fun() + self.send_kv(MSG_KEY_SYNC, MSG_VALUE_DUMMY) + + self.__handshake_timer = threading.Timer(seconds, timer_handler) + self.__handshake_timer.start() + + def handshake_timer_cancel(self): + """Cancel the current handshake timer.""" + try: + self.__handshake_timer.cancel() + except AttributeError: + pass + finally: + self.__handshake_timer = None + + def heartbeat_timeout_handler(self): + """Handler for the heartbeat timeout. + + Compute the time span of the last heartbeat sequence. + Set self.current_case.data_to_send to CASE_DATA_INVALID if no heartbeat was received. + Set self.current_case.data_to_send to CASE_DATA_INSUFF_HB if only one heartbeat was + received. + """ + self.drop_heartbeat_messages = True + dev_data = CASE_DATA_INVALID + if len(self.hb_timestamps_us) == 1: + dev_data = CASE_DATA_INSUFF_HB + self.log('Not enough heartbeats received.') + elif len(self.hb_timestamps_us) >= 2: + dev_data = int(round(0.001 * (self.hb_timestamps_us[-1] - self.hb_timestamps_us[0]))) + self.log('Heartbeat time span was {} ms.'.format(dev_data)) + self.current_case = TestCaseData(self.current_case.index, dev_data) + + def setup(self): + sync_delay = self.get_config_item('forced_reset_timeout') + self.sync_delay = sync_delay if sync_delay is not None else DEFAULT_SYNC_DELAY + self.register_callback(MSG_KEY_DEVICE_READY, self.cb_device_ready) + self.register_callback(MSG_KEY_DEVICE_RESET, self.cb_device_reset) + self.register_callback(MSG_KEY_HEARTBEAT, self.cb_heartbeat) + + def teardown(self): + self.handshake_timer_cancel() + + def cb_device_ready(self, key, value, timestamp): + """Advance the device test suite to a proper test case. + + Additionally, send test case data to the device. + """ + self.handshake_timer_cancel() + msg_value = '{0.index:02x},{0.data_to_send:08x}'.format(self.current_case) + self.send_kv(MSG_KEY_START_CASE, msg_value) + self.drop_heartbeat_messages = False + self.hb_timestamps_us = [] + + def cb_device_reset(self, key, value, timestamp): + """Keep track of the test case number. + + Also set a new handshake timeout, so when the device gets + restarted by the watchdog, the communication will be restored + by the __handshake_timer. + """ + self.handshake_timer_cancel() + case_num, dev_reset_delay_ms = (int(i, base=16) for i in value.split(',')) + self.current_case = TestCaseData(case_num, CASE_DATA_PHASE2_OK) + self.handshake_timer_start(self.sync_delay + dev_reset_delay_ms / 1000.0) + + def cb_heartbeat(self, key, value, timestamp): + """Save the timestamp of a heartbeat message. + + Additionally, keep track of the test case number. + + Also each heartbeat sets a new timeout, so when the device gets + restarted by the watchdog, the communication will be restored + by the __handshake_timer. + """ + if self.drop_heartbeat_messages: + return + self.handshake_timer_cancel() + case_num, timestamp_us = (int(i, base=16) for i in value.split(',')) + self.current_case = TestCaseData(case_num, CASE_DATA_INVALID) + self.hb_timestamps_us.append(timestamp_us) + self.handshake_timer_start( + seconds=(MAX_HB_PERIOD + self.sync_delay), + pre_sync_fun=self.heartbeat_timeout_handler) diff --git a/TESTS/mbed_drivers/reset_reason/ResetReason_tests.h b/TESTS/mbed_drivers/reset_reason/ResetReason_tests.h new file mode 100644 index 00000000000..7311c24f7e4 --- /dev/null +++ b/TESTS/mbed_drivers/reset_reason/ResetReason_tests.h @@ -0,0 +1,41 @@ +/* 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_reset_reason_tests + * @{ + */ + +#ifndef MBED_DRIVERS_RESET_REASON_TESTS_H +#define MBED_DRIVERS_RESET_REASON_TESTS_H + +#if DEVICE_RESET_REASON + +/** Test the ResetReason driver API + * + * Given a device supporting a ResetReason API, + * when the device is restarted, + * then the device returns a correct reset reason for every restart. + */ +void test_reset_reason(); + +#endif + +#endif + +/** @}*/ + diff --git a/TESTS/mbed_drivers/reset_reason/main.cpp b/TESTS/mbed_drivers/reset_reason/main.cpp new file mode 100644 index 00000000000..4b1eaa8e3a7 --- /dev/null +++ b/TESTS/mbed_drivers/reset_reason/main.cpp @@ -0,0 +1,142 @@ +/* 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_RESET_REASON +#error [NOT_SUPPORTED] Reset reason API not supported for this target +#endif + +#include "greentea-client/test_env.h" +#include "unity/unity.h" +#include "drivers/ResetReason.h" +#include "ResetReason_tests.h" +#include "mbed.h" + +#if DEVICE_WATCHDOG +#include "drivers/Watchdog.h" + +#define MSG_VALUE_WATCHDOG_STATUS "wdg_present" +#define WDG_TIMEOUT_MS 50UL +#define WDG_TIMEOUT_DELTA_MS 50UL + +#else +#define MSG_VALUE_WATCHDOG_STATUS "no_wdg" +#endif + +#define MSG_VALUE_DUMMY "0" +#define MSG_VALUE_RESET_REASON_GET "get" +#define MSG_VALUE_RESET_REASON_CLEAR "clear" +#define MSG_VALUE_RESET_REASON_CLEAR_ACK "cleared" +#define MSG_VALUE_DEVICE_RESET_ACK "ack" +#define MSG_VALUE_DEVICE_RESET_NVIC "nvic" +#define MSG_VALUE_DEVICE_RESET_WATCHDOG "watchdog" +#define MSG_VALUE_LEN 16 +#define MSG_KEY_LEN 16 + +#define MSG_KEY_DEVICE_READY "ready" +#define MSG_KEY_RESET_REASON_RAW "reason_raw" +#define MSG_KEY_RESET_REASON "reason" +#define MSG_KEY_DEVICE_RESET "reset" + +typedef enum { + CMD_STATUS_CONTINUE, + CMD_STATUS_ERROR +} cmd_status_t; + +static cmd_status_t handle_command(const char *key, const char *value) +{ + if (strcmp(key, MSG_KEY_RESET_REASON_RAW) == 0) { + uint32_t raw_reason = ResetReason::get_raw(); + char raw_reason_hex_str[9] = { }; + int raw_reason_hex_str_len = snprintf(raw_reason_hex_str, + sizeof raw_reason_hex_str, "%08lx", raw_reason); + + if (raw_reason_hex_str_len != (sizeof raw_reason_hex_str) - 1) { + TEST_ASSERT_MESSAGE(0, "Failed to compose raw reset reason hex string."); + return CMD_STATUS_ERROR; + } + + greentea_send_kv(MSG_KEY_RESET_REASON_RAW, raw_reason_hex_str); + return CMD_STATUS_CONTINUE; + } + + if (strcmp(key, MSG_KEY_RESET_REASON) == 0 && strcmp(value, MSG_VALUE_RESET_REASON_GET) == 0) { + int reason = (int) ResetReason::get(); + greentea_send_kv(MSG_KEY_RESET_REASON, reason); + return CMD_STATUS_CONTINUE; + } + + if (strcmp(key, MSG_KEY_RESET_REASON) == 0 && strcmp(value, MSG_VALUE_RESET_REASON_CLEAR) == 0) { + /* In order to keep this code compatible with a host script common for + * both HAL API tests and driver API tests, ignore the 'clear' command + * received from host. + * + * The driver API does not provide clear() function directly. + */ + greentea_send_kv(MSG_KEY_RESET_REASON, MSG_VALUE_RESET_REASON_CLEAR_ACK); + return CMD_STATUS_CONTINUE; + } + + if (strcmp(key, MSG_KEY_DEVICE_RESET) == 0 && strcmp(value, MSG_VALUE_DEVICE_RESET_NVIC) == 0) { + greentea_send_kv(MSG_KEY_DEVICE_RESET, MSG_VALUE_DEVICE_RESET_ACK); + wait_ms(10); // Wait for the serial buffers to flush. + NVIC_SystemReset(); + TEST_ASSERT_MESSAGE(0, "NVIC_SystemReset did not reset the device as expected."); + return CMD_STATUS_ERROR; + } + +#if DEVICE_WATCHDOG + 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."); + return CMD_STATUS_ERROR; + } + wait_ms(WDG_TIMEOUT_MS + WDG_TIMEOUT_DELTA_MS); + TEST_ASSERT_MESSAGE(0, "Watchdog did not reset the device as expected."); + return CMD_STATUS_ERROR; + } +#endif + + TEST_ASSERT_MESSAGE(0, "Invalid message key."); + return CMD_STATUS_ERROR; +} + +void test_reset_reason() +{ + // Report readiness and watchdog status. + greentea_send_kv(MSG_KEY_DEVICE_READY, MSG_VALUE_WATCHDOG_STATUS); + + cmd_status_t cmd_status = CMD_STATUS_CONTINUE; + static char _key[MSG_KEY_LEN + 1] = { }; + static char _value[MSG_VALUE_LEN + 1] = { }; + + // Let the host side decide what to do and just handle the commands. + while (CMD_STATUS_CONTINUE == cmd_status) { + memset(_key, 0, sizeof _key); + memset(_value, 0, sizeof _value); + greentea_parse_kv(_key, _value, MSG_KEY_LEN, MSG_VALUE_LEN); + cmd_status = handle_command(_key, _value); + } +} + +int main() +{ + GREENTEA_SETUP(60, "reset_reason"); + test_reset_reason(); // The result of this test suite is reported by the host side. + GREENTEA_TESTSUITE_RESULT(0); // Fail on any error. +} diff --git a/TESTS/mbed_drivers/watchdog/Watchdog_tests.h b/TESTS/mbed_drivers/watchdog/Watchdog_tests.h new file mode 100644 index 00000000000..86f20fc84e0 --- /dev/null +++ b/TESTS/mbed_drivers/watchdog/Watchdog_tests.h @@ -0,0 +1,114 @@ +/* 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 new file mode 100644 index 00000000000..d9ed790055f --- /dev/null +++ b/TESTS/mbed_drivers/watchdog/main.cpp @@ -0,0 +1,227 @@ +/* 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/Watchdog_reset_tests.h b/TESTS/mbed_drivers/watchdog_reset/Watchdog_reset_tests.h new file mode 100644 index 00000000000..548dea1c2a0 --- /dev/null +++ b/TESTS/mbed_drivers/watchdog_reset/Watchdog_reset_tests.h @@ -0,0 +1,77 @@ +/* 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_RESET_TESTS_H +#define MBED_DRIVERS_WATCHDOG_RESET_TESTS_H + +#if DEVICE_WATCHDOG + +/** Test Watchdog reset + * + * Given a device with a Watchdog started, + * when the Watchdog timeout expires, + * then the device is restarted. + */ +void test_simple_reset(); + +/** Test Watchdog reset in sleep mode + * + * 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, + * then the device is not restarted. + * When the Watchdog is started again and its timeout expires, + * then the device is restarted. + */ +void test_restart_reset(); + +/** Test Watchdog kick + * + * Given a device with a Watchdog 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, + * then the device is restarted. + */ +void test_kick_reset(); + +#endif + +#endif + +/** @}*/ + diff --git a/TESTS/mbed_drivers/watchdog_reset/main.cpp b/TESTS/mbed_drivers/watchdog_reset/main.cpp new file mode 100644 index 00000000000..2ce3c845648 --- /dev/null +++ b/TESTS/mbed_drivers/watchdog_reset/main.cpp @@ -0,0 +1,286 @@ +/* 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 + +#include "greentea-client/test_env.h" +#include "utest/utest.h" +#include "unity/unity.h" +#include "drivers/Watchdog.h" +#include "Watchdog_reset_tests.h" +#include "mbed.h" + +#define TIMEOUT_MS 500UL +#define TIMEOUT_DELTA_MS 50UL + +#define MSG_VALUE_DUMMY "0" +#define CASE_DATA_INVALID 0xffffffffUL +#define CASE_DATA_PHASE2_OK 0xfffffffeUL + +#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 "dev_reset" + +using utest::v1::Case; +using utest::v1::Specification; +using utest::v1::Harness; + +struct testcase_data { + int index; + int start_index; + uint32_t received_data; +}; + +void release_sem(Semaphore *sem) +{ + sem->release(); +} + +testcase_data current_case; + +bool send_reset_notification(testcase_data *tcdata, uint32_t delay_ms) +{ + char msg_value[12]; + int str_len = snprintf(msg_value, sizeof msg_value, "%02x,%08lx", tcdata->start_index + tcdata->index, delay_ms); + if (str_len != (sizeof msg_value) - 1) { + utest_printf("Failed to compose a value string to be sent to host."); + return false; + } + greentea_send_kv(MSG_KEY_DEVICE_RESET, msg_value); + return true; +} + +void test_simple_reset() +{ + // Phase 2. -- verify the test results. + // Verify if this test case passed based on data received from host. + 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. + // Init the watchdog and wait for a device reset. + Watchdog watchdog; + 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)); + wait_ms(TIMEOUT_MS + TIMEOUT_DELTA_MS); // Device reset expected. + + // Watchdog reset should have occurred during wait_ms() 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_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"); + return; + } + + // 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. + 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); + + 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)); + wait_ms(TIMEOUT_MS + TIMEOUT_DELTA_MS); // Device reset expected. + + // Watchdog reset should have occurred during that wait() above; + + watchdog.kick(); // Just to buy some time for testsuite failure handling. + TEST_ASSERT_MESSAGE(0, "Watchdog did not reset the device as expected."); +} + +void test_kick_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; + 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_MESSAGE(0, "Dev-host communication error."); + return; + } + wait_ms(TIMEOUT_MS + TIMEOUT_DELTA_MS); // Device reset expected. + + // Watchdog reset should have occurred during that wait() above; + + watchdog.kick(); // Just to buy some time for testsuite failure handling. + TEST_ASSERT_MESSAGE(0, "Watchdog did not reset the device as expected."); +} + +utest::v1::status_t case_setup(const Case *const source, const size_t index_of_case) +{ + current_case.index = index_of_case; + return utest::v1::greentea_case_setup_handler(source, index_of_case); +} + +int testsuite_setup(const size_t number_of_cases) +{ + GREENTEA_SETUP(90, "watchdog_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; + } + + int num_args = sscanf(value, "%02x,%08lx", &(current_case.start_index), &(current_case.received_data)); + if (num_args == 0 || num_args == EOF) { + utest_printf("Invalid data received from host\n"); + return utest::v1::STATUS_ABORT; + } + + utest_printf("This test suite is composed of %i test cases. Starting at index %i.\n", number_of_cases, + current_case.start_index); + return current_case.start_index; +} + +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), +}; + +Specification specification((utest::v1::test_setup_handler_t) testsuite_setup, cases); + +int main() +{ + // Harness will start with a test case index provided by host script. + return !Harness::run(specification); +} diff --git a/TESTS/mbed_hal/reset_reason/main.cpp b/TESTS/mbed_hal/reset_reason/main.cpp new file mode 100644 index 00000000000..2dc68f63557 --- /dev/null +++ b/TESTS/mbed_hal/reset_reason/main.cpp @@ -0,0 +1,151 @@ +/* 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_RESET_REASON +#error [NOT_SUPPORTED] Reset reason API not supported for this target +#endif + +#include "greentea-client/test_env.h" +#include "unity/unity.h" +#include "hal/reset_reason_api.h" +#include "reset_reason_api_tests.h" +#include "mbed.h" + +#if DEVICE_WATCHDOG +#include "hal/watchdog_api.h" + +#define MSG_VALUE_WATCHDOG_STATUS "wdg_present" +#define WDG_TIMEOUT_MS 50UL +#define WDG_TIMEOUT_DELTA_MS 50UL + +#else +#define MSG_VALUE_WATCHDOG_STATUS "no_wdg" +#endif + +#define MSG_VALUE_DUMMY "0" +#define MSG_VALUE_RESET_REASON_GET "get" +#define MSG_VALUE_RESET_REASON_CLEAR "clear" +#define MSG_VALUE_RESET_REASON_CLEAR_ACK "cleared" +#define MSG_VALUE_DEVICE_RESET_ACK "ack" +#define MSG_VALUE_DEVICE_RESET_NVIC "nvic" +#define MSG_VALUE_DEVICE_RESET_WATCHDOG "watchdog" +#define MSG_VALUE_LEN 16 +#define MSG_KEY_LEN 16 + +#define MSG_KEY_DEVICE_READY "ready" +#define MSG_KEY_RESET_REASON_RAW "reason_raw" +#define MSG_KEY_RESET_REASON "reason" +#define MSG_KEY_DEVICE_RESET "reset" + +/* To prevent loss of Greentea data, flush serial buffers before the UART peripheral shutdown. The UART shutdown happens when the + * device is entering the deepsleep mode or performing a reset. + * + * With the current API, it is not possible to check if the hardware buffers + * are empty. However, you can determine the amount of time required for the + * buffers to flush. + * + * For example, NUMAKER_PFM_NUC472: + * The UART peripheral has 16-byte Tx FIFO. With a baud rate of 9600, + * flushing the Tx FIFO would take: 16 * 8 * 1000 / 9600 = 13.3 ms. + * To be on the safe side, set the wait time to 20 ms. + */ +#define SERIAL_FLUSH_TIME_MS 20 + +typedef enum { + CMD_STATUS_CONTINUE, + CMD_STATUS_ERROR +} cmd_status_t; + +static cmd_status_t handle_command(const char *key, const char *value) +{ + if (strcmp(key, MSG_KEY_RESET_REASON_RAW) == 0) { + uint32_t raw_reason = hal_reset_reason_get_raw(); + char raw_reason_hex_str[9] = { }; + int raw_reason_hex_str_len = snprintf(raw_reason_hex_str, + sizeof raw_reason_hex_str, "%08lx", raw_reason); + + if (raw_reason_hex_str_len != (sizeof raw_reason_hex_str) - 1) { + TEST_ASSERT_MESSAGE(0, "Failed to compose raw reset reason hex string."); + return CMD_STATUS_ERROR; + } + + greentea_send_kv(MSG_KEY_RESET_REASON_RAW, raw_reason_hex_str); + return CMD_STATUS_CONTINUE; + } + + if (strcmp(key, MSG_KEY_RESET_REASON) == 0 && strcmp(value, MSG_VALUE_RESET_REASON_GET) == 0) { + int reason = (int) hal_reset_reason_get(); + greentea_send_kv(MSG_KEY_RESET_REASON, reason); + return CMD_STATUS_CONTINUE; + } + + if (strcmp(key, MSG_KEY_RESET_REASON) == 0 && strcmp(value, MSG_VALUE_RESET_REASON_CLEAR) == 0) { + hal_reset_reason_clear(); + greentea_send_kv(MSG_KEY_RESET_REASON, MSG_VALUE_RESET_REASON_CLEAR_ACK); + return CMD_STATUS_CONTINUE; + } + + if (strcmp(key, MSG_KEY_DEVICE_RESET) == 0 && strcmp(value, MSG_VALUE_DEVICE_RESET_NVIC) == 0) { + greentea_send_kv(MSG_KEY_DEVICE_RESET, MSG_VALUE_DEVICE_RESET_ACK); + wait_ms(SERIAL_FLUSH_TIME_MS); // Wait for the serial buffers to flush. + NVIC_SystemReset(); + TEST_ASSERT_MESSAGE(0, "NVIC_SystemReset did not reset the device as expected."); + return CMD_STATUS_ERROR; + } + +#if DEVICE_WATCHDOG + 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(SERIAL_FLUSH_TIME_MS); // Wait for the serial buffers to flush. + 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); + TEST_ASSERT_MESSAGE(0, "Watchdog did not reset the device as expected."); + return CMD_STATUS_ERROR; + } +#endif + + TEST_ASSERT_MESSAGE(0, "Invalid message key."); + return CMD_STATUS_ERROR; +} + +void test_reset_reason() +{ + // Report readiness and watchdog status. + greentea_send_kv(MSG_KEY_DEVICE_READY, MSG_VALUE_WATCHDOG_STATUS); + + cmd_status_t cmd_status = CMD_STATUS_CONTINUE; + static char _key[MSG_KEY_LEN + 1] = { }; + static char _value[MSG_VALUE_LEN + 1] = { }; + + // Let the host side decide what to do and just handle the commands. + while (CMD_STATUS_CONTINUE == cmd_status) { + memset(_key, 0, sizeof _key); + memset(_value, 0, sizeof _value); + greentea_parse_kv(_key, _value, MSG_KEY_LEN, MSG_VALUE_LEN); + cmd_status = handle_command(_key, _value); + } +} + +int main() +{ + GREENTEA_SETUP(60, "reset_reason"); + test_reset_reason(); // The result of this test suite is reported by the host side. + GREENTEA_TESTSUITE_RESULT(0); // Fail on any error. +} diff --git a/TESTS/mbed_hal/reset_reason/reset_reason_api_tests.h b/TESTS/mbed_hal/reset_reason/reset_reason_api_tests.h new file mode 100644 index 00000000000..5bf3fecdd95 --- /dev/null +++ b/TESTS/mbed_hal/reset_reason/reset_reason_api_tests.h @@ -0,0 +1,49 @@ +/* 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 hal_reset_reason_tests + * @{ + */ + +#ifndef MBED_HAL_RESET_REASON_API_TESTS_H +#define MBED_HAL_RESET_REASON_API_TESTS_H + +#if DEVICE_RESET_REASON + +#ifdef __cplusplus +extern "C" { +#endif + +/** Test the Reset Reason HAL API + * + * Given a device supporting a Reset Reason API, + * when the device is restarted, + * then the device returns a correct reset reason for every restart. + */ +void test_reset_reason(); + +#ifdef __cplusplus +} +#endif + +#endif + +#endif + +/** @}*/ + diff --git a/TESTS/mbed_hal/watchdog/main.cpp b/TESTS/mbed_hal/watchdog/main.cpp new file mode 100644 index 00000000000..04c5b0d5186 --- /dev/null +++ b/TESTS/mbed_hal/watchdog/main.cpp @@ -0,0 +1,238 @@ +/* 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 + +#include "greentea-client/test_env.h" +#include "hal/watchdog_api.h" +#include "mbed_wait_api.h" +#include "unity/unity.h" +#include "utest/utest.h" +#include "watchdog_api_tests.h" + +#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" + +/* To prevent a loss of Greentea data, the serial buffers have to be flushed + * before the UART peripheral shutdown. The UART shutdown happens when the + * device is entering the deepsleep mode or performing a reset. + * + * With the current API, it is not possible to check if the hardware buffers + * are empty. However, it is possible to determine the time required for the + * buffers to flush. + * + * Take NUMAKER_PFM_NUC472 as an example: + * The UART peripheral has 16-byte Tx FIFO. With a baud rate set to 9600, + * flushing the Tx FIFO would take: 16 * 8 * 1000 / 9600 = 13.3 ms. + * To be on the safe side, set the wait time to 20 ms. + */ +#define SERIAL_FLUSH_TIME_MS 20 + +int CASE_INDEX_START; +int CASE_INDEX_CURRENT; + +using utest::v1::Case; +using utest::v1::Specification; +using utest::v1::Harness; + +const watchdog_config_t WDG_CONFIG_DEFAULT = { .timeout_ms = WDG_TIMEOUT_MS }; + +void test_max_timeout_is_valid() +{ + TEST_ASSERT(hal_watchdog_get_platform_features().max_timeout > 1UL); +} + +void test_restart_is_possible() +{ + watchdog_features_t features = hal_watchdog_get_platform_features(); + if (!features.disable_watchdog) { + TEST_IGNORE_MESSAGE("Disabling watchdog not supported for this platform"); + return; + } + TEST_ASSERT(features.update_config); +} + +void test_stop() +{ + watchdog_features_t features = hal_watchdog_get_platform_features(); + if (!features.disable_watchdog) { + TEST_ASSERT_EQUAL(WATCHDOG_STATUS_NOT_SUPPORTED, hal_watchdog_stop()); + TEST_IGNORE_MESSAGE("Disabling watchdog not supported for this platform"); + return; + } + + TEST_ASSERT_EQUAL(WATCHDOG_STATUS_OK, hal_watchdog_stop()); + + TEST_ASSERT_EQUAL(WATCHDOG_STATUS_OK, hal_watchdog_init(&WDG_CONFIG_DEFAULT)); + TEST_ASSERT_EQUAL(WATCHDOG_STATUS_OK, hal_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, hal_watchdog_stop()); +} + +void test_update_config() +{ + watchdog_features_t features = hal_watchdog_get_platform_features(); + if (!features.update_config) { + TEST_IGNORE_MESSAGE("Updating watchdog config not supported for this platform"); + return; + } + + watchdog_config_t config = WDG_CONFIG_DEFAULT; + TEST_ASSERT_EQUAL(WATCHDOG_STATUS_OK, hal_watchdog_init(&config)); + TEST_ASSERT_UINT32_WITHIN(WORST_TIMEOUT_RESOLUTION_MS, config.timeout_ms, hal_watchdog_get_reload_value()); + + config.timeout_ms = features.max_timeout - 2 * WORST_TIMEOUT_RESOLUTION_MS; + TEST_ASSERT_EQUAL(WATCHDOG_STATUS_OK, hal_watchdog_init(&config)); + TEST_ASSERT_UINT32_WITHIN(WORST_TIMEOUT_RESOLUTION_MS, config.timeout_ms, hal_watchdog_get_reload_value()); + + config.timeout_ms = features.max_timeout; + TEST_ASSERT_EQUAL(WATCHDOG_STATUS_OK, hal_watchdog_init(&config)); + TEST_ASSERT_UINT32_WITHIN(WORST_TIMEOUT_RESOLUTION_MS, config.timeout_ms, hal_watchdog_get_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(SERIAL_FLUSH_TIME_MS); // Wait for the serial buffers to flush. + NVIC_SystemReset(); + return status; // Reset is instant so this line won't be reached. +} + +utest::v1::status_t case_teardown_wdg_stop_or_reset(const Case *const source, const size_t passed, const size_t failed, + const utest::v1::failure_t failure) +{ + watchdog_features_t features = hal_watchdog_get_platform_features(); + if (features.disable_watchdog) { + hal_watchdog_stop(); + return utest::v1::greentea_case_teardown_handler(source, passed, failed, failure); + } + + return case_teardown_sync_on_reset(source, passed, failed, failure); +} + +template +void test_init() +{ + watchdog_config_t config = { timeout_ms }; + TEST_ASSERT_EQUAL(WATCHDOG_STATUS_OK, hal_watchdog_init(&config)); + TEST_ASSERT_UINT32_WITHIN(WORST_TIMEOUT_RESOLUTION_MS, timeout_ms, hal_watchdog_get_reload_value()); +} + +void test_init_max_timeout() +{ + watchdog_features_t features = hal_watchdog_get_platform_features(); + watchdog_config_t config = { .timeout_ms = features.max_timeout }; + TEST_ASSERT_EQUAL(WATCHDOG_STATUS_OK, hal_watchdog_init(&config)); + TEST_ASSERT_UINT32_WITHIN(WORST_TIMEOUT_RESOLUTION_MS, features.max_timeout, hal_watchdog_get_reload_value()); +} + +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("Platform feature max_timeout is valid", test_max_timeout_is_valid), + Case("Stopped watchdog can be started again", test_restart_is_possible), + Case("Watchdog can be stopped", test_stop), + + Case("Update config with multiple init calls", + (utest::v1::case_setup_handler_t) case_setup_sync_on_reset, + test_update_config, + (utest::v1::case_teardown_handler_t) case_teardown_wdg_stop_or_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("Init, 500 ms", (utest::v1::case_setup_handler_t) case_setup_sync_on_reset, + test_init<500UL>, (utest::v1::case_teardown_handler_t) case_teardown_sync_on_reset), + Case("Init, max_timeout", (utest::v1::case_setup_handler_t) case_setup_sync_on_reset, + test_init_max_timeout, (utest::v1::case_teardown_handler_t) case_teardown_sync_on_reset), +}; + +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_hal/watchdog/watchdog_api_tests.h b/TESTS/mbed_hal/watchdog/watchdog_api_tests.h new file mode 100644 index 00000000000..e89cf4317ab --- /dev/null +++ b/TESTS/mbed_hal/watchdog/watchdog_api_tests.h @@ -0,0 +1,113 @@ +/* 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 hal_watchdog_tests + * @{ + */ + +#ifndef MBED_HAL_WATCHDOG_API_TESTS_H +#define MBED_HAL_WATCHDOG_API_TESTS_H + +#if DEVICE_WATCHDOG + +/** Test max_timeout validity + * + * Given a device supporting Watchdog HAL API, + * when @a hal_watchdog_get_platform_features() is called, + * then max_timeout member of returned watchdog_features_t struct is greater than 1. + */ +void test_max_timeout_is_valid(); + +/** Test Watchdog features if a stopped Watchdog can be started again + * + * Given a device supporting Watchdog HAL API, + * when the device supports the @a disable_watchdog feature, + * then the device also supports @a update_config feature. + */ +void test_restart_is_possible(); + +/** Test Watchdog stop + * + * Given a device without a support for the @a disable_watchdog feature, + * when @a hal_watchdog_stop() is called, + * then WATCHDOG_STATUS_NOT_SUPPORTED is returned. + * + * Otherwise, given the device with @a disable_watchdog feature support: + * + * Given the Watchdog is *NOT* running, + * when @a hal_watchdog_stop() is called, + * then WATCHDOG_STATUS_OK is returned. + * + * Given the Watchdog is running, + * when @a hal_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 hal_watchdog_stop() is called, + * then WATCHDOG_STATUS_OK is returned. + */ +void test_stop(); + +/** Test Watchdog init multiple times + * + * Given @a max_timeout value returned by @a hal_watchdog_get_platform_features(): + * + * Given @a config.timeout_ms is set to WDG_TIMEOUT_MS, + * when @a hal_watchdog_init() is called, + * then @a WATCHDOG_STATUS_OK is returned + * and @a hal_watchdog_get_reload_value() returns WDG_TIMEOUT_MS. + * + * Given @a config.timeout_ms is set to max_timeout-delta, + * when @a hal_watchdog_init() is called, + * then @a WATCHDOG_STATUS_OK is returned + * and @a hal_watchdog_get_reload_value() returns max_timeout-delta. + * + * Given @a config.timeout_ms is set to max_timeout, + * when @a hal_watchdog_init() is called, + * then @a WATCHDOG_STATUS_OK is returned + * and @a hal_watchdog_get_reload_value() returns max_timeout. + */ +void test_update_config(); + +/** Test Watchdog init with a valid config + * + * Given @a config.timeout_ms is set to X ms, + * which is within supported Watchdog timeout range, + * when @a hal_watchdog_init() is called, + * then @a WATCHDOG_STATUS_OK is returned + * and @a hal_watchdog_get_reload_value() returns X. + */ +template +void test_init(); + +/** Test Watchdog init with a max_timeout + * + * Given @a config.timeout_ms is set to max_timeout, + * which is a value returned by @a hal_watchdog_get_platform_features(), + * when @a hal_watchdog_init() is called, + * then @a WATCHDOG_STATUS_OK is returned + * and @a hal_watchdog_get_reload_value() returns max_timeout. + */ +void test_init_max_timeout(); + +#endif + +#endif + +/** @}*/ + diff --git a/TESTS/mbed_hal/watchdog_reset/main.cpp b/TESTS/mbed_hal/watchdog_reset/main.cpp new file mode 100644 index 00000000000..9421f222cf6 --- /dev/null +++ b/TESTS/mbed_hal/watchdog_reset/main.cpp @@ -0,0 +1,308 @@ +/* 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 + +#include "greentea-client/test_env.h" +#include "utest/utest.h" +#include "unity/unity.h" +#include "hal/watchdog_api.h" +#include "watchdog_reset_tests.h" +#include "mbed.h" + +#define TIMEOUT_MS 500UL +#if TARGET_NUMAKER_PFM_NANO130 +/* On NUMAKER_PFM_NANO130 target, WDT's clock source is fixed to LIRC, which is more + * inaccurate than other targets. Enlarge this delta define to pass this test. */ +#define TIMEOUT_DELTA_MS 100UL +#else +#define TIMEOUT_DELTA_MS 50UL +#endif + +#define MSG_VALUE_DUMMY "0" +#define CASE_DATA_INVALID 0xffffffffUL +#define CASE_DATA_PHASE2_OK 0xfffffffeUL + +#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 "dev_reset" + +/* To prevent a loss of Greentea data, the serial buffers have to be flushed + * before the UART peripheral shutdown. The UART shutdown happens when the + * device is entering the deepsleep mode or performing a reset. + * + * With the current API, it is not possible to check if the hardware buffers + * are empty. However, it is possible to determine the time required for the + * buffers to flush. + * + * Take NUMAKER_PFM_NUC472 as an example: + * The UART peripheral has 16-byte Tx FIFO. With a baud rate set to 9600, + * flushing the Tx FIFO would take: 16 * 8 * 1000 / 9600 = 13.3 ms. + * To be on the safe side, set the wait time to 20 ms. + */ +#define SERIAL_FLUSH_TIME_MS 20 + +using utest::v1::Case; +using utest::v1::Specification; +using utest::v1::Harness; + +struct testcase_data { + int index; + int start_index; + uint32_t received_data; +}; + +void release_sem(Semaphore *sem) +{ + sem->release(); +} + +testcase_data current_case; + +bool send_reset_notification(testcase_data *tcdata, uint32_t delay_ms) +{ + char msg_value[12]; + int str_len = snprintf(msg_value, sizeof msg_value, "%02x,%08lx", tcdata->start_index + tcdata->index, delay_ms); + if (str_len != (sizeof msg_value) - 1) { + utest_printf("Failed to compose a value string to be sent to host."); + return false; + } + greentea_send_kv(MSG_KEY_DEVICE_RESET, msg_value); + return true; +} + +void test_simple_reset() +{ + // Phase 2. -- verify the test results. + // Verify if this test case passed based on data received from host. + 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. + // Init the watchdog and wait for a device reset. + watchdog_config_t config = { TIMEOUT_MS }; + 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, hal_watchdog_init(&config)); + wait_ms(TIMEOUT_MS + TIMEOUT_DELTA_MS); // Device reset expected. + + // Watchdog reset should have occurred during wait_ms() above; + + hal_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_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_config_t config = { TIMEOUT_MS }; + 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, hal_watchdog_init(&config)); + 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; + + hal_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_config_t config = { TIMEOUT_MS }; + 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, hal_watchdog_init(&config)); + lp_timeout.attach_us(mbed::callback(release_sem, &sem), 1000ULL * (TIMEOUT_MS + TIMEOUT_DELTA_MS)); + wait_ms(SERIAL_FLUSH_TIME_MS); // 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; + + hal_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_features_t features = hal_watchdog_get_platform_features(); + if (!features.disable_watchdog) { + TEST_IGNORE_MESSAGE("Disabling Watchdog not supported for this platform"); + return; + } + + // 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_config_t config = { TIMEOUT_MS }; + TEST_ASSERT_EQUAL(WATCHDOG_STATUS_OK, hal_watchdog_init(&config)); + wait_ms(TIMEOUT_MS / 2UL); + TEST_ASSERT_EQUAL(WATCHDOG_STATUS_OK, hal_watchdog_stop()); + // Check that stopping the Watchdog prevents a device reset. + wait_ms(TIMEOUT_MS / 2UL + TIMEOUT_DELTA_MS); + + 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, hal_watchdog_init(&config)); + wait_ms(TIMEOUT_MS + TIMEOUT_DELTA_MS); // Device reset expected. + + // Watchdog reset should have occurred during that wait() above; + + hal_watchdog_kick(); // Just to buy some time for testsuite failure handling. + TEST_ASSERT_MESSAGE(0, "Watchdog did not reset the device as expected."); +} + +void test_kick_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_config_t config = { TIMEOUT_MS }; + TEST_ASSERT_EQUAL(WATCHDOG_STATUS_OK, hal_watchdog_init(&config)); + for (int i = 3; i; i--) { + wait_ms(TIMEOUT_MS / 2UL); + hal_watchdog_kick(); + } + if (send_reset_notification(¤t_case, TIMEOUT_MS + TIMEOUT_DELTA_MS) == false) { + TEST_ASSERT_MESSAGE(0, "Dev-host communication error."); + return; + } + wait_ms(TIMEOUT_MS + TIMEOUT_DELTA_MS); // Device reset expected. + + // Watchdog reset should have occurred during that wait() above; + + hal_watchdog_kick(); // Just to buy some time for testsuite failure handling. + TEST_ASSERT_MESSAGE(0, "Watchdog did not reset the device as expected."); +} + +utest::v1::status_t case_setup(const Case *const source, const size_t index_of_case) +{ + current_case.index = index_of_case; + return utest::v1::greentea_case_setup_handler(source, index_of_case); +} + +int testsuite_setup(const size_t number_of_cases) +{ + GREENTEA_SETUP(90, "watchdog_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; + } + + int num_args = sscanf(value, "%02x,%08lx", &(current_case.start_index), &(current_case.received_data)); + if (num_args == 0 || num_args == EOF) { + utest_printf("Invalid data received from host\n"); + return utest::v1::STATUS_ABORT; + } + + utest_printf("This test suite is composed of %i test cases. Starting at index %i.\n", number_of_cases, + current_case.start_index); + return current_case.start_index; +} + +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), +}; + +Specification specification((utest::v1::test_setup_handler_t) testsuite_setup, cases); + +int main() +{ + // Harness will start with a test case index provided by host script. + return !Harness::run(specification); +} diff --git a/TESTS/mbed_hal/watchdog_reset/watchdog_reset_tests.h b/TESTS/mbed_hal/watchdog_reset/watchdog_reset_tests.h new file mode 100644 index 00000000000..548340aeff4 --- /dev/null +++ b/TESTS/mbed_hal/watchdog_reset/watchdog_reset_tests.h @@ -0,0 +1,77 @@ +/* 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 hal_watchdog_tests + * @{ + */ + +#ifndef MBED_HAL_WATCHDOG_RESET_TESTS_H +#define MBED_HAL_WATCHDOG_RESET_TESTS_H + +#if DEVICE_WATCHDOG + +/** Test watchdog reset + * + * Given a device with a watchdog started, + * when a watchdog timeout expires, + * then the device is restarted. + */ +void test_simple_reset(); + +/** Test watchdog reset in sleep mode + * + * 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, + * then the device is not restarted. + * When the watchdog is started again and its timeout expires, + * then the device is restarted. + */ +void test_restart_reset(); + +/** Test watchdog kick + * + * Given a device with a watchdog 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, + * then the device is restarted. + */ +void test_kick_reset(); + +#endif + +#endif + +/** @}*/ + diff --git a/TESTS/mbed_hal/watchdog_timing/main.cpp b/TESTS/mbed_hal/watchdog_timing/main.cpp new file mode 100644 index 00000000000..8e9d3940c35 --- /dev/null +++ b/TESTS/mbed_hal/watchdog_timing/main.cpp @@ -0,0 +1,150 @@ +/* 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 + +#include "greentea-client/test_env.h" +#include "hal/watchdog_api.h" +#include "unity/unity.h" +#include "us_ticker_api.h" +#include "utest/utest.h" +#include "watchdog_timing_tests.h" + +#define MSG_VALUE_DUMMY "0" +#define CASE_DATA_INVALID 0xffffffffUL + +#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_HEARTBEAT "hb" + +using utest::v1::Case; +using utest::v1::Specification; +using utest::v1::Harness; + +struct testcase_data { + int index; + int start_index; + uint32_t received_data; +}; + +testcase_data current_case; + +template +void test_timing() +{ + // Phase 2. -- verify the test results. + // Verify the heartbeat time span sent by host is within given delta. + if (current_case.received_data != CASE_DATA_INVALID) { + TEST_ASSERT_UINT32_WITHIN(delta_ms, timeout_ms, current_case.received_data); + current_case.received_data = CASE_DATA_INVALID; + return; + } + + // Phase 1. -- run the test code. + // Send heartbeat messages to host until the watchdeg resets the device. + const ticker_data_t *const us_ticker = get_us_ticker_data(); + us_timestamp_t current_ts, next_ts, expected_reset_ts, divider, ts_increment; + char msg_value[12]; + + watchdog_config_t config = { timeout_ms }; + TEST_ASSERT_EQUAL(WATCHDOG_STATUS_OK, hal_watchdog_init(&config)); + next_ts = ticker_read_us(us_ticker); + expected_reset_ts = next_ts + 1000ULL * timeout_ms; + + divider = 0x2ULL; + while (1) { + current_ts = ticker_read_us(us_ticker); + if (current_ts < next_ts) { + continue; + } + + int str_len = snprintf(msg_value, sizeof msg_value, "%02x,%08lx", current_case.start_index + current_case.index, + (uint32_t) current_ts); + if (str_len != (sizeof msg_value) - 1) { + utest_printf("Failed to compose a value string to be sent to host."); + return; + } + greentea_send_kv(MSG_KEY_HEARTBEAT, msg_value); + + // The closer to expected reset, the smaller heartbeat time difference. + // This should reduce measurement error present for heartbeat with + // equal periods. + ts_increment = (1000ULL * timeout_ms / divider); + next_ts += ts_increment; + + if (current_ts <= expected_reset_ts) { + divider <<= 1; + } else if (divider > 0x2ULL) { + divider >>= 1; + } + } +} + +utest::v1::status_t case_setup(const Case *const source, const size_t index_of_case) +{ + current_case.index = index_of_case; + return utest::v1::greentea_case_setup_handler(source, index_of_case); +} + +int testsuite_setup(const size_t number_of_cases) +{ + GREENTEA_SETUP(90, "watchdog_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; + } + + int num_args = sscanf(value, "%02x,%08lx", &(current_case.start_index), &(current_case.received_data)); + if (num_args == 0 || num_args == EOF) { + utest_printf("Invalid data received from host\n"); + return utest::v1::STATUS_ABORT; + } + + utest_printf("This test suite is composed of %i test cases. Starting at index %i.\n", number_of_cases, + current_case.start_index); + return current_case.start_index; +} + +Case cases[] = { + Case("Timing, 200 ms", case_setup, test_timing<200UL, 55UL>), + Case("Timing, 500 ms", case_setup, test_timing<500UL, 130UL>), + Case("Timing, 1000 ms", case_setup, test_timing<1000UL, 255UL>), + Case("Timing, 3000 ms", case_setup, test_timing<3000UL, 380UL>), +}; + +Specification specification((utest::v1::test_setup_handler_t) testsuite_setup, cases); + +int main() +{ + // Harness will start with a test case index provided by host script. + return !Harness::run(specification); +} diff --git a/TESTS/mbed_hal/watchdog_timing/watchdog_timing_tests.h b/TESTS/mbed_hal/watchdog_timing/watchdog_timing_tests.h new file mode 100644 index 00000000000..daba3cb3cea --- /dev/null +++ b/TESTS/mbed_hal/watchdog_timing/watchdog_timing_tests.h @@ -0,0 +1,48 @@ +/* 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 hal_watchdog_tests + * @{ + */ + +#ifndef MBED_HAL_WATCHDOG_TIMING_TESTS_H +#define MBED_HAL_WATCHDOG_TIMING_TESTS_H + +#if DEVICE_WATCHDOG + +/** Test watchdog timing accuracy + * + * Phase 1. + * Given a watchdog timer started with a timeout value of X ms, + * when the time of X ms elapses, + * then the device is restarted by the watchdog. + * + * Phase 2. + * Given a device restarted by the watchdog timer, + * when the device receives time measurement from the host, + * then time measured by host equals X ms. + */ +template +void test_timing(); + +#endif + +#endif + +/** @}*/ + diff --git a/doxyfile_options b/doxyfile_options index b74069ed017..48e41448d45 100644 --- a/doxyfile_options +++ b/doxyfile_options @@ -2089,6 +2089,7 @@ PREDEFINED = DOXYGEN_ONLY \ DEVICE_PORTINOUT \ DEVICE_PORTOUT \ DEVICE_PWMOUT \ + DEVICE_RESET_REASON \ DEVICE_RTC \ DEVICE_TRNG \ DEVICE_SERIAL \ @@ -2100,6 +2101,7 @@ PREDEFINED = DOXYGEN_ONLY \ DEVICE_SPISLAVE \ DEVICE_QSPI \ DEVICE_STORAGE \ + DEVICE_WATCHDOG \ COMPONENT_SPE \ COMPONENT_SPM_MAILBOX \ "TFM_LVL=1" \ diff --git a/doxygen_options.json b/doxygen_options.json index 156f42c74b3..64f23888b8e 100644 --- a/doxygen_options.json +++ b/doxygen_options.json @@ -6,7 +6,7 @@ "SEARCH_INCLUDES": "YES", "INCLUDE_PATH": "", "INCLUDE_FILE_PATTERNS": "", - "PREDEFINED": "DOXYGEN_ONLY DEVICE_ANALOGIN DEVICE_ANALOGOUT DEVICE_CAN DEVICE_CRC DEVICE_ETHERNET DEVICE_EMAC DEVICE_FLASH DEVICE_I2C DEVICE_I2CSLAVE DEVICE_I2C_ASYNCH DEVICE_INTERRUPTIN DEVICE_ITM DEVICE_LPTICKER DEVICE_MPU DEVICE_PORTIN DEVICE_PORTINOUT DEVICE_PORTOUT DEVICE_PWMOUT DEVICE_RTC DEVICE_TRNG DEVICE_SERIAL DEVICE_SERIAL_ASYNCH DEVICE_SERIAL_FC DEVICE_SLEEP DEVICE_SPI DEVICE_SPI_ASYNCH DEVICE_SPISLAVE DEVICE_QSPI DEVICE_STORAGE COMPONENT_SPE COMPONENT_SPM_MAILBOX \"TFM_LVL=1\" \"MBED_DEPRECATED_SINCE(f, g)=\" \"MBED_ENABLE_IF_CALLBACK_COMPATIBLE(F, M)=\" \"MBED_DEPRECATED(s)=\" \"BLE_ROLE_OBSERVER=1\" \"BLE_ROLE_BROADCASTER=1\" \"BLE_ROLE_PERIPHERAL=1\" \"BLE_ROLE_CENTRAL=1\" \"BLE_FEATURE_GATT_CLIENT=1\" \"BLE_FEATURE_GATT_SERVER=1\" \"BLE_FEATURE_SECURITY=1\" \"BLE_FEATURE_SECURE_CONNECTIONS=1\" \"BLE_FEATURE_SIGNING=1\" \"BLE_FEATURE_PHY_MANAGEMENT=1\" \"BLE_FEATURE_WHITELIST=1\" \"BLE_FEATURE_PRIVACY=1\" \"BLE_FEATURE_PERIODIC_ADVERTISING=1\" \"BLE_FEATURE_EXTENDED_ADVERTISING=1\"", + "PREDEFINED": "DOXYGEN_ONLY DEVICE_ANALOGIN DEVICE_ANALOGOUT DEVICE_CAN DEVICE_CRC DEVICE_ETHERNET DEVICE_EMAC DEVICE_FLASH DEVICE_I2C DEVICE_I2CSLAVE DEVICE_I2C_ASYNCH DEVICE_INTERRUPTIN DEVICE_ITM DEVICE_LPTICKER DEVICE_MPU DEVICE_PORTIN DEVICE_PORTINOUT DEVICE_PORTOUT DEVICE_PWMOUT DEVICE_RTC DEVICE_TRNG DEVICE_SERIAL DEVICE_SERIAL_ASYNCH DEVICE_SERIAL_FC DEVICE_SLEEP DEVICE_SPI DEVICE_SPI_ASYNCH DEVICE_SPISLAVE DEVICE_QSPI DEVICE_STORAGE DEVICE_WATCHDOG COMPONENT_SPE COMPONENT_SPM_MAILBOX \"TFM_LVL=1\" \"MBED_DEPRECATED_SINCE(f, g)=\" \"MBED_ENABLE_IF_CALLBACK_COMPATIBLE(F, M)=\" \"MBED_DEPRECATED(s)=\" \"BLE_ROLE_OBSERVER=1\" \"BLE_ROLE_BROADCASTER=1\" \"BLE_ROLE_PERIPHERAL=1\" \"BLE_ROLE_CENTRAL=1\" \"BLE_FEATURE_GATT_CLIENT=1\" \"BLE_FEATURE_GATT_SERVER=1\" \"BLE_FEATURE_SECURITY=1\" \"BLE_FEATURE_SECURE_CONNECTIONS=1\" \"BLE_FEATURE_SIGNING=1\" \"BLE_FEATURE_PHY_MANAGEMENT=1\" \"BLE_FEATURE_WHITELIST=1\" \"BLE_FEATURE_PRIVACY=1\" \"BLE_FEATURE_PERIODIC_ADVERTISING=1\" \"BLE_FEATURE_EXTENDED_ADVERTISING=1\"", "EXPAND_AS_DEFINED": "", "SKIP_FUNCTION_MACROS": "NO", "STRIP_CODE_COMMENTS": "NO", diff --git a/drivers/ResetReason.cpp b/drivers/ResetReason.cpp new file mode 100644 index 00000000000..9614fb82627 --- /dev/null +++ b/drivers/ResetReason.cpp @@ -0,0 +1,47 @@ +/* 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. + */ + +#include "ResetReason.h" + +#ifdef DEVICE_RESET_REASON + +namespace mbed { + +reset_reason_t ResetReason::get() +{ + // Store the reason statically so it can be accessed after the first call to + // this function resets it. + const static reset_reason_t reason = hal_reset_reason_get(); + + // Call get raw to cache the reset reason before clearing the registers. + ResetReason::get_raw(); + + hal_reset_reason_clear(); + + return reason; +} + +uint32_t ResetReason::get_raw() +{ + const static uint32_t reason = hal_reset_reason_get_raw(); + + return reason; +} + +} // namespace mbed + +#endif // DEVICE_RESET_REASON diff --git a/drivers/ResetReason.h b/drivers/ResetReason.h new file mode 100644 index 00000000000..7bc74255b34 --- /dev/null +++ b/drivers/ResetReason.h @@ -0,0 +1,69 @@ +/* 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_RESET_REASON_H +#define MBED_RESET_REASON_H + +#ifdef DEVICE_RESET_REASON + +#include "reset_reason_api.h" + +namespace mbed { +/** \addtogroup drivers */ +/** ResetReason API. When the system is restarted, the reason for the restart is + * contained in the system registers at boot time in a platform specific manner, + * this API provides a generic method of fetching the reason for the restart. + * + * @ingroup drivers + */ +class ResetReason { +public: + /** Get the platform-independent reason code for the last system reset. + * + * Example: + * @code + * const reset_reason_t reason = ResetReason::get(); + * + * if (reason == RESET_REASON_WATCHDOG) { + * printf("Watchdog reset\n"); + * rollback(); + * } + * @endcode + */ + static reset_reason_t get(); + + /** Get the platform specific reason code for the last system reset. + * + * Platform specific reasons that are not covered by the reset_reason_t enum + * will cause the ResetReason::get() function to return + * RESET_REASON_PLATFORM. In order to get the actual reason the register + * value must be fetched directly using this function and interpreted in a + * platform specific manner. + * + * Example: + * @code + * if (ResetReason::get() == RESET_REASON_PLATFORM) { + * const uint32_t platform_reason = ResetReason::get_raw(); + * } + * @endcode + */ + static uint32_t get_raw(); +}; + +} // namespace mbed + +#endif // DEVICE_RESET_REASON +#endif // MBED_RESET_REASON_H diff --git a/drivers/Watchdog.cpp b/drivers/Watchdog.cpp new file mode 100644 index 00000000000..0214173e456 --- /dev/null +++ b/drivers/Watchdog.cpp @@ -0,0 +1,67 @@ +/* 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_status_t Watchdog::start(const uint32_t timeout) +{ + if (timeout == 0) { + return WATCHDOG_STATUS_INVALID_ARGUMENT; + } + + if (timeout > max_timeout()) { + return WATCHDOG_STATUS_INVALID_ARGUMENT; + } + + watchdog_config_t config; + config.timeout_ms = timeout; + + return hal_watchdog_init(&config); +} + + +void Watchdog::kick() +{ + hal_watchdog_kick(); +} + + +watchdog_status_t Watchdog::stop() +{ + return hal_watchdog_stop(); +} + + +uint32_t Watchdog::reload_value() const +{ + return hal_watchdog_get_reload_value(); +} + + +uint32_t Watchdog::max_timeout() +{ + const watchdog_features_t features = hal_watchdog_get_platform_features(); + + return features.max_timeout; +} + +} // namespace mbed + +#endif // DEVICE_WATCHDOG diff --git a/drivers/Watchdog.h b/drivers/Watchdog.h new file mode 100644 index 00000000000..24f029d449a --- /dev/null +++ b/drivers/Watchdog.h @@ -0,0 +1,108 @@ +/* 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_H +#define MBED_WATCHDOG_H + +#ifdef DEVICE_WATCHDOG + +#include "watchdog_api.h" + +#include + + +namespace mbed { +/** \addtogroup drivers */ +/** A system timer that will reset the system in the case of system failures or + * malfunctions. + * + * Example: + * @code + * + * Watchdog watchdog = Watchdog(); + * watchdog.start(2000); + * + * while (true) { + * watchdog.kick(); + * + * // Application code + * } + * @endcode + * @ingroup drivers + */ +class Watchdog { +public: + 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. + */ + watchdog_status_t start(const uint32_t timeout); + + + /** 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 + */ + void kick(); + + + /** Stops the watchdog timer + * + * Calling this function will attempt to disable any currently running + * watchdog timers if supported by the current platform. + * + * @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. + */ + watchdog_status_t 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 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(); +}; + +} // namespace mbed + +#endif // DEVICE_WATCHDOG +#endif // MBED_WATCHDOG_H diff --git a/hal/reset_reason_api.h b/hal/reset_reason_api.h new file mode 100644 index 00000000000..b43b2fbcabc --- /dev/null +++ b/hal/reset_reason_api.h @@ -0,0 +1,110 @@ +/** \addtogroup hal */ +/** @{*/ +/* 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. + */ +#ifndef MBED_RESET_REASON_API_H +#define MBED_RESET_REASON_API_H + +#if DEVICE_RESET_REASON + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \defgroup hal_reset_reason Reset Reason HAL API + * @{ + */ + +/** Definitions of different reset reasons + */ +typedef enum { + RESET_REASON_POWER_ON, /**< Set when power is initially applied to the board. The power-on-reset circuit causes a POWER_ON reset when this occurs */ + RESET_REASON_PIN_RESET, /**< Set when a reset is triggered by the hardware pin on the board */ + RESET_REASON_BROWN_OUT, /**< Triggered when the voltage drops below the low voltage detect (LVD) threshold the system will be held in a reset until the voltage rises above the threshold */ + RESET_REASON_SOFTWARE, /**< Set during software reset, typically triggered by writing the SYSRESETREQ bit in the Application Interrupt and Reset Control register */ + RESET_REASON_WATCHDOG, /**< Set when a running watchdog timer fails to be refreshed */ + RESET_REASON_LOCKUP, /**< Set when the core is locked because of an unrecoverable exception */ + RESET_REASON_WAKE_LOW_POWER, /**< Set when waking from deep sleep mode */ + RESET_REASON_ACCESS_ERROR, /**< Umbrella value that encompasses any access related reset */ + RESET_REASON_BOOT_ERROR, /**< Umbrella value that encompasses any boot related reset */ + RESET_REASON_MULTIPLE, /**< Set if multiple reset reasons are set within the board. Occurs when the reset reason registers aren't cleared between resets */ + RESET_REASON_PLATFORM, /**< Platform specific reset reason not captured in this enum */ + RESET_REASON_UNKNOWN /**< Unknown or unreadable reset reason **/ +} reset_reason_t; + +/** Fetch the reset reason for the last system reset + * + * This function must return the contents of the system reset reason registers + * cast to an appropriate platform independent reset reason. If multiple reset + * reasons are set this function should return RESET_REASON_MULTIPLE. If the + * reset reason does not match any existing platform independent value this + * function should return RESET_REASON_PLATFORM. If no reset reason can be + * determined this function should return RESET_REASON_UNKNOWN. + * + * This function is not idempotent, there is no guarantee that the system + * reset reason will not be cleared between calls to this function altering the + * return value between calls. + * + * Note: Some platforms contain reset reason registers that persist through + * system resets. If the registers haven't been cleared before calling this + * function multiple reasons may be set within the registers. If multiple reset + * reasons are detected this function will return RESET_REASON_MULTIPLE. + * + * @return enum containing the last reset reason for the board. + */ +reset_reason_t hal_reset_reason_get(void); + + +/** Fetch the raw platform specific reset reason register value + * + * This function must return the raw contents of the system reset reason + * registers cast to a uint32_t value. If the platform contains reset reasons + * that span multiple registers/addresses the value should be concatenated into + * the return type. + * + * This function is not idempotent, there is no guarantee that the system + * reset reason will not be cleared between calls to this function altering the + * return value between calls. + * + * @return value containing the reset reason register for the given platform. + * If the platform contains reset reasons across multiple registers they + * will be concatenated here. + */ +uint32_t hal_reset_reason_get_raw(void); + +/** Clear the reset reason from registers + * + * Reset the value of the reset status registers, the reset reason will persist + * between system resets on certain platforms so the registers should be cleared + * before the system resets. Failing to do so may make it difficult to determine + * the cause of any subsequent system resets. + */ +void hal_reset_reason_clear(void); + +/**@}*/ + +#ifdef __cplusplus +} +#endif + +#endif // DEVICE_RESET_REASON + +#endif // MBED_RESET_REASON_API_H + +/** @}*/ diff --git a/hal/watchdog_api.h b/hal/watchdog_api.h new file mode 100644 index 00000000000..e7fe9da6d71 --- /dev/null +++ b/hal/watchdog_api.h @@ -0,0 +1,149 @@ +/** \addtogroup hal */ +/** @{*/ +/* 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. + */ + +#ifndef MBED_WATCHDOG_API_H +#define MBED_WATCHDOG_API_H + +#if DEVICE_WATCHDOG + +#include +#include + +/** + * \defgroup hal_watchdog Watchdog HAL API + * @{ + */ + +/** \file watchdog_api.h + * + * This module provides platform independent access to the system watchdog timer + * which is an embedded peripheral that will reset the system in the case of + * system failures or malfunctions. + * + * The watchdog timer initialises a system timer with a time period specified in + * the configuration. This timer counts down and triggers a system reset when it + * wraps. To prevent the system reset the timer must be continually + * kicked/refreshed by calling hal_watchdog_kick which will reset the countdown + * to the user specified reset value. + * + * The Watchdog timer must continue to operate in low power modes. It + * must count down and trigger a reset from within both sleep and deep sleep + * modes unless the chip is woken to refresh the timer. + */ + +typedef struct { + /** + * Refresh value for the watchdog in milliseconds. The maximum value of this + * setting is platform dependent, to find the maximum value for the current + * platform call hal_watchdog_get_features() and check the timeout value + * member. The minimum valid value for this setting is 1, attempting to + * initialise the watchdog with a timeout of 0ms will return + * WATCHDOG_STATUS_INVALID_ARGUMENT. + */ + uint32_t timeout_ms; +} watchdog_config_t; + + +typedef struct { + /** + * Maximum timeout value for the watchdog in milliseconds. + */ + uint32_t max_timeout; + /** + * Watchdog configuration can be updated after the watchdog has been started + */ + bool update_config; + /** + * Watchdog can be stopped after it is started without a reset + */ + bool disable_watchdog; +} watchdog_features_t; + + +typedef enum { + WATCHDOG_STATUS_OK, + WATCHDOG_STATUS_NOT_SUPPORTED, + WATCHDOG_STATUS_INVALID_ARGUMENT +} watchdog_status_t; + +#ifdef __cplusplus +extern "C" { +#endif + +/** Initialise and start a watchdog timer with the given configuration. + * + * If the watchdog timer is configured and started successfully this + * function will return WATCHDOG_STATUS_OK. + * + * If the timeout specified is outside the range supported by the platform + * it will return WATCHDOG_STATUS_INVALID_ARGUMENT. + * + * @param[in] config Configuration settings for the watchdog timer + * + * @return WATCHDOG_STATUS_OK if the watchdog is configured correctly and + * has started. Otherwise a status indicating the fault. + */ +watchdog_status_t hal_watchdog_init(const watchdog_config_t *config); + +/** Refreshes the watchdog timer. + * + * This function should be called periodically before the watchdog times out. + * Otherwise, the system is reset. + * + * If a watchdog is not currently running this function does nothing + */ +void hal_watchdog_kick(void); + +/** Stops the watchdog timer + * + * Calling this function will attempt to disable any currently running watchdog + * timers if supported by the current platform + * + * @return Returns WATCHDOG_STATUS_OK if the watchdog timer was succesfully + * stopped, or if the timer was never started. Returns + * WATCHDOG_STATUS_NOT_SUPPORTED if the watchdog cannot be disabled on + * the current platform. + */ +watchdog_status_t hal_watchdog_stop(void); + +/** Get the watchdog timer refresh value + * + * This function returns the configured refresh timeout of the watchdog timer. + * + * @return Reload value for the watchdog timer in milliseconds. + */ +uint32_t hal_watchdog_get_reload_value(void); + +/** Get information on the current platforms supported watchdog functionality + * + * @return watchdog_feature_t indicating supported watchdog features on the + * current platform + */ +watchdog_features_t hal_watchdog_get_platform_features(void); + +/**@}*/ + +#ifdef __cplusplus +} +#endif + +#endif // DEVICE_WATCHDOG + +#endif // MBED_WATCHDOG_API_H + +/** @}*/ diff --git a/mbed.h b/mbed.h index b66b6b77e12..3272ca56d6a 100644 --- a/mbed.h +++ b/mbed.h @@ -76,6 +76,8 @@ #include "drivers/QSPI.h" // mbed Internal components +#include "drivers/ResetReason.h" +#include "drivers/Watchdog.h" #include "drivers/Timer.h" #include "drivers/Ticker.h" #include "drivers/Timeout.h" diff --git a/targets/TARGET_Freescale/TARGET_MCUXpresso_MCUS/TARGET_MCU_K64F/reset_reason.c b/targets/TARGET_Freescale/TARGET_MCUXpresso_MCUS/TARGET_MCU_K64F/reset_reason.c new file mode 100644 index 00000000000..6a646211d81 --- /dev/null +++ b/targets/TARGET_Freescale/TARGET_MCUXpresso_MCUS/TARGET_MCU_K64F/reset_reason.c @@ -0,0 +1,108 @@ +/* mbed Microcontroller Library + * Copyright (c) 2006-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 "reset_reason_api.h" + +#if DEVICE_RESET_REASON + +#include "fsl_rcm.h" + +reset_reason_t hal_reset_reason_get(void) +{ + const uint32_t reset_sources = + RCM_GetPreviousResetSources(RCM) & kRCM_SourceAll; + + // Low power mode is exited via the RESET pin. Therefore, when this reset is + // triggered both the PIN and WAKEUP will have bits set, so check this flag + // first. +#if (defined(FSL_FEATURE_RCM_HAS_WAKEUP) && FSL_FEATURE_RCM_HAS_WAKEUP) + if ((reset_sources & kRCM_SourceWakeup) != 0) { + return RESET_REASON_PLATFORM; + } +#endif + + // Check POR flag first. During a POR reset there will be two reset sources + // set: POR and LVD. As during the power on phase the low voltage detector + // circuit will detect a low voltage while the voltage is initially ramping + // up and set the BROWN_OUT flag. Therefore, if LVD is set we must check the + // POR to determine what the actual cause was. + if ((reset_sources & kRCM_SourcePor) != 0) { + return RESET_REASON_POWER_ON; + } + + if ((reset_sources & kRCM_SourceLvd) != 0) { + return RESET_REASON_BROWN_OUT; + } + + if ((reset_sources & kRCM_SourceWdog) != 0) { + return RESET_REASON_WATCHDOG; + } + + if ((reset_sources & kRCM_SourcePin) != 0) { + return RESET_REASON_PIN_RESET; + } + + if ((reset_sources & kRCM_SourceSw) != 0) { + return RESET_REASON_SOFTWARE; + } + +#if (defined(FSL_FEATURE_RCM_HAS_LOC) && FSL_FEATURE_RCM_HAS_LOC) + if ((reset_sources & kRCM_SourceLoc) != 0) { + return RESET_REASON_PLATFORM; + } +#endif + +#if (defined(FSL_FEATURE_RCM_HAS_LOL) && FSL_FEATURE_RCM_HAS_LOL) + if ((reset_sources & kRCM_SourceLol) != 0) { + return RESET_REASON_PLATFORM; + } +#endif + +#if (defined(FSL_FEATURE_RCM_HAS_JTAG) && FSL_FEATURE_RCM_HAS_JTAG) + if ((reset_sources & kRCM_SourceJtag) != 0) { + return RESET_REASON_PLATFORM; + } +#endif + +#if (defined(FSL_FEATURE_RCM_HAS_MDM_AP) && FSL_FEATURE_RCM_HAS_MDM_AP) + if ((reset_sources & kRCM_SourceMdmap) != 0) { + return RESET_REASON_PLATFORM; + } +#endif + +#if (defined(FSL_FEATURE_RCM_HAS_EZPORT) && FSL_FEATURE_RCM_HAS_EZPORT) + if ((reset_sources & kRCM_SourceEzpt) != 0) { + return RESET_REASON_PLATFORM; + } +#endif + + return RESET_REASON_UNKNOWN; +} + + +uint32_t hal_reset_reason_get_raw(void) +{ + return (RCM_GetPreviousResetSources(RCM) & kRCM_SourceAll); +} + + +void hal_reset_reason_clear(void) +{ +#if (defined(FSL_FEATURE_RCM_HAS_SSRS) && FSL_FEATURE_RCM_HAS_SSRS) + RCM_ClearStickyResetSources(RCM, kRCM_SourceAll); +#endif +} + +#endif // DEVICE_RESET_REASON diff --git a/targets/TARGET_Freescale/TARGET_MCUXpresso_MCUS/TARGET_MCU_K64F/watchdog_api.c b/targets/TARGET_Freescale/TARGET_MCUXpresso_MCUS/TARGET_MCU_K64F/watchdog_api.c new file mode 100644 index 00000000000..1b2f72feb45 --- /dev/null +++ b/targets/TARGET_Freescale/TARGET_MCUXpresso_MCUS/TARGET_MCU_K64F/watchdog_api.c @@ -0,0 +1,130 @@ +/* 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 + +#include "reset_reason_api.h" +#include "fsl_wdog.h" +#include "fsl_clock.h" +#include "platform/mbed_wait_api.h" + +// Platform specific watchdog definitions +#define LPO_CLOCK_FREQUENCY 1000 +#define MAX_PRESCALER 8 +#define MAX_TIMEOUT 0xFFFFFFFFUL +#define WCT_IN_BUS_CYCLES 256U // Watchdog configuration time (WCT) in bus clock cycles. + +// Number of decrements in the timeout register per millisecond +#define TICKS_PER_MS ((LPO_CLOCK_FREQUENCY) / 1000) + +// Maximum timeout that can be specified in milliseconds +#define MAX_TIMEOUT_MS_UINT64 (1ULL * ((MAX_TIMEOUT) / (TICKS_PER_MS)) * (MAX_PRESCALER)) +#if (MAX_TIMEOUT_MS_UINT64 > UINT32_MAX) +#define MAX_TIMEOUT_MS UINT32_MAX +#else +#define MAX_TIMEOUT_MS (MAX_TIMEOUT_MS_UINT64 & 0xFFFFFFFFUL) +#endif + +// Maximum supported watchdog timeout for given prescaler value +#define CALCULATE_MAX_TIMEOUT_MS_UINT64(scale) \ + (1ULL * ((MAX_TIMEOUT) / (TICKS_PER_MS)) * (scale)) + + +static uint32_t calculate_prescaler_value(const uint32_t timeout_ms) +{ + if (timeout_ms > MAX_TIMEOUT_MS) { + return 0; + } + + for (uint32_t scale = 1; scale <= MAX_PRESCALER; ++scale) { + if (timeout_ms <= CALCULATE_MAX_TIMEOUT_MS_UINT64(scale)) { + return scale; + } + } + + return 0; +} + +// Wait until watchdog configuration time window closes. +static inline void wait_WCT(void) { + uint32_t WCT_us = (WCT_IN_BUS_CYCLES) * 1000000UL / CLOCK_GetBusClkFreq(); + wait_us(WCT_us); +} + +watchdog_status_t hal_watchdog_init(const watchdog_config_t *config) +{ + wdog_config_t cfg; + cfg.enableWdog = true; + cfg.clockSource = kWDOG_LpoClockSource; + cfg.windowValue = 0; + cfg.enableUpdate = true; + cfg.enableInterrupt = false; + cfg.enableWindowMode = false; + cfg.workMode.enableWait = true; + cfg.workMode.enableStop = false; + cfg.workMode.enableDebug = false; + + const uint32_t prescaler = calculate_prescaler_value(config->timeout_ms); + + if (prescaler == 0) { + return WATCHDOG_STATUS_INVALID_ARGUMENT; + } + + cfg.prescaler = (wdog_clock_prescaler_t)(prescaler - 1); + cfg.timeoutValue = (TICKS_PER_MS * config->timeout_ms) / prescaler; + + WDOG_Init(WDOG, &cfg); + wait_WCT(); // Updates in the write-once registers take effect only after the WCT window closes. + + return WATCHDOG_STATUS_OK; +} + +void hal_watchdog_kick(void) +{ + WDOG_Refresh(WDOG); +} + +watchdog_status_t hal_watchdog_stop(void) +{ + WDOG_Deinit(WDOG); + wait_WCT(); // Updates in the write-once registers take effect only after the WCT window closes. + + return WATCHDOG_STATUS_OK; +} + +uint32_t hal_watchdog_get_reload_value(void) +{ + const uint32_t timeout = + (((WDOG->TOVALH & 0xFFFFU) << 16U) | (WDOG->TOVALL & 0xFFFFU)); + + const uint32_t prescaler = WDOG_PRESC_PRESCVAL(WDOG->PRESC); + + return ((timeout / TICKS_PER_MS) * (prescaler + 1)); +} + + +watchdog_features_t hal_watchdog_get_platform_features(void) +{ + watchdog_features_t features; + features.max_timeout = MAX_TIMEOUT_MS; + features.update_config = true; + features.disable_watchdog = true; + + return features; +} + +#endif // DEVICE_WATCHDOG diff --git a/targets/TARGET_NUVOTON/TARGET_M451/reset_reason.c b/targets/TARGET_NUVOTON/TARGET_M451/reset_reason.c new file mode 100644 index 00000000000..91c29b6512a --- /dev/null +++ b/targets/TARGET_NUVOTON/TARGET_M451/reset_reason.c @@ -0,0 +1,106 @@ +/* mbed Microcontroller Library + * Copyright (c) 2017-2018 Nuvoton + * + * 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 "reset_reason_api.h" + +#if DEVICE_RESET_REASON + +#include "cmsis.h" + +/* All reset source flags */ +#define SYS_RSTSTS_ALLRF_Msk \ + (SYS_RSTSTS_PORF_Msk | \ + SYS_RSTSTS_PINRF_Msk | \ + SYS_RSTSTS_WDTRF_Msk | \ + SYS_RSTSTS_LVRF_Msk | \ + SYS_RSTSTS_BODRF_Msk | \ + SYS_RSTSTS_SYSRF_Msk | \ + SYS_RSTSTS_CPURF_Msk | \ + SYS_RSTSTS_CPULKRF_Msk) + +reset_reason_t hal_reset_reason_get(void) +{ + uint32_t reset_reason_raw = hal_reset_reason_get_raw(); + reset_reason_t reset_reason_cast; + uint32_t reset_reason_count = 0; + + if (SYS_IS_POR_RST()) { + reset_reason_cast = RESET_REASON_POWER_ON; + reset_reason_count ++; + } + + if (SYS_IS_RSTPIN_RST()) { + reset_reason_cast = RESET_REASON_PIN_RESET; + reset_reason_count ++; + } + + if (SYS_IS_WDT_RST()) { + reset_reason_cast = RESET_REASON_WATCHDOG; + reset_reason_count ++; + } + + if (SYS_IS_LVR_RST()) { + reset_reason_cast = RESET_REASON_PLATFORM; + reset_reason_count ++; + } + + if (SYS_IS_BOD_RST()) { + reset_reason_cast = RESET_REASON_BROWN_OUT; + reset_reason_count ++; + } + + /* This MCU supports MKROM and we need to take care of bootloader/SYSRESETREQ flow. */ + if (SYS_IS_SYSTEM_RST()) { + if (reset_reason_raw & (SYS_RSTSTS_ALLRF_Msk & ~SYS_RSTSTS_SYSRF_Msk)) { + /* We may boot from bootloader which would reset to go to LDROM/APROM at its end + * through writing 1 to SYSRESETREQ bit. If there are other reset reasons, we think + * it is just the case and ignore the reset reason with SYSRESETREQ. */ + } else { + reset_reason_cast = RESET_REASON_SOFTWARE; + reset_reason_count ++; + } + } + + if (SYS_IS_CPU_RST()) { + reset_reason_cast = RESET_REASON_PLATFORM; + reset_reason_count ++; + } + + if (reset_reason_raw & SYS_RSTSTS_CPULKRF_Msk) { + reset_reason_cast = RESET_REASON_LOCKUP; + reset_reason_count ++; + } + + if (reset_reason_count == 0) { + reset_reason_cast = RESET_REASON_UNKNOWN; + } else if (reset_reason_count >= 2) { + reset_reason_cast = RESET_REASON_MULTIPLE; + } + + return reset_reason_cast; +} + +uint32_t hal_reset_reason_get_raw(void) +{ + return (SYS->RSTSTS & SYS_RSTSTS_ALLRF_Msk); +} + +void hal_reset_reason_clear(void) +{ + SYS_CLEAR_RST_SOURCE(SYS->RSTSTS); +} + +#endif diff --git a/targets/TARGET_NUVOTON/TARGET_M451/watchdog_api.c b/targets/TARGET_NUVOTON/TARGET_M451/watchdog_api.c new file mode 100644 index 00000000000..9bc73d2266e --- /dev/null +++ b/targets/TARGET_NUVOTON/TARGET_M451/watchdog_api.c @@ -0,0 +1,206 @@ +/* mbed Microcontroller Library + * Copyright (c) 2017-2018 Nuvoton + * 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 "watchdog_api.h" + +#if DEVICE_WATCHDOG + +#include "cmsis.h" + +/* Micro seconds per second */ +#define NU_US_PER_SEC 1000000 + +/* Watchdog clock per second */ +#define NU_WDTCLK_PER_SEC (__LIRC) + +/* Convert watchdog clock to nearest ms */ +#define NU_WDTCLK2MS(WDTCLK) (((WDTCLK) * 1000 + ((NU_WDTCLK_PER_SEC) / 2)) / (NU_WDTCLK_PER_SEC)) + +/* Convert ms to nearest watchdog clock */ +#define NU_MS2WDTCLK(MS) (((MS) * (NU_WDTCLK_PER_SEC) + 500) / 1000) + +/* List of hardware-supported watchdog timeout in clocks */ +#define NU_WDT_16CLK 16 +#define NU_WDT_64CLK 64 +#define NU_WDT_256CLK 256 +#define NU_WDT_1024CLK 1024 +#define NU_WDT_4096CLK 4096 +#define NU_WDT_16384CLK 16384 +#define NU_WDT_65536CLK 65536 +#define NU_WDT_262144CLK 262144 + +/* Watchdog reset delay */ +#define NU_WDT_RESET_DELAY_RSTDSEL WDT_RESET_DELAY_3CLK + +/* Support watchdog timeout values beyond H/W + * + * Watchdog Timer H/W just supports timeout values of 2^4, 2^6, ..., 2^18 clocks. + * To extend the support range to 1 and UINT32_MAX, we cascade multiple small timeouts to + * reach one large timeout specified in hal_watchdog_init. + */ + +/* Track if WDT H/W has been initialized */ +static bool wdt_hw_inited = 0; +/* Hold initially-configured timeout in hal_watchdog_init */ +static uint32_t wdt_timeout_reload_ms = 0; +/* Track remaining timeout for cascading */ +static uint32_t wdt_timeout_rmn_clk = 0; + +static void watchdog_setup_cascade_timeout(void); +static void WDT_IRQHandler(void); + +watchdog_status_t hal_watchdog_init(const watchdog_config_t *config) +{ + /* Check validity of arguments */ + if (! config || ! config->timeout_ms) { + return WATCHDOG_STATUS_INVALID_ARGUMENT; + } + + wdt_timeout_reload_ms = config->timeout_ms; + wdt_timeout_rmn_clk = NU_MS2WDTCLK(wdt_timeout_reload_ms); + + if (! wdt_hw_inited) { + wdt_hw_inited = 1; + + /* Enable IP module clock */ + CLK_EnableModuleClock(WDT_MODULE); + + /* Select IP clock source */ + CLK_SetModuleClock(WDT_MODULE, CLK_CLKSEL1_WDTSEL_LIRC, 0); + + /* Set up IP interrupt */ + NVIC_SetVector(WDT_IRQn, (uint32_t) WDT_IRQHandler); + NVIC_EnableIRQ(WDT_IRQn); + } + + watchdog_setup_cascade_timeout(); + + return WATCHDOG_STATUS_OK; +} + +void hal_watchdog_kick(void) +{ + wdt_timeout_rmn_clk = NU_MS2WDTCLK(wdt_timeout_reload_ms); + watchdog_setup_cascade_timeout(); +} + +watchdog_status_t hal_watchdog_stop(void) +{ + SYS_UnlockReg(); + + /* Clear all flags & Disable interrupt & Disable WDT */ + WDT->CTL = (WDT->CTL & ~(WDT_CTL_WDTEN_Msk | WDT_CTL_INTEN_Msk)) | (WDT_CTL_WKF_Msk | WDT_CTL_IF_Msk | WDT_CTL_RSTF_Msk); + + SYS_LockReg(); + + return WATCHDOG_STATUS_OK; +} + +uint32_t hal_watchdog_get_reload_value(void) +{ + return wdt_timeout_reload_ms; +} + +watchdog_features_t hal_watchdog_get_platform_features(void) +{ + watchdog_features_t wdt_feat; + + /* We can support timeout values between 1 and UINT32_MAX by cascading. */ + wdt_feat.max_timeout = UINT32_MAX; + /* Support re-configuring watchdog timer */ + wdt_feat.update_config = 1; + /* Support stopping watchdog timer */ + wdt_feat.disable_watchdog = 1; + + return wdt_feat; +} + +static void watchdog_setup_cascade_timeout(void) +{ + uint32_t wdt_timeout_clk_toutsel; + + if (wdt_timeout_rmn_clk >= NU_WDT_262144CLK) { + wdt_timeout_rmn_clk -= NU_WDT_262144CLK; + wdt_timeout_clk_toutsel = WDT_TIMEOUT_2POW18; + } else if (wdt_timeout_rmn_clk >= NU_WDT_65536CLK) { + wdt_timeout_rmn_clk -= NU_WDT_65536CLK; + wdt_timeout_clk_toutsel = WDT_TIMEOUT_2POW16; + } else if (wdt_timeout_rmn_clk >= NU_WDT_16384CLK) { + wdt_timeout_rmn_clk -= NU_WDT_16384CLK; + wdt_timeout_clk_toutsel = WDT_TIMEOUT_2POW14; + } else if (wdt_timeout_rmn_clk >= NU_WDT_4096CLK) { + wdt_timeout_rmn_clk -= NU_WDT_4096CLK; + wdt_timeout_clk_toutsel = WDT_TIMEOUT_2POW12; + } else if (wdt_timeout_rmn_clk >= NU_WDT_1024CLK) { + wdt_timeout_rmn_clk -= NU_WDT_1024CLK; + wdt_timeout_clk_toutsel = WDT_TIMEOUT_2POW10; + } else if (wdt_timeout_rmn_clk >= NU_WDT_256CLK) { + wdt_timeout_rmn_clk -= NU_WDT_256CLK; + wdt_timeout_clk_toutsel = WDT_TIMEOUT_2POW8; + } else if (wdt_timeout_rmn_clk >= NU_WDT_64CLK) { + wdt_timeout_rmn_clk -= NU_WDT_64CLK; + wdt_timeout_clk_toutsel = WDT_TIMEOUT_2POW6; + } else if (wdt_timeout_rmn_clk >= NU_WDT_16CLK) { + wdt_timeout_rmn_clk -= NU_WDT_16CLK; + wdt_timeout_clk_toutsel = WDT_TIMEOUT_2POW4; + } else if (wdt_timeout_rmn_clk) { + wdt_timeout_rmn_clk = 0; + wdt_timeout_clk_toutsel = WDT_TIMEOUT_2POW4; + } else { + /* WDT has timed-out and will restart system soon. We just disable interrupt to escape + * getting stuck in WDT ISR. */ + SYS_UnlockReg(); + + /* Clear all flags & Disable interrupt */ + WDT->CTL = (WDT->CTL & ~WDT_CTL_INTEN_Msk) | (WDT_CTL_WKF_Msk | WDT_CTL_IF_Msk | WDT_CTL_RSTF_Msk); + + SYS_LockReg(); + return; + } + + SYS_UnlockReg(); + + /* Configure reset delay on timeout */ + WDT->ALTCTL = NU_WDT_RESET_DELAY_RSTDSEL; + + /* Configure another piece of cascaded WDT timeout */ + WDT->CTL = wdt_timeout_clk_toutsel | // Timeout interval + WDT_CTL_WDTEN_Msk | // Enable watchdog timer + WDT_CTL_INTEN_Msk | // Enable interrupt + WDT_CTL_WKF_Msk | // Clear wake-up flag + WDT_CTL_WKEN_Msk | // Enable wake-up on timeout + WDT_CTL_IF_Msk | // Clear interrupt flag + WDT_CTL_RSTF_Msk | // Clear reset flag + (wdt_timeout_rmn_clk ? 0 : WDT_CTL_RSTEN_Msk) | // Enable reset on last cascaded timeout + WDT_CTL_RSTCNT_Msk; // Reset watchdog timer + + SYS_LockReg(); +} + +void WDT_IRQHandler(void) +{ + /* Check WDT interrupt flag */ + if (WDT_GET_TIMEOUT_INT_FLAG()) { + /* Continue another piece of cascaded WDT timeout */ + watchdog_setup_cascade_timeout(); + } else { + /* Clear all flags */ + WDT->CTL |= (WDT_CTL_WKF_Msk | WDT_CTL_IF_Msk | WDT_CTL_RSTF_Msk); + } +} + +#endif diff --git a/targets/TARGET_NUVOTON/TARGET_M480/reset_reason.c b/targets/TARGET_NUVOTON/TARGET_M480/reset_reason.c new file mode 100644 index 00000000000..91c29b6512a --- /dev/null +++ b/targets/TARGET_NUVOTON/TARGET_M480/reset_reason.c @@ -0,0 +1,106 @@ +/* mbed Microcontroller Library + * Copyright (c) 2017-2018 Nuvoton + * + * 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 "reset_reason_api.h" + +#if DEVICE_RESET_REASON + +#include "cmsis.h" + +/* All reset source flags */ +#define SYS_RSTSTS_ALLRF_Msk \ + (SYS_RSTSTS_PORF_Msk | \ + SYS_RSTSTS_PINRF_Msk | \ + SYS_RSTSTS_WDTRF_Msk | \ + SYS_RSTSTS_LVRF_Msk | \ + SYS_RSTSTS_BODRF_Msk | \ + SYS_RSTSTS_SYSRF_Msk | \ + SYS_RSTSTS_CPURF_Msk | \ + SYS_RSTSTS_CPULKRF_Msk) + +reset_reason_t hal_reset_reason_get(void) +{ + uint32_t reset_reason_raw = hal_reset_reason_get_raw(); + reset_reason_t reset_reason_cast; + uint32_t reset_reason_count = 0; + + if (SYS_IS_POR_RST()) { + reset_reason_cast = RESET_REASON_POWER_ON; + reset_reason_count ++; + } + + if (SYS_IS_RSTPIN_RST()) { + reset_reason_cast = RESET_REASON_PIN_RESET; + reset_reason_count ++; + } + + if (SYS_IS_WDT_RST()) { + reset_reason_cast = RESET_REASON_WATCHDOG; + reset_reason_count ++; + } + + if (SYS_IS_LVR_RST()) { + reset_reason_cast = RESET_REASON_PLATFORM; + reset_reason_count ++; + } + + if (SYS_IS_BOD_RST()) { + reset_reason_cast = RESET_REASON_BROWN_OUT; + reset_reason_count ++; + } + + /* This MCU supports MKROM and we need to take care of bootloader/SYSRESETREQ flow. */ + if (SYS_IS_SYSTEM_RST()) { + if (reset_reason_raw & (SYS_RSTSTS_ALLRF_Msk & ~SYS_RSTSTS_SYSRF_Msk)) { + /* We may boot from bootloader which would reset to go to LDROM/APROM at its end + * through writing 1 to SYSRESETREQ bit. If there are other reset reasons, we think + * it is just the case and ignore the reset reason with SYSRESETREQ. */ + } else { + reset_reason_cast = RESET_REASON_SOFTWARE; + reset_reason_count ++; + } + } + + if (SYS_IS_CPU_RST()) { + reset_reason_cast = RESET_REASON_PLATFORM; + reset_reason_count ++; + } + + if (reset_reason_raw & SYS_RSTSTS_CPULKRF_Msk) { + reset_reason_cast = RESET_REASON_LOCKUP; + reset_reason_count ++; + } + + if (reset_reason_count == 0) { + reset_reason_cast = RESET_REASON_UNKNOWN; + } else if (reset_reason_count >= 2) { + reset_reason_cast = RESET_REASON_MULTIPLE; + } + + return reset_reason_cast; +} + +uint32_t hal_reset_reason_get_raw(void) +{ + return (SYS->RSTSTS & SYS_RSTSTS_ALLRF_Msk); +} + +void hal_reset_reason_clear(void) +{ + SYS_CLEAR_RST_SOURCE(SYS->RSTSTS); +} + +#endif diff --git a/targets/TARGET_NUVOTON/TARGET_M480/watchdog_api.c b/targets/TARGET_NUVOTON/TARGET_M480/watchdog_api.c new file mode 100644 index 00000000000..96150ae7e7f --- /dev/null +++ b/targets/TARGET_NUVOTON/TARGET_M480/watchdog_api.c @@ -0,0 +1,205 @@ +/* mbed Microcontroller Library + * Copyright (c) 2017-2018 Nuvoton + * + * 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 + +#include "cmsis.h" + +/* Micro seconds per second */ +#define NU_US_PER_SEC 1000000 + +/* Watchdog clock per second */ +#define NU_WDTCLK_PER_SEC (__LIRC) + +/* Convert watchdog clock to nearest ms */ +#define NU_WDTCLK2MS(WDTCLK) (((WDTCLK) * 1000 + ((NU_WDTCLK_PER_SEC) / 2)) / (NU_WDTCLK_PER_SEC)) + +/* Convert ms to nearest watchdog clock */ +#define NU_MS2WDTCLK(MS) (((MS) * (NU_WDTCLK_PER_SEC) + 500) / 1000) + +/* List of hardware-supported watchdog timeout in clocks */ +#define NU_WDT_16CLK 16 +#define NU_WDT_64CLK 64 +#define NU_WDT_256CLK 256 +#define NU_WDT_1024CLK 1024 +#define NU_WDT_4096CLK 4096 +#define NU_WDT_16384CLK 16384 +#define NU_WDT_65536CLK 65536 +#define NU_WDT_262144CLK 262144 + +/* Watchdog reset delay */ +#define NU_WDT_RESET_DELAY_RSTDSEL WDT_RESET_DELAY_3CLK + +/* Support watchdog timeout values beyond H/W + * + * Watchdog Timer H/W just supports timeout values of 2^4, 2^6, ..., 2^18 clocks. + * To extend the support range to 1 and UINT32_MAX, we cascade multiple small timeouts to + * reach one large timeout specified in hal_watchdog_init. + */ + +/* Track if WDT H/W has been initialized */ +static bool wdt_hw_inited = 0; +/* Hold initially-configured timeout in hal_watchdog_init */ +static uint32_t wdt_timeout_reload_ms = 0; +/* Track remaining timeout for cascading */ +static uint32_t wdt_timeout_rmn_clk = 0; + +static void watchdog_setup_cascade_timeout(void); +static void WDT_IRQHandler(void); + +watchdog_status_t hal_watchdog_init(const watchdog_config_t *config) +{ + /* Check validity of arguments */ + if (! config || ! config->timeout_ms) { + return WATCHDOG_STATUS_INVALID_ARGUMENT; + } + + wdt_timeout_reload_ms = config->timeout_ms; + wdt_timeout_rmn_clk = NU_MS2WDTCLK(wdt_timeout_reload_ms); + + if (! wdt_hw_inited) { + wdt_hw_inited = 1; + + /* Enable IP module clock */ + CLK_EnableModuleClock(WDT_MODULE); + + /* Select IP clock source */ + CLK_SetModuleClock(WDT_MODULE, CLK_CLKSEL1_WDTSEL_LIRC, 0); + + /* Set up IP interrupt */ + NVIC_SetVector(WDT_IRQn, (uint32_t) WDT_IRQHandler); + NVIC_EnableIRQ(WDT_IRQn); + } + + watchdog_setup_cascade_timeout(); + + return WATCHDOG_STATUS_OK; +} + +void hal_watchdog_kick(void) +{ + wdt_timeout_rmn_clk = NU_MS2WDTCLK(wdt_timeout_reload_ms); + watchdog_setup_cascade_timeout(); +} + +watchdog_status_t hal_watchdog_stop(void) +{ + SYS_UnlockReg(); + + /* Clear all flags & Disable interrupt & Disable WDT */ + WDT->CTL = (WDT->CTL & ~(WDT_CTL_WDTEN_Msk | WDT_CTL_INTEN_Msk)) | (WDT_CTL_WKF_Msk | WDT_CTL_IF_Msk | WDT_CTL_RSTF_Msk); + + SYS_LockReg(); + + return WATCHDOG_STATUS_OK; +} + +uint32_t hal_watchdog_get_reload_value(void) +{ + return wdt_timeout_reload_ms; +} + +watchdog_features_t hal_watchdog_get_platform_features(void) +{ + watchdog_features_t wdt_feat; + + /* We can support timeout values between 1 and UINT32_MAX by cascading. */ + wdt_feat.max_timeout = UINT32_MAX; + /* Support re-configuring watchdog timer */ + wdt_feat.update_config = 1; + /* Support stopping watchdog timer */ + wdt_feat.disable_watchdog = 1; + + return wdt_feat; +} + +static void watchdog_setup_cascade_timeout(void) +{ + uint32_t wdt_timeout_clk_toutsel; + + if (wdt_timeout_rmn_clk >= NU_WDT_262144CLK) { + wdt_timeout_rmn_clk -= NU_WDT_262144CLK; + wdt_timeout_clk_toutsel = WDT_TIMEOUT_2POW18; + } else if (wdt_timeout_rmn_clk >= NU_WDT_65536CLK) { + wdt_timeout_rmn_clk -= NU_WDT_65536CLK; + wdt_timeout_clk_toutsel = WDT_TIMEOUT_2POW16; + } else if (wdt_timeout_rmn_clk >= NU_WDT_16384CLK) { + wdt_timeout_rmn_clk -= NU_WDT_16384CLK; + wdt_timeout_clk_toutsel = WDT_TIMEOUT_2POW14; + } else if (wdt_timeout_rmn_clk >= NU_WDT_4096CLK) { + wdt_timeout_rmn_clk -= NU_WDT_4096CLK; + wdt_timeout_clk_toutsel = WDT_TIMEOUT_2POW12; + } else if (wdt_timeout_rmn_clk >= NU_WDT_1024CLK) { + wdt_timeout_rmn_clk -= NU_WDT_1024CLK; + wdt_timeout_clk_toutsel = WDT_TIMEOUT_2POW10; + } else if (wdt_timeout_rmn_clk >= NU_WDT_256CLK) { + wdt_timeout_rmn_clk -= NU_WDT_256CLK; + wdt_timeout_clk_toutsel = WDT_TIMEOUT_2POW8; + } else if (wdt_timeout_rmn_clk >= NU_WDT_64CLK) { + wdt_timeout_rmn_clk -= NU_WDT_64CLK; + wdt_timeout_clk_toutsel = WDT_TIMEOUT_2POW6; + } else if (wdt_timeout_rmn_clk >= NU_WDT_16CLK) { + wdt_timeout_rmn_clk -= NU_WDT_16CLK; + wdt_timeout_clk_toutsel = WDT_TIMEOUT_2POW4; + } else if (wdt_timeout_rmn_clk) { + wdt_timeout_rmn_clk = 0; + wdt_timeout_clk_toutsel = WDT_TIMEOUT_2POW4; + } else { + /* WDT has timed-out and will restart system soon. We just disable interrupt to escape + * getting stuck in WDT ISR. */ + SYS_UnlockReg(); + + /* Clear all flags & Disable interrupt */ + WDT->CTL = (WDT->CTL & ~WDT_CTL_INTEN_Msk) | (WDT_CTL_WKF_Msk | WDT_CTL_IF_Msk | WDT_CTL_RSTF_Msk); + + SYS_LockReg(); + return; + } + + SYS_UnlockReg(); + + /* Configure reset delay on timeout */ + WDT->ALTCTL = NU_WDT_RESET_DELAY_RSTDSEL; + + /* Configure another piece of cascaded WDT timeout */ + WDT->CTL = wdt_timeout_clk_toutsel | // Timeout interval + WDT_CTL_WDTEN_Msk | // Enable watchdog timer + WDT_CTL_INTEN_Msk | // Enable interrupt + WDT_CTL_WKF_Msk | // Clear wake-up flag + WDT_CTL_WKEN_Msk | // Enable wake-up on timeout + WDT_CTL_IF_Msk | // Clear interrupt flag + WDT_CTL_RSTF_Msk | // Clear reset flag + (wdt_timeout_rmn_clk ? 0 : WDT_CTL_RSTEN_Msk) | // Enable reset on last cascaded timeout + WDT_CTL_RSTCNT_Msk; // Reset watchdog timer + + SYS_LockReg(); +} + +void WDT_IRQHandler(void) +{ + /* Check WDT interrupt flag */ + if (WDT_GET_TIMEOUT_INT_FLAG()) { + /* Continue another piece of cascaded WDT timeout */ + watchdog_setup_cascade_timeout(); + } else { + /* Clear all flags */ + WDT->CTL |= (WDT_CTL_WKF_Msk | WDT_CTL_IF_Msk | WDT_CTL_RSTF_Msk); + } +} + +#endif diff --git a/targets/TARGET_NUVOTON/TARGET_NANO100/reset_reason.c b/targets/TARGET_NUVOTON/TARGET_NANO100/reset_reason.c new file mode 100644 index 00000000000..a2ea29c464c --- /dev/null +++ b/targets/TARGET_NUVOTON/TARGET_NANO100/reset_reason.c @@ -0,0 +1,87 @@ +/* mbed Microcontroller Library + * Copyright (c) 2017-2018 Nuvoton + * + * 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 "reset_reason_api.h" + +#if DEVICE_RESET_REASON + +#include "cmsis.h" + +/* All reset source flags */ +#define SYS_RST_SRC_ALL_Msk \ + (SYS_RST_SRC_RSTS_POR_Msk | \ + SYS_RST_SRC_RSTS_PAD_Msk | \ + SYS_RST_SRC_RSTS_WDT_Msk | \ + SYS_RST_SRC_RSTS_BOD_Msk | \ + SYS_RST_SRC_RSTS_SYS_Msk | \ + SYS_RST_SRC_RSTS_CPU_Msk) + +reset_reason_t hal_reset_reason_get(void) +{ + reset_reason_t reset_reason_cast; + uint32_t reset_reason_count = 0; + + if (SYS_IS_POR_RST()) { + reset_reason_cast = RESET_REASON_POWER_ON; + reset_reason_count ++; + } + + if (SYS_IS_RSTPIN_RST()) { + reset_reason_cast = RESET_REASON_PIN_RESET; + reset_reason_count ++; + } + + if (SYS_IS_WDT_RST()) { + reset_reason_cast = RESET_REASON_WATCHDOG; + reset_reason_count ++; + } + + if (SYS_IS_BOD_RST()) { + reset_reason_cast = RESET_REASON_BROWN_OUT; + reset_reason_count ++; + } + + /* This MCU doesn't support MKROM and we needn't take care of bootloader/SYSRESETREQ flow. */ + if (SYS_IS_SYSTEM_RST()) { + reset_reason_cast = RESET_REASON_SOFTWARE; + reset_reason_count ++; + } + + if (SYS_IS_CPU_RST()) { + reset_reason_cast = RESET_REASON_PLATFORM; + reset_reason_count ++; + } + + if (reset_reason_count == 0) { + reset_reason_cast = RESET_REASON_UNKNOWN; + } else if (reset_reason_count >= 2) { + reset_reason_cast = RESET_REASON_MULTIPLE; + } + + return reset_reason_cast; +} + +uint32_t hal_reset_reason_get_raw(void) +{ + return (SYS->RST_SRC & SYS_RST_SRC_ALL_Msk); +} + +void hal_reset_reason_clear(void) +{ + SYS_CLEAR_RST_SOURCE(SYS->RST_SRC); +} + +#endif diff --git a/targets/TARGET_NUVOTON/TARGET_NANO100/watchdog_api.c b/targets/TARGET_NUVOTON/TARGET_NANO100/watchdog_api.c new file mode 100644 index 00000000000..376d2cb6393 --- /dev/null +++ b/targets/TARGET_NUVOTON/TARGET_NANO100/watchdog_api.c @@ -0,0 +1,211 @@ +/* mbed Microcontroller Library + * Copyright (c) 2017-2018 Nuvoton + * + * 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 + +#include "cmsis.h" + +/* Micro seconds per second */ +#define NU_US_PER_SEC 1000000 + +/* Watchdog clock per second */ +#define NU_WDTCLK_PER_SEC (__LIRC) + +/* Convert watchdog clock to nearest ms */ +#define NU_WDTCLK2MS(WDTCLK) (((WDTCLK) * 1000 + ((NU_WDTCLK_PER_SEC) / 2)) / (NU_WDTCLK_PER_SEC)) + +/* Convert ms to nearest watchdog clock */ +#define NU_MS2WDTCLK(MS) (((MS) * (NU_WDTCLK_PER_SEC) + 500) / 1000) + +/* List of hardware-supported watchdog timeout in clocks */ +#define NU_WDT_16CLK 16 +#define NU_WDT_64CLK 64 +#define NU_WDT_256CLK 256 +#define NU_WDT_1024CLK 1024 +#define NU_WDT_4096CLK 4096 +#define NU_WDT_16384CLK 16384 +#define NU_WDT_65536CLK 65536 +#define NU_WDT_262144CLK 262144 + +/* Watchdog reset delay */ +#define NU_WDT_RESET_DELAY_RSTDSEL WDT_RESET_DELAY_3CLK + +/* Support watchdog timeout values beyond H/W + * + * Watchdog Timer H/W just supports timeout values of 2^4, 2^6, ..., 2^18 clocks. + * To extend the support range to 1 and UINT32_MAX, we cascade multiple small timeouts to + * reach one large timeout specified in hal_watchdog_init. + */ + +/* Track if WDT H/W has been initialized */ +static bool wdt_hw_inited = 0; +/* Hold initially-configured timeout in hal_watchdog_init */ +static uint32_t wdt_timeout_reload_ms = 0; +/* Track remaining timeout for cascading */ +static uint32_t wdt_timeout_rmn_clk = 0; + +static void watchdog_setup_cascade_timeout(void); +/* NOTE: Don't add static modifier here. These IRQ handler symbols are for linking. + Vector table relocation is not actually supported for low-resource target. */ +void WDT_IRQHandler(void); + +watchdog_status_t hal_watchdog_init(const watchdog_config_t *config) +{ + /* Check validity of arguments */ + if (! config || ! config->timeout_ms) { + return WATCHDOG_STATUS_INVALID_ARGUMENT; + } + + wdt_timeout_reload_ms = config->timeout_ms; + wdt_timeout_rmn_clk = NU_MS2WDTCLK(wdt_timeout_reload_ms); + + if (! wdt_hw_inited) { + wdt_hw_inited = 1; + + /* Enable IP module clock */ + CLK_EnableModuleClock(WDT_MODULE); + + /* Select IP clock source */ + CLK_SetModuleClock(WDT_MODULE, 0, 0); + + /* Set up IP interrupt */ + NVIC_SetVector(WDT_IRQn, (uint32_t) WDT_IRQHandler); + NVIC_EnableIRQ(WDT_IRQn); + } + + watchdog_setup_cascade_timeout(); + + return WATCHDOG_STATUS_OK; +} + +void hal_watchdog_kick(void) +{ + wdt_timeout_rmn_clk = NU_MS2WDTCLK(wdt_timeout_reload_ms); + watchdog_setup_cascade_timeout(); +} + +watchdog_status_t hal_watchdog_stop(void) +{ + SYS_UnlockReg(); + + /* Clear all flags */ + WDT->ISR = WDT_ISR_IS_Msk | WDT_ISR_RST_IS_Msk | WDT_ISR_WAKE_IS_Msk; + /* Disable interrupt */ + WDT->IER &= ~WDT_IER_IE_Msk; + /* Disable WDT */ + WDT->CTL &= ~WDT_CTL_WTE_Msk; + + SYS_LockReg(); + + return WATCHDOG_STATUS_OK; +} + +uint32_t hal_watchdog_get_reload_value(void) +{ + return wdt_timeout_reload_ms; +} + +watchdog_features_t hal_watchdog_get_platform_features(void) +{ + watchdog_features_t wdt_feat; + + /* We can support timeout values between 1 and UINT32_MAX by cascading. */ + wdt_feat.max_timeout = UINT32_MAX; + /* Support re-configuring watchdog timer */ + wdt_feat.update_config = 1; + /* Support stopping watchdog timer */ + wdt_feat.disable_watchdog = 1; + + return wdt_feat; +} + +static void watchdog_setup_cascade_timeout(void) +{ + uint32_t wdt_timeout_clk_toutsel; + + if (wdt_timeout_rmn_clk >= NU_WDT_262144CLK) { + wdt_timeout_rmn_clk -= NU_WDT_262144CLK; + wdt_timeout_clk_toutsel = WDT_TIMEOUT_2POW18; + } else if (wdt_timeout_rmn_clk >= NU_WDT_65536CLK) { + wdt_timeout_rmn_clk -= NU_WDT_65536CLK; + wdt_timeout_clk_toutsel = WDT_TIMEOUT_2POW16; + } else if (wdt_timeout_rmn_clk >= NU_WDT_16384CLK) { + wdt_timeout_rmn_clk -= NU_WDT_16384CLK; + wdt_timeout_clk_toutsel = WDT_TIMEOUT_2POW14; + } else if (wdt_timeout_rmn_clk >= NU_WDT_4096CLK) { + wdt_timeout_rmn_clk -= NU_WDT_4096CLK; + wdt_timeout_clk_toutsel = WDT_TIMEOUT_2POW12; + } else if (wdt_timeout_rmn_clk >= NU_WDT_1024CLK) { + wdt_timeout_rmn_clk -= NU_WDT_1024CLK; + wdt_timeout_clk_toutsel = WDT_TIMEOUT_2POW10; + } else if (wdt_timeout_rmn_clk >= NU_WDT_256CLK) { + wdt_timeout_rmn_clk -= NU_WDT_256CLK; + wdt_timeout_clk_toutsel = WDT_TIMEOUT_2POW8; + } else if (wdt_timeout_rmn_clk >= NU_WDT_64CLK) { + wdt_timeout_rmn_clk -= NU_WDT_64CLK; + wdt_timeout_clk_toutsel = WDT_TIMEOUT_2POW6; + } else if (wdt_timeout_rmn_clk >= NU_WDT_16CLK) { + wdt_timeout_rmn_clk -= NU_WDT_16CLK; + wdt_timeout_clk_toutsel = WDT_TIMEOUT_2POW4; + } else if (wdt_timeout_rmn_clk) { + wdt_timeout_rmn_clk = 0; + wdt_timeout_clk_toutsel = WDT_TIMEOUT_2POW4; + } else { + /* WDT has timed-out and will restart system soon. We just disable interrupt to escape + * getting stuck in WDT ISR. */ + SYS_UnlockReg(); + + /* Clear all flags */ + WDT->ISR = WDT_ISR_IS_Msk | WDT_ISR_RST_IS_Msk | WDT_ISR_WAKE_IS_Msk; + /* Disable interrupt */ + WDT->IER &= ~WDT_IER_IE_Msk; + + SYS_LockReg(); + return; + } + + SYS_UnlockReg(); + + /* Clear all flags */ + WDT->ISR = WDT_ISR_IS_Msk | WDT_ISR_RST_IS_Msk | WDT_ISR_WAKE_IS_Msk; + /* Enable interrupt */ + WDT->IER = WDT_IER_IE_Msk; + /* Configure another piece of cascaded WDT timeout */ + WDT->CTL = NU_WDT_RESET_DELAY_RSTDSEL | // Reset delay on timeout + wdt_timeout_clk_toutsel | // Timeout interval + WDT_CTL_WTE_Msk | // Enable watchdog timer + WDT_CTL_WTWKE_Msk | // Enable wake-up on timeout + (wdt_timeout_rmn_clk ? 0 : WDT_CTL_WTRE_Msk) | // Enable reset on last cascaded timeout + WDT_CTL_WTR_Msk; // Reset watchdog timer + + SYS_LockReg(); +} + +void WDT_IRQHandler(void) +{ + /* Check WDT interrupt flag */ + if (WDT_GET_TIMEOUT_INT_FLAG()) { + /* Continue another piece of cascaded WDT timeout */ + watchdog_setup_cascade_timeout(); + } else { + /* Clear all flags */ + WDT->ISR = WDT_ISR_IS_Msk | WDT_ISR_RST_IS_Msk | WDT_ISR_WAKE_IS_Msk; + } +} + +#endif diff --git a/targets/TARGET_NUVOTON/TARGET_NUC472/device/StdDriver/nuc472_wdt.c b/targets/TARGET_NUVOTON/TARGET_NUC472/device/StdDriver/nuc472_wdt.c index 41905b792d3..4a9c34ac67e 100644 --- a/targets/TARGET_NUVOTON/TARGET_NUC472/device/StdDriver/nuc472_wdt.c +++ b/targets/TARGET_NUVOTON/TARGET_NUC472/device/StdDriver/nuc472_wdt.c @@ -1,8 +1,8 @@ /**************************************************************************//** * @file wdt.c * @version V1.00 - * $Revision: 6 $ - * $Date: 14/10/02 7:19p $ + * $Revision: 9 $ + * $Date: 15/11/12 9:49a $ * @brief NUC472/NUC442 WDT driver source file * * @note @@ -49,9 +49,10 @@ void WDT_Open(uint32_t u32TimeoutInterval, uint32_t u32EnableWakeup) { - WDT->CTL = u32TimeoutInterval | u32ResetDelay | WDT_CTL_WDTEN_Msk | + WDT->CTL = u32TimeoutInterval | WDT_CTL_WDTEN_Msk | (u32EnableReset << WDT_CTL_RSTEN_Pos) | (u32EnableWakeup << WDT_CTL_WKEN_Pos); + WDT->ALTCTL = u32ResetDelay; return; } diff --git a/targets/TARGET_NUVOTON/TARGET_NUC472/reset_reason.c b/targets/TARGET_NUVOTON/TARGET_NUC472/reset_reason.c new file mode 100644 index 00000000000..2f8f5bc5158 --- /dev/null +++ b/targets/TARGET_NUVOTON/TARGET_NUC472/reset_reason.c @@ -0,0 +1,93 @@ +/* mbed Microcontroller Library + * Copyright (c) 2017-2018 Nuvoton + * + * 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 "reset_reason_api.h" + +#if DEVICE_RESET_REASON + +#include "cmsis.h" + +/* All reset source flags */ +#define SYS_RSTSTS_ALLRF_Msk \ + (SYS_RSTSTS_PORF_Msk | \ + SYS_RSTSTS_PINRF_Msk | \ + SYS_RSTSTS_WDTRF_Msk | \ + SYS_RSTSTS_LVRF_Msk | \ + SYS_RSTSTS_BODRF_Msk | \ + SYS_RSTSTS_SYSRF_Msk | \ + SYS_RSTSTS_CPURF_Msk) + +reset_reason_t hal_reset_reason_get(void) +{ + reset_reason_t reset_reason_cast; + uint32_t reset_reason_count = 0; + + if (SYS_IS_POR_RST()) { + reset_reason_cast = RESET_REASON_POWER_ON; + reset_reason_count ++; + } + + if (SYS_IS_RSTPIN_RST()) { + reset_reason_cast = RESET_REASON_PIN_RESET; + reset_reason_count ++; + } + + if (SYS_IS_WDT_RST()) { + reset_reason_cast = RESET_REASON_WATCHDOG; + reset_reason_count ++; + } + + if (SYS_IS_LVR_RST()) { + reset_reason_cast = RESET_REASON_PLATFORM; + reset_reason_count ++; + } + + if (SYS_IS_BOD_RST()) { + reset_reason_cast = RESET_REASON_BROWN_OUT; + reset_reason_count ++; + } + + /* This MCU doesn't support MKROM and we needn't take care of bootloader/SYSRESETREQ flow. */ + if (SYS_IS_SYSTEM_RST()) { + reset_reason_cast = RESET_REASON_SOFTWARE; + reset_reason_count ++; + } + + if (SYS_IS_CPU_RST()) { + reset_reason_cast = RESET_REASON_PLATFORM; + reset_reason_count ++; + } + + if (reset_reason_count == 0) { + reset_reason_cast = RESET_REASON_UNKNOWN; + } else if (reset_reason_count >= 2) { + reset_reason_cast = RESET_REASON_MULTIPLE; + } + + return reset_reason_cast; +} + +uint32_t hal_reset_reason_get_raw(void) +{ + return (SYS->RSTSTS & SYS_RSTSTS_ALLRF_Msk); +} + +void hal_reset_reason_clear(void) +{ + SYS_CLEAR_RST_SOURCE(SYS->RSTSTS); +} + +#endif diff --git a/targets/TARGET_NUVOTON/TARGET_NUC472/watchdog_api.c b/targets/TARGET_NUVOTON/TARGET_NUC472/watchdog_api.c new file mode 100644 index 00000000000..96150ae7e7f --- /dev/null +++ b/targets/TARGET_NUVOTON/TARGET_NUC472/watchdog_api.c @@ -0,0 +1,205 @@ +/* mbed Microcontroller Library + * Copyright (c) 2017-2018 Nuvoton + * + * 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 + +#include "cmsis.h" + +/* Micro seconds per second */ +#define NU_US_PER_SEC 1000000 + +/* Watchdog clock per second */ +#define NU_WDTCLK_PER_SEC (__LIRC) + +/* Convert watchdog clock to nearest ms */ +#define NU_WDTCLK2MS(WDTCLK) (((WDTCLK) * 1000 + ((NU_WDTCLK_PER_SEC) / 2)) / (NU_WDTCLK_PER_SEC)) + +/* Convert ms to nearest watchdog clock */ +#define NU_MS2WDTCLK(MS) (((MS) * (NU_WDTCLK_PER_SEC) + 500) / 1000) + +/* List of hardware-supported watchdog timeout in clocks */ +#define NU_WDT_16CLK 16 +#define NU_WDT_64CLK 64 +#define NU_WDT_256CLK 256 +#define NU_WDT_1024CLK 1024 +#define NU_WDT_4096CLK 4096 +#define NU_WDT_16384CLK 16384 +#define NU_WDT_65536CLK 65536 +#define NU_WDT_262144CLK 262144 + +/* Watchdog reset delay */ +#define NU_WDT_RESET_DELAY_RSTDSEL WDT_RESET_DELAY_3CLK + +/* Support watchdog timeout values beyond H/W + * + * Watchdog Timer H/W just supports timeout values of 2^4, 2^6, ..., 2^18 clocks. + * To extend the support range to 1 and UINT32_MAX, we cascade multiple small timeouts to + * reach one large timeout specified in hal_watchdog_init. + */ + +/* Track if WDT H/W has been initialized */ +static bool wdt_hw_inited = 0; +/* Hold initially-configured timeout in hal_watchdog_init */ +static uint32_t wdt_timeout_reload_ms = 0; +/* Track remaining timeout for cascading */ +static uint32_t wdt_timeout_rmn_clk = 0; + +static void watchdog_setup_cascade_timeout(void); +static void WDT_IRQHandler(void); + +watchdog_status_t hal_watchdog_init(const watchdog_config_t *config) +{ + /* Check validity of arguments */ + if (! config || ! config->timeout_ms) { + return WATCHDOG_STATUS_INVALID_ARGUMENT; + } + + wdt_timeout_reload_ms = config->timeout_ms; + wdt_timeout_rmn_clk = NU_MS2WDTCLK(wdt_timeout_reload_ms); + + if (! wdt_hw_inited) { + wdt_hw_inited = 1; + + /* Enable IP module clock */ + CLK_EnableModuleClock(WDT_MODULE); + + /* Select IP clock source */ + CLK_SetModuleClock(WDT_MODULE, CLK_CLKSEL1_WDTSEL_LIRC, 0); + + /* Set up IP interrupt */ + NVIC_SetVector(WDT_IRQn, (uint32_t) WDT_IRQHandler); + NVIC_EnableIRQ(WDT_IRQn); + } + + watchdog_setup_cascade_timeout(); + + return WATCHDOG_STATUS_OK; +} + +void hal_watchdog_kick(void) +{ + wdt_timeout_rmn_clk = NU_MS2WDTCLK(wdt_timeout_reload_ms); + watchdog_setup_cascade_timeout(); +} + +watchdog_status_t hal_watchdog_stop(void) +{ + SYS_UnlockReg(); + + /* Clear all flags & Disable interrupt & Disable WDT */ + WDT->CTL = (WDT->CTL & ~(WDT_CTL_WDTEN_Msk | WDT_CTL_INTEN_Msk)) | (WDT_CTL_WKF_Msk | WDT_CTL_IF_Msk | WDT_CTL_RSTF_Msk); + + SYS_LockReg(); + + return WATCHDOG_STATUS_OK; +} + +uint32_t hal_watchdog_get_reload_value(void) +{ + return wdt_timeout_reload_ms; +} + +watchdog_features_t hal_watchdog_get_platform_features(void) +{ + watchdog_features_t wdt_feat; + + /* We can support timeout values between 1 and UINT32_MAX by cascading. */ + wdt_feat.max_timeout = UINT32_MAX; + /* Support re-configuring watchdog timer */ + wdt_feat.update_config = 1; + /* Support stopping watchdog timer */ + wdt_feat.disable_watchdog = 1; + + return wdt_feat; +} + +static void watchdog_setup_cascade_timeout(void) +{ + uint32_t wdt_timeout_clk_toutsel; + + if (wdt_timeout_rmn_clk >= NU_WDT_262144CLK) { + wdt_timeout_rmn_clk -= NU_WDT_262144CLK; + wdt_timeout_clk_toutsel = WDT_TIMEOUT_2POW18; + } else if (wdt_timeout_rmn_clk >= NU_WDT_65536CLK) { + wdt_timeout_rmn_clk -= NU_WDT_65536CLK; + wdt_timeout_clk_toutsel = WDT_TIMEOUT_2POW16; + } else if (wdt_timeout_rmn_clk >= NU_WDT_16384CLK) { + wdt_timeout_rmn_clk -= NU_WDT_16384CLK; + wdt_timeout_clk_toutsel = WDT_TIMEOUT_2POW14; + } else if (wdt_timeout_rmn_clk >= NU_WDT_4096CLK) { + wdt_timeout_rmn_clk -= NU_WDT_4096CLK; + wdt_timeout_clk_toutsel = WDT_TIMEOUT_2POW12; + } else if (wdt_timeout_rmn_clk >= NU_WDT_1024CLK) { + wdt_timeout_rmn_clk -= NU_WDT_1024CLK; + wdt_timeout_clk_toutsel = WDT_TIMEOUT_2POW10; + } else if (wdt_timeout_rmn_clk >= NU_WDT_256CLK) { + wdt_timeout_rmn_clk -= NU_WDT_256CLK; + wdt_timeout_clk_toutsel = WDT_TIMEOUT_2POW8; + } else if (wdt_timeout_rmn_clk >= NU_WDT_64CLK) { + wdt_timeout_rmn_clk -= NU_WDT_64CLK; + wdt_timeout_clk_toutsel = WDT_TIMEOUT_2POW6; + } else if (wdt_timeout_rmn_clk >= NU_WDT_16CLK) { + wdt_timeout_rmn_clk -= NU_WDT_16CLK; + wdt_timeout_clk_toutsel = WDT_TIMEOUT_2POW4; + } else if (wdt_timeout_rmn_clk) { + wdt_timeout_rmn_clk = 0; + wdt_timeout_clk_toutsel = WDT_TIMEOUT_2POW4; + } else { + /* WDT has timed-out and will restart system soon. We just disable interrupt to escape + * getting stuck in WDT ISR. */ + SYS_UnlockReg(); + + /* Clear all flags & Disable interrupt */ + WDT->CTL = (WDT->CTL & ~WDT_CTL_INTEN_Msk) | (WDT_CTL_WKF_Msk | WDT_CTL_IF_Msk | WDT_CTL_RSTF_Msk); + + SYS_LockReg(); + return; + } + + SYS_UnlockReg(); + + /* Configure reset delay on timeout */ + WDT->ALTCTL = NU_WDT_RESET_DELAY_RSTDSEL; + + /* Configure another piece of cascaded WDT timeout */ + WDT->CTL = wdt_timeout_clk_toutsel | // Timeout interval + WDT_CTL_WDTEN_Msk | // Enable watchdog timer + WDT_CTL_INTEN_Msk | // Enable interrupt + WDT_CTL_WKF_Msk | // Clear wake-up flag + WDT_CTL_WKEN_Msk | // Enable wake-up on timeout + WDT_CTL_IF_Msk | // Clear interrupt flag + WDT_CTL_RSTF_Msk | // Clear reset flag + (wdt_timeout_rmn_clk ? 0 : WDT_CTL_RSTEN_Msk) | // Enable reset on last cascaded timeout + WDT_CTL_RSTCNT_Msk; // Reset watchdog timer + + SYS_LockReg(); +} + +void WDT_IRQHandler(void) +{ + /* Check WDT interrupt flag */ + if (WDT_GET_TIMEOUT_INT_FLAG()) { + /* Continue another piece of cascaded WDT timeout */ + watchdog_setup_cascade_timeout(); + } else { + /* Clear all flags */ + WDT->CTL |= (WDT_CTL_WKF_Msk | WDT_CTL_IF_Msk | WDT_CTL_RSTF_Msk); + } +} + +#endif diff --git a/targets/TARGET_ONSEMI/TARGET_NCS36510/watchdog_api.c b/targets/TARGET_ONSEMI/TARGET_NCS36510/watchdog_api.c new file mode 100644 index 00000000000..02c98c71f7a --- /dev/null +++ b/targets/TARGET_ONSEMI/TARGET_NCS36510/watchdog_api.c @@ -0,0 +1,114 @@ +/** + ******************************************************************************* + * @file watchdog_api.c + * @brief Implementation of watchdog_api + * @internal + * @author ON Semiconductor + ****************************************************************************** + * Copyright 2018 Semiconductor Components Industries LLC (d/b/a “ON Semiconductor”). + * All rights reserved. This software and/or documentation is licensed by ON Semiconductor + * under limited terms and conditions. The terms and conditions pertaining to the software + * and/or documentation are available at http://www.onsemi.com/site/pdf/ONSEMI_T&C.pdf + * (“ON Semiconductor Standard Terms and Conditions of Sale, Section 8 Software”) and + * if applicable the software license agreement. Do not use this software and/or + * documentation unless you have carefully read and you agree to the limited terms and + * conditions. By using this software and/or documentation, you agree to the limited + * terms and conditions. + * + * THIS SOFTWARE IS PROVIDED "AS IS". NO WARRANTIES, WHETHER EXPRESS, IMPLIED + * OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. + * ON SEMICONDUCTOR SHALL NOT, IN ANY CIRCUMSTANCES, BE LIABLE FOR SPECIAL, + * INCIDENTAL, OR CONSEQUENTIAL DAMAGES, FOR ANY REASON WHATSOEVER. + * @endinternal + * + */ +#include "watchdog_api.h" +#if DEVICE_WATCHDOG + +#include "clock.h" // Peripheral clock control definitions. +#include "wdt_map.h" // Watchdog hardware register definitions. +#include "memory_map.h" // Pointer to watchdog peripheral in memory. + + +// watchdog_api feature definitions +#define WDT_MAX_TIMEOUT_MS ((uint32_t)8000) +#define WDT_CAN_UPDATE ((boolean)True) +#define WDT_CAN_STOP ((boolean)True) + +// WDT LOAD register definitions +#define WDT_MAX_LOAD_VAL ((uint32_t)0x3FFFF) +#define WDT_TICKS_PER_MS (WDT_MAX_LOAD_VAL / WDT_MAX_TIMEOUT_MS) + +// WDT KICK register definitions +#define WDT_KICK_VAL ((uint32_t)1) + +// WDT LOCK register definitions +#define WDT_LOCK_DISABLE ((uint32_t)0x1ACCE551) +#define WDT_LOCK_ENABLE ((uint32_t)0x00000000) + + +watchdog_status_t hal_watchdog_init(const watchdog_config_t *config) +{ + if (!config || config->timeout_ms > WDT_MAX_TIMEOUT_MS || config->timeout_ms == 0) { + return WATCHDOG_STATUS_INVALID_ARGUMENT; + } + + if (!CLOCK_IS_ENABLED(CLOCK_WDOG)) { + CLOCK_ENABLE(CLOCK_WDOG); + } + + // Disable write lock in case WDT is being reconfigured. + WDTREG->LOCK = WDT_LOCK_DISABLE; + + while (WDTREG->STATUS.BITS.WRITE_BUSY_ANY); + WDTREG->LOAD = config->timeout_ms * WDT_TICKS_PER_MS; + + while (WDTREG->STATUS.BITS.WRITE_BUSY_LOAD); + WDTREG->CONTROL.BITS.WDT_EN = True; + + while (WDTREG->STATUS.BITS.WRITE_BUSY_CONTROL); + WDTREG->LOCK = WDT_LOCK_ENABLE; + + return WATCHDOG_STATUS_OK; +} + +void hal_watchdog_kick(void) +{ + // Write any value to kick watchdog. + WDTREG->KICK = WDT_KICK_VAL; +} + +watchdog_status_t hal_watchdog_stop(void) +{ + WDTREG->LOCK = WDT_LOCK_DISABLE; + + while (WDTREG->STATUS.BITS.WRITE_BUSY_ANY); + WDTREG->CONTROL.BITS.WDT_EN = False; + + while (WDTREG->STATUS.BITS.WRITE_BUSY_ANY); + CLOCK_DISABLE(CLOCK_WDOG); + + return WATCHDOG_STATUS_OK; +} + +uint32_t hal_watchdog_get_reload_value(void) +{ + while (WDTREG->STATUS.BITS.WRITE_BUSY_LOAD); + return WDTREG->LOAD / WDT_TICKS_PER_MS; +} + +watchdog_features_t hal_watchdog_get_platform_features(void) +{ + const watchdog_features_t features = { + .max_timeout = WDT_MAX_TIMEOUT_MS, + .update_config = WDT_CAN_UPDATE, + .disable_watchdog = WDT_CAN_STOP + }; + + return features; +} + + +#endif // DEVICE_WATCHDOG + diff --git a/targets/TARGET_STM/TARGET_STM32F0/device/stm32f0xx_hal_iwdg.c b/targets/TARGET_STM/TARGET_STM32F0/device/stm32f0xx_hal_iwdg.c index 1899f1f862d..c0be82e22e1 100644 --- a/targets/TARGET_STM/TARGET_STM32F0/device/stm32f0xx_hal_iwdg.c +++ b/targets/TARGET_STM/TARGET_STM32F0/device/stm32f0xx_hal_iwdg.c @@ -124,10 +124,8 @@ /** @defgroup IWDG_Private_Defines IWDG Private Defines * @{ */ -/* Status register need 5 RC LSI divided by prescaler clock to be updated. With - higher prescaler (256), and according to LSI variation, we need to wait at - least 6 cycles so 39 ms. */ -#define HAL_IWDG_DEFAULT_TIMEOUT 39U +/* MBED */ +#define HAL_IWDG_DEFAULT_TIMEOUT 96u /** * @} */ diff --git a/targets/TARGET_STM/TARGET_STM32F1/device/stm32f1xx_hal_iwdg.c b/targets/TARGET_STM/TARGET_STM32F1/device/stm32f1xx_hal_iwdg.c index d654662cc5d..b762736e78c 100644 --- a/targets/TARGET_STM/TARGET_STM32F1/device/stm32f1xx_hal_iwdg.c +++ b/targets/TARGET_STM/TARGET_STM32F1/device/stm32f1xx_hal_iwdg.c @@ -121,6 +121,7 @@ higher prescaler (256), and according to HSI variation, we need to wait at least 6 cycles so 48 ms. */ #define HAL_IWDG_DEFAULT_TIMEOUT 48U + /** * @} */ diff --git a/targets/TARGET_STM/TARGET_STM32F2/device/stm32f2xx_hal_iwdg.c b/targets/TARGET_STM/TARGET_STM32F2/device/stm32f2xx_hal_iwdg.c index b680c070fdd..f75e2eac66c 100644 --- a/targets/TARGET_STM/TARGET_STM32F2/device/stm32f2xx_hal_iwdg.c +++ b/targets/TARGET_STM/TARGET_STM32F2/device/stm32f2xx_hal_iwdg.c @@ -119,10 +119,8 @@ /** @defgroup IWDG_Private_Defines IWDG Private Defines * @{ */ -/* Status register need 5 RC LSI divided by prescaler clock to be updated. With - higher prescaler (256), and according to HSI variation, we need to wait at - least 6 cycles so 48 ms. */ -#define HAL_IWDG_DEFAULT_TIMEOUT 48U +/* MBED */ +#define HAL_IWDG_DEFAULT_TIMEOUT 96u /** * @} */ diff --git a/targets/TARGET_STM/TARGET_STM32F3/device/stm32f3xx_hal_iwdg.c b/targets/TARGET_STM/TARGET_STM32F3/device/stm32f3xx_hal_iwdg.c index 479f4d95072..b576778df2c 100644 --- a/targets/TARGET_STM/TARGET_STM32F3/device/stm32f3xx_hal_iwdg.c +++ b/targets/TARGET_STM/TARGET_STM32F3/device/stm32f3xx_hal_iwdg.c @@ -124,10 +124,8 @@ /** @defgroup IWDG_Private_Defines IWDG Private Defines * @{ */ -/* Status register need 5 RC LSI divided by prescaler clock to be updated. With - higher prescaler (256U), and according to HSI variation, we need to wait at - least 6 cycles so 48 ms. */ -#define HAL_IWDG_DEFAULT_TIMEOUT 48u +/* MBED */ +#define HAL_IWDG_DEFAULT_TIMEOUT 96u /** * @} */ diff --git a/targets/TARGET_STM/TARGET_STM32F4/device/stm32f4xx_hal_iwdg.c b/targets/TARGET_STM/TARGET_STM32F4/device/stm32f4xx_hal_iwdg.c index 672aad16246..721f6011b30 100644 --- a/targets/TARGET_STM/TARGET_STM32F4/device/stm32f4xx_hal_iwdg.c +++ b/targets/TARGET_STM/TARGET_STM32F4/device/stm32f4xx_hal_iwdg.c @@ -117,10 +117,8 @@ /** @defgroup IWDG_Private_Defines IWDG Private Defines * @{ */ -/* Status register need 5 RC LSI divided by prescaler clock to be updated. With - higher prescaler (256), and according to HSI variation, we need to wait at - least 6 cycles so 48 ms. */ -#define HAL_IWDG_DEFAULT_TIMEOUT 48U +/* MBED */ +#define HAL_IWDG_DEFAULT_TIMEOUT 96u /** * @} */ diff --git a/targets/TARGET_STM/TARGET_STM32F7/device/stm32f7xx_hal_iwdg.c b/targets/TARGET_STM/TARGET_STM32F7/device/stm32f7xx_hal_iwdg.c index 4a2dda8465f..85dc4794462 100644 --- a/targets/TARGET_STM/TARGET_STM32F7/device/stm32f7xx_hal_iwdg.c +++ b/targets/TARGET_STM/TARGET_STM32F7/device/stm32f7xx_hal_iwdg.c @@ -124,10 +124,8 @@ /** @defgroup IWDG_Private_Defines IWDG Private Defines * @{ */ -/* Status register need 5 RC LSI divided by prescaler clock to be updated. With - higher prescaler (256), and according to LSI variation, we need to wait at - least 6 cycles so 48 ms. */ -#define HAL_IWDG_DEFAULT_TIMEOUT 48u +/* MBED */ +#define HAL_IWDG_DEFAULT_TIMEOUT 96u /** * @} */ diff --git a/targets/TARGET_STM/TARGET_STM32L0/device/stm32l0xx_hal_conf.h b/targets/TARGET_STM/TARGET_STM32L0/device/stm32l0xx_hal_conf.h index b157dc88ee5..364c00cbe74 100644 --- a/targets/TARGET_STM/TARGET_STM32L0/device/stm32l0xx_hal_conf.h +++ b/targets/TARGET_STM/TARGET_STM32L0/device/stm32l0xx_hal_conf.h @@ -132,7 +132,7 @@ * @brief Internal Low Speed oscillator (LSI) value. */ #if !defined (LSI_VALUE) - #define LSI_VALUE ((uint32_t)37000U) /*!< LSI Typical Value in Hz*/ + #define LSI_VALUE (37000U) /*!< LSI Typical Value in Hz*/ #endif /* LSI_VALUE */ /*!< Value of the Internal Low Speed oscillator in Hz The real value may vary depending on the variations in voltage and temperature.*/ diff --git a/targets/TARGET_STM/TARGET_STM32L0/device/stm32l0xx_hal_iwdg.c b/targets/TARGET_STM/TARGET_STM32L0/device/stm32l0xx_hal_iwdg.c index 6310686cd80..9e45c4a8219 100644 --- a/targets/TARGET_STM/TARGET_STM32L0/device/stm32l0xx_hal_iwdg.c +++ b/targets/TARGET_STM/TARGET_STM32L0/device/stm32l0xx_hal_iwdg.c @@ -197,7 +197,7 @@ HAL_StatusTypeDef HAL_IWDG_Init(IWDG_HandleTypeDef *hiwdg) /* Check pending flag, if previous update not done, return timeout */ tickstart = HAL_GetTick(); - /* Wait for register to be updated */ + /* Wait for register to be updated */ while(hiwdg->Instance->SR != RESET) { if((HAL_GetTick() - tickstart ) > HAL_IWDG_DEFAULT_TIMEOUT) @@ -206,12 +206,12 @@ HAL_StatusTypeDef HAL_IWDG_Init(IWDG_HandleTypeDef *hiwdg) } } - /* If window parameter is different than current value, modify window + /* If window parameter is different than current value, modify window register */ if(hiwdg->Instance->WINR != hiwdg->Init.Window) { /* Write to IWDG WINR the IWDG_Window value to compare with. In any case, - even if window feature is disabled, Watchdog will be reloaded by writing + even if window feature is disabled, Watchdog will be reloaded by writing windows register */ hiwdg->Instance->WINR = hiwdg->Init.Window; } diff --git a/targets/TARGET_STM/TARGET_STM32L1/device/stm32l1xx_hal_iwdg.c b/targets/TARGET_STM/TARGET_STM32L1/device/stm32l1xx_hal_iwdg.c index 7f89b5611aa..c057cff97c6 100644 --- a/targets/TARGET_STM/TARGET_STM32L1/device/stm32l1xx_hal_iwdg.c +++ b/targets/TARGET_STM/TARGET_STM32L1/device/stm32l1xx_hal_iwdg.c @@ -118,10 +118,8 @@ /** @defgroup IWDG_Private_Defines IWDG Private Defines * @{ */ -/* Status register need 5 RC LSI divided by prescaler clock to be updated. With - higher prescaler (256), and according to HSI variation, we need to wait at - least 6 cycles so 48 ms. */ -#define HAL_IWDG_DEFAULT_TIMEOUT 48u +/* MBED */ +#define HAL_IWDG_DEFAULT_TIMEOUT 96u /** * @} */ diff --git a/targets/TARGET_STM/TARGET_STM32L4/device/stm32l4xx_hal_conf.h b/targets/TARGET_STM/TARGET_STM32L4/device/stm32l4xx_hal_conf.h index 5ad54eb7133..ae27ce2da11 100644 --- a/targets/TARGET_STM/TARGET_STM32L4/device/stm32l4xx_hal_conf.h +++ b/targets/TARGET_STM/TARGET_STM32L4/device/stm32l4xx_hal_conf.h @@ -148,7 +148,7 @@ * @brief Internal Low Speed oscillator (LSI) value. */ #if !defined (LSI_VALUE) - #define LSI_VALUE ((uint32_t)32000U) /*!< LSI Typical Value in Hz*/ + #define LSI_VALUE (32000U) /*!< LSI Typical Value in Hz*/ #endif /* LSI_VALUE */ /*!< Value of the Internal Low Speed oscillator in Hz The real value may vary depending on the variations in voltage and temperature.*/ diff --git a/targets/TARGET_STM/TARGET_STM32L4/device/stm32l4xx_hal_iwdg.c b/targets/TARGET_STM/TARGET_STM32L4/device/stm32l4xx_hal_iwdg.c index e5080c41b1e..a415996a538 100644 --- a/targets/TARGET_STM/TARGET_STM32L4/device/stm32l4xx_hal_iwdg.c +++ b/targets/TARGET_STM/TARGET_STM32L4/device/stm32l4xx_hal_iwdg.c @@ -124,10 +124,8 @@ /** @defgroup IWDG_Private_Defines IWDG Private Defines * @{ */ -/* Status register need 5 RC LSI divided by prescaler clock to be updated. With - higher prescaler (256), and according to HSI variation, we need to wait at - least 6 cycles so 48 ms. */ -#define HAL_IWDG_DEFAULT_TIMEOUT 48u +/* MBED */ +#define HAL_IWDG_DEFAULT_TIMEOUT 96u /** * @} */ diff --git a/targets/TARGET_STM/reset_reason.c b/targets/TARGET_STM/reset_reason.c new file mode 100644 index 00000000000..6772c34ebc4 --- /dev/null +++ b/targets/TARGET_STM/reset_reason.c @@ -0,0 +1,81 @@ +/* mbed Microcontroller Library + * Copyright (c) 2006-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 "reset_reason_api.h" + +#ifdef DEVICE_RESET_REASON + +#include "device.h" + +reset_reason_t hal_reset_reason_get(void) +{ +#ifdef RCC_FLAG_LPWRRST + if (__HAL_RCC_GET_FLAG(RCC_FLAG_LPWRRST)) { + return RESET_REASON_WAKE_LOW_POWER; + } +#endif + +#ifdef RCC_FLAG_WWDGRST + if (__HAL_RCC_GET_FLAG(RCC_FLAG_WWDGRST)) { + return RESET_REASON_WATCHDOG; + } +#endif + +#ifdef RCC_FLAG_IWDGRST + if (__HAL_RCC_GET_FLAG(RCC_FLAG_IWDGRST)) { + return RESET_REASON_WATCHDOG; + } +#endif + +#ifdef RCC_FLAG_SFTRST + if (__HAL_RCC_GET_FLAG(RCC_FLAG_SFTRST)) { + return RESET_REASON_SOFTWARE; + } +#endif + +#ifdef RCC_FLAG_PORRST + if (__HAL_RCC_GET_FLAG(RCC_FLAG_PORRST)) { + return RESET_REASON_POWER_ON; + } +#endif + +#ifdef RCC_FLAG_BORRST + if (__HAL_RCC_GET_FLAG(RCC_FLAG_BORRST)) { + return RESET_REASON_BROWN_OUT; + } +#endif + +#ifdef RCC_FLAG_PINRST + if (__HAL_RCC_GET_FLAG(RCC_FLAG_PINRST)) { + return RESET_REASON_PIN_RESET; + } +#endif + + return RESET_REASON_UNKNOWN; +} + + +uint32_t hal_reset_reason_get_raw(void) +{ + return RCC->CSR; +} + + +void hal_reset_reason_clear(void) +{ + __HAL_RCC_CLEAR_RESET_FLAGS(); +} + +#endif // DEVICE_RESET_REASON diff --git a/targets/TARGET_STM/watchdog_api.c b/targets/TARGET_STM/watchdog_api.c new file mode 100644 index 00000000000..47da70cc6c9 --- /dev/null +++ b/targets/TARGET_STM/watchdog_api.c @@ -0,0 +1,130 @@ +/* mbed Microcontroller Library + * Copyright (c) 2006-2018 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. + */ + + +#ifdef DEVICE_WATCHDOG + +#include "watchdog_api.h" +#include "reset_reason_api.h" +#include "device.h" +#include "mbed_error.h" +#include + +#define MAX_IWDG_PR 0x6 // Max value of Prescaler_divider bits (PR) of Prescaler_register (IWDG_PR) +#define MAX_IWDG_RL 0xFFFUL // Max value of Watchdog_counter_reload_value bits (RL) of Reload_register (IWDG_RLR). + +// Convert Prescaler_divider bits (PR) of Prescaler_register (IWDG_PR) to a prescaler_divider value. +#define PR2PRESCALER_DIV(PR_BITS) \ + (4UL << (PR_BITS)) + +// Convert Prescaler_divider bits (PR) of Prescaler_register (IWDG_PR) +// and Watchdog_counter_reload_value bits (RL) of Reload_register (IWDG_RLR) +// to a timeout value [ms]. +#define PR_RL2UINT64_TIMEOUT_MS(PR_BITS, RL_BITS) \ + ((PR2PRESCALER_DIV(PR_BITS)) * (RL_BITS) * 1000ULL / (LSI_VALUE)) + +// Convert Prescaler_divider bits (PR) of Prescaler_register (IWDG_PR) and a timeout value [ms] +// to Watchdog_counter_reload_value bits (RL) of Reload_register (IWDG_RLR) +#define PR_TIMEOUT_MS2RL(PR_BITS, TIMEOUT_MS) \ + ((TIMEOUT_MS) * (LSI_VALUE) / (PR2PRESCALER_DIV(PR_BITS)) / 1000UL) + +#define MAX_TIMEOUT_MS_UINT64 PR_RL2UINT64_TIMEOUT_MS(MAX_IWDG_PR, MAX_IWDG_RL) +#if (MAX_TIMEOUT_MS_UINT64 > UINT32_MAX) +#define MAX_TIMEOUT_MS UINT32_MAX +#else +#define MAX_TIMEOUT_MS (MAX_TIMEOUT_MS_UINT64 & 0xFFFFFFFFUL) +#endif + +#define INVALID_IWDG_PR ((MAX_IWDG_PR) + 1) // Arbitrary value used to mark an invalid PR bits value. + +// Pick a minimal Prescaler_divider bits (PR) value suitable for given timeout. +static uint8_t pick_min_iwdg_pr(const uint32_t timeout_ms) { + for (uint8_t pr = 0; pr <= MAX_IWDG_PR; pr++) { + // Check that max timeout for given pr is greater than + // or equal to timeout_ms. + if (PR_RL2UINT64_TIMEOUT_MS(pr, MAX_IWDG_RL) >= timeout_ms) { + return pr; + } + } + return INVALID_IWDG_PR; +} + +IWDG_HandleTypeDef IwdgHandle; + +watchdog_status_t hal_watchdog_init(const watchdog_config_t *config) +{ + const uint8_t pr = pick_min_iwdg_pr(config->timeout_ms); + if (pr == INVALID_IWDG_PR) { + return WATCHDOG_STATUS_INVALID_ARGUMENT; + } + const uint32_t rl = PR_TIMEOUT_MS2RL(pr, config->timeout_ms); + + IwdgHandle.Instance = IWDG; + + IwdgHandle.Init.Prescaler = pr; + IwdgHandle.Init.Reload = rl; +#if defined IWDG_WINR_WIN + IwdgHandle.Init.Window = IWDG_WINDOW_DISABLE; +#endif + + if (HAL_IWDG_Init(&IwdgHandle) != HAL_OK) + { + error("HAL_IWDG_Init error\n"); + } + + return WATCHDOG_STATUS_OK; +} + +void hal_watchdog_kick(void) +{ + HAL_IWDG_Refresh(&IwdgHandle); +} + +watchdog_status_t hal_watchdog_stop(void) +{ + return WATCHDOG_STATUS_NOT_SUPPORTED; +} + +uint32_t hal_watchdog_get_reload_value(void) +{ + // Wait for the Watchdog_prescaler_value_update bit (PVU) of + // Status_register (IWDG_SR) to be reset. + while (READ_BIT(IWDG->SR, IWDG_SR_PVU)) { + } + // Read Prescaler_divider bits (PR) of Prescaler_register (IWDG_PR). + const uint8_t pr = (IWDG->PR & IWDG_PR_PR_Msk) >> IWDG_PR_PR_Pos; + + // Wait for the Watchdog_counter_reload_value_update bit (RVU) of + // Status_register (IWDG_SR) to be reset. + while (READ_BIT(IWDG->SR, IWDG_SR_RVU)) { + } + // Read Watchdog_counter_reload_value bits (RL) of Reload_register (IWDG_RLR). + const uint32_t rl = (IWDG->RLR & IWDG_RLR_RL_Msk) >> IWDG_RLR_RL_Pos; + + return PR_RL2UINT64_TIMEOUT_MS(pr, rl); +} + +watchdog_features_t hal_watchdog_get_platform_features(void) +{ + watchdog_features_t features; + features.max_timeout = MAX_TIMEOUT_MS; + features.update_config = true; + features.disable_watchdog = false; + + return features; +} + +#endif // DEVICE_WATCHDOG diff --git a/targets/TARGET_Silicon_Labs/TARGET_EFM32/resetreason_api.c b/targets/TARGET_Silicon_Labs/TARGET_EFM32/resetreason_api.c new file mode 100644 index 00000000000..e0fd55135b3 --- /dev/null +++ b/targets/TARGET_Silicon_Labs/TARGET_EFM32/resetreason_api.c @@ -0,0 +1,132 @@ +/***************************************************************************//** + * @file resetreason_api.c + ******************************************************************************* + * @section License + * (C) Copyright 2018 Silicon Labs, http://www.silabs.com + ******************************************************************************* + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +#include "device.h" +#include "reset_reason_api.h" + +#if DEVICE_RESET_REASON + +#include "em_rmu.h" + +reset_reason_t hal_reset_reason_get(void) +{ + uint32_t reasonmask = RMU_ResetCauseGet(); + + /* Only care about the upper bit, since that is 'most significant' reset reason */ + reasonmask = 1 << (31 - __CLZ(reasonmask)); + + if (reasonmask & RMU_RSTCAUSE_PORST) { + return RESET_REASON_POWER_ON; + } +#if defined(RMU_RSTCAUSE_BODUNREGRST) + if (reasonmask & RMU_RSTCAUSE_BODUNREGRST) { + return RESET_REASON_BROWN_OUT; + } +#endif +#if defined(RMU_RSTCAUSE_BODREGRST) + if (reasonmask & RMU_RSTCAUSE_BODREGRST) { + return RESET_REASON_BROWN_OUT; + } +#endif +#if defined(RMU_RSTCAUSE_AVDDBOD) + if (reasonmask & RMU_RSTCAUSE_AVDDBOD) { + return RESET_REASON_BROWN_OUT; + } +#endif +#if defined(RMU_RSTCAUSE_DVDDBOD) + if (reasonmask & RMU_RSTCAUSE_DVDDBOD) { + return RESET_REASON_BROWN_OUT; + } +#endif +#if defined(RMU_RSTCAUSE_DECBOD) + if (reasonmask & RMU_RSTCAUSE_DECBOD) { + return RESET_REASON_BROWN_OUT; + } +#endif + if (reasonmask & RMU_RSTCAUSE_EXTRST) { + return RESET_REASON_PIN_RESET; + } + if (reasonmask & RMU_RSTCAUSE_WDOGRST) { + return RESET_REASON_WATCHDOG; + } + if (reasonmask & RMU_RSTCAUSE_LOCKUPRST) { + return RESET_REASON_LOCKUP; + } + if (reasonmask & RMU_RSTCAUSE_SYSREQRST) { + return RESET_REASON_SOFTWARE; + } +#if defined(RMU_RSTCAUSE_EM4RST) + if (reasonmask & RMU_RSTCAUSE_EM4RST) { + return RESET_REASON_WAKE_LOW_POWER; + } +#endif +#if defined(RMU_RSTCAUSE_EM4WURST) + if (reasonmask & RMU_RSTCAUSE_EM4WURST) { + return RESET_REASON_WAKE_LOW_POWER; + } +#endif +#if defined(RMU_RSTCAUSE_BODAVDD0) + if (reasonmask & RMU_RSTCAUSE_BODAVDD0) { + return RESET_REASON_BROWN_OUT; + } +#endif +#if defined(RMU_RSTCAUSE_BODAVDD1) + if (reasonmask & RMU_RSTCAUSE_BODAVDD1) { + return RESET_REASON_BROWN_OUT; + } +#endif +#if defined(BU_PRESENT) && defined(_SILICON_LABS_32B_SERIES_0) + if (reasonmask & RMU_RSTCAUSE_BUBODVDDDREG) { + return RESET_REASON_BROWN_OUT; + } + if (reasonmask & RMU_RSTCAUSE_BUBODBUVIN) { + return RESET_REASON_BROWN_OUT; + } + if (reasonmask & RMU_RSTCAUSE_BUBODUNREG) { + return RESET_REASON_BROWN_OUT; + } + if (reasonmask & RMU_RSTCAUSE_BUBODREG) { + return RESET_REASON_BROWN_OUT; + } + if (reasonmask & RMU_RSTCAUSE_BUMODERST) { + return RESET_REASON_PLATFORM; + } +#elif defined(RMU_RSTCAUSE_BUMODERST) + if (reasonmask & RMU_RSTCAUSE_BUMODERST) { + return RESET_REASON_PLATFORM; + } +#endif + return RESET_REASON_UNKNOWN; +} + +uint32_t hal_reset_reason_get_raw(void) +{ + return RMU->RSTCAUSE; +} + +void hal_reset_reason_clear(void) +{ + RMU_ResetCauseClear(); +} + +#endif /* DEVICE_RESET_REASON */ diff --git a/targets/TARGET_Silicon_Labs/TARGET_EFM32/watchdog_api.c b/targets/TARGET_Silicon_Labs/TARGET_EFM32/watchdog_api.c new file mode 100644 index 00000000000..72a64a99d61 --- /dev/null +++ b/targets/TARGET_Silicon_Labs/TARGET_EFM32/watchdog_api.c @@ -0,0 +1,100 @@ +/***************************************************************************//** + * @file watchdog_api.c + ******************************************************************************* + * @section License + * (C) Copyright 2018 Silicon Labs, http://www.silabs.com + ******************************************************************************* + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +#include "device.h" +#include "watchdog_api.h" + +#if DEVICE_WATCHDOG + +#include "clocking.h" +#include "em_cmu.h" +#include "em_wdog.h" + +watchdog_status_t hal_watchdog_init(const watchdog_config_t *config) +{ + if (config == 0 || config->timeout_ms > 262145 || config->timeout_ms == 0) { + return WATCHDOG_STATUS_INVALID_ARGUMENT; + } + + uint32_t timeout = config->timeout_ms; + if (timeout <= 9) { + timeout = 0; + } else { + timeout -= 2; + /* get bit position of highest bit set */ + timeout = 31 - __CLZ(timeout); + /* need to go one over */ + timeout += 1; + /* convert to wdog setting. if bit 4 is the highest, then the + * watchdog setting == 1. That means watchdog setting 0xF == 2^18 = 256k */ + if (timeout > 18) { + return WATCHDOG_STATUS_INVALID_ARGUMENT; + } + + timeout -= 3; + } + + WDOG_Init_TypeDef wdog_init = WDOG_INIT_DEFAULT; + wdog_init.clkSel = wdogClkSelULFRCO; + wdog_init.em2Run = true; + wdog_init.em3Run = true; + wdog_init.perSel = timeout; + + WDOGn_Init(DEFAULT_WDOG, &wdog_init); + + return WATCHDOG_STATUS_OK; +} + +void hal_watchdog_kick(void) +{ + WDOGn_Feed(DEFAULT_WDOG); +} + +watchdog_status_t hal_watchdog_stop(void) +{ + WDOGn_Enable(DEFAULT_WDOG, false); + return WATCHDOG_STATUS_OK; +} + +uint32_t hal_watchdog_get_reload_value(void) +{ + uint32_t period = (DEFAULT_WDOG->CTRL & _WDOG_CTRL_PERSEL_MASK) >> _WDOG_CTRL_PERSEL_SHIFT; + if (period == 0) { + return 9; + } else { + period += 3; + return (1U << period) + 1; + } +} + +watchdog_features_t hal_watchdog_get_platform_features(void) +{ + watchdog_features_t feat = { + .max_timeout = 262145, + .update_config = true, + .disable_watchdog = true + }; + return feat; +} + +#endif /* DEVICE_WATCHDOG */ diff --git a/targets/TARGET_TOSHIBA/TARGET_TMPM066/reset_reason_api.c b/targets/TARGET_TOSHIBA/TARGET_TMPM066/reset_reason_api.c new file mode 100644 index 00000000000..817a1f489fa --- /dev/null +++ b/targets/TARGET_TOSHIBA/TARGET_TMPM066/reset_reason_api.c @@ -0,0 +1,128 @@ +/* mbed Microcontroller Library + * (C)Copyright TOSHIBA ELECTRONIC DEVICES & STORAGE CORPORATION 2017 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 "reset_reason_api.h" + +#ifdef DEVICE_RESET_REASON + +#include "TMPM066.h" +#include + +#define MAXRSTREASON 6 + +static uint8_t set_bit_count(uint32_t reg); +static uint8_t bit_pos(uint32_t reg); +static bool bit_status(uint32_t reg, uint8_t bit_no); + +static reset_reason_t reset_reason1[MAXRSTREASON] = { + RESET_REASON_POWER_ON, + RESET_REASON_UNKNOWN, + RESET_REASON_UNKNOWN, + RESET_REASON_PIN_RESET, + RESET_REASON_UNKNOWN, + RESET_REASON_BROWN_OUT +}; + +static reset_reason_t reset_reason2[MAXRSTREASON] = { + RESET_REASON_SOFTWARE, + RESET_REASON_UNKNOWN, + RESET_REASON_WATCHDOG +}; + +void hal_reset_reason_clear(void) +{ + TSB_AOREG->RSTFLG = 0x00; + TSB_AOREG->RSTFLG1 = 0x00; +} + +uint32_t hal_reset_reason_get_raw(void) +{ + uint32_t ret = 0x00; + + ret = (((TSB_AOREG->RSTFLG1 & 0xFF) << 8) | (TSB_AOREG->RSTFLG & 0xFF)); + + return ret; +} + +reset_reason_t hal_reset_reason_get(void) +{ + reset_reason_t ret; + + uint8_t NoOfSetBitCountReg1 = set_bit_count(TSB_AOREG->RSTFLG); + uint8_t NoOfSetBitCountReg2 = set_bit_count(TSB_AOREG->RSTFLG1); + + if (NoOfSetBitCountReg1 != 0x00) { + if (NoOfSetBitCountReg1 > 0x01) { + if (bit_status(TSB_AOREG->RSTFLG, 0) && bit_status(TSB_AOREG->RSTFLG, 3)) { + ret = RESET_REASON_POWER_ON; + } else { + ret = RESET_REASON_MULTIPLE; + } + } else { + ret = reset_reason1[bit_pos(TSB_AOREG->RSTFLG)]; + } + } else if (NoOfSetBitCountReg2 != 0x00) { + if (NoOfSetBitCountReg2 > 0x01) { + ret = RESET_REASON_MULTIPLE; + } else { + ret = reset_reason2[bit_pos(TSB_AOREG->RSTFLG1)]; + } + } else { + ret = RESET_REASON_UNKNOWN; + } + + return ret; +} + +static bool bit_status(uint32_t reg, uint8_t bit_no) +{ + bool status = false; + + if (reg & (1 << bit_no)) { + status = true; + } + + return status; +} + +static uint8_t set_bit_count(uint32_t reg) +{ + uint8_t count = 0; + int8_t index = 0; + + for (index = 0; index < (sizeof(uint32_t) * 8); index++) { + if (reg & (1 << index)) { + count++; + if (count > 0x01) { + break; + } + } + } + + return count; +} + +static uint8_t bit_pos(uint32_t reg) +{ + uint8_t bit_no = 0; + + for (bit_no = 0; bit_no < (sizeof(uint32_t) * 8); bit_no++) { + if (reg & (1 << bit_no)) { + return bit_no; + } + } +} + +#endif // DEVICE_RESET_REASON diff --git a/targets/TARGET_TOSHIBA/TARGET_TMPM3H6/reset_reason_api.c b/targets/TARGET_TOSHIBA/TARGET_TMPM3H6/reset_reason_api.c new file mode 100644 index 00000000000..d5b50a952b4 --- /dev/null +++ b/targets/TARGET_TOSHIBA/TARGET_TMPM3H6/reset_reason_api.c @@ -0,0 +1,125 @@ +/* mbed Microcontroller Library + * (C)Copyright TOSHIBA ELECTRONIC DEVICES & STORAGE CORPORATION 2018 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 "reset_reason_api.h" + +#ifdef DEVICE_RESET_REASON + +#include "TMPM3H6.h" +#include + +static uint8_t set_bit_count(uint32_t reg); +static uint8_t bit_pos(uint32_t reg); +static bool bit_status(uint32_t reg, uint8_t bit_no); + +static reset_reason_t reset_reason1[6] = { + RESET_REASON_POWER_ON, + RESET_REASON_UNKNOWN, + RESET_REASON_UNKNOWN, + RESET_REASON_PIN_RESET, + RESET_REASON_WAKE_LOW_POWER, + RESET_REASON_BROWN_OUT +}; + +static reset_reason_t reset_reason2[4] = { + RESET_REASON_SOFTWARE, + RESET_REASON_LOCKUP, + RESET_REASON_WATCHDOG, + RESET_REASON_PLATFORM +}; + +void hal_reset_reason_clear(void) +{ + TSB_RLM->RSTFLG0 = 0; + TSB_RLM->RSTFLG1 = 0; +} + +uint32_t hal_reset_reason_get_raw(void) +{ + uint32_t ret = (((TSB_RLM->RSTFLG1 & 0xFF) << 8) | (TSB_RLM->RSTFLG0 & 0xFF)); + return ret; +} + +reset_reason_t hal_reset_reason_get(void) +{ + char multi_flag = 0; + reset_reason_t ret; + + uint8_t NoOfSetBitCountReg1 = set_bit_count(TSB_RLM->RSTFLG0); + uint8_t NoOfSetBitCountReg2 = set_bit_count(TSB_RLM->RSTFLG1); + + if (NoOfSetBitCountReg1 != 0x00) { + if (NoOfSetBitCountReg1 > 0x01) { + if (bit_status(TSB_RLM->RSTFLG0, 3)) { + ret = RESET_REASON_POWER_ON; + } else { + ret = RESET_REASON_MULTIPLE; + } + } else { + ret = reset_reason1[bit_pos(TSB_RLM->RSTFLG0)]; + } + } else if (NoOfSetBitCountReg2 != 0x00) { + if (NoOfSetBitCountReg2 > 0x01) { + ret = RESET_REASON_MULTIPLE; + } else { + ret = reset_reason2[bit_pos(TSB_RLM->RSTFLG1)]; + } + } else { + ret = RESET_REASON_UNKNOWN; + } + + return ret; +} + +static bool bit_status(uint32_t reg, uint8_t bit_no) +{ + bool status = false; + + if (reg & (1 << bit_no)) { + status = true; + } + + return status; +} + +static uint8_t set_bit_count(uint32_t reg) +{ + uint8_t count = 0; + int8_t index = 0; + + for (index = 0; index < (sizeof(uint32_t) * 8); index++) { + if (reg & (1 << index)) { + count++; + if (count > 0x01) { + break; + } + } + } + + return count; +} + +static uint8_t bit_pos(uint32_t reg) +{ + uint8_t bit_no = 0; + + for (bit_no = 0; bit_no < (sizeof(uint32_t) * 8); bit_no++) { + if (reg & (1 << bit_no)) { + return bit_no; + } + } +} + +#endif // DEVICE_RESET_REASON diff --git a/targets/TARGET_TOSHIBA/TARGET_TMPM3HQ/reset_reason_api.c b/targets/TARGET_TOSHIBA/TARGET_TMPM3HQ/reset_reason_api.c new file mode 100644 index 00000000000..f5873ea798b --- /dev/null +++ b/targets/TARGET_TOSHIBA/TARGET_TMPM3HQ/reset_reason_api.c @@ -0,0 +1,125 @@ +/* mbed Microcontroller Library + * (C)Copyright TOSHIBA ELECTRONIC DEVICES & STORAGE CORPORATION 2018 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 "reset_reason_api.h" + +#ifdef DEVICE_RESET_REASON + +#include "TMPM3HQ.h" +#include + +static uint8_t set_bit_count(uint32_t reg); +static uint8_t bit_pos(uint32_t reg); +static bool bit_status(uint32_t reg, uint8_t bit_no); + +static reset_reason_t reset_reason1[6] = { + RESET_REASON_POWER_ON, + RESET_REASON_UNKNOWN, + RESET_REASON_UNKNOWN, + RESET_REASON_PIN_RESET, + RESET_REASON_WAKE_LOW_POWER, + RESET_REASON_BROWN_OUT +}; + +static reset_reason_t reset_reason2[4] = { + RESET_REASON_SOFTWARE, + RESET_REASON_LOCKUP, + RESET_REASON_WATCHDOG, + RESET_REASON_PLATFORM +}; + +void hal_reset_reason_clear(void) +{ + TSB_RLM->RSTFLG0 = 0; + TSB_RLM->RSTFLG1 = 0; +} + +uint32_t hal_reset_reason_get_raw(void) +{ + uint32_t ret = (((TSB_RLM->RSTFLG1 & 0xFF) << 8) | (TSB_RLM->RSTFLG0 & 0xFF)); + return ret; +} + +reset_reason_t hal_reset_reason_get(void) +{ + char multi_flag = 0; + reset_reason_t ret; + + uint8_t NoOfSetBitCountReg1 = set_bit_count(TSB_RLM->RSTFLG0); + uint8_t NoOfSetBitCountReg2 = set_bit_count(TSB_RLM->RSTFLG1); + + if (NoOfSetBitCountReg1 != 0x00) { + if (NoOfSetBitCountReg1 > 0x01) { + if (bit_status(TSB_RLM->RSTFLG0, 3)) { + ret = RESET_REASON_POWER_ON; + } else { + ret = RESET_REASON_MULTIPLE; + } + } else { + ret = reset_reason1[bit_pos(TSB_RLM->RSTFLG0)]; + } + } else if (NoOfSetBitCountReg2 != 0x00) { + if (NoOfSetBitCountReg2 > 0x01) { + ret = RESET_REASON_MULTIPLE; + } else { + ret = reset_reason2[bit_pos(TSB_RLM->RSTFLG1)]; + } + } else { + ret = RESET_REASON_UNKNOWN; + } + + return ret; +} + +static bool bit_status(uint32_t reg, uint8_t bit_no) +{ + bool status = false; + + if (reg & (1 << bit_no)) { + status = true; + } + + return status; +} + +static uint8_t set_bit_count(uint32_t reg) +{ + uint8_t count = 0; + int8_t index = 0; + + for (index = 0; index < (sizeof(uint32_t) * 8); index++) { + if (reg & (1 << index)) { + count++; + if (count > 0x01) { + break; + } + } + } + + return count; +} + +static uint8_t bit_pos(uint32_t reg) +{ + uint8_t bit_no = 0; + + for (bit_no = 0; bit_no < (sizeof(uint32_t) * 8); bit_no++) { + if (reg & (1 << bit_no)) { + return bit_no; + } + } +} + +#endif // DEVICE_RESET_REASON diff --git a/targets/TARGET_TOSHIBA/TARGET_TMPM46B/reset_reason_api.c b/targets/TARGET_TOSHIBA/TARGET_TMPM46B/reset_reason_api.c new file mode 100644 index 00000000000..1d6546fceb5 --- /dev/null +++ b/targets/TARGET_TOSHIBA/TARGET_TMPM46B/reset_reason_api.c @@ -0,0 +1,94 @@ +/* mbed Microcontroller Library + * (C)Copyright TOSHIBA ELECTRONIC DEVICES & STORAGE CORPORATION 2018 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 "reset_reason_api.h" + +#ifdef DEVICE_RESET_REASON + +#include "TMPM46B.h" +#include + +static uint8_t set_bit_count(uint32_t reg); +static uint8_t bit_pos(uint32_t reg); + +static reset_reason_t reset_reason[7] = { + RESET_REASON_PIN_RESET, + RESET_REASON_UNKNOWN, + RESET_REASON_WATCHDOG, + RESET_REASON_WAKE_LOW_POWER, + RESET_REASON_SOFTWARE, + RESET_REASON_UNKNOWN, + RESET_REASON_BROWN_OUT +}; + +void hal_reset_reason_clear(void) +{ + TSB_CG->RSTFLG = 0x00; +} + +uint32_t hal_reset_reason_get_raw(void) +{ + uint32_t ret = 0; + + ret = TSB_CG->RSTFLG; + + return ret; +} + +reset_reason_t hal_reset_reason_get(void) +{ + reset_reason_t ret; + + uint8_t NoOfSetBitCountReg = set_bit_count(TSB_CG->RSTFLG); + + if (NoOfSetBitCountReg != 0x00) { + if (NoOfSetBitCountReg > 0x01) { + ret = RESET_REASON_MULTIPLE; + } else { + ret = reset_reason[bit_pos(TSB_CG->RSTFLG)]; + } + } else { + ret = RESET_REASON_UNKNOWN; + } + + return ret; +} + +static uint8_t set_bit_count(uint32_t reg) +{ + uint8_t count = 0; + int8_t index = 0; + + for (index = 0; index < (sizeof(uint32_t) * 8); index++) { + if ((reg & (1 << index)) && index != 1) { + count++; + } + } + + return count; +} + +static uint8_t bit_pos(uint32_t reg) +{ + uint8_t bit_no = 0; + + for (bit_no = 0; bit_no < (sizeof(uint32_t) * 8); bit_no++) { + if ((reg & (1 << bit_no)) && bit_no != 1) { + return bit_no; + } + } +} + +#endif // DEVICE_RESET_REASON diff --git a/targets/TARGET_TOSHIBA/TARGET_TMPM4G9/reset_reason_api.c b/targets/TARGET_TOSHIBA/TARGET_TMPM4G9/reset_reason_api.c new file mode 100644 index 00000000000..36a2144ca82 --- /dev/null +++ b/targets/TARGET_TOSHIBA/TARGET_TMPM4G9/reset_reason_api.c @@ -0,0 +1,128 @@ +/* mbed Microcontroller Library + * (C)Copyright TOSHIBA ELECTRONIC DEVICES & STORAGE CORPORATION 2018 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 "reset_reason_api.h" + +#ifdef DEVICE_RESET_REASON + +#include "TMPM4G9.h" +#include + +static uint8_t set_bit_count(uint32_t reg); +static uint8_t bit_pos(uint32_t reg); +static bool bit_status(uint32_t reg, uint8_t bit_no); + +static reset_reason_t reset_reason1[6] = { + RESET_REASON_POWER_ON, + RESET_REASON_UNKNOWN, + RESET_REASON_UNKNOWN, + RESET_REASON_PIN_RESET, + RESET_REASON_WAKE_LOW_POWER, + RESET_REASON_BROWN_OUT +}; + +static reset_reason_t reset_reason2[4] = { + RESET_REASON_SOFTWARE, + RESET_REASON_LOCKUP, + RESET_REASON_WATCHDOG, + RESET_REASON_PLATFORM +}; + +void hal_reset_reason_clear(void) +{ + TSB_RLM->RSTFLG0 = 0; + TSB_RLM->RSTFLG1 = 0; +} + +uint32_t hal_reset_reason_get_raw(void) +{ + uint32_t ret = 0; + + ret = (((TSB_RLM->RSTFLG1 & 0xFF) << 8) | (TSB_RLM->RSTFLG0 & 0xFF)); + + return ret; +} + +reset_reason_t hal_reset_reason_get(void) +{ + char multi_flag = 0; + reset_reason_t ret; + + uint8_t NoOfSetBitCountReg1 = set_bit_count(TSB_RLM->RSTFLG0); + uint8_t NoOfSetBitCountReg2 = set_bit_count(TSB_RLM->RSTFLG1); + + if (NoOfSetBitCountReg1 != 0x00) { + if (NoOfSetBitCountReg1 > 0x01) { + if (bit_status(TSB_RLM->RSTFLG0, 3)) { + ret = RESET_REASON_POWER_ON; + } else { + ret = RESET_REASON_MULTIPLE; + } + } else { + ret = reset_reason1[bit_pos(TSB_RLM->RSTFLG0)]; + } + } else if (NoOfSetBitCountReg2 != 0x00) { + if (NoOfSetBitCountReg2 > 0x01) { + ret = RESET_REASON_MULTIPLE; + } else { + ret = reset_reason2[bit_pos(TSB_RLM->RSTFLG1)]; + } + } else { + ret = RESET_REASON_UNKNOWN; + } + + return ret; +} + +static bool bit_status(uint32_t reg, uint8_t bit_no) +{ + bool status = false; + + if (reg & (1 << bit_no)) { + status = true; + } + + return status; +} + +static uint8_t set_bit_count(uint32_t reg) +{ + uint8_t count = 0; + int8_t index = 0; + + for (index = 0; index < (sizeof(uint32_t) * 8); index++) { + if (reg & (1 << index)) { + count++; + if (count > 0x01) { + break; + } + } + } + + return count; +} + +static uint8_t bit_pos(uint32_t reg) +{ + uint8_t bit_no = 0; + + for (bit_no = 0; bit_no < (sizeof(uint32_t) * 8); bit_no++) { + if (reg & (1 << bit_no)) { + return bit_no; + } + } +} + +#endif // DEVICE_RESET_REASON diff --git a/targets/targets.json b/targets/targets.json index b806976ac67..221d9c20d96 100644 --- a/targets/targets.json +++ b/targets/targets.json @@ -1503,6 +1503,7 @@ "PORTINOUT", "PORTOUT", "PWMOUT", + "RESET_REASON", "SERIAL", "SERIAL_FC", "SERIAL_ASYNCH", @@ -1513,7 +1514,8 @@ "STDIO_MESSAGES", "TRNG", "FLASH", - "USBDEVICE" + "USBDEVICE", + "WATCHDOG" ], "release_versions": ["2", "5"], "device_name": "MK64FN1M0xxx12", @@ -1655,6 +1657,7 @@ "PORTINOUT", "PORTOUT", "PWMOUT", + "RESET_REASON", "SERIAL", "SERIAL_ASYNCH", "SERIAL_FC", @@ -1664,7 +1667,8 @@ "SPISLAVE", "STDIO_MESSAGES", "TRNG", - "FLASH" + "FLASH", + "WATCHDOG" ], "default_lib": "std", "release_versions": ["2", "5"], @@ -1902,7 +1906,9 @@ "SPI", "SPISLAVE", "SPI_ASYNCH", - "STDIO_MESSAGES" + "STDIO_MESSAGES", + "WATCHDOG", + "RESET_REASON" ] }, "MIMXRT1050_EVK": { @@ -3212,7 +3218,8 @@ ], "release_versions": ["2", "5"], "device_name": "STM32H743ZI", - "bootloader_supported": true + "bootloader_supported": true, + "device_has_remove": ["WATCHDOG"] }, "UHURU_RAVEN": { "inherits": ["FAMILY_STM32"], @@ -5791,6 +5798,7 @@ "PORTINOUT", "PORTOUT", "PWMOUT", + "RESET_REASON", "RTC", "SERIAL", "SERIAL_ASYNCH", @@ -5802,7 +5810,8 @@ "USTICKER", "FLASH", "ITM", - "MPU" + "MPU", + "WATCHDOG" ], "forced_reset_timeout": 2, "config": { @@ -5868,6 +5877,7 @@ "PORTINOUT", "PORTOUT", "PWMOUT", + "RESET_REASON", "RTC", "SERIAL", "SERIAL_ASYNCH", @@ -5878,7 +5888,8 @@ "STDIO_MESSAGES", "USTICKER", "FLASH", - "MPU" + "MPU", + "WATCHDOG" ], "forced_reset_timeout": 2, "device_name": "EFM32LG990F256", @@ -5946,6 +5957,7 @@ "PORTINOUT", "PORTOUT", "PWMOUT", + "RESET_REASON", "RTC", "SERIAL", "SERIAL_ASYNCH", @@ -5956,7 +5968,8 @@ "STDIO_MESSAGES", "USTICKER", "FLASH", - "MPU" + "MPU", + "WATCHDOG" ], "forced_reset_timeout": 2, "config": { @@ -6022,6 +6035,7 @@ "PORTINOUT", "PORTOUT", "PWMOUT", + "RESET_REASON", "SERIAL", "SERIAL_ASYNCH", "SLEEP", @@ -6029,7 +6043,8 @@ "SPISLAVE", "SPI_ASYNCH", "STDIO_MESSAGES", - "USTICKER" + "USTICKER", + "WATCHDOG" ], "forced_reset_timeout": 2, "config": { @@ -6095,6 +6110,7 @@ "PORTINOUT", "PORTOUT", "PWMOUT", + "RESET_REASON", "SERIAL", "SERIAL_ASYNCH", "SLEEP", @@ -6102,7 +6118,8 @@ "SPISLAVE", "SPI_ASYNCH", "STDIO_MESSAGES", - "USTICKER" + "USTICKER", + "WATCHDOG" ], "forced_reset_timeout": 2, "config": { @@ -6168,6 +6185,7 @@ "PORTINOUT", "PORTOUT", "PWMOUT", + "RESET_REASON", "RTC", "SERIAL", "SERIAL_ASYNCH", @@ -6178,7 +6196,8 @@ "STDIO_MESSAGES", "USTICKER", "FLASH", - "MPU" + "MPU", + "WATCHDOG" ], "forced_reset_timeout": 2, "config": { @@ -6267,6 +6286,7 @@ "PORTINOUT", "PORTOUT", "PWMOUT", + "RESET_REASON", "RTC", "SERIAL", "SERIAL_ASYNCH", @@ -6277,7 +6297,8 @@ "STDIO_MESSAGES", "USTICKER", "FLASH", - "MPU" + "MPU", + "WATCHDOG" ], "forced_reset_timeout": 2, "config": { @@ -6337,6 +6358,7 @@ "PORTINOUT", "PORTOUT", "PWMOUT", + "RESET_REASON", "RTC", "SERIAL", "SERIAL_ASYNCH", @@ -6347,7 +6369,8 @@ "STDIO_MESSAGES", "USTICKER", "FLASH", - "MPU" + "MPU", + "WATCHDOG" ], "forced_reset_timeout": 5, "config": { @@ -6414,6 +6437,7 @@ "PORTINOUT", "PORTOUT", "PWMOUT", + "RESET_REASON", "RTC", "SERIAL", "SERIAL_ASYNCH", @@ -6425,7 +6449,8 @@ "USTICKER", "TRNG", "FLASH", - "MPU" + "MPU", + "WATCHDOG" ], "forced_reset_timeout": 2, "config": { @@ -6502,6 +6527,7 @@ "PORTINOUT", "PORTOUT", "PWMOUT", + "RESET_REASON", "RTC", "SERIAL", "SERIAL_ASYNCH", @@ -6513,7 +6539,8 @@ "USTICKER", "TRNG", "FLASH", - "MPU" + "MPU", + "WATCHDOG" ], "forced_reset_timeout": 5, "config": { @@ -6743,6 +6770,7 @@ "PORTINOUT", "PORTOUT", "PWMOUT", + "RESET_REASON", "SERIAL", "SERIAL_ASYNCH", "SERIAL_FC", @@ -7360,7 +7388,8 @@ "CAN", "FLASH", "EMAC", - "MPU" + "MPU", + "WATCHDOG" ], "release_versions": ["5"], "device_name": "NUC472HI8AE", @@ -7425,6 +7454,7 @@ "SPI", "TRNG", "SPISLAVE", + "WATCHDOG", "802_15_4_PHY", "MPU", "USTICKER" @@ -7487,7 +7517,9 @@ "SPI_ASYNCH", "CAN", "FLASH", - "MPU" + "MPU", + "WATCHDOG", + "RESET_REASON" ], "components_add": ["FLASHIAP"], "release_versions": ["2", "5"], @@ -7554,7 +7586,9 @@ "SLEEP", "SPI", "SPISLAVE", - "SPI_ASYNCH" + "SPI_ASYNCH", + "WATCHDOG", + "RESET_REASON" ], "release_versions": ["5"], "device_name": "NANO130KE3BN", @@ -7912,7 +7946,9 @@ "FLASH", "CAN", "EMAC", - "MPU" + "MPU", + "WATCHDOG", + "RESET_REASON" ], "release_versions": ["5"], "bootloader_supported": true, @@ -7949,6 +7985,7 @@ "PORTIN", "PORTINOUT", "PORTOUT", + "RESET_REASON", "SERIAL", "SLEEP", "I2C", @@ -8012,6 +8049,7 @@ "PORTINOUT", "PORTOUT", "PWMOUT", + "RESET_REASON", "SERIAL", "SERIAL_FC", "SPI", @@ -8185,6 +8223,7 @@ "PORTINOUT", "PORTOUT", "PWMOUT", + "RESET_REASON", "SERIAL", "SLEEP", "SPI", @@ -8212,6 +8251,7 @@ "PORTINOUT", "PORTOUT", "PWMOUT", + "RESET_REASON", "SERIAL", "SPI", "I2C", @@ -8546,6 +8586,7 @@ "PORTINOUT", "PORTOUT", "PWMOUT", + "RESET_REASON", "SERIAL", "SLEEP", "SPI", @@ -8727,7 +8768,6 @@ "macros": ["__TT_M4G9__"], "supported_toolchains": ["GCC_ARM", "ARM", "IAR"], "device_has": ["USTICKER", - "RESET_REASON", "ANALOGIN", "ANALOGOUT", "SERIAL",