----- Ursprüngliche Nachricht ----- Von: David Brown Gesendet am: 03.September.2012 13:01:31
>On 03/09/2012 00:10, William "Chops" Westfield wrote: >> AVR has >> http://www.nongnu.org/avr-libc/user-manual/group__util__atomic.html >> As far as I know, it just inserts appropriate disable/reenable >> interrupt instructions. > Yes, the AVR "atomics" are exactly about disabling interrupts around a > block of instructions (like mspgcc's "critical" function attribute). > There is no way a peephole optimiser could change a DINT/<opcode>/EINT > set. The set of instructions has a different effect from the <opcode> > on its own - namely it also enables interrupts. indeed, that's a side-effect of many 'cheap' implementations of an atomic block. This is why the mentioned AVR atomic macro collection provides different parameters on how to deal with this. The apparent solution is to store the current state on the stack. But this has some disadvantages too. Mainly that it takes two bytes on the stack. Since you don't know how many nested sections you might have (an atomic block only means it isn't interrupted, but this does not imply that it has to be short or won't call functions), you'll end up with a stack overflow due to the added status register contents. Also, saving the status register and restoring it, will also restore the content of all other bits, not just GIE. And finally, at least with MSPGCC 3.2.3, I found situations where the compiler didn't like the status register being changed by the push because of the influence it had on the reference to local variables. (in cases where the optimizer optimized R4 as frame pointer away in favor of R1). I didn't find a way to tell that R1 is changed in the course of the assembly instruction. Isolating the state of GIE and writing it into a global variable doesn't allow nesting and also adds soem code overhead. So I have come up with a macro that uses a global counter to count the nesting level. It will only perform an eint if the counter reaches 0 on block exit. This of course assumes that interrupts are on outside the first atomic block and that no dint/eint operations are uses within. unsigned int ATOMIC_CNT; #define ATOMIC(x) do { __asm__ __volatile__ ("inc %0\n dint\n ":"=m"(ATOMIC_CNT):); x; __asm__ __volatile__ ("dec %0\n jnz .LA%=\n eint\n .LA%=:":"=m"(ATOMIC_CNT):);}while(0) However, even with GIE clear, an operation can still be interrupted by NMI or DMA. Only for single RMW operations, none of the two can interrupt them. And for DMA, only if the DMA is programmed to not do it. Also, only for single RMW instructions, you know hwo much time will pass between the read and the write. If the operation is spread across multiple instructions, even though not interrupted, you don't know how much time it takes. And sometimes, this is critical. Usually, when dealing with multi-flag hardware registers. Too often, Beginners aren't aware of this or simply don't care, assuming a 'simple' C instruction resulting in an evenly 'simple' binary code. The macros Peter posted are a good extension of the language and ot less portabel than an intrinsic :) JMGross ------------------------------------------------------------------------------ Live Security Virtual Conference Exclusive live event will cover all the ways today's security and threat landscape has changed and how IT managers can respond. Discussions will include endpoint security, mobile security and the latest in malware threats. http://www.accelacomm.com/jaw/sfrnl04242012/114/50122263/ _______________________________________________ Mspgcc-users mailing list Mspgcc-users@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/mspgcc-users