EEVblog Electronics Community Forum
Electronics => Microcontrollers => Topic started by: HwAoRrDk on October 04, 2019, 12:45:12 am
-
I have a simple function, where all it needs to do is return a volatile multi-byte (uint32_t) global variable. Of course, being prudent, I should use the appropriate mechanisms to access this variable atomically, using ATOMIC_BLOCK. But, it seems inefficient to me to copy the global variable into a local variable inside the atomic block, then return the local one.
So, the question is, can I do the following?
uint32_t get_foo() {
ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
return foo; // A global volatile uint32_t
}
}
I understand that ATOMIC_BLOCK uses some macro and compiler magic to ensure that SREG is saved and restored no matter what exit path from the block is taken, but does this apply to a return?
I took a look at the resultant assembly, and I think it's doing what it should, but I would be happy if someone more knowledgeable of AVR assembler were to look at it and confirm.
00000712 <get_foo>:
712: 2f b7 in r18, 0x3f ; 63
714: f8 94 cli
716: 60 91 03 02 lds r22, 0x0203 ; 0x800203 <foo>
71a: 70 91 04 02 lds r23, 0x0204 ; 0x800204 <foo+0x1>
71e: 80 91 05 02 lds r24, 0x0205 ; 0x800205 <foo+0x2>
722: 90 91 06 02 lds r25, 0x0206 ; 0x800206 <foo+0x3>
726: 2f bf out 0x3f, r18 ; 63
728: 08 95 ret
If I'm understanding this correctly, it's saving SREG in r18, disabling interrupts, copying foo into function return registers, restoring SREG, and finally returning.
If this is all correct and hunky-dory, and follows my intention to atomically access the foo variable, then great. :D All I need to do is take care of the compiler warning saying "control reaches end of non-void function". I guess I could whack in a second return statement (that will never be reached) outside the block.
-
This discusses the warning (and the same situation):
https://www.avrfreaks.net/forum/atomicblock-control-reaches-end-non-void-function (https://www.avrfreaks.net/forum/atomicblock-control-reaches-end-non-void-function)
You can use a temporary variable, as listed above.
Or: don't put it in a function at all. If this example is truly all that the function contains, then it is a trivial example, and should either be inlined (on -O3, this will probably happen automatically), put into a macro (#define get_foo ...), or just pasted into the calling function verbatim. :)
Tim
-
Coincidentally I found and have just been reading that linked thread. :)
Yes, even though it's a trivial function, I need it to be a function because it will be a callback function pointer that is passed to something else. In fact, I have specifically marked it with an attribute to prevent it being inlined.
I just tried putting in a second return statement ("return 0;" after the atomic block), and the compiler actually ignores it and emits no additional instructions for it.
So, I guess I just need to know if the assembly I posted previously is actually doing the right thing. Hopefully someone can confirm my interpretation.
-
https://godbolt.org/z/B_y4c3
Compilers are pretty smart, so usually no need to try to out-think it.
-
I lot of compilers will implement a "return" in the middle of a function as a jump to the function epilogue, anyway. (that way they only need the epilogue once.) So all that's needed is to make sure that the re-enable of interrupts happens gets added to what gcc considers the epilogue of the function. IIRC, ATOMIC_BLOCK actually does some magic to make sure that this is the case...
Ah, yes. See https://blog.oddbit.com/post/2019-02-01-atomicblock-magic-in-avrlibc/