ARM cortex: mutex using bit banding

2019-04-08 10:03发布

问题:

Given that, on the ARM Cortex M3, I can:

  • atomically read a single bit
  • atomically set a single bit
  • atomically clear a single bit

How can I combine these for a mutex style set of operations:

try lock
take lock
release lock

It seems that try_lock or take_lock would require two operations that would not be atomic.

Do I need more control to accomplish this? Disable global interrupts would do it but it seems there should be a more surgical approach.

回答1:

Your rwl_TryLock() doesn't necessarily return a failure if the lock is already held when it's called (your compiler should be giving at least a warning about a code path that has no return value). Try the following:

int rwl_TryLock(volatile uint32_t *lock, int who){

    Var_SetBit_BB((uint32_t)lock, who);
    if(*lock == (1<<who)){ // check that we have exclusive access
        // got the lock!
        return 1;
    } 

    // do not have the lock
    Var_ResetBit_BB((uint32_t)lock, who); // clear the lock flag
    return 0;
}

Note that the above will not work for recursively claiming the same lock (ie., if the task specified by who == 1 already has the lock and tries to claim it again, the code above will not work correctly), but that was true of your original as well.

Also, interrupts can be disabled/enabled on the Cortex M3 pretty quickly (it's a simple update to an NVIC register). Are you sure your system can't live with an additional few cycles of interrupt latency to keep the code for handling the lock data structures simple (which generally means easier to make correct)?



回答2:

Bit banding won't work for this situation. It is just a really neat way of setting bits in device register files and your memory. Use the Load Exclusive and Store Exclusive instructions to implement your semaphore/mutex. Here's an example document you can use which implements a semaphore using these instructions and it details how this works.

http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0439b/CHDDIGAC.html

That being said, you can reduce the memory footprint of your mutexes by using bit banding...



回答3:

Some for info after some searching:

" ARM Cortex-M3 bit-banding ARM's microcontroller core offers yet another way to implement semaphores. Write access to variables in the bit-band alias region causes an atomic read–modify–write access to a memory location in the bit-band region at system bus level. How does that translate into semaphores? A variable in the bit-band region could serve as container for semaphores. Every client "owns" a bit in that container. Whenever a client needs to claim the semaphore, it sets its own bit by writing a 1 to the corresponding location in the bit-band alias region. It would then read the container (bit-band region) and check that no other bits are set, meaning the client has sucessfully claimed the semaphore. In case that other bits are set, the client would have to clear its own bit again, and retry (perhaps after waiting). " (source)

Here is my crude (untested) interpretation:

/*
 * Frees a lock.
 *
 * @note lock must point to a fully aligned 32 bit integer.
 * (atomically set to 0)
 *
 * @returns 1 if successfull
 */
int rwl_FreeLock(volatile uint32_t *lock){
    *lock = 0;
    return 1; // always successful
}

/*
 * Attempts to acquire a lock
 * @param who is the client taking the lock
 * @lock pointer to the mutex (uint32_t value in memory)
 * @note lock must point to a fully aligned 32 bit integer.
 * (atomically set to 1 only if set to 0)
 */
int rwl_TryLock(volatile uint32_t *lock, int who){
    // initial check of lock
    if(*lock == 0){
        Var_SetBit_BB((uint32_t)lock, who);
        if(*lock == (1<<who)){ // check that we still have exclusive access
            // got the lock!
            return 1;
        } else {
                    // do not have the lock
            Var_ResetBit_BB((uint32_t)lock, who); // clear the lock flag
            return 0;
        }
    }
}

Var_Set_BB / Var_Reset_BB: set / clear a bit using bit banding. (atomic)

However, it does not work!!!



回答4:

I've never used bit-banding on the ARM; my inclination instead would be to use load-exclusive/store-conditional for all such operations. Use a loop to load-exclusive the old value, compute the new value, and use a conditional store to write it back. Loop until the conditional store succeeds (which it probably will the second time, if it doesn't the first).