Author Topic: iInterruptLevel question  (Read 13216 times)

Offline dkg

  • Newbie
  • *
  • Posts: 48
    • View Profile
iInterruptLevel question
« on: November 18, 2009, 03:06:17 PM »
I added a new interrupt to our application (using IRQ7 and the demo code for port interrupts) and can now get into a state where the tick interrupt is no longer happening. Looking into the problem I noticed that iInterruptLevel seems to be used as a nesting counter in the uDisable_Interrupt and uEnable_Interrupt routines. However, I also see almost everywhere else it is being set directly to 0 or 1 as if it was some kind of flag.

So I wondered what the value of this variable was while we are in the condition where we lost our system tick. Sure enough it is set to -1 which I don't think should ever happen. I believe this might be happening if the interrupts got nested and one routine sets it to zero on exit and the other routine calls uEnable_Interrupt which decrements before enabling the interrupts. But when it decremented to -1 it did not enable the interrupt as it should have.

So is iInterruptLevel a nesting counter or a flag? If it really is meant to be a counter shouldn't all 78 places in M5223X.c be doing increments and decrements instead of setting it to 0 and 1? I see the code for the CAN interrupt is doing that but very few if any other routines do it.

Dave G.

Offline mark

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 3236
    • View Profile
    • uTasker
Re: iInterruptLevel question
« Reply #1 on: November 18, 2009, 07:09:03 PM »
Hi Dave

The flag is in fact a counter - but in interrupts, which can not occur when the flag is set (because the flag indicates that interrupts are masked), it must be 0 when entering so setting directly to 1 is the same as incrementing. Routines which subsequently call uDisable_Interrupt() increment so that their uEnable_Interrupt() pair doesn't unmask the interrupt before it should, if it was not initially 0.

When the IRQ is exited setting directly to 0 is then the same as decrementing.

However if you are using an interrupt with IRQ7 level it is a non-maskable interrupt. Here you need to be very careful since your routine could be interrupting another interrupts or a "protected region", which is presently in the process of incrementing or decrementing this or other such important variables. The NMI can not be protected against and, if it does manipulate such data at the wrong point in time, it will sooner or later result in a blocked interrupt (or other similar erroneous state).

Generally any NMI routines should not use any OS calls since they could be corrupting something. Also the interrupt disable counter probably has no relevance in this case.

Perhaps you can use a level 6 (assuming NMI status is not required) but otherwise "Tread very carefully..." ;-)

Regards

Mark

Offline dkg

  • Newbie
  • *
  • Posts: 48
    • View Profile
Re: iInterruptLevel question
« Reply #2 on: November 18, 2009, 07:54:08 PM »
Hi Mark,

As I understand it, what you are saying is the IRQ7 pin is hard-coded in the Coldfire to be treated like a level 7 interrupt (non-maskable). And you are saying it is not safe to call the OS (InterruptMessage) in a non-maskable isr.

Unfortunately, in our case, there are no other edge port IRQ pins available to use since the others are used for various alternate purposes. To use a GPIO for our interrupt source puts some additional requirements on the hardware to maintain the interrupt signal until we can service it.

Do I have this right?

BTW, it appears to me the uTasker calling InterruptMessage from the test_irq_7 function. And that function is the isr for IRQ7. So is this legitimate?

Thanks for the help.

Dave G.
« Last Edit: November 18, 2009, 08:54:03 PM by dkg »

Offline mark

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 3236
    • View Profile
    • uTasker
Re: iInterruptLevel question
« Reply #3 on: November 19, 2009, 12:07:50 AM »
Hi Dave

Yes you are correct. The IRQ7 input has a fixed level 7 (can not be modified) and so is non-maskable in nature. (The level of the others corresponds to their IRQ number - also fixed).
The demo does indeed use InterruptMessage(), which performs a write in the input queue of a task. This is dangerous since it could collide with another task doing the same (or a timer putting an event into the queue etc.) - although the manipulation of queue counters (these must be protected) is performed in protected regions the NIM can still interrupt them and cause queue corruption (this may result in a lost or corrupted message but also in something even worse...). This is thus a bad example and I will remove it to just change an LED or something.

Don't forget that, in emergencies (eg. when your ISR must access OS routines or variables that may be in use), it is possible to use other peripherals as interrupts - eg. the capture input pin of a general purpose time (see http://www.utasker.com/forum/index.php?topic=393.msg1603#msg1603 and setting the count to 1) or even ADC triggers - the result is more or less an edge port interrupt but the level can be configured.

