Wednesday, 28 April 2010

Monitor Functions in the IAR Compilers for AVR

In AVR chips, game-changing actions like changing the clock prescaler or setting the watchdog are so important that they are afforded a protection mechanism all of their own. Timed write sequences are used to prevent accidental or rogue writes from fundamentally messing with your day.

These require you to perform a sequence of write operations within a defined time period - typically you need to do two writes within four clock cycles.

Care must be taken when performing such a write sequence that no other operations could take place that would violate the required timing. Typical candidates for such mischievous behaviour are interrupt service routines, which have an uncanny knack of firing at the most inconvenient times.

The obvious way to ensure that your timed write sequence is interrupt-safe is to disable interrupts around it, like so:

void do_timed_write_operation( void )
{
__disable_interrupt();
// timed write sequence goes here
__enable interrupt();
}

Unfortunately this contains a nasty little gotcha: if you call this function from code in which interrupts are disabled, they will be enabled on return from the function. This is almost certainly not what is intended, and can lead to late night debugging sessions.

A safer approach is to save the interrupt state on function entry, disable interrupts, do your business, and then put the interrupts back the way you found them:

void do_timed_write_operation_2( void )
{
uint8_t interrupt_state = __save_interrupt();
__disable_interrupt();
// timed write sequence goes here
__restore_interrupt( interrupt_state );
}

This approach is so common and idiomatic that the IAR compilers for AVR support it directly with the __monitor keyword. The following function is equivalent to the previous one:

__monitor void do_timed_write_operation_3( void )
{
// timed write sequence goes here
}

Unfortunately declaring a monitor function involves a code overhead - after all, even though you can't see it explicitly in the C, a quick squint at the list files will show that the interrupts are still being saved, disabled, and restored.

A saving grace here is that most such Big Important Things tend to happen during system initialisation, when interrupts are disabled anyway. In such circumstances you can perform your timed writes au naturel, without any interrupt protection. However you really need to convince yourself that it is safe to do so. It would be a brave man indeed who called the first function above once the system was up and running without spending a very long time looking at possible consequences.

In general I would suggest that all timed write sequences be protected by default. Such protection can be removed if you're convinced that it's safe to do so - and of course if you document this assumption in the comments.


No comments: