How does sd_app_evt_wait() work when the Event Register is set on exception exit?

I've disassembled sd_app_evt_wait() in SoftDevice S140 v7.0.1 and clearly see that inside a loop, it calls WFE -> SEV -> WFE. (It also manipulates the MWU and looks at interrupts to decide whether to loop again or return).

The ARMv7-M Architecture Reference Manual version "ARM DDI 0403E" page B1-561 (see https://developer.arm.com/documentation/ddi0403/latest) clearly states that "the execution of an exception return function" sets the Event Register.

 

My question is this- calling sd_app_evt_wait() invokes a SVC, which is one of the core ARM exceptions. When sd_app_evt_wait returns from the SVC back to my code, that sets the Event Register, right? So, when I call sd_app_evt_wait() again, doesn't the WFE -> SEV -> WFE just fall through without sleeping, because the first WFE clears the Event Register without sleeping?

I'm certainly misunderstanding something, or sd_app_evt_wait() would never sleep, and it does sleep most of the time. (I'm debugging a situation where it doesn't)

What machinery am I missing here?

Thanks in advance!

Charles

Parents Reply
  • Thanks for the response, Sigurd. Yup, Hung is helping me dig in about that; I filed this thread because I was curious about the event register behavior separately.

    Sorry if I'm being dense and not understanding the system well, but I'm still confused-

    Why does it matter what mode the sleep is done in, if sd_app_evt_wait() is defined as a macro that does "SVC <x>; BX LR;"? It's coming back from the SVC instruction that sets the event register, right?

    Thanks for taking the time to walk me through this detail; I appreciate it a lot.

Children
  • Hello- If anyone has any information on this I'd really love to understand it better!

    tl;dr: sd_app_evt_wait() is defined via SVCALL like this:

     

    The ARMv7M manual says that returning from SVC sets the event flag. If the event flag is then already set the next time I call sd_app_evt_wait(), what mechanism clears it so that the nRF52840 can sleep?

    Does the loop inside the SoftDevice's SVC implementation of sd_app_evt_wait() always run at least twice or something; once to clear the event flag from the previous exit, then again to actually sleep?

  • Hi,

    From the docs we have:

    When waiting for application events using the API, the CPU goes to an IDLE state whenever the SoftDevice is not using the CPU, and interrupts handled directly by the SoftDevice do not wake the application.
    Application interrupts will wake the application as expected.
    sd_app_evt_wait() calls wait in the sequence of __WFE() __SEV() __WFE(). sd_app_evt_wait() is looping until an application interrupt happens. And it's run in thread mode, i.e. it's no longer in SVC when the wait sequence is called.

  • I'm not sure what you mean when you say it's run in "thread mode" and "no longer in SVC".

    My understanding of Cortex-M4 is as follows- please correct me if you spot my error because I do want to understand this thoroughly!

    When the user-app "Thread" mode side code calls "SVC x", that raises the SVCALL exception. From that point on, the CPU is in "Handler" mode as it processes the SVCALL exception, which happens inside of the SoftDevice binary blob.

    During this time, the CPU is inside the SVCALL exception handler until the SoftDevice handler returns. This happens by way of the compiler emitting code that pops LR from the stack into the PC register; the CPU explicitly sets LR to 0xFFFFFFF1 upon entering the SVCALL exception, and the CPU sees that it's exiting the exception handler when, during the SoftDevice SVCALL handler epilogue, the PC register is assigned this special LR value. At this point, the CPU also sets the Event Register, per the architecture manual reference I provided in my initial post.

    My hunch is that the Event Register is simply _always_ set upon returning from the SoftDevice SVCALL, but because sd_app_evt_wait() runs in a WFE/SEV/WFE loop, the first spin through that loop doesn't actually sleep. The first spin through the loop instead just serves to clear the event flag, then the loop sees that none of the user interrupts are pending, so it jumps back to the top of the loop, and the second WFE is what actually causes the CPU to sleep.

    Is this right? I don't understand what you mean by "it's no longer in SVC when the wait sequence is called"- if it's not in SVC, then it's back in my application code, and it's definitely not back in my application code because this wfe/sev/wfe loop is part of the SoftDevice binary!

    Thanks,

    Charles

Related