From: David Brown on
ChrisQ wrote:
> David Brown wrote:
>
>>>
>>
>> Do you mean something like this :
>>
>> void foo(void) {
>> doSomethingUnlocked();
>> memoryBarrier();
>> oldSREG = SREG; // Preserve old interrupt state
>> cli(); // Ensure interrupts are disabled now
>> doSomethingLocked();
>> memoryBarrier();
>> SREG = oldSREG; // Restore old interrupt flag
>> doMoreUnlocked();
>> }
>>
>> The details vary from processor to processor, but the principle is the
>> same.
>>
>> Some compilers have extensions or extra headers making it easier. For
>> example, avr-gcc standard library has ATOMIC_BLOCK macros for this
>> purpose, and msp430-gcc has an extra "critical" function __attribute__
>> that wraps a function in an interrupt disable/restore pair.
>
> Yes, but using an array, rather than a single variable. The code looked
> like:
>
>
> static U8 u8PwmInterruptStack [SIZE];
> static U8 u8PwmStackPointer = 0;
>
> /* Push Pwm Interrupt State and Mask Interrupts */
> /* -------------------------------------------- */
> void vPwmPushMaskInterruptState (void) {
>
> if (u8PwmStackPointer < PWM_STACKDEPTH) {
> u8PwmInterruptStack [u8PwmStackPointer++] = u8PwmReadIntState ();
> vPwmMaskInterrupts ();
> }
> }
>
> /* Pop Pwm Interrupt State */
> /* ----------------------- */
> void vPwmPopInterruptState (void) {
>
> if (u8PwmStackPointer > 0) {
> u8PwmStackPointer--;
> if ((u8PwmInterruptStack [u8PwmStackPointer] & PWM_INTMASK) != 0) {
> vPwmUnmaskInterrupts ();
> }
> }
> }
>
> The range check is just there for safety and in practice, the array is
> sized to suit the application. The first function is called before
> twiddling the shared data and second after.
>
> Turned out to be a simple solution, but the first time i've had to use
> anything like it...
>
> Regards,
>
> Chris
>

This may sound like a silly question, but why don't you just use a local
variable to hold the old interrupt status? It is rare that you would
want to disable interrupts in one function, and restore the state in a
different function - since you want to keep the disable time to a
minimum, and be very clear about when they are disabled or not, your
disable and enable are typically done at the same level. Thus you have

{
uint8_t oldState = u8PwmReadIntState ();
vPwmMaskInterrupts ()
doSomething();
if ((oldState & PWM_INTMASK) != 0) {
vPwmUnmaskInterrupts ();
}
}

The code in doSomething() could quite happily have the same sort of
sequence. There is no need to have a special stack - normal locals suffice.


If you have a complex system with particular interrupts (rather than
global ones) enabled and disabled from different threads in a program,
then local variables like that won't be sufficient. But I'd first
suggest thinking hard about the structure of the program - there are
probably better, clearer and therefore safer ways to get the effect you
want. However, if you want to do something like that, you don't need a
stack - you just need a counter variable tracking the number of times
the interrupt has been disabled. Of course, you need to disable global
interrupts around access to this counter...

From: Mark Piffer on
On 30 Okt., 07:45, bastia...(a)yahoo.com (42Bastian Schick) wrote:
> On Tue, 27 Oct 2009 22:07:40 +0100, David Brown
>
> <david.br...(a)hesbynett.removethisbit.no> wrote:
> >Reservations are hardware intensive to implement, unless you are willing
> >to pay extra memory latency.  But they scale well for multi-processor
> >systems, making them a good solution for "standard" PPC processors.  The
> >PPC core in the MPC55xx is more like a microcontroller core than a "big"
> >processor core, and Freescale probably saved a fair amount of silicon
> >and complexity by dropping reservations so that you must use traditional
> >interrupt disabling.
>
> If FSL only would have placed a big sticker on the manual. :-)
> Seriously, the manual states reservation works, and some place later
> in the EBI (external bus interface) description they write it does not
> work for EBI.
> => I learned, I have to read every single word of a manual.
>
> --
> 42Bastian
> Do not email to bastia...(a)yahoo.com, it's a spam-only account :-)
> Use <same-name>@monlynx.de instead !

