MCF5234: functionality of the interrupt controller

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

MCF5234: functionality of the interrupt controller

1,985 Views
sirlenzelot
Contributor II


Hi All!

 

Dealing with interrupt service routines I ran into some questions. Maybe you can help?

 

1)

In 13.2.1.6 of the reference manual they say that you have to program the ICRnx registers with "unique and non-overlapping level and priority definitions".

In 13.3 they tell about prioritization between INTC0 and INTC1 and that INTC0 will be served first, if there are two interrupts with the same level and priority.

 

Am I right that I am allowed to have two interrupts with the same level and priority if they have different interrupt controllers?

 

2)

Maybe I missed it, but what is the correct time to clear the interrupt flags? At the beginning or at the end of the interrupt service routine?

 

3)

The MCF5234 supports nested interrupts. What happens if I clear the interrupt flag at the beginning of the ISR and before the ISR has ended, the same interrupt occurs again? Will the active ISR run to its end or will it be interrupted?

 

4)

Is there any possibility that an ISR is not executed until its end? The activate_HISR()-routine does some OS-specific stuff. fetching pointers, some if/else, lockout all interrupts for putting a pointer on a list and permitting all interrupts again. (for those, who know: OS is Nucleus, activate_HISR() calls NU_Activate_HISR().)

 

Sometimes I am missing the results of the activate_HISR()-routine. It seems, that it was not executed. So I modified the ISR for some testing.

 

original ISR:

interrupt void TpuInterruptEntry12(void)

{

   ClearIntRequest();

   activate_HISR();

}

 

modified ISR:

int TCRSTATE = 0;

interrupt void TpuInterruptEntry12(void)

{

  if (TCRSTATE != 0)

  {

       TCRSTATE++;

       ClearIntRequest();

       TCRSTATE = 0;

  }

  else

  {

       TCRSTATE = 1;

       ClearIntRequest();

       activate_HISR();

       TCRSTATE = 0;

  }

}

 

Sometimes the ISR runs into the if-case. I thought this could not happen.

There are other interrupts which use the same ISR but run without any problems.

 

 

 

Thanks.

 

vy 73

Dirk

Labels (1)
0 Kudos
Reply
6 Replies

1,613 Views
sirlenzelot
Contributor II

Tom,

digging into our OS I found out why the strange behaviour happens:

The timer interrupt of the OS is not left by RTE in case of normal operation. Instead it calls the system scheduler.

The OS's interrupt handling is based on certain structures dealing with interrupts. We did override theese structures because they made interrupt handling too slow for our application. By overriding we also disabled the correct handling of nested interrupts inside the timer interrupt.

What happens in case of nested interrupt?

e.g. PIT interrupt occurs and is interrupted by OS interrupt. OS interrupt does not know about the interrupted interrupt and works like normal resulting in not returning by RTE but calling the system's scheduler and before that, changing the stack. So it was never possible to return to the interrupted interrupt.

If one of our interrupts is interrupted by any other of our interrupts, nesting works like a charm.

Now that I know, I was able to modify our interrupt routines for correct nested interrupt handling for the timer os.

I implemented an OS-like interrupt recognition in each of our interrupt handler and  everything looks good. No "double" interrupts anymore.

Thanks for your ideas and help.

DIrk

0 Kudos
Reply

1,613 Views
TomE
Specialist II

> 1. No, different INTCn has different interrrupt source...

The level and priority only have to be unique on the one controller. We do that a lot as CAN and the TPU chew up a huge number of interrupts.

The clearing depends on the peripheral. But I would always recommend "clear at the start and then service". If you do the reverse you risk servicing an interrupt (say from a received character on a device), then having another character arrive when you're in the routine, then you clear the interrupt for THAT one and you've lost the interrupt. So you should always read the interrupt status register, clear all the interrupts set in that register (usually by reading the status, then writing that exact data back to the write-ones-to-clear register) and then service the interrupts indicated in the status value read at the top. Then exit. Any new interrupts that popped up while executing that code will be handled in the next interrupt.

You shouldn't have to touch the interrupt controller after it has been set up. All (most) interrupt handling happens by reading and writing the peripheral interrupt status and control registers. This is not a broken CPU chip like the ones that only have one interrupt request level, where you have to play the bolt-on interrupt controller to handle interrupts. This is a real hardware multi-level interrupt CPU.

