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 @@
+
+
+
+
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