From f098baeac3dc262646d044fb09c0d3dda24464b4 Mon Sep 17 00:00:00 2001 From: Kevin Bracey Date: Fri, 11 Jan 2019 17:32:14 +0200 Subject: [PATCH] NRF5x: rationalise sleep+critical This follows up from previous closed PRs #8366 and #9211, in an attempt to fix the issue originally reported as #5647. As discussed in those PRs, the sleep behaviour of the NRF5x family is apparently unsafe, at least for the cases where the SoftDevice is disabled. This commit fixes it up by: * Collapsing down to 1 implementation, from 3. * Using standard PRIMASK disable when SoftDevice is disabled. * Using standard WFI when SoftDevice is disabled. This commit shouldn't change the functionality for when it is enabled - it retains the "SEVONPEND" and "FPU pending" fudges. If SoftDevice is enabled, we continue to hope that its sd_app_evt_wait() call does the Right Thing(TM). However, this seems unlikely, as documentation and the "nosd" stubs visible suggests they just do a "WFE", when we are looking for a "WFI" equivalent. Or it's possible their real implementation does the unsafe and racy "SEV; WFE; WFE;" sequence. Comments added to clarify what we are hoping for from SoftDevice. --- .../hal_patch/critical_section_api.c | 84 ----------- .../TARGET_MCU_NRF51822/hal_patch/sleep.c | 71 --------- .../TARGET_NRF51/nordic_critical.c | 107 -------------- .../TARGET_NRF5x/TARGET_NRF51/sleep.c | 93 ------------ .../TARGET_NRF52/critical_section_api.c | 43 ------ .../TARGET_NRF5x/TARGET_NRF52/sleep.c | 100 ------------- .../TARGET_SDK_11/mbed_nrf_sdk_adaptation.h | 32 +++++ .../TARGET_SDK_14_2/mbed_nrf_sdk_adaptation.h | 32 +++++ .../TARGET_NRF5x/nordic_critical.c | 136 ++++++++++++++++++ targets/TARGET_NORDIC/TARGET_NRF5x/sleep.c | 118 +++++++++++++++ 10 files changed, 318 insertions(+), 498 deletions(-) delete mode 100644 features/FEATURE_BLE/targets/TARGET_NORDIC/TARGET_NORDIC_SOFTDEVICE/TARGET_MCU_NRF51822/hal_patch/critical_section_api.c delete mode 100644 features/FEATURE_BLE/targets/TARGET_NORDIC/TARGET_NORDIC_SOFTDEVICE/TARGET_MCU_NRF51822/hal_patch/sleep.c delete mode 100644 targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_NRF51/nordic_critical.c delete mode 100644 targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_NRF51/sleep.c delete mode 100644 targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_NRF52/critical_section_api.c delete mode 100644 targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_NRF52/sleep.c create mode 100644 targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_SDK_11/mbed_nrf_sdk_adaptation.h create mode 100644 targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_SDK_14_2/mbed_nrf_sdk_adaptation.h create mode 100644 targets/TARGET_NORDIC/TARGET_NRF5x/nordic_critical.c create mode 100644 targets/TARGET_NORDIC/TARGET_NRF5x/sleep.c diff --git a/features/FEATURE_BLE/targets/TARGET_NORDIC/TARGET_NORDIC_SOFTDEVICE/TARGET_MCU_NRF51822/hal_patch/critical_section_api.c b/features/FEATURE_BLE/targets/TARGET_NORDIC/TARGET_NORDIC_SOFTDEVICE/TARGET_MCU_NRF51822/hal_patch/critical_section_api.c deleted file mode 100644 index 2a009c464d9..00000000000 --- a/features/FEATURE_BLE/targets/TARGET_NORDIC/TARGET_NORDIC_SOFTDEVICE/TARGET_MCU_NRF51822/hal_patch/critical_section_api.c +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright (c) 2015-2017, ARM Limited, All Rights Reserved - * 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 "cmsis.h" -#include "nrf_error.h" -#include "nrf_sdm.h" -#include "nrf_soc.h" - -#include -#include - -static union { - uint32_t _PRIMASK_state; - uint8_t _sd_state; -} _state = {0}; - -static bool _use_softdevice_routine = false; -static bool _state_saved = false; - -void hal_critical_section_enter(void) -{ - // Fetch the current state of interrupts - uint32_t primask = __get_PRIMASK(); - uint8_t temp_state = 0; - - // If interrupts are enabled, try to use the soft device - uint8_t sd_enabled; - if ((primask == 0) && - (sd_softdevice_is_enabled(&sd_enabled) == NRF_SUCCESS) && - (sd_enabled == 1)) { - // If the softdevice can be used, use it. - sd_nvic_critical_region_enter(&temp_state); - _use_softdevice_routine = true; - - if (_state_saved == false) { - _state._sd_state = temp_state; - } - } else { - // If interrupts are enabled, disable them. - if (primask == 0) { - __disable_irq(); - } - - // Store PRIMASK state, it will be restored when exiting critical - // section. - _use_softdevice_routine = false; - - if (_state_saved == false) { - _state._PRIMASK_state = primask; - } - } - - _state_saved = true; -} - -void hal_critical_section_exit(void) -{ - _state_saved = false; - - // Restore the state as it was prior to entering the critical section. - if (_use_softdevice_routine) { - sd_nvic_critical_region_exit(_state._sd_state); - } else { - __set_PRIMASK(_state._PRIMASK_state); - } -} - -bool hal_in_critical_section(void) -{ - return (_state_saved == true); -} diff --git a/features/FEATURE_BLE/targets/TARGET_NORDIC/TARGET_NORDIC_SOFTDEVICE/TARGET_MCU_NRF51822/hal_patch/sleep.c b/features/FEATURE_BLE/targets/TARGET_NORDIC/TARGET_NORDIC_SOFTDEVICE/TARGET_MCU_NRF51822/hal_patch/sleep.c deleted file mode 100644 index aec280a0fbf..00000000000 --- a/features/FEATURE_BLE/targets/TARGET_NORDIC/TARGET_NORDIC_SOFTDEVICE/TARGET_MCU_NRF51822/hal_patch/sleep.c +++ /dev/null @@ -1,71 +0,0 @@ -/* mbed Microcontroller Library - * Copyright (c) 2006-2013 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 "sleep_api.h" -#include "cmsis.h" -#include "mbed_interface.h" -#include "softdevice_handler.h" -#include "nrf_soc.h" - -// Mask of reserved bits of the register ICSR in the System Control Block peripheral -// In this case, bits which are equal to 0 are the bits reserved in this register -#define SCB_ICSR_RESERVED_BITS_MASK 0x9E43F03F - -void hal_sleep(void) -{ - // ensure debug is disconnected if semihost is enabled.... - - // Trigger an event when an interrupt is pending. This allows to wake up - // the processor from disabled interrupts. - SCB->SCR |= SCB_SCR_SEVONPEND_Msk; - - // If the SoftDevice is enabled, its API must be used to go to sleep. - if (softdevice_handler_isEnabled()) { - sd_power_mode_set(NRF_POWER_MODE_LOWPWR); - sd_app_evt_wait(); - } else { - NRF_POWER->TASKS_LOWPWR = 1; - - // Note: it is not sufficient to just use WFE here, since the internal - // event register may be already set from an event that occurred in the - // past (like an SVC call to the SoftDevice) and in such case WFE will - // just clear the register and continue execution. - // Therefore, the strategy here is to first clear the event register - // by using SEV/WFE pair, and then execute WFE again, unless there is - // a pending interrupt. - - // Set an event and wake up whatsoever, this will clear the event - // register from all previous events set (SVC call included) - __SEV(); - __WFE(); - - // Test if there is an interrupt pending (mask reserved regions) - if (SCB->ICSR & (SCB_ICSR_RESERVED_BITS_MASK)) { - // Ok, there is an interrut pending, no need to go to sleep - return; - } else { - // next event will wakeup the CPU - // If an interrupt occured between the test of SCB->ICSR and this - // instruction, WFE will just not put the CPU to sleep - __WFE(); - } - } -} - -void hal_deepsleep(void) -{ - hal_sleep(); - // NRF_POWER->SYSTEMOFF=1; -} diff --git a/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_NRF51/nordic_critical.c b/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_NRF51/nordic_critical.c deleted file mode 100644 index f1937d3787d..00000000000 --- a/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_NRF51/nordic_critical.c +++ /dev/null @@ -1,107 +0,0 @@ -/* - * Copyright (c) 2015-2016, ARM Limited, All Rights Reserved - * 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 -#include "app_util_platform.h" - -#if defined(SOFTDEVICE_PRESENT) -static volatile bool state_saved = false; - -static void nordic_nvic_critical_region_enter(void); -static void nordic_nvic_critical_region_exit(void); -#endif - -void hal_critical_section_enter() -{ -#ifdef NRF52 - ASSERT(APP_LEVEL_PRIVILEGED == privilege_level_get()) -#endif - -#if defined(SOFTDEVICE_PRESENT) - /* return value can be safely ignored */ - nordic_nvic_critical_region_enter(); -#else - app_util_disable_irq(); -#endif -} - -void hal_critical_section_exit() -{ -#ifdef NRF52 - ASSERT(APP_LEVEL_PRIVILEGED == privilege_level_get()) -#endif - -#if defined(SOFTDEVICE_PRESENT) - /* return value can be safely ignored */ - nordic_nvic_critical_region_exit(); -#else - app_util_enable_irq(); -#endif -} - - -bool hal_in_critical_section(void) -{ - return (state_saved != 0); -} - - -#if defined(SOFTDEVICE_PRESENT) -/**@brief Enters critical region. - * - * @post Application interrupts will be disabled. - * @sa nordic_nvic_critical_region_exit - */ -static inline void nordic_nvic_critical_region_enter(void) -{ - int was_masked = __sd_nvic_irq_disable(); - - if (state_saved == false) { - nrf_nvic_state.__irq_masks[0] = ( NVIC->ICER[0] & __NRF_NVIC_APP_IRQS_0 ); - NVIC->ICER[0] = __NRF_NVIC_APP_IRQS_0; -#ifdef NRF52 - nrf_nvic_state.__irq_masks[1] = ( NVIC->ICER[1] & __NRF_NVIC_APP_IRQS_1 ); - NVIC->ICER[1] = __NRF_NVIC_APP_IRQS_1; -#endif - } - - state_saved = true; - - if (!was_masked) { - __sd_nvic_irq_enable(); - } -} - -/**@brief Exit critical region. - * - * @pre Application has entered a critical region using ::nordic_nvic_critical_region_enter. - * @post If not in a nested critical region, the application interrupts will restored to the state before ::nordic_nvic_critical_region_enter was called. - */ -static inline void nordic_nvic_critical_region_exit(void) -{ - state_saved = false; - - int was_masked = __sd_nvic_irq_disable(); - NVIC->ISER[0] = nrf_nvic_state.__irq_masks[0]; -#ifdef NRF52 - NVIC->ISER[1] = nrf_nvic_state.__irq_masks[1]; -#endif - if (!was_masked) { - __sd_nvic_irq_enable(); - } -} -#endif diff --git a/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_NRF51/sleep.c b/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_NRF51/sleep.c deleted file mode 100644 index 97e22c65413..00000000000 --- a/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_NRF51/sleep.c +++ /dev/null @@ -1,93 +0,0 @@ -/* mbed Microcontroller Library - * Copyright (c) 2006-2013 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 "sleep_api.h" -#include "cmsis.h" -#include "mbed_interface.h" -#include "softdevice_handler.h" -#include "nrf_soc.h" -#include "nrf_timer.h" -#include "us_ticker.h" - -// Mask of reserved bits of the register ICSR in the System Control Block peripheral -// In this case, bits which are equal to 0 are the bits reserved in this register -#define SCB_ICSR_RESERVED_BITS_MASK 0x9E43F03F - -#define FPU_EXCEPTION_MASK 0x0000009F - -extern bool us_ticker_initialized; - -void hal_sleep(void) -{ - // ensure debug is disconnected if semihost is enabled.... - - // Trigger an event when an interrupt is pending. This allows to wake up - // the processor from disabled interrupts. - SCB->SCR |= SCB_SCR_SEVONPEND_Msk; - -#if defined(NRF52) || defined(NRF52840_XXAA) - /* Clear exceptions and PendingIRQ from the FPU unit */ - __set_FPSCR(__get_FPSCR() & ~(FPU_EXCEPTION_MASK)); - (void) __get_FPSCR(); - NVIC_ClearPendingIRQ(FPU_IRQn); -#endif - - // If the SoftDevice is enabled, its API must be used to go to sleep. - if (softdevice_handler_is_enabled()) { - sd_power_mode_set(NRF_POWER_MODE_LOWPWR); - sd_app_evt_wait(); - } else { - NRF_POWER->TASKS_LOWPWR = 1; - - // Note: it is not sufficient to just use WFE here, since the internal - // event register may be already set from an event that occurred in the - // past (like an SVC call to the SoftDevice) and in such case WFE will - // just clear the register and continue execution. - // Therefore, the strategy here is to first clear the event register - // by using SEV/WFE pair, and then execute WFE again, unless there is - // a pending interrupt. - - // Set an event and wake up whatsoever, this will clear the event - // register from all previous events set (SVC call included) - __SEV(); - __WFE(); - - // Test if there is an interrupt pending (mask reserved regions) - if (SCB->ICSR & (SCB_ICSR_RESERVED_BITS_MASK)) { - // Ok, there is an interrut pending, no need to go to sleep - return; - } else { - // next event will wakeup the CPU - // If an interrupt occured between the test of SCB->ICSR and this - // instruction, WFE will just not put the CPU to sleep - __WFE(); - } - } -} - -void hal_deepsleep(void) -{ - if (us_ticker_initialized) { - nrf_timer_task_trigger(NRF_TIMER1, NRF_TIMER_TASK_STOP); - } - - hal_sleep(); - - if (us_ticker_initialized) { - nrf_timer_task_trigger(NRF_TIMER1, NRF_TIMER_TASK_START); - } - - // NRF_POWER->SYSTEMOFF=1; -} diff --git a/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_NRF52/critical_section_api.c b/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_NRF52/critical_section_api.c deleted file mode 100644 index 599709f7e2d..00000000000 --- a/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_NRF52/critical_section_api.c +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (c) 2015-2016, ARM Limited, All Rights Reserved - * 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 "hal/critical_section_api.h" -#include "nrf_nvic.h" - -#include - -static uint8_t outside_critical_region = 1; - -void hal_critical_section_enter() -{ - /* expect 1 or N calls but only lock on the first one */ - if (outside_critical_region) { - sd_nvic_critical_region_enter(&outside_critical_region); - } -} - -void hal_critical_section_exit() -{ - /* unlock on first call */ - outside_critical_region = 1; - sd_nvic_critical_region_exit(0); -} - -bool hal_in_critical_section(void) -{ - return !outside_critical_region; -} diff --git a/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_NRF52/sleep.c b/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_NRF52/sleep.c deleted file mode 100644 index a58591dd65a..00000000000 --- a/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_NRF52/sleep.c +++ /dev/null @@ -1,100 +0,0 @@ -/* mbed Microcontroller Library - * Copyright (c) 2006-2013 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 "sleep_api.h" -#include "cmsis.h" -#include "mbed_interface.h" -#include "nrf_soc.h" -#include "nrf_timer.h" -#include "us_ticker.h" - -#if defined(SOFTDEVICE_PRESENT) -#include "nrf_sdh.h" -#define NRF_HAL_SLEEP_SD_IS_ENABLED() nrf_sdh_is_enabled() -#else -#define NRF_HAL_SLEEP_SD_IS_ENABLED() 0 -#endif - -// Mask of reserved bits of the register ICSR in the System Control Block peripheral -// In this case, bits which are equal to 0 are the bits reserved in this register -#define SCB_ICSR_RESERVED_BITS_MASK 0x9E43F03F - -#define FPU_EXCEPTION_MASK 0x0000009F - -extern bool us_ticker_initialized; - -void hal_sleep(void) -{ - // ensure debug is disconnected if semihost is enabled.... - - // Trigger an event when an interrupt is pending. This allows to wake up - // the processor from disabled interrupts. - SCB->SCR |= SCB_SCR_SEVONPEND_Msk; - -#if defined(NRF52) || defined(NRF52840_XXAA) - /* Clear exceptions and PendingIRQ from the FPU unit */ - __set_FPSCR(__get_FPSCR() & ~(FPU_EXCEPTION_MASK)); - (void) __get_FPSCR(); - NVIC_ClearPendingIRQ(FPU_IRQn); -#endif - - // If the SoftDevice is enabled, its API must be used to go to sleep. - if (NRF_HAL_SLEEP_SD_IS_ENABLED()) { -#if defined(SOFTDEVICE_PRESENT) - sd_power_mode_set(NRF_POWER_MODE_LOWPWR); - sd_app_evt_wait(); -#endif - } else { - NRF_POWER->TASKS_LOWPWR = 1; - - // Note: it is not sufficient to just use WFE here, since the internal - // event register may be already set from an event that occurred in the - // past (like an SVC call to the SoftDevice) and in such case WFE will - // just clear the register and continue execution. - // Therefore, the strategy here is to first clear the event register - // by using SEV/WFE pair, and then execute WFE again, unless there is - // a pending interrupt. - - // Set an event and wake up whatsoever, this will clear the event - // register from all previous events set (SVC call included) - __SEV(); - __WFE(); - - // Test if there is an interrupt pending (mask reserved regions) - if (SCB->ICSR & (SCB_ICSR_RESERVED_BITS_MASK)) { - // Ok, there is an interrut pending, no need to go to sleep - return; - } else { - // next event will wakeup the CPU - // If an interrupt occured between the test of SCB->ICSR and this - // instruction, WFE will just not put the CPU to sleep - __WFE(); - } - } -} - -void hal_deepsleep(void) -{ - if (us_ticker_initialized) { - nrf_timer_task_trigger(NRF_TIMER1, NRF_TIMER_TASK_STOP); - } - - hal_sleep(); - - if (us_ticker_initialized) { - nrf_timer_task_trigger(NRF_TIMER1, NRF_TIMER_TASK_START); - } - // NRF_POWER->SYSTEMOFF=1; -} diff --git a/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_SDK_11/mbed_nrf_sdk_adaptation.h b/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_SDK_11/mbed_nrf_sdk_adaptation.h new file mode 100644 index 00000000000..26e9e1a4242 --- /dev/null +++ b/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_SDK_11/mbed_nrf_sdk_adaptation.h @@ -0,0 +1,32 @@ +/* mbed Microcontroller Library + * Copyright (c) 2019 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_NRF_SDK_ADAPTATION_H_ +#define MBED_NRF_SDK_ADAPTATION_H_ + +#include +#include "softdevice_handler.h" + +#ifdef SOFTDEVICE_PRESENT +static inline bool mbed_softdevice_is_enabled() +{ + return softdevice_handler_is_enabled(); +} +#else +#define mbed_softdevice_is_enabled() false +#endif + +#endif /* MBED_NRF_SDK_ADAPTATION_H_ */ diff --git a/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_SDK_14_2/mbed_nrf_sdk_adaptation.h b/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_SDK_14_2/mbed_nrf_sdk_adaptation.h new file mode 100644 index 00000000000..1074467b706 --- /dev/null +++ b/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_SDK_14_2/mbed_nrf_sdk_adaptation.h @@ -0,0 +1,32 @@ +/* mbed Microcontroller Library + * Copyright (c) 2019 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_NRF_SDK_ADAPTATION_H_ +#define MBED_NRF_SDK_ADAPTATION_H_ + +#include +#include "nrf_sdh.h" + +#ifdef SOFTDEVICE_PRESENT +static inline bool mbed_softdevice_is_enabled() +{ + return nrf_sdh_is_enabled(); +} +#else +#define mbed_softdevice_is_enabled() false +#endif + +#endif /* MBED_NRF_SDK_ADAPTATION_H_ */ diff --git a/targets/TARGET_NORDIC/TARGET_NRF5x/nordic_critical.c b/targets/TARGET_NORDIC/TARGET_NRF5x/nordic_critical.c new file mode 100644 index 00000000000..47d2165d14f --- /dev/null +++ b/targets/TARGET_NORDIC/TARGET_NRF5x/nordic_critical.c @@ -0,0 +1,136 @@ +/* + * Copyright (c) 2015-2016, ARM Limited, All Rights Reserved + * 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 +#include +#include "hal/critical_section_api.h" +#include "platform/mbed_assert.h" +#include "mbed_nrf_sdk_adaptation.h" + +/* all these variables are only modified from inside the critical section, + * whether it be __disable_irq or sd_nvic_critical_region_enter. + * + * It is safe to read state_saved as a check of critical section state, as + * it must always read as false from outside the critical section. The other + * variables are only valid when state_saved is true (which indicates we're + * in a critical section). + * + * Note that `entered_using_softdevice` is only a slight optimisation, on the + * assumption that storing this locally is likey faster than calling + * mbed_softdevice_is_enabled(). We still have to assume that the softdevice + * can't become enabled or disabled during a critical section, so this isn't + * to get a different answer from making the call. + */ +static bool state_saved; // have entered, and written other state +static bool critical_interrupts_enabled; // interrupts were enabled when we entered (ie state to restore on exit) +static bool entered_using_softdevice; // we used softdevice when entering + +static bool are_interrupts_enabled(void) +{ + return (__get_PRIMASK() & 0x1) == 0; +} + +/* Standard routines are copied from mbed_critical_section_api.c */ +static void standard_critical_section_enter(void) +{ + const bool interrupt_state = are_interrupts_enabled(); + + __disable_irq(); + + if (state_saved) { + return; + } + + critical_interrupts_enabled = interrupt_state; + entered_using_softdevice = false; + state_saved = true; +} + +static void standard_critical_section_exit(void) +{ + // Interrupts must be disabled on invoking an exit from a critical section + MBED_ASSERT(!are_interrupts_enabled()); + state_saved = false; + + // Restore the IRQs to their state prior to entering the critical section + if (critical_interrupts_enabled) { + __enable_irq(); + } +} + +/* Behaviour here carefully mirrors standard version - noting and restoring + * original state. */ +static void nordic_nvic_critical_region_enter(void) +{ + uint8_t nested; + +#ifdef MBED_DEBUG + uint32_t sd_result = sd_nvic_critical_region_enter(&nested); + // Will likely recurse, as assert failure will try to take critical region + MBED_ASSERT(sd_result == NRF_SUCCESS); +#else + (void) sd_nvic_critical_region_enter(&nested); +#endif + + if (state_saved) { + return; + } + + critical_interrupts_enabled = !nested; + entered_using_softdevice = true; + state_saved = true; +} + +static void nordic_nvic_critical_region_exit(void) +{ + state_saved = false; + +#ifdef MBED_DEBUG + uint32_t sd_result = sd_nvic_critical_region_exit(!critical_interrupts_enabled); + MBED_ASSERT(sd_result == NRF_SUCCESS); +#else + (void) sd_nvic_critical_region_exit(!critical_interrupts_enabled); +#endif +} + +void hal_critical_section_enter() +{ + if (mbed_softdevice_is_enabled()) { + nordic_nvic_critical_region_enter(); + } else { + standard_critical_section_enter(); + } +} + +void hal_critical_section_exit() +{ + MBED_ASSERT(state_saved); +#ifdef MBED_DEBUG + MBED_ASSERT(entered_using_softdevice == mbed_softdevice_is_enabled()); +#endif + if (entered_using_softdevice) { + nordic_nvic_critical_region_exit(); + } else { + standard_critical_section_exit(); + } +} + +bool hal_in_critical_section(void) +{ + return state_saved; +} + diff --git a/targets/TARGET_NORDIC/TARGET_NRF5x/sleep.c b/targets/TARGET_NORDIC/TARGET_NRF5x/sleep.c new file mode 100644 index 00000000000..d70728bb2d5 --- /dev/null +++ b/targets/TARGET_NORDIC/TARGET_NRF5x/sleep.c @@ -0,0 +1,118 @@ +/* mbed Microcontroller Library + * Copyright (c) 2006-2013 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 "sleep_api.h" +#include "cmsis.h" +#include "mbed_interface.h" +#include "mbed_nrf_sdk_adaptation.h" +#include "nrf_soc.h" +#include "nrf_timer.h" +#include "us_ticker.h" + +#define FPU_EXCEPTION_MASK 0x0000009F + +extern bool us_ticker_initialized; + +void hal_sleep(void) +{ + // ensure debug is disconnected if semihost is enabled.... + + // If the SoftDevice is enabled, its API must be used to go to sleep. + if (mbed_softdevice_is_enabled()) { + // SoftDevice documentation says this is required. This implies they + // are using WFE, which sounds like bad news for us - we want WFI + // semantics. + SCB->SCR |= SCB_SCR_SEVONPEND_Msk; + + // SoftDevice documentation suggests they are looking at pending IRQs + // to decide when to return, without caring which we actually want + // enabled. That would explain this logic, but the suggested implementation + // below would not require this. +#if defined(NRF52) || defined(NRF52840_XXAA) + /* Clear exceptions and PendingIRQ from the FPU unit */ + __set_FPSCR(__get_FPSCR() & ~(FPU_EXCEPTION_MASK)); + (void) __get_FPSCR(); + NVIC_ClearPendingIRQ(FPU_IRQn); +#endif + + // We can only hope that if they are using WFE, they have a reliable + // mechanism. The "nosd" variants with just a lone WFE visible in some + // directories are certainly not. + // + // I think two possible viable implementations are: + // static inline bool app_irq_pending(void) + // { + // return (NVIC->ICPR[0] & nrf_nvic_state.__irq_masks[0]) || + // (NVIC->ICPR[1] & nrf_nvic_state.__irq_masks[1]); + // } + // + // void sd_app_evt_wait(void) + // { + // // Function as WFI (only return when we have an IRQ pending) + // // or WFE (return when event flag set, maybe spuriously), + // // depending on cr_flag + // if (nrf_nvic_state.__cr_flag) { + // while (!app_irq_pending()) { + // __DSB(); __WFE(); // relies on SEVONPEND + // } + // } else { + // __DSB(); __WFE(); + // } + // } + // + // or, avoiding need for SEVONPEND: + // + // void sd_app_evt_wait(void) + // { + // // Function as WFI or WFE, depending on cr_flag + // if (nrf_nvic_state.__cr_flag) { + // while (!app_irq_pending()) { + // __disable_irq(); + // NVIC->ISER[0] = nrf_nvic_state.__irq_masks[0]; + // NVIC->ISER[1] = nrf_nvic_state.__irq_masks[1]; + // __DSB(); __WFI(); + // NVIC->ICER[0] = __NRF_NVIC_APP_IRQS_0; + // NVIC->ICER[1] = __NRF_NVIC_APP_IRQS_1; + // __enable_irq(); + // __ISB(); + // } + // } else { + // __DSB(); __WFE(); + // } + // } + // + sd_power_mode_set(NRF_POWER_MODE_LOWPWR); + sd_app_evt_wait(); + } else { + NRF_POWER->TASKS_LOWPWR = 1; + + __DSB(); + __WFI(); + } +} + +void hal_deepsleep(void) +{ + if (us_ticker_initialized) { + nrf_timer_task_trigger(NRF_TIMER1, NRF_TIMER_TASK_STOP); + } + + hal_sleep(); + + if (us_ticker_initialized) { + nrf_timer_task_trigger(NRF_TIMER1, NRF_TIMER_TASK_START); + } + // NRF_POWER->SYSTEMOFF=1; +}