> What happens if I clear the interrupt flag at the beginning of the ISR

When the CPU is interrupted at Level "N" the CPU is automatically set to priority level "N". Don't mess with it. Don't change the CPU priority level. While it is like that (in this ISR) it can't be interrupted by any level "N" interrupts (or lower) until it has finished and returned to the previous interrupt level. It can only be interrupted by higher level interrupts.

So if you're doing anything at all complicated, it is essential NOT to use any "interrupt enable" and "interrupt disable" functions. People usually write these to force the CPU IPL to "7" and "0" respectively. Then they call them from the middle of an interrupt service routine, the CPU gets dropped to Level 0 and the same interrupt hits again. Don't do that. If you must change the interrupt level, use functions that SAVE the previous level before changing it. Read the following for this:

https://community.nxp.com/message/316241#comment-316241

Concerning the TPU, is that a common service routine for multiple TPU channels? Have you got those TPU channels configured to interrupt at different LEVELS? That's likely as there are sixteen TPU interrupt sources and only 8 priorities, so you are forced to use two levels, Our code has the TPU interrupting on levels 3 and 4:

typedef struct

{

    uint8_t controller;

    uint8_t source;

    uint8_t level;

    uint8_t priority;

} INT_INFO;

static const INT_INFO int_etpu[ETPU_NUM_CHANNELS] =

{

    { 1, 27, 4, 7 }, { 1, 28, 4, 6 }, { 1, 29, 4, 5 }, { 1, 30, 4, 4 },

    { 1, 31, 4, 3 }, { 1, 32, 4, 2 }, { 1, 33, 4, 1 }, { 1, 34, 4, 0 },

    { 1, 35, 3, 7 }, { 1, 36, 3, 6 }, { 1, 37, 3, 5 }, { 1, 38, 3, 4 },

    { 1, 39, 3, 3 }, { 1, 40, 3, 2 }, { 1, 41, 3, 1 }, { 1, 42, 3, 0 },

};

You either have to have two separate service routines for the different levels, or on interrupt entry you have to force the interrupt level to the highest one being used to stop the other one from re-entering.

Tom

0 Kudos
Reply

1,613 Views
sirlenzelot
Contributor II

Hi Tom,

thanks for answering.

For 1-3 I am clear.

4) I did not write, but we don't simply enable/diable the interrupts in the ISR. As you stated, the IPL is set to 7 and restored to the previous IPL when the critical part is finished.

Each interrupt has its own and unique ISR with unique priority/level.

(We had that priority/level problem before and fixed it in the past.)

I attached a screenshot below. Have a look at the Exception Stack Frame 0x429C2000. It's vector 167 (TPU 12) but with SR 2000, doesn't that mean IPL = 0? TPU12 runs at level 5, prio 3. The interrupt level mask is set to the active interrupt level during the ISR, isn't it? So it should be 5.

When TPUChannel12 ISR is active, there are only 10 interrupts that are higer priorized. 5 of them are disabled, 4 cannot occur, because they are in standby. The highest priorized interrupt is the timer interrupt of our OS, which is fired every 10ms by PIT0. I'll check that code, but I don't believe that there are errors in it, because some strange behaviour like this should have happened before.

2016-06-13 10_37_11-EDGE Debug - TPU_Sources_tpuchannel.cpp - EDGE.jpg

The handling of the ipl is somewhat strange in our OS. They use a global variable to store and work with the actual ipl. At certain times they lockout all interrupts, e.g. protection mechanisms against simultaneous access to qs, memory pool, task switching, etc.

Thanks

Dirk

0 Kudos
Reply

1,613 Views
TomE
Specialist II

I suggest you read "11.1.2 Exception Stack Frame Definition" in the "CFPRM.pdf" (ColdFire Family Programmer’s Reference Manual).

Using that to see if "0x429C2000" is what I think it is (the exception frame), it decodes as:

Aligned (4)

FS=00 00 - Ordinary Interrupt

VEC= 29C (Vector 167)

*PREVIOUS* SR is 0x2000

