GRTC peripheral not always emitting scheduled events

I'm trying to use the new GRTC peripheral in nRF54L15 but in various situations I notice that the events are not triggered as expected.

Here is a "minimal reproducible example", based on the "Create a blank application" template in VS Code with NCS 2.8.0.

main.c:

#include <zephyr/kernel.h>

int main(void)
{
    // Turn off LED1 and LED3 on the devkit
    NRF_P1->PIN_CNF[10] = (GPIO_PIN_CNF_DIR_Output << GPIO_PIN_CNF_DIR_Pos) | (GPIO_PIN_CNF_INPUT_Disconnect << GPIO_PIN_CNF_INPUT_Pos);
    NRF_P1->PIN_CNF[14] = (GPIO_PIN_CNF_DIR_Output << GPIO_PIN_CNF_DIR_Pos) | (GPIO_PIN_CNF_INPUT_Disconnect << GPIO_PIN_CNF_INPUT_Pos);
    NRF_P1->OUTCLR = (1U << 10) | (1U << 14);

    // Reset GRTC to a known state and start
    NRF_GRTC->TASKS_STOP = 1;
    for (volatile int i = 0; i < 200; i++) {}
    NRF_GRTC->MODE = (GRTC_MODE_AUTOEN_Default << GRTC_MODE_AUTOEN_Pos) | (GRTC_MODE_SYSCOUNTEREN_Disabled << GRTC_MODE_SYSCOUNTEREN_Pos);
    NRF_GRTC->TIMEOUT = 0;
    NRF_GRTC->WAKETIME = 3;
    NRF_GRTC->SHORTS = 0;
    NRF_GRTC->PWMCONFIG = GRTC_PWMCONFIG_ResetValue;
    NRF_GRTC->CLKOUT = GRTC_CLKOUT_ResetValue;
    NRF_GRTC->CLKCFG = (GRTC_CLKCFG_CLKSEL_SystemLFCLK << GRTC_CLKCFG_CLKSEL_Pos) | (1 << GRTC_CLKCFG_CLKFASTDIV_Pos);
    NRF_GRTC->MODE = (GRTC_MODE_AUTOEN_Default << GRTC_MODE_AUTOEN_Pos) | (GRTC_MODE_SYSCOUNTEREN_Enabled << GRTC_MODE_SYSCOUNTEREN_Pos);
    NRF_GRTC->TASKS_CLEAR = 1;
    for (volatile int i = 0; i < 200; i++) {}
    for (int i = 0; i < 4; i++) NRF_GRTC->SYSCOUNTER[i].ACTIVE = 0;
    for (int i = 0; i < 12; i++) NRF_GRTC->CC[i].CCEN = 0;
    NRF_GRTC->TASKS_START = 1;

    // Delay ~3 LFCLK cycles
    NRF_TIMER20->TASKS_STOP = 1;
    NRF_TIMER20->TASKS_CLEAR = 1;
    NRF_TIMER20->PRESCALER = 4;
    NRF_TIMER20->EVENTS_COMPARE[0] = 0;
    NRF_TIMER20->CC[0] = 93;
    NRF_TIMER20->TASKS_START = 1;
    while (!NRF_TIMER20->EVENTS_COMPARE[0]) {
    }
    NRF_TIMER20->TASKS_STOP = 1;
    NRF_TIMER20->TASKS_CLEAR = 1;

    // These lines are not necessary to reproduce the issue
    for (;;) {
        (void)NRF_GRTC->SYSCOUNTER[0].SYSCOUNTERL;
        if (!(NRF_GRTC->SYSCOUNTER[0].SYSCOUNTERH & GRTC_SYSCOUNTER_SYSCOUNTERH_BUSY_Msk)) {
            break;
        }
    }

    // Set up to trigger a one-shot event when syscounter >= CC[0]
    NRF_GRTC->EVENTS_COMPARE[0] = 0;
    NRF_GRTC->CC[0].CCL = 5000;
    NRF_GRTC->CC[0].CCH = 0; // This write also enables the CC[0] event

    uint32_t cnt = 0;
    while (!NRF_GRTC->EVENTS_COMPARE[0]) {
        if (++cnt == 1000000) {
            // Turn on LED3
            // Often gets stuck here
            NRF_P1->OUTSET = 1U << 14;
        }
    }

    // Turn on LED1
    // Success case, happens very seldom
    NRF_P1->OUTSET = 1U << 10;
    return 0;
}

