Stack Crash when pm_device_action_run(uart_dev, PM_DEVICE_ACTION_SUSPEND) used for UART

Hi,

SoC: nRF54L15, SDK 3.1, battery powered application.

Our product  uses UART.  When power management is used (pm_device_action_run(uart_dev, PM_DEVICE_ACTION_SUSPEND)), the stack crashes.  We used Matter door lock samples to  try this. Please see the configuration and code used.

Thanks.


Subu

CONFIG_USE_SEGGER_RTT=n
CONFIG_SHELL=n
CONFIG_OPENTHREAD_SHELL=n
CONFIG_CONSOLE=n
CONFIG_UART_CONSOLE=n

#********** Enable UART ********************#
CONFIG_SERIAL=y
CONFIG_UART_ASYNC_API=y

CONFIG_UART_20_ASYNC=n
CONFIG_UART_20_INTERRUPT_DRIVEN=n
CONFIG_UART_20_NRF_ASYNC_LOW_POWER=n

CONFIG_NRFX_UARTE30=y
CONFIG_UART_30_ASYNC=y
CONFIG_UART_30_INTERRUPT_DRIVEN=n
CONFIG_UART_30_NRF_ASYNC_LOW_POWER=y

CONFIG_LOG=n
CONFIG_LOG_MODE_MINIMAL=n
CONFIG_ASSERT_VERBOSE=n
CONFIG_ASSERT_NO_FILE_INFO=n
CONFIG_PRINTK=n
CONFIG_PRINTK_SYNC=n
CONFIG_THREAD_NAME=n
CONFIG_BOOT_BANNER=n

#include "user_console.h"

UserConsole::UserConsole(const struct device *uart_dev)
    : uart_dev(uart_dev)
{
    atomic_set(&suspended, 0);
}

UserConsole::~UserConsole() {
    suspend();
}

bool UserConsole::init() {
    if (!device_is_ready(uart_dev)) {
        printk("UART not ready\n");
        return false;
    }

    uart_callback_set(uart_dev, uart_event_cb, this);

    // Request HFCLK before enabling RX
    request_hfclk();

    int ret = uart_rx_enable(uart_dev, rx_dma_buf, sizeof(rx_dma_buf), SYS_FOREVER_US);
    if (ret) {
        printk("uart_rx_enable failed: %d\n", ret);
        release_hfclk();
        return false;
    }

    printk("UART Initialized -> Suspending URAT to sleep \n");
//    UserConsole::suspend();

    return true;
}

void UserConsole::sendChar(uint8_t c) {
    resume();
    uart_tx(uart_dev, &c, 1, SYS_FOREVER_US);
}

void UserConsole::sendBuffer(const uint8_t *buf, size_t len) {
    resume();
    uart_tx(uart_dev, buf, len, SYS_FOREVER_US);
}

bool UserConsole::available() {
    return rx_head != rx_tail;
}

uint8_t UserConsole::readChar() {
    if (rx_head == rx_tail) {
        return 0;
    }
    uint8_t c = rx_ring[rx_tail];
    rx_tail = (rx_tail + 1) % RX_BUF_SIZE;
    return c;
}

void UserConsole::suspend() {
    int err;
    if (atomic_cas(&suspended, 0, 1)) {
         err = uart_rx_disable(uart_dev);
         if(err){
            printk("UART RX is not disabled: error=%d \n", err);
            return;
        }
        err = uart_tx_abort(uart_dev);
        if(err){
            printk("UART TX is not aborted: error=%d \n", err);
            return;
        }
        
        k_msleep(100);
        printk("Call UART Suspend \n");
        pm_device_action_run(uart_dev, PM_DEVICE_ACTION_SUSPEND);
        if (err) {
		    printk("Failed to suspend uart (err: %d)", err);
		    return;
        }
        printk("Release clock");
        release_hfclk();

	    printk("UART suspended..\n");

    }
}

void UserConsole::resume() {
    if (atomic_cas(&suspended, 1, 0)) {
        pm_device_action_run(uart_dev, PM_DEVICE_ACTION_RESUME);
        request_hfclk();
    }
}