That's the previous SR the was active when the interrupt happened, It is pushed onto the stack (so it can be restored later). If you want to see what the CURRENT interrupt level is, check the CPU registers during that debug breakpoint.

To see how it all works, read CFPRM "Chapter 11, Exception Processing, 11.1 Overview".

If you still seem to be getting a "double interrupt", hitting that "if" condition, then check the stack frame when you hit that breakpoint. The stack should show two sets of interrupt. Note that it is possible that the debugger won't be able to decode the stack frame properly, so you may have to take a raw memory dump of the stack and decode it yourself.

> The highest priorized interrupt is the timer interrupt of our OS

Does it just "count the time" or does it actively context switch between threads? The latter gets tricky if it interrupts an interrupt service routine. It can't switch stacks to switch threads when there's an interrupt on it.

It is possible that your OS was written for a "single interrupt level" processor, and expects you to break the Coldfire system by writing all interrupt service routines so they emulate a "single interrupt level" CPU. At the end of the "Overview" section of the CFPRM it states "ColdFire processors inhibit sampling for interrupts during the first instruction of all exception handlers.

This allows any handler to effectively disable interrupts, if necessary, by raising the interrupt mask level

in the SR.". That's how you fake this if you have to. That means you force the first instruction to "move.w #0x2700, SR" to do this. If you need to do that you can't use the "interrupt" keyword to have the compiler generate the function preamble (unless it has an option to add this), but have to write the interrupt function yourself in assembly.

Or the OS timer interrupt has to be the LOWEST priority interrupt in the system so it can't interrupt an interrupt service routine..

What does "activate_HISR()" actually do? How does it know which interrupt happened (and so which service routine to call) if you're not passing any parameters into that function. Or was your quoted code only "pseudocode"?

The upside of an OS is you don't have to write all this tricky stuff yourself. The downside is you have to understand it when it goes wrong.

Tom

0 Kudos
Reply

1,613 Views
sirlenzelot
Contributor II

Thanks, Tom, for your answer.

The exeption stack frame contains the previous level. I read that over.

I had a look at the stack and figured out that I have two different conditions in which the "double interrupt" occur.

a) I can see only one ex. stack frame on the stack.

b) I can see a second ex. stack frame on the stack: 0x41902500. Which means interrupt 100 (PIT0, the timer of our OS, runs every 10ms) and priority level 5, which is the level of the TPU12 interrupt I have problems with.

The timer interrupt processes the actual hardware interrupt. Processing includes updating the system clock and the count-down timer and the time-slice timer. If one or both of the timers expire, the timer HISR is activated. The timer HISR is responsible for initiating the task switching which is done by the scheduler.

The timer interrupt itself does not do any active context switching. It's only copying pointers.

A HISR is a so called Highlevel Interrupt Service Routine. It's a high priority task which is called by the system-scheduler and will be executed before any other task. The activate_HISR() does some register saving, updating some counter variables, locks out all interrupts, copies the pointer of the HISR-entry-function onto a list which is handled by the scheduler.

The HISR is needed because you can't handle Qs, Events, etc. in the ISR.

Our OS has some sort of mechanism that all interrupts use the same ISR. Inside this global ISR the active interrupt is recognized and handled by the OS. This mechanism was too slow for our needs. So we decided to use our own ISRs and also use the HISR-mechanism like they do.

activate_HISR() here is pseudo code, indeed. As we code in c++, every ISR belongs to a class which is derived from the HISR-class of our OS. The ISR fetches an instance of the class it belongs to and then knows how to activate the HISR.

I will try, what you suggested to do.

1) lockout all interrupts in this specific ISR.

2) decrease the level of the OS timer interrupt. (I'm courious about what will happen to the rest of our software if I do this.)

After changing some code each test needs at least 20-30 Minutes until the "double interrupt" occurs again.

Dirk

0 Kudos
Reply

1,613 Views
miduo
NXP Employee
NXP Employee

Hi,

Answer to your question:

1. No, different INTCn has different interrrupt source and for any interrupt source, it is important that the interrupt level and priority combinations be unique for all interrupts.

2 and 3. For different interrupt, the clear mechanism is different, See Table 13-13. Interupt Source Assignment For INTC0 in the manual.

4. I will try to deep dive on this regard and respond soon.

0 Kudos
Reply