This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts
This discussion has been locked.
You can no longer post new replies to this discussion. If you have a question you can start a new discussion

nRF51822 - atomic increment of a variable

Using:

  • GCC 4.8
  • pure-gcc Setup
  • Sdk 5.2.0
  • Softdevice 6.0

I execute some code within interrupt context (softdevice handler) and also in main thread. I need to increment / decrement an integer variable concurrent in both thread context (e.g. for some refcounted objects within my inter-thread communication).

Since we unfortunately have no __sync_add_and_fetch nor std::atomic on the M0 core, I have to synchronize the concurrent access in a different way.

1st approach using CRITICAL_REGION_ENTER/EXIT()

Currently, I synchronize the access using CRITICAL_REGION_ENTER/EXIT(), but it seems a bit brutally.

2nd approach using sd_mutex_…()

Might that be a better solution? What the costs (ram & runtime) for SD mutexes?

3nd approach disabling interrupts

In some places I'd read that it was a bad idea to disable interrupts during ble communication. Also a bad idea for the small time of just a int val = ++(*refcount); ?

  

Edit 2014-08-28: I'd now made further investigations into the 3 approaches.

CRITICAL_REGION_ENTER/EXIT()

CRITICAL_REGION_ENTER();
(*refcount)++;
CRITICAL_REGION_EXIT();

The duration of these instructions is about 25 µs.

sd_mutex

errCode = sd_mutex_acquire(&mutexHandle);
APP_ERROR_CHECK(errCode);
(*refcount)++;
errCode = sd_mutex_release(&mutexHandle);
APP_ERROR_CHECK(errCode);

The duration of these instructions is about 20 µs. But this approach won't work for my problem. sd_mutex_acquire() is not that kind of mutex lock we know from threading concurrency. The call is more like a »try lock«. If the mutex already is locked, then sd_mutex_acquire() returns with NRF_ERROR_SOC_MUTEX_ALREADY_TAKEN instead of passing control to the code which holds the lock (see here).

disabling interrupts

__disable_irq();
(*refcount)++;
__enable_irq();

The duration of these instructions is about 800 ns.

Finally, I decide to use CRITICAL_REGION_ENTER/EXIT() (even it's much slower than disabling irq) because I don't want to disturb the radio traffic. Since I like to have it in C++, I'd wrote a small RAII class based on the sdk macro code:

class CriticalSection {
public:
    explicit CriticalSection()
        :   intPrio(current_int_priority_get())
        ,   nested(0)
    {
        if (intPrio != APP_IRQ_PRIORITY_HIGH) {
            uint32_t errCode = sd_nvic_critical_region_enter(&nested);
            if (errCode == NRF_ERROR_SOFTDEVICE_NOT_ENABLED)
                __disable_irq();
            else
                APP_ERROR_CHECK(errCode);
        }
    }
    ~CriticalSection() {
        if (intPrio != APP_IRQ_PRIORITY_HIGH) {
            __enable_irq();
            uint32_t errCode = sd_nvic_critical_region_exit(nested);
            if (errCode != NRF_ERROR_SOFTDEVICE_NOT_ENABLED)
                APP_ERROR_CHECK(errCode);
        }
    }
    int getIntPrio() const { return intPrio; }
private:
    uint8_t intPrio;
    uint8_t nested;
};

And my code now looks like this:

{
    CriticalSection cs;
    (*refcount)++;
}

Edit: Please note that this don't work for nested critical sections when softdevice is disabled (see also this post).

Cheers, Joe

Parents
  • When you call sd_nvic_critical_region_enter() it'll disable all your application interrupts but doesn't affect softdevice interrupts. Why do you want to disable all interrupts including softdevice? Softdevice interrupts won't change your variable.

    I think the best way is to disable app interrupts in main on variable write.

  • I'd thought about __disable_irq() because of performance reasons (about 800ns against 25µs of critical region), but I now finally decide for critical region.

Reply Children
No Data
Related