diff --git a/CODEOWNERS b/CODEOWNERS index 90dfff8726e9..ab693fcf3a1e 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -233,6 +233,7 @@ Kconfig* @tejlmand /subsys/nrf_profiler/ @pdunaj /subsys/shell/ @nordic-krch /subsys/nonsecure/ @SebastianBoe +/subsys/net_core_monitor/ @maje-emb /modules/ @tejlmand /modules/tfm/ @SebastianBoe @joerchan /subsys/zigbee/ @tomchy @sebastiandraus diff --git a/doc/nrf/libraries/others/images/ncm_register.svg b/doc/nrf/libraries/others/images/ncm_register.svg new file mode 100644 index 000000000000..dd5b0f479afc --- /dev/null +++ b/doc/nrf/libraries/others/images/ncm_register.svg @@ -0,0 +1,118 @@ + + + + + + + + Page-1 + + Sheet.158 + + Dynamic connector.37 + + Sheet.38 + + + + + + Dynamic connector.39 + + Sheet.40 + + + + + + Dynamic connector.41 + + Sheet.42 + + + + + + Dynamic connector.43 + + Sheet.44 + + + + + + Dynamic connector.45 + + Sheet.46 + + + + + + Sheet.145 + 31 + + 31 + + Sheet.148 + 31 + + 31 + + Sheet.149 + 16 + + 16 + + Sheet.150 + 0 + + 0 + + Sheet.151 + 0 + + 0 + + Sheet.152 + RESETREAS + + RESETREAS + + Sheet.153 + FLAGS + + FLAGS + + Sheet.154 + COUNTER + + COUNTER + + Sheet.155 + GPMEM[0] + + GPMEM[0] + + Sheet.157 + GPMEM[1] + + GPMEM[1] + + + diff --git a/doc/nrf/libraries/others/network_core_monitor.rst b/doc/nrf/libraries/others/network_core_monitor.rst new file mode 100644 index 000000000000..8494a1f92d70 --- /dev/null +++ b/doc/nrf/libraries/others/network_core_monitor.rst @@ -0,0 +1,115 @@ +.. _network_core_monitor: + +Network core monitor +#################### + +.. contents:: + :local: + :depth: 2 + +This library monitors the network core status of the nRF5340 processor. + +Overview +******** + +It detects suspensions and resets in the network processor. +When a reset occurs, you can read the cause of the reset. + +Implementation +============== + +The library uses two general-purpose registers of the IPC peripheral in the application core and writes the state of the network processor to them. + +.. figure:: images/ncm_register.svg + :alt: Application of IPC peripheral registers + + IPC peripheral registers + +The ``GPMEM[0]`` register is divided into two 16-bit parts. + +The ``COUNTER`` value is incremented by the network core every :kconfig:option:`CONFIG_NCM_FEEDING_INTERVAL_MSEC`. +If the network core is suspended, the counter value is not updated. + +The ``FLAGS`` field has the ``Reset`` flag as the bit 0. + +The reset bit is set at the boot of the network core. +It informs the application core that a reset of the network core has occurred. + +During the startup of the network core, the reset bit is set and the cause of the reset is written to the IPC register ``GPMEM[1]``. +This value is rewritten from the network core's ``RESET.RESETREAS`` register. +For a detailed description of the bits in this register, see the `RESETREAS`_ section for nRF5340. + +The :c:func:`ncm_net_core_event_handler` function may be implemented by your application. +On the application core, the network core monitor checks the values of IPC registers written by the network core every :kconfig:option:`CONFIG_NCM_FEEDING_INTERVAL_MSEC`. +If the network core malfunctions and fails to increment the ``COUNTER`` value, the :c:func:`ncm_net_core_event_handler` function is called on the application core. +This function is also called when the network core is restarted. +The network core monitor provides a ``__weak`` implementation of that function in the :file:`nrf/subsys/net_core_monitor/app_core.c` file. + +The following events are supported and also listed in the :file:`nrf/include/net_core_monitor.h` file: + +* :c:macro:`NCM_EVT_NET_CORE_RESET` + + * Event triggered when a network core reset occurs. + * The ``reset_reas`` variable holds the reason for the reset. + It is rewritten from the ``RESET.RESETREAS`` register. + +* :c:macro:`NCM_EVT_NET_CORE_FREEZE` + + * Event triggered when the network core is not responding. + +Configuration +************* + +To enable this library, set the :kconfig:option:`CONFIG_NET_CORE_MONITOR` Kconfig option to ``y`` on both network and application cores. + +The :kconfig:option:`CONFIG_NCM_APP_FEEDING_INTERVAL_MSEC` Kconfig option specifies how often the counter is updated by the network core. +The default value is 500 milliseconds. + +The :kconfig:option:`CONFIG_NCM_RESET_INIT_PRIORITY` Kconfig option sets priority for the initialization function. +The function reads the cause of the processor reset and passes this information to the application core. +It is executed at the network core boot. + +Usage +***** + +To enable the Network core monitor library, set the :kconfig:option:`CONFIG_NET_CORE_MONITOR` Kconfig option. + +The :c:func:`ncm_net_core_event_handler` function can be used to notify the application about the status of the network core. +To define the user action for the event, you must override the ``__weak`` function definition of :c:func:`ncm_net_core_event_handler`. +Otherwise, the ``__weak``` definition is called and it prints information about the event. + +See the following usage example. + +.. code-block:: + + #include "net_core_monitor.h" + ... + /* This is the override for the __weak handler. */ + void ncm_net_core_event_handler(enum ncm_event_type event, uint32_t reset_reas) + { + switch (event) { + case NCM_EVT_NET_CORE_RESET: + printk("The network core reset.\n"); + /* do something */ + break; + case NCM_EVT_NET_CORE_FREEZE: + printk("The network core is not responding.\n"); + /* do something */ + break; + } + } + +Dependencies +************ + +The module uses two general-purpose registers, ``GPMEM[0]`` and ``GPMEM[1]``, of the application core's IPC peripheral. + +API documentation +***************** + +| Header file: :file:`include/net_core_monitor.h` +| Source files: :file:`subsys/net_core_monitor/` + +.. doxygengroup:: net_core_monitor + :project: nrf + :members: diff --git a/doc/nrf/links.txt b/doc/nrf/links.txt index a4e38bab13b1..a04eeb465cb2 100644 --- a/doc/nrf/links.txt +++ b/doc/nrf/links.txt @@ -490,6 +490,8 @@ .. _`nRF5340 Audio DK Hardware`: https://infocenter.nordicsemi.com/topic/ug_nrf5340_audio/UG/nrf5340_audio/intro.html .. _`Requirements for external flash memory DFU`: https://infocenter.nordicsemi.com/topic/ug_nrf5340_audio/UG/nrf5340_audio/hw_external_memory.html +.. _`RESETREAS`: https://infocenter.nordicsemi.com/index.jsp?topic=%2Fps_nrf5340%2Fchapters%2Freset%2Fdoc%2Freset.html&cp=4_0_0_3_9_10_0&anchor=register.RESETREAS + .. _`nRF52840 Product Specification`: https://infocenter.nordicsemi.com/topic/ps_nrf52840/keyfeatures_html5.html .. _`System OFF mode`: https://infocenter.nordicsemi.com/topic/ps_nrf52840/power.html?cp=5_0_0_4_2_2#unique_220399309 .. _`nRF52840 DK User Guide`: https://infocenter.nordicsemi.com/topic/ug_nrf52840_dk/UG/dk/intro.html diff --git a/include/debug/ppi_trace.h b/include/debug/ppi_trace.h index bba9c97139dc..76c82cab4c83 100644 --- a/include/debug/ppi_trace.h +++ b/include/debug/ppi_trace.h @@ -6,6 +6,8 @@ #ifndef __PPI_TRACE_H #define __PPI_TRACE_H +#include + #ifdef __cplusplus extern "C" { #endif @@ -45,6 +47,25 @@ void *ppi_trace_config(uint32_t pin, uint32_t evt); */ void *ppi_trace_pair_config(uint32_t pin, uint32_t start_evt, uint32_t stop_evt); +/** @brief Configure and enable a PPI trace pin for tracing a DPPI channel. + * + * This function allows to trace DPPI triggers without knowing any events being the source of the + * trigger. Configuration of events so that they publish to the given DPPI and enabling the DPPI is + * out of scope of this function and must be done externally. + * This function allows also to trace DPPI channels which are triggered by multiple events or + * the set of events publishing to the DPPI channel changes in run-time. + * + * @note Supported only on platforms equipped with DPPI. + * + * @param pin Pin to use for tracing. + * @param dppi_ch DPPI channel number to be traced on the pin. + * + * @retval 0 The configuration succeeded. + * @retval -ENOMEM The configuration failed, due to lack of necessary resources. + * @retval -ENOTSUP The function is not supported on current hardware platform. + */ +int ppi_trace_dppi_ch_trace(uint32_t pin, uint32_t dppi_ch); + /** @brief Enable PPI trace pin. * * @param handle Handle. diff --git a/include/net_core_monitor.h b/include/net_core_monitor.h new file mode 100644 index 000000000000..3b30ec156d92 --- /dev/null +++ b/include/net_core_monitor.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2023 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +#ifndef NET_CORE_MONITOR_H_ +#define NET_CORE_MONITOR_H_ + +/** + * @file + * @defgroup net_core_monitor Network Core Monitor API + * @{ + * @brief API for the Network Core Monitor. + */ + +#ifdef __cplusplus +extern "C" { +#endif + +/** @brief Network core monitor event types, used to signal the application. */ +enum ncm_event_type { + /** Event triggered when a network core reset occurs. + * The ``reset_reas`` variable holds the reason for the reset. + * It is rewritten from the RESET.RESETREAS register. + */ + NCM_EVT_NET_CORE_RESET, + + /** Event triggered when the network core is not responding. */ + NCM_EVT_NET_CORE_FREEZE +}; + +/** @brief Event handler that is called by the Network core monitor library when an event occurs. + * + * @note This function should be defined by the application. + * Otherwise, `__weak` definition will called and it prints information about the event. + * + * @param[out] event Event occurring. + * @param[out] reset_reas Reason for network core reset. + * When the NCM_EVT_NET_CORE_RESET event was triggered the variable is set. + * This value is rewritten from the network core's RESET.RESETREAS register. + */ +extern void ncm_net_core_event_handler(enum ncm_event_type event, uint32_t reset_reas); + +#ifdef __cplusplus +} +#endif + +/** + * @} + */ + +#endif /* NET_CORE_MONITOR_H_ */ diff --git a/modules/nrfxlib/nrf_802154/sl/platform/nrf_802154_platform_sl_lptimer_zephyr.c b/modules/nrfxlib/nrf_802154/sl/platform/nrf_802154_platform_sl_lptimer_zephyr.c index 9878ad2118ca..d2d97a5abbf7 100644 --- a/modules/nrfxlib/nrf_802154/sl/platform/nrf_802154_platform_sl_lptimer_zephyr.c +++ b/modules/nrfxlib/nrf_802154/sl/platform/nrf_802154_platform_sl_lptimer_zephyr.c @@ -11,12 +11,35 @@ #include #include +#include +#include #include #include "platform/nrf_802154_clock.h" #include "nrf_802154_sl_utils.h" #define RTC_CHAN_INVALID (-1) +#if defined(CONFIG_SOC_NRF5340_CPUNET) + +BUILD_ASSERT(CONFIG_MPSL); + +#define HW_TASK_RTC NRF_RTC0 +#define HW_TASK_RTC_CHAN (3) + +#define RTC_COUNTER_BIT_WIDTH 24U +#define RTC_COUNTER_SPAN BIT(RTC_COUNTER_BIT_WIDTH) +#define RTC_COUNTER_MAX (RTC_COUNTER_SPAN - 1U) +#define RTC_COUNTER_HALF_SPAN (RTC_COUNTER_SPAN / 2U) + +#define RTC_MIN_CYCLES_FROM_NOW 3 +#endif + +/** @brief Macro for creating the interrupt bitmask for the specified compare channel. */ +#define NRF_RTC_CHANNEL_INT_MASK(ch) ((uint32_t)(NRF_RTC_INT_COMPARE0_MASK) << (ch)) + +/** @brief Macro for obtaining the compare event for the specified channel. */ +#define NRF_RTC_CHANNEL_EVENT_ADDR(ch) \ + (nrf_rtc_event_t)((NRF_RTC_EVENT_COMPARE_0) + (ch) * sizeof(uint32_t)) struct timer_desc { z_nrf_rtc_timer_compare_handler_t handler; @@ -98,40 +121,152 @@ static void timer_start_at(struct timer_desc *timer, uint64_t target_time) nrf_802154_sl_mcu_critical_exit(state); } +#if defined(CONFIG_SOC_NRF5340_CPUNET) +static inline uint32_t rtc_counter_diff(uint32_t a, uint32_t b) +{ + return (a - b) & RTC_COUNTER_MAX; +} +#endif + static bool hw_task_state_set(enum hw_task_state_type expected_state, enum hw_task_state_type new_state) { return atomic_cas(&m_hw_task.state, expected_state, new_state); } -static void cc_bind_to_ppi(int32_t cc_num, uint32_t ppi_num) +static inline uint32_t hw_task_rtc_get_compare_evt_address(int32_t cc_num) { - uint32_t event_address = z_nrf_rtc_timer_compare_evt_address_get(cc_num); +#if defined(CONFIG_SOC_NRF5340_CPUNET) + return nrf_rtc_event_address_get(HW_TASK_RTC, nrf_rtc_compare_event_get(cc_num)); +#else + return z_nrf_rtc_timer_compare_evt_address_get(cc_num); +#endif +} + +static void hw_task_rtc_cc_bind_to_ppi(int32_t cc_num, uint32_t ppi_num) +{ + uint32_t event_address = hw_task_rtc_get_compare_evt_address(cc_num); nrfx_gppi_event_endpoint_setup(ppi_num, event_address); } -static void cc_unbind(int32_t cc_num, uint32_t ppi_num) +static void hw_task_rtc_cc_unbind(int32_t cc_num, uint32_t ppi_num) { if (ppi_num != NRF_802154_SL_HW_TASK_PPI_INVALID) { - uint32_t event_address = z_nrf_rtc_timer_compare_evt_address_get(cc_num); + uint32_t event_address = hw_task_rtc_get_compare_evt_address(cc_num); nrfx_gppi_event_endpoint_clear(ppi_num, event_address); } } -static bool cc_event_check(int32_t cc_num) +static bool hw_task_rtc_cc_event_check(int32_t cc_num) { - uint32_t event_address = z_nrf_rtc_timer_compare_evt_address_get(cc_num); + uint32_t event_address = hw_task_rtc_get_compare_evt_address(cc_num); return *(volatile uint32_t *)event_address; } +static void hw_task_rtc_timer_abort(void) +{ +#if defined(CONFIG_SOC_NRF5340_CPUNET) + /* No interrupt disabling/clearing is needed, as HW task does not use interrupts*/ + nrf_rtc_event_disable(HW_TASK_RTC, NRF_RTC_CHANNEL_INT_MASK(m_hw_task.chan)); + nrf_rtc_event_clear(HW_TASK_RTC, NRF_RTC_CHANNEL_EVENT_ADDR(m_hw_task.chan)); +#else + z_nrf_rtc_timer_abort(m_hw_task.chan); +#endif +} + +#if defined(CONFIG_SOC_NRF5340_CPUNET) +static uint32_t zephyr_rtc_ticks_to_hw_task_rtc_counter(uint64_t ticks) +{ + uint64_t zephyr_rtc_counter_now = 0; + uint32_t hw_task_rtc_counter_now = 0; + uint32_t hw_task_rtc_counter_now_2 = 0; + + /** + * The RTC0 counter is read twice - once before and once after reading RTC1. + * If the two reads are different, the procedure is repeated. + * This is to make sure that no RTC tick occurred between reading RTC0 and + * RTC1 and so the calculated difference between the timers is correct. + */ + do { + hw_task_rtc_counter_now = nrf_rtc_counter_get(HW_TASK_RTC); + zephyr_rtc_counter_now = z_nrf_rtc_timer_read(); + barrier_dmem_fence_full(); + hw_task_rtc_counter_now_2 = nrf_rtc_counter_get(HW_TASK_RTC); + + } while (hw_task_rtc_counter_now != hw_task_rtc_counter_now_2); + + /* This function can only be called in close proximity of ticks */ + assert(ticks >= zephyr_rtc_counter_now + || zephyr_rtc_counter_now - ticks < RTC_COUNTER_HALF_SPAN); + assert(ticks <= zephyr_rtc_counter_now + || ticks - zephyr_rtc_counter_now < RTC_COUNTER_HALF_SPAN); + + uint32_t diff = rtc_counter_diff(hw_task_rtc_counter_now, + (uint32_t) zephyr_rtc_counter_now % RTC_COUNTER_SPAN); + + return (uint32_t) ((ticks + diff) % RTC_COUNTER_SPAN); +} + +static int hw_task_rtc_counter_compare_set(uint32_t cc_value) +{ + uint32_t current_counter_value = nrf_rtc_counter_get(HW_TASK_RTC); + + /* As the HW tasks are setup only shortly before they are triggered, + * it is certain that the required fire time is not further away + * than one half of the RTC counter span. + * If it is it means that the trigger time is already in the past. + */ + if (rtc_counter_diff(cc_value, current_counter_value) > RTC_COUNTER_HALF_SPAN) { + return -EINVAL; + } + + nrf_rtc_event_disable(HW_TASK_RTC, NRF_RTC_CHANNEL_INT_MASK(m_hw_task.chan)); + nrf_rtc_event_clear(HW_TASK_RTC, NRF_RTC_CHANNEL_EVENT_ADDR(m_hw_task.chan)); + + nrf_rtc_cc_set(HW_TASK_RTC, m_hw_task.chan, cc_value); + + nrf_rtc_event_enable(HW_TASK_RTC, NRF_RTC_CHANNEL_INT_MASK(m_hw_task.chan)); + + current_counter_value = nrf_rtc_counter_get(HW_TASK_RTC); + + if (rtc_counter_diff(cc_value, current_counter_value + RTC_MIN_CYCLES_FROM_NOW) + > (RTC_COUNTER_HALF_SPAN - RTC_MIN_CYCLES_FROM_NOW)) { + nrf_rtc_event_disable(HW_TASK_RTC, NRF_RTC_CHANNEL_INT_MASK(m_hw_task.chan)); + nrf_rtc_event_clear(HW_TASK_RTC, NRF_RTC_CHANNEL_EVENT_ADDR(m_hw_task.chan)); + return -EINVAL; + } + + return 0; +} +#endif + +static int hw_task_rtc_timer_set(uint64_t fire_lpticks) +{ +#if defined(CONFIG_SOC_NRF5340_CPUNET) + uint32_t hw_task_rtc_counter_ticks = zephyr_rtc_ticks_to_hw_task_rtc_counter(fire_lpticks); + + return hw_task_rtc_counter_compare_set(hw_task_rtc_counter_ticks); +#else + return z_nrf_rtc_timer_exact_set(m_hw_task.chan, fire_lpticks, NULL, NULL); +#endif +} + void nrf_802154_platform_sl_lp_timer_init(void) { m_in_critical_section = false; m_hw_task.state = HW_TASK_STATE_IDLE; - m_hw_task.chan = RTC_CHAN_INVALID; +#if defined(CONFIG_SOC_NRF5340_CPUNET) + m_hw_task.chan = HW_TASK_RTC_CHAN; +#else + m_hw_task.chan = z_nrf_rtc_timer_chan_alloc(); + if (m_hw_task.chan < 0) { + assert(false); + return; + } +#endif m_hw_task.ppi = NRF_802154_SL_HW_TASK_PPI_INVALID; m_timer.handler = timer_handler; m_sync_timer.handler = sync_timer_handler; @@ -159,7 +294,9 @@ void nrf_802154_platform_sl_lp_timer_deinit(void) z_nrf_rtc_timer_chan_free(m_sync_timer.chan); if (m_hw_task.chan != RTC_CHAN_INVALID) { +#if !defined(CONFIG_SOC_NRF5340_CPUNET) z_nrf_rtc_timer_chan_free(m_hw_task.chan); +#endif m_hw_task.chan = RTC_CHAN_INVALID; } } @@ -270,30 +407,18 @@ nrf_802154_sl_lptimer_platform_result_t nrf_802154_platform_sl_lptimer_hw_task_p return NRF_802154_SL_LPTIMER_PLATFORM_NO_RESOURCES; } - /* Allocate the channel if not already done. */ - if (m_hw_task.chan == RTC_CHAN_INVALID) { - /* The channel allocation cannot take place during module - * initialization, because for nRF53 it would run - * out of resources - some other module temporarily needs - * an rtc channel to initialize. For this reason, this is where - * the allocation is made. - * Once a channel has been successfully allocated, it will be held - * until nrf_802154_platform_sl_lp_timer_deinit is called. - */ - m_hw_task.chan = z_nrf_rtc_timer_chan_alloc(); - if (m_hw_task.chan < 0) { - m_hw_task.chan = RTC_CHAN_INVALID; - hw_task_state_set(HW_TASK_STATE_SETTING_UP, HW_TASK_STATE_IDLE); - return NRF_802154_SL_LPTIMER_PLATFORM_NO_RESOURCES; - } - } - - if (z_nrf_rtc_timer_set(m_hw_task.chan, fire_lpticks, NULL, NULL) != 0) { + if (hw_task_rtc_timer_set(fire_lpticks) != 0) { hw_task_state_set(HW_TASK_STATE_SETTING_UP, HW_TASK_STATE_IDLE); - return NRF_802154_SL_LPTIMER_PLATFORM_TOO_DISTANT; + uint64_t now = z_nrf_rtc_timer_read(); + + if ((fire_lpticks > now) && + (fire_lpticks - now > NRF_RTC_TIMER_MAX_SCHEDULE_SPAN/2)) { + return NRF_802154_SL_LPTIMER_PLATFORM_TOO_DISTANT; + } + return NRF_802154_SL_LPTIMER_PLATFORM_TOO_LATE; } - evt_address = z_nrf_rtc_timer_compare_evt_address_get(m_hw_task.chan); + evt_address = hw_task_rtc_get_compare_evt_address(m_hw_task.chan); nrf_802154_sl_mcu_critical_enter(mcu_cs_state); @@ -301,9 +426,9 @@ nrf_802154_sl_lptimer_platform_result_t nrf_802154_platform_sl_lptimer_hw_task_p if ((z_nrf_rtc_timer_read() + 2) > fire_lpticks) { /* it is too late */ nrf_802154_sl_mcu_critical_exit(mcu_cs_state); - cc_unbind(m_hw_task.chan, ppi_channel); + hw_task_rtc_cc_unbind(m_hw_task.chan, ppi_channel); m_hw_task.ppi = NRF_802154_SL_HW_TASK_PPI_INVALID; - z_nrf_rtc_timer_abort(m_hw_task.chan); + hw_task_rtc_timer_abort(); hw_task_state_set(HW_TASK_STATE_SETTING_UP, HW_TASK_STATE_IDLE); return NRF_802154_SL_LPTIMER_PLATFORM_TOO_LATE; } @@ -324,9 +449,9 @@ nrf_802154_sl_lptimer_platform_result_t nrf_802154_platform_sl_lptimer_hw_task_c return NRF_802154_SL_LPTIMER_PLATFORM_WRONG_STATE; } - z_nrf_rtc_timer_abort(m_hw_task.chan); + hw_task_rtc_timer_abort(); - cc_unbind(m_hw_task.chan, m_hw_task.ppi); + hw_task_rtc_cc_unbind(m_hw_task.chan, m_hw_task.ppi); m_hw_task.ppi = NRF_802154_SL_HW_TASK_PPI_INVALID; hw_task_state_set(HW_TASK_STATE_CLEANING, HW_TASK_STATE_IDLE); @@ -347,10 +472,10 @@ nrf_802154_sl_lptimer_platform_result_t nrf_802154_platform_sl_lptimer_hw_task_u nrf_802154_sl_mcu_critical_enter(mcu_cs_state); - cc_bind_to_ppi(m_hw_task.chan, ppi_channel); + hw_task_rtc_cc_bind_to_ppi(m_hw_task.chan, ppi_channel); m_hw_task.ppi = ppi_channel; - cc_triggered = cc_event_check(m_hw_task.chan); + cc_triggered = hw_task_rtc_cc_event_check(m_hw_task.chan); if (z_nrf_rtc_timer_read() >= m_hw_task.fire_lpticks) { cc_triggered = true; } diff --git a/samples/nrf5340/multiprotocol_rpmsg/prj.conf b/samples/nrf5340/multiprotocol_rpmsg/prj.conf index 0ac36bed354d..ef12069ce6c4 100644 --- a/samples/nrf5340/multiprotocol_rpmsg/prj.conf +++ b/samples/nrf5340/multiprotocol_rpmsg/prj.conf @@ -23,5 +23,5 @@ CONFIG_DEBUG_INFO=y CONFIG_EXCEPTION_STACK_TRACE=y CONFIG_NRF_802154_SER_RADIO=y -CONFIG_NRF_RTC_TIMER_USER_CHAN_COUNT=3 +CONFIG_NRF_RTC_TIMER_USER_CHAN_COUNT=2 CONFIG_IPC_SERVICE_BACKEND_RPMSG=y diff --git a/samples/peripheral/802154_phy_test/boards/nrf5340dk_nrf5340_cpunet.conf b/samples/peripheral/802154_phy_test/boards/nrf5340dk_nrf5340_cpunet.conf index 959584867a5d..c13e99e80c29 100644 --- a/samples/peripheral/802154_phy_test/boards/nrf5340dk_nrf5340_cpunet.conf +++ b/samples/peripheral/802154_phy_test/boards/nrf5340dk_nrf5340_cpunet.conf @@ -1,2 +1,4 @@ # Include empty image in the APP core CONFIG_NCS_SAMPLE_EMPTY_APP_CORE_CHILD_IMAGE=y + +CONFIG_NRF_RTC_TIMER_USER_CHAN_COUNT=2 diff --git a/samples/zigbee/light_switch/child_image/multiprotocol_rpmsg/boards/nrf5340dk_nrf5340_cpunet_fota.conf b/samples/zigbee/light_switch/child_image/multiprotocol_rpmsg/boards/nrf5340dk_nrf5340_cpunet_fota.conf index 772e394e3fa0..bb3baf56c83c 100644 --- a/samples/zigbee/light_switch/child_image/multiprotocol_rpmsg/boards/nrf5340dk_nrf5340_cpunet_fota.conf +++ b/samples/zigbee/light_switch/child_image/multiprotocol_rpmsg/boards/nrf5340dk_nrf5340_cpunet_fota.conf @@ -29,5 +29,5 @@ CONFIG_DEBUG_INFO=y CONFIG_EXCEPTION_STACK_TRACE=y CONFIG_NRF_802154_SER_RADIO=y -CONFIG_NRF_RTC_TIMER_USER_CHAN_COUNT=3 +CONFIG_NRF_RTC_TIMER_USER_CHAN_COUNT=2 CONFIG_IPC_SERVICE_BACKEND_RPMSG=y diff --git a/subsys/CMakeLists.txt b/subsys/CMakeLists.txt index 7d7cfac757f2..332e0539ec5c 100644 --- a/subsys/CMakeLists.txt +++ b/subsys/CMakeLists.txt @@ -68,3 +68,4 @@ add_subdirectory_ifdef(CONFIG_NRF_RPC nrf_rpc) add_subdirectory_ifdef(CONFIG_NRF_802154_RADIO_DRIVER ieee802154) add_subdirectory_ifdef(CONFIG_NRF_DM dm) add_subdirectory_ifdef(CONFIG_EMDS emds) +add_subdirectory_ifdef(CONFIG_NET_CORE_MONITOR net_core_monitor) diff --git a/subsys/Kconfig b/subsys/Kconfig index 558b1378a138..ccf7c514c984 100644 --- a/subsys/Kconfig +++ b/subsys/Kconfig @@ -54,3 +54,5 @@ rsource "caf/Kconfig" rsource "ieee802154/Kconfig" rsource "dm/Kconfig" + +rsource "net_core_monitor/Kconfig" diff --git a/subsys/debug/ppi_trace/ppi_trace.c b/subsys/debug/ppi_trace/ppi_trace.c index f2b312cec268..ea14563203c6 100644 --- a/subsys/debug/ppi_trace/ppi_trace.c +++ b/subsys/debug/ppi_trace/ppi_trace.c @@ -196,6 +196,33 @@ void *ppi_trace_pair_config(uint32_t pin, uint32_t start_evt, uint32_t stop_evt) #endif } +int ppi_trace_dppi_ch_trace(uint32_t pin, uint32_t dppi_ch) +{ +#ifdef DPPI_PRESENT + uint32_t task; + int gpiote_ch; + nrf_gpiote_task_t task_id; + + gpiote_ch = gpiote_channel_alloc(pin); + if (gpiote_ch < 0) { + LOG_ERR("Failed to allocate GPIOTE channel."); + return -ENOMEM; + } + + task_id = offsetof(NRF_GPIOTE_Type, TASKS_OUT[gpiote_ch]); + task = nrf_gpiote_task_address_get(NRF_GPIOTE, task_id); + + *SUBSCRIBE_ADDR(task) = DPPIC_SUBSCRIBE_CHG_EN_EN_Msk | dppi_ch; + + return 0; +#else + (void)pin; + (void)dppi_ch; + + return -ENOTSUP; +#endif +} + static uint32_t ppi_channel_mask_get(void *handle) { return IS_PAIR(handle) ? diff --git a/subsys/mpsl/init/mpsl_init.c b/subsys/mpsl/init/mpsl_init.c index 8f958d825964..0015e258fa40 100644 --- a/subsys/mpsl/init/mpsl_init.c +++ b/subsys/mpsl/init/mpsl_init.c @@ -26,6 +26,8 @@ LOG_MODULE_REGISTER(mpsl_init, CONFIG_MPSL_LOG_LEVEL); const uint32_t z_mpsl_used_nrf_ppi_channels = MPSL_RESERVED_PPI_CHANNELS; const uint32_t z_mpsl_used_nrf_ppi_groups; +extern void rtc_pretick_rtc0_isr_hook(void); + #if IS_ENABLED(CONFIG_SOC_COMPATIBLE_NRF52X) #define MPSL_LOW_PRIO_IRQn SWI5_IRQn #elif IS_ENABLED(CONFIG_SOC_SERIES_NRF53X) @@ -87,6 +89,11 @@ static void mpsl_rtc0_isr_wrapper(const void *args) { ARG_UNUSED(args); + if (IS_ENABLED(CONFIG_SOC_NRF53_RTC_PRETICK) && + IS_ENABLED(CONFIG_SOC_NRF5340_CPUNET)) { + rtc_pretick_rtc0_isr_hook(); + } + MPSL_IRQ_RTC0_Handler(); ISR_DIRECT_PM(); @@ -139,6 +146,10 @@ ISR_DIRECT_DECLARE(mpsl_timer0_isr_wrapper) ISR_DIRECT_DECLARE(mpsl_rtc0_isr_wrapper) { + if (IS_ENABLED(CONFIG_SOC_NRF53_RTC_PRETICK) && + IS_ENABLED(CONFIG_SOC_NRF5340_CPUNET)) { + rtc_pretick_rtc0_isr_hook(); + } MPSL_IRQ_RTC0_Handler(); ISR_DIRECT_PM(); diff --git a/subsys/net_core_monitor/CMakeLists.txt b/subsys/net_core_monitor/CMakeLists.txt new file mode 100644 index 000000000000..b6a2be62b2e7 --- /dev/null +++ b/subsys/net_core_monitor/CMakeLists.txt @@ -0,0 +1,13 @@ +# +# Copyright (c) 2023 Nordic Semiconductor +# +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause +# + +if(CONFIG_SOC_NRF5340_CPUAPP) + zephyr_library_sources(app_core.c) +endif() + +if(CONFIG_SOC_NRF5340_CPUNET) + zephyr_library_sources(net_core.c) +endif() diff --git a/subsys/net_core_monitor/Kconfig b/subsys/net_core_monitor/Kconfig new file mode 100644 index 000000000000..c80d8dc34489 --- /dev/null +++ b/subsys/net_core_monitor/Kconfig @@ -0,0 +1,39 @@ +# +# Copyright (c) 2023 Nordic Semiconductor +# +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause +# + +menuconfig NET_CORE_MONITOR + bool "NCM (Network Core Monitor) module [EXPERIMENTAL]" + select EXPERIMENTAL + depends on (SOC_NRF5340_CPUAPP || SOC_NRF5340_CPUNET) + help + Enable the Network Core Monitor module. + To define the user action for event, you need to override the + weak function definition of the ncm_net_core_event_handler. + +if NET_CORE_MONITOR + +menu "Net Core Monitor" + +config NCM_FEEDING_INTERVAL_MSEC + default 500 if SOC_NRF5340_CPUNET + default 1000 if SOC_NRF5340_CPUAPP + int "Feeding interval in milliseconds" + help + The value of this parameter on the application core must be greater + than the value on the network core. Otherwise the network core monitor + will report false positive network code malfunctions. + +config NCM_RESET_INIT_PRIORITY + int "Reset init priority" + default KERNEL_INIT_PRIORITY_DEFAULT + +endmenu + +module = NET_CORE_MONITOR +module-str = NET_CORE_MONITOR +source "${ZEPHYR_BASE}/subsys/logging/Kconfig.template.log_config" + +endif #NET_CORE_MONITOR diff --git a/subsys/net_core_monitor/app_core.c b/subsys/net_core_monitor/app_core.c new file mode 100644 index 000000000000..9eeedf90021e --- /dev/null +++ b/subsys/net_core_monitor/app_core.c @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2023 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +#include +#include +#include +#include +#include +#include "common.h" +#include +#include "net_core_monitor.h" + +#include + +LOG_MODULE_REGISTER(net_core_monitor, CONFIG_NET_CORE_MONITOR_LOG_LEVEL); + +#define IPC_MEM_CNT_IDX 0 +#define IPC_MEM_REAS_IDX 1 + +#define NET_CORE_CHECK_INTERVAL_MSEC CONFIG_NCM_FEEDING_INTERVAL_MSEC + +static void ncm_work_handler(struct k_work *work); +static K_WORK_DELAYABLE_DEFINE(ncm_work, ncm_work_handler); + +static int ncm_net_status_check(uint32_t * const reset_reas) +{ + uint32_t gpmem; + uint16_t cnt; + static uint16_t prv_cnt; + + gpmem = nrfx_ipc_gpmem_get(IPC_MEM_CNT_IDX); + cnt = (uint16_t)((gpmem & CNT_MSK) >> CNT_POS); + + if (gpmem & (FLAGS_RESET << FLAGS_POS)) { + gpmem &= ~(FLAGS_RESET << FLAGS_POS); + nrfx_ipc_gpmem_set(IPC_MEM_CNT_IDX, gpmem); + + /* Read the reason for the reset. */ + gpmem = nrfx_ipc_gpmem_get(IPC_MEM_REAS_IDX); + nrfx_ipc_gpmem_set(IPC_MEM_REAS_IDX, 0); + + if (reset_reas) { + *reset_reas = gpmem; + } + + prv_cnt = 0; + return -EFAULT; + } + + if (prv_cnt == cnt) { + return -EBUSY; + } + + prv_cnt = cnt; + return 0; +} + +static void ncm_work_handler(struct k_work *work) +{ + int ret; + uint32_t reset_reas; + + ret = ncm_net_status_check(&reset_reas); + + if (ret == -EBUSY) { + ncm_net_core_event_handler(NCM_EVT_NET_CORE_FREEZE, 0); + } else if (ret == -EFAULT) { + ncm_net_core_event_handler(NCM_EVT_NET_CORE_RESET, reset_reas); + } else { + /* Nothing to do. */ + } + + k_work_reschedule(&ncm_work, K_MSEC(NET_CORE_CHECK_INTERVAL_MSEC)); +} + +__weak void ncm_net_core_event_handler(enum ncm_event_type event, uint32_t reset_reas) +{ + switch (event) { + case NCM_EVT_NET_CORE_RESET: + LOG_DBG("The network core reset."); + if (reset_reas & NRF_RESET_RESETREAS_RESETPIN_MASK) { + LOG_DBG("Reset by pin-reset."); + } else if (reset_reas & NRF_RESET_RESETREAS_DOG0_MASK) { + LOG_DBG("Reset by application watchdog timer 0."); + } else if (reset_reas & NRF_RESET_RESETREAS_SREQ_MASK) { + LOG_DBG("Reset by soft-reset"); + } else if (reset_reas) { + LOG_DBG("Reset by a different source (0x%08X).", reset_reas); + } else { + /* Nothing to do. */ + } + + break; + case NCM_EVT_NET_CORE_FREEZE: + LOG_DBG("The network core is not responding."); + break; + } +} + +static int app_init(void) +{ + LOG_DBG("Network Core Monitor Init"); + k_work_schedule(&ncm_work, K_MSEC(NET_CORE_CHECK_INTERVAL_MSEC)); + return 0; +} + +SYS_INIT(app_init, APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT); diff --git a/subsys/net_core_monitor/common.h b/subsys/net_core_monitor/common.h new file mode 100644 index 000000000000..460564e8aeae --- /dev/null +++ b/subsys/net_core_monitor/common.h @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2023 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +#ifndef COMMON_H_ +#define COMMON_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#define CNT_POS (0UL) /* Position of Counter field. */ +#define CNT_MSK (0xFFFF << CNT_POS) /* Bit mask of Counter field. */ +#define FLAGS_POS (16UL) /* Position of Flags field. */ +#define FLAGS_MSK (0xFFFF << FLAGS_POS) /* Bit mask of Flags field. */ +#define FLAGS_RESET BIT(0) /* Reset bit. */ +#define CNT_INIT_VAL (0x0055) /* Initialization value for counter. */ + +#define IPC_MEM_CNT_IDX 0 /** Index of the memory cell that stores the counter. */ +#define IPC_MEM_REAS_IDX 1 /** Index of the memory cell that stores the reset reason. */ + +#ifdef __cplusplus +} +#endif + +#endif /* COMMON_H_ */ diff --git a/subsys/net_core_monitor/net_core.c b/subsys/net_core_monitor/net_core.c new file mode 100644 index 000000000000..1f2b0db90fa1 --- /dev/null +++ b/subsys/net_core_monitor/net_core.c @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2023 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +#include +#include +#include +#include +#include +#include "common.h" + +#include + +LOG_MODULE_REGISTER(net_core_monitor, CONFIG_NET_CORE_MONITOR_LOG_LEVEL); + +/* General purpose memory IPC of the application core. + * Access possible only through the memory address. + */ +#define APP_IPC_GPMEM_0_S ((volatile uint32_t *)(0x5002A610)) +#define APP_IPC_GPMEM_0_NS ((volatile uint32_t *)(0x4002A610)) +#define APP_IPC_GPMEM APP_IPC_GPMEM_0_S + +static void ncm_work_handler(struct k_work *work); +static K_WORK_DELAYABLE_DEFINE(ncm_work, ncm_work_handler); + +static void ncm_work_handler(struct k_work *work) +{ + static uint16_t live_cnt = CNT_INIT_VAL; + uint32_t gpmem = APP_IPC_GPMEM[IPC_MEM_CNT_IDX]; + + live_cnt++; + gpmem = (gpmem & (~CNT_MSK)) | (live_cnt << CNT_POS); + APP_IPC_GPMEM[IPC_MEM_CNT_IDX] = gpmem; + + k_work_reschedule(&ncm_work, K_MSEC(CONFIG_NCM_FEEDING_INTERVAL_MSEC)); +} + +static int reset(void) +{ + uint32_t reas; + + reas = nrf_reset_resetreas_get(NRF_RESET); + nrf_reset_resetreas_clear(NRF_RESET, reas); + + /* A notification that a core reset has occurred. + * And set the non-zero value of the counter. + */ + APP_IPC_GPMEM[IPC_MEM_CNT_IDX] = (APP_IPC_GPMEM[IPC_MEM_CNT_IDX] & FLAGS_MSK) + | (FLAGS_RESET << FLAGS_POS) + | (CNT_INIT_VAL << CNT_POS); + + /* Save the reason for the reset. */ + APP_IPC_GPMEM[IPC_MEM_REAS_IDX] = reas; + + return 0; +} + +static int net_init(void) +{ + LOG_DBG("Network Core Monitor Init"); + k_work_schedule(&ncm_work, K_NO_WAIT); + return 0; +} + +SYS_INIT(reset, PRE_KERNEL_1, CONFIG_NCM_RESET_INIT_PRIORITY); +SYS_INIT(net_init, APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT); diff --git a/west.yml b/west.yml index 47feb2e720ab..2de551e3041e 100644 --- a/west.yml +++ b/west.yml @@ -59,7 +59,7 @@ manifest: # https://developer.nordicsemi.com/nRF_Connect_SDK/doc/latest/zephyr/guides/modules.html - name: zephyr repo-path: sdk-zephyr - revision: v3.3.99-ncs1-2 + revision: c0aa4d27d3f2767e88c6c8c4634425086156f7f5 import: # In addition to the zephyr repository itself, NCS also # imports the contents of zephyr/west.yml at the above @@ -179,7 +179,7 @@ manifest: compare-by-default: false - name: homekit repo-path: sdk-homekit - revision: v2.4.3 + revision: b5d8e3ec3ad42b5789b94dbc4caf850c146df068 groups: - homekit - name: find-my