----- 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

Reply via email to