I'm trying to build a library of lock-less atomic operations for the 56800EX DSC. I can achieve basic reads and stores and also use the atomic bit mask operations for bit manipulation.
However I'm trying to work out if it's possible to build a function with a prototype like:
int atomic_fetch_and_add(atomic_t a, int i);
Which would add 'i' to the atomic value 'a' (which is really just an int) and then return the value of 'a' prior to the add. I imagine this will be an inline assembly function (the others I've do so far are). But as far as I can see there's no way to do this without turning off interrupts because the read and add actions have to be separate. I can't use a parallel move because I can't directly add to a memory location at the same time. I'm wondering if there's some instruction that I've missed that would help with this. Alternatively I could make the atomic_t more complex and have some sort of flag to detect an interrupted access and attempt the action again, but I can't see how to do that either.
Perhaps someone else has done this, or something similar. Obviously this function (and related sub/inc/dec functions) would be very useful in controlling a data structure that was shared between multiple interrupt levels. If it's not possible then I'd have to look at some other solution to provide safe data structures.
Solved! Go to Solution.
Hi. No I don't think that is possible.
You would have to be blocked waiting for the single bit set and test to indicate that your process has locked "the resource that controls all resources".
Hi. No I don't think that is possible.
You would have to be blocked waiting for the single bit set and test to indicate that your process has locked "the resource that controls all resources".
Thanks John, I'd pretty much came to the same conclusion. That means I need to disable interrupts to safely do a fetch_and_add. Since I'm in a function I'm wondering if I can used the delayed return, i.e.:
asm static int atomic_fetch_and_add(atomic_t *a, int i)
{
.optimize_iasm off;
MOVE.W Y0, X0; // Save 'i', 1 cycle
RTSD; // Delayed return, 5 cycles
MOVE.W X:(R2), Y0; // Store value to return, 1 cycle
ADD.W X:(R2), X0; // Add atomic value to 'i', 2 cycles
MOVE.W X0, X:(R2); // Store back in atomic value, 1 cycle
}
Since interrupts are disabled for three delayed instructions after the RTSD nothing can interrupt and they will execute as an atomic block.
I'm using assembly because if I'm providing an atomic library I want to be sure that the operation is definitely atomic and it's in a function so that I can turn optimization off for that block without impacting the calling code (and I'm assuming it'll effectively work as a compiler barrier to prevent the compiler re-ordering the code on either side of the call).
Are my assumptions correct, is that always going to work atomically and is there any better way to do it?
It certainly appears to work and given the debugger can't step through the delayed instructions I assume that nothing else can interrupt them either.
I think disabling interrupts for 4 cycles is acceptable, if it ever isn't then I'll have to look at implementing a more complex system with separate storage per interrupt level or something like that.