Hi all, I have come across what appears to be a bug in the MSP430 interrupt
hardware.
I would appreciate critiques and comments.
The specific case is where interrupts are disabled upon exit from an
interrupt handler:
Why should this not happen? An interrupt should only occur when interrupts
are enabled. So upon exit from the ISR, the previous state (interrupts
enabled)
should be restored.
Bug:
Consider code that looks like this:
:
code_where_interrupts_are_enabled();
:
_DINT();
_NOP();
If an interrupt occurs before the _DINT(); that is well and good since
interrupts are enabled and upon exit from the ISR,
the state is enabled.
Interrupts cannot occur after the _DINT(); since interrupts are disabled.
HOWEVER, it seems that an interrupt occurring sometime during (or
immediately before) the execution of _DINT();
is serviced by an ISR but within the value of the status register pushed on
the stack, the GIE bit is not set.
So upon exit from the ISR, using an RETI instruction, the popped value has
interrupts disabled.
Is this a problem?
----------------------------------------------------------------------------
Not for most people. If you explicitly disabled interrupts using _DINT();
then your eventual goal is to
have interrupts disabled, and this will be the case after the ISR.
When is it a problem?
----------------------------------------------------------------------------
If from an ISR, you want to force another interrupt after the completion of
the current ISR, you will be out of luck.
Since upon exit from the ISR, interrupts are disabled.
* There may be other cases that I haven't thought about.
Here is a code listing illustrating the problem. It is a simple busy loop
that performs _DINT(); and _EINT();
There is a simple timer interrupt that fires periodically. The timer clock
is driven from ACLK.
This will run for some time before an error occurs.
An error is detected within the ISR by examining the
value of the status register that was pushed on the stack as the interrupt
was serviced.
By the semantics of interrupts, this status word should have GIE enabled.
The error is flagged when GIE is not enabled.
Code Listing illustrating problem. On my MSP430x1611 the error gets flagged
after about half a minute of free running.
----------------------------------------------------------------------------------------------------------
#include <io.h>
#include <signal.h>
#include <isr_compat.h>
#include <util.h>
#define R_LED BIT4
#define G_LED BIT5
#define B_LED BIT6
#define Y_LED BIT7
#define IDLE_LED (G_LED)
#define ERROR_LED (Y_LED)
#define ERROR_INDICATE_ON() P5OUT |= ERROR_LED;
#define ERROR_INDICATE_OFF() P5OUT &= ~ERROR_LED;
#define INTERRUPT_BEGIN() P5OUT |= R_LED;
#define INTERRUPT_END() P5OUT &= ~R_LED;
#define _GET_SR_IRQ \
({ \
uint16_t __x; \
__asm__ __volatile__( \
"mov .L__FrameOffset_" __FUNCTION__ "(r1), %0" \
: "=r" ((uint16_t) __x) \
:); \
__x; \
})
interrupt ( TIMERA1_VECTOR ) isr_counter_rollover(void)
{
uint16_t status;
INTERRUPT_BEGIN();
/* This reads and resets it */
status = TAIV;
delayms(1);
status = _GET_SR_IRQ ;
if (!(status & GIE)) {
ERROR_INDICATE_ON();
}
INTERRUPT_END();
}
int main(void)
{
int i;
WDTCTL = WDTPW|WDTHOLD; //Init watchdog timer
while ((IFG1 & OFIFG) != 0) {
// Wait for LF Osc to startup
IFG1 &= ~OFIFG;
delayms(100);
}
P5DIR = (BIT4|BIT5|BIT6|BIT7);
// --------------------------------------------------
// Set up a timer interrupt on timer A.
// The timer is driven from the 32 kHz clock.
// --------------------------------------------------
/* 1. Setup up max count */
TACCR0 = 512; /* Interrupt every 15 ms */
/* 2. Set synchronous */
TACCTL0 = SCS;
/* 3. Set up options. 32kHz source, Interrupt enable,
continous, clear. */
TACTL = TASSEL_ACLK | TAIE | MC_UPTO_CCR0 | TACLR;
_EINT(); // Enable interrupts
// --------------------------------------------------
while (1) { //main loop, never ends...
for (i=0; i<512; i++) {
if (i == 0)
P5OUT |= IDLE_LED;
else if (i == 256)
P5OUT &= ~IDLE_LED;
_DINT(); _NOP();
delayms(1);
_EINT(); _NOP();
}
}
}