prj.conf:

CONFIG_SERIAL=n

The build settings use "nrf54l15dk/nrf54l15/cpuapp", "Nordic Kits" and the rest uses the default configuration. Flash the board and then press the reset button on the devkit and see which of the two LEDs of LED1 and LED3 that lights up. Press the reset button multiple times to repeat the test many times and notice the failure rate.

In this program, I configure a compare event to be triggered after 5 milliseconds. However, the event is never triggered, so the LED3 on the devkit turns on after a short timeout. The expected outcome is that the event is triggered so that LED1 lights up. In this particular example, the failure frequency is 100% on my new nRF54L15-PDK 0.8.1 but around 70% on my older devkit 0.7.0. Not sure if the difference is due to difference versions or if it is individual chip differences. It seems pretty random when the success case happens and when the failure case happens.

I've tried to read through the GRTC section in the data sheet multiple times to see if there is something I have missed, but have not found anything.

Could there be something important in the data sheet that is missing regarding configuration or required register write sequence, or is this a hardware bug?

Note that how easy it is to trigger the issue depends on e.g. the WAKETIME/TIMEOUT register settings. I have managed to reproduce the problem with a non-zero TIMEOUT and/or a smaller WAKETIME but that requires slightly more code. This is more or less the most simple example I could come up with.

Note that if I at the same time as I turn on LED3 due to timeout also implicitly force the GRTC into active state, e.g. by reading SYSCOUNTERL, then the event triggers at that point.

Note that it appears the problem cannot be reproduced with CONFIG_SERIAL=y.

I have also tried to run the same code in a "bare metal" project without Zephyr but the same issue is observed.

I'm attaching the project so you can test it directly. I guess you can flash the merged.hex file in the build directory in case the project cannot be built in VS Code for some reason.

grtc_test.tar.gz

Parents
  • Hi, 

    First of all, thanks for the detailed description and code example. I will need to ask a bit internally about this one, this may take a bit of time since it likely will need to involve several disciplines. 

    Two immediate comment I have is whether you see any difference if you enable at least one SYSCOUNTER, or whether you see any difference if you explicit start and wait for the LFCLK source you want to use before using GRTC. 

    Kenneth

  • The clock source selection, or in case I use LFXO, whether I wait for LFXO to start first, does not seem to have any impact.

    I don't understand exactly what you mean by "if you enable at least one SYSCOUNTER"? The MODE register only has a single bit for enabling the SYSCOUNTER which of course is needed in order to run the example in the first place.

  • I got the impression looking at the code that all SYSCOUNTER[n].ACTIVE is 0?

    Edit: bad wording from my btw when I wrote "enable". 

    Edit2: Never mind, I think I misunderstood what ACTIVE did.

    Kenneth

  • The ACTIVE register has this documentation: "Request to keep the SYSCOUNTER in the active state and prevent going to sleep for index [n]"

    where 0 (NotActive) means "Allow SYSCOUNTER to go to sleep".

    I want to use the sleep mode since that is the "ultra-low power sleep mode". Per the "SYSCOUNTER sleep mode" section, the peripheral internally automatically programs an internal low frequency timer compare match based on the next expected SYSCOUNTER compare match using CC[n] configuration. When that happens, it is gets into active mode so that the event can be emitted at the exact µs.

    With active mode on all the time, the power consumption is 141 µA.

Reply
  • The ACTIVE register has this documentation: "Request to keep the SYSCOUNTER in the active state and prevent going to sleep for index [n]"

    where 0 (NotActive) means "Allow SYSCOUNTER to go to sleep".

    I want to use the sleep mode since that is the "ultra-low power sleep mode". Per the "SYSCOUNTER sleep mode" section, the peripheral internally automatically programs an internal low frequency timer compare match based on the next expected SYSCOUNTER compare match using CC[n] configuration. When that happens, it is gets into active mode so that the event can be emitted at the exact µs.

    With active mode on all the time, the power consumption is 141 µA.

Children
Related