I remember reading somewhere, something like Motorola documentation
departments being the only US organizations running with CMMI level 5.
Their documents suck with a high standard definitively. :)

Mark
From: ChrisQ on
David Brown wrote:

>
> This may sound like a silly question, but why don't you just use a local
> variable to hold the old interrupt status? It is rare that you would
> want to disable interrupts in one function, and restore the state in a
> different function - since you want to keep the disable time to a
> minimum, and be very clear about when they are disabled or not, your
> disable and enable are typically done at the same level. Thus you have

The product is typically a 128 x 192 x 2 colour led pixel graphics
display, driven via serial comms at 9.6k. The leds are driven from ti
led drivers, which handle 8 leds each, and use a set of serial control
lines to clock and latch data and switch between data transfer and self
test modes. Intensity is done via pwm on an enable line, which itself is
one of the control lines. The absolute requirement to avoid any flicker
at all on the display means that most control functions and data
transfers can only be done in interrupt context, synchronised to the pwm
off period. From a programming point of view, it's not ideal but that's
the hardware that we had to work with. There's no rtos, rather state
driven, with more state machinery in the comms, data transfer and data
self test areas.

The software was developed over 2 years and now runs on many variants.
Problem was that there were many layers of code and an overall lock
requirement that was difficult to resolve across the layers. The only
way to keep track was to save and restore context at each point. Yes,
you could do this with a local variable in each function, but it is more
code and more bug prone than a single line function call for disable and
enable. The functionality is encapsulated and I don't have to think
about the internals, nor remember how many instances there are in the
code if it needs to be changed. Run time speed is not an issue either,
so an added function call or so is irrelevant.

Ideal world rarely maps onto real products in practice :-)...

Regards,

Chris




From: Stefan Reuther on
David Brown wrote:
> Stefan Reuther wrote:
>> All this is totally simple when you can use CLI/STI:
>> void atomic_swap(node** a, node** b, node* c) {
>> cli(); *a = *b; *b = c; sti();
>> }
>>
>> // prepend new node to a list (easier than appending...)
>> node* list;
>> node* newNode;
>> atomic_swap(&newNode->next, &list, newNode);
>> (some strategically-placed 'volatile' can be appropriate.)
>
> Here you have missed one important point that many people fail to
> understand about interrupt disabling (and the related topic of volatile
> accesses) - memory barriers. Unless your cli() and sti() macros /
> assembly functions specifically include memory barriers, then your
> atomic_swap function is not safe.

Yep. The declaration should've been
void atomic_swap(node*volatile* a, node*volatile* b, node* c);
I was just too lazy to figure out where to place the 'volatile's :-)
That, together with cli/sti macros acting as memory barriers (honestly,
cli/sti which are not such barriers are useless) fixes the routine for a
single processor system. Multiprocessor is a little harder.

> In the atomic_swap function above, for example, the compiler will
> probably see the cli() and sti() as volatile accesses (depending on the
> compiler and the definition of these macros). But the code in the
> middle does not use volatile accesses - the compiler can therefore
> freely move these operations before or after the cli() and sti()
> operations.

Actually, our compiler moved the accesses even though I used 'volatile'.
Fortunately the vendor fixed it quickly, but in the meantime I've made
an asm version to fix it forever.


Stefan

From: Hans-Bernhard Bröker on
David Brown wrote:

> {
> uint8_t oldState = u8PwmReadIntState ();
> vPwmMaskInterrupts ()

That won't work, because it has the exact same problem that the concept
of a critical section is supposed to work against. Interrupt state can
change between these two instructions, and that would leave you
restoring an out-of-date oldState.

Ultimately, you need atomic query-and-change operations to resolve this.