Regards

Mark

Offline dkg

  • Newbie
  • *
  • Posts: 48
    • View Profile
Re: iInterruptLevel question
« Reply #4 on: November 19, 2009, 03:24:34 AM »
Hi Mark,

Yes, I know we can use other inputs and we were looking at using the PAIF (GPT3) interrupt as an alternative.

I agree you should change IRQ7 demo because it was what we based our code on when we discovered the problem. It's unfortunate that when the Coldfire is configured to use the Ethernet controller it consumes 3 of the 4 dedicated IRQ pins with the unused one being the non-maskable interrupt. :(

Thanks again for your help.

Dave G.

Offline mark

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 3236
    • View Profile
    • uTasker
Re: iInterruptLevel question
« Reply #5 on: November 19, 2009, 03:21:52 PM »
Hi Dave

1) Yes the available edge-port IRQ lines on the Kirin3 are a little limited, however the FEC consumes only 2 of the 4 available lines (FEC_MDC shared with IRQ5 and FEC_MDIO shared with IRQ3). IRQ1 and IRQ7 should be available.

2) I am looking at adapting the demo and am wondering whether the following may be possible (I am assuming that you would like the IRQ to wake a task so that it can process something based on the event - possibly also doing some fast critical stuff in the routine itself). As already discussed, sending an interrupt event, or other message, is dangerous since it could cause queue corruption.
However I believe that it should be safe to use uTaskerStateChange(OWN_TASK, UTASKER_ACTIVATE) in order to cause the task to be scheduled (without using its queue). As long as no other user code is doing uTaskerStateChange(OWN_TASK, UTASKER_STOP) on the same task, I don't 'see' and risk.
By using a flag, the task can know that it was scheduled due to the IRQ and so behave the same way as when reacting to an interrupt event. One must still be careful with the flag since a second NMI could still try to set it again while the task is trying to clear it (for example), so the following may be a solution to safely using the NMI:

static unsigned long ulNMI_event_count = 0;
static unsigned long ulNMI_processed_count = 0;

NMI // interrupt routine
{
    ulNMI_event_count++;                     // mark that new event has occurred
    uTaskerStateChange(OWN_TASK, UTASKER_ACTIVATE); // safely schedule the task to handle the event
}

TASK // task which could have been scheduled due to the NMI (as well as other reasons)
{
    while (ulNMI_event_count != ulNMI_processed_count) { // if there are open events
        ulNMI_processed_count++;     // this one processed
        // process once for each NMI occurrence
    }
}


Here the NMI and task have their own counters and so there is no risk involved as when the flag is shared.

If this could be a solution for you it may be an idea to adapt the demo to also use it to illustrate the precaution involved when using an NMI.

Regards

Mark


Offline dkg

  • Newbie
  • *
  • Posts: 48
    • View Profile
Re: iInterruptLevel question
« Reply #6 on: November 19, 2009, 04:07:34 PM »
HI Mark,

Unfortunately, our Ethernet hardware uses IRQ1 for the interrupt from the PHY chip. So, in our case Ethernet ended up using the 3 dedicated IRQ lines.

We thought of using global variables similar to your example and, in our case, the information from the NMI was going to be processed by a task that ran when we got a TCP message so it didn't need waking up. The only concern is the fact that this interrupt will interrupt other interrupt routines like Ethernet and whether the context switch and isr code is going to cause any issues.

I'll implement your suggestion here and let you know how it goes.

Dave G.

Offline dkg

  • Newbie
  • *
  • Posts: 48
    • View Profile
Re: iInterruptLevel question
« Reply #7 on: November 23, 2009, 03:24:32 PM »
Hi Mark,

Did you have any thoughts on how often the NMI interrupt would have to hit to cause a negative effect on other interrupts? I just don't have a good understanding of the time it takes to do a context switch on the Coldfire.

Dave G.

Offline mark

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 3236
    • View Profile
    • uTasker
Re: iInterruptLevel question
« Reply #8 on: November 23, 2009, 05:50:43 PM »
Hi Dave

Unfortunately I don't know the figures either - probably the exact number of instructions required to save and restore the context is somewhere in the Coldfire core documentation somewhere - a rough guess would be 20..40 clocks (?). Then the interrupt routine code comes on top of that and may be the most significant value, depending on how much work it actually does. It is also possible to manipulate the interrupt level mask while in the NMI to allow other interrupts to interrupt this, if it makes sense and doesn't cause new difficulties.

Regards

Mark