void UserConsole::uart_event_cb(const struct device *dev,
                                struct uart_event *evt,
                                void *user_data) {
    auto *self = static_cast<UserConsole *>(user_data);

    switch (evt->type) {
    case UART_RX_RDY:
        for (size_t i = 0; i < evt->data.rx.len; i++) {
            uint8_t c = evt->data.rx.buf[evt->data.rx.offset + i];
            size_t next = (self->rx_head + 1) % RX_BUF_SIZE;
            if (next != self->rx_tail) {
                self->rx_ring[self->rx_head] = c;
                self->rx_head = next;
            }
        }
        break;

    case UART_RX_DISABLED:
        // Re-enable RX when disabled
        uart_rx_enable(dev, self->rx_dma_buf, sizeof(self->rx_dma_buf), SYS_FOREVER_US);
        break;

    case UART_TX_DONE:
        self->suspend();
        break;

    default:
        break;
    }
}

/* ===== HFCLK control ===== */

void UserConsole::rx_hfclk_callback() {
    // Nothing needed here; just ensures HFCLK is running before UART RX
}

void UserConsole::request_hfclk() {
    struct onoff_manager *mgr =
        z_nrf_clock_control_get_onoff(CLOCK_CONTROL_NRF_SUBSYS_HF);

    sys_notify_init_callback(&clk_client.notify, rx_hfclk_callback);
    int err = onoff_request(mgr, &clk_client);
    __ASSERT_NO_MSG(err >= 0);
}

void UserConsole::release_hfclk() {
    struct onoff_manager *mgr =
        z_nrf_clock_control_get_onoff(CLOCK_CONTROL_NRF_SUBSYS_HF);

    (void)onoff_cancel_or_release(mgr, &clk_client);
}

&uart30 {
    status = "okay";
    current-speed = <115200>;
    pinctrl-0 = <&uart30_default>;
    pinctrl-1 = <&uart30_sleep>;
    pinctrl-names = "default", "sleep";

    //    interrupts = <10>, <NRF_DEFAULT_IRQ_PRIORITY>;
};

&uart30_sleep {
    group1 {
        psels = <NRF_PSEL(UART_TX, 0, 0)>, /* Port P0.0 */
                <NRF_PSEL_DISCONNECTED(UART_RTS)>;
        low-power-enable;
    };
    group2 {
        psels = <NRF_PSEL(UART_RX, 0, 1)>, /* Port P0.1  */
                <NRF_PSEL_DISCONNECTED(UART_CTS)>;
        low-power-enable;
        bias-pull-up;
    };
};

&uart30_default {
    group1 {
        psels = <NRF_PSEL(UART_TX, 0, 0)>,
                <NRF_PSEL_DISCONNECTED(UART_RTS)>;
    };
    group2 {
        psels = <NRF_PSEL(UART_RX, 0, 1)>,
                <NRF_PSEL_DISCONNECTED(UART_CTS)>;
        bias-pull-up;
    };
};

&button0 {
    gpios = <&gpio2 10 (GPIO_PULL_DOWN | GPIO_ACTIVE_HIGH)>;
    label = "Push button 0";
    zephyr,code = <INPUT_KEY_0>;
};

&button1 {
    status = "disabled";
};

&button2 {
    status = "disabled";
};

Parents
  • Hi Subu,

    Here's a minimal project that highlights toggling between UART enabled and UART powered down every 2 seconds, where it is an echo application when it is enabled. I included power measurements in the project as well.

    See here: https://github.com/droidecahedron/nrf54L_uarte_async_pm

    Please use this minimal sample as a litmus to see where your C++ implementation is differing to result in a hard fault. I suspect it is with the HFCLK logic in your end -- I did not need to perform those actions in the SDK version you are using to reach 2.83 uA UART current with UART disabled. (or with your disable logic)

    Best regards,

Reply
  • Hi Subu,

    Here's a minimal project that highlights toggling between UART enabled and UART powered down every 2 seconds, where it is an echo application when it is enabled. I included power measurements in the project as well.

    See here: https://github.com/droidecahedron/nrf54L_uarte_async_pm

    Please use this minimal sample as a litmus to see where your C++ implementation is differing to result in a hard fault. I suspect it is with the HFCLK logic in your end -- I did not need to perform those actions in the SDK version you are using to reach 2.83 uA UART current with UART disabled. (or with your disable logic)

    Best regards,

Children
No Data
Related