Detecting skipped ISR calls for timers

Go To Last Post
10 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hello all,

I'm implementing a pseudo-real-time application using a SAM3U.

The idea is to have a timer running at a frequency of 4Khz, and calling a function when the end of the period (system tick) is completed. The complication is that the function (routine) is not as short as other ISRs I have seen before, but it is quite hard to make it any shorter, and I suspect that the system ticks may be running too fast for it.

 

What I would like to do is to have a deterministic, firmware based, way to tell when the function has executed for more than one "system tick period" time. 

 

I was hoping to implement this using re-entrant interrupts. When the interrupt routine is called I raise a flag, and lower it when It leaves the routine. If I check for the flag when entering the routine I would be able to know if the execution is still on-going and notify the user about the situation. Unfortunately the SAM3U doesn't seem to support re-enty on interrupts, so I'm out of ideas.

 

Any help would be appreciated.

 

Carlos.

This topic has a solution.
Last Edited: Tue. May 15, 2018 - 08:40 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I'd be surprised if the ARM cortex m3 can't do interruptible isrs. Read up on the interrupt system and the NVIC. I think what you really want is a RTOS. This will disconnect the system tick from your task and avoid the issue you describe. As for detecting a missed 'tick' that should become simple. Something like freertos should be suitable.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks for your reply Kartman,

The SAM3 does support interruptible ISRs, but it doesn't seem to support re-entrant (as in trying to run a routine before its previous call has completed).

 

I'm contemplating the migration to RTOS, but it's a bit late in the development cycle to switch... it would take longer than what we can afford.

 

Is there a hack to detect when an interrupt is being executed when a new (of the same) interrupt is generated? If I change the interrupt priority once I go into the ISR to a smaller number than before, would the next interrupt pause the execution of the previous call?

 

I may be getting too creative here.

 

Carlos.

 

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

If you read your "own" timer at the end of the ISR then you will know if you are late (perhaps in the isr have higher compare match or reload value depending of mode).

But in general never be late in isr, then rather miss something in main. (often just set a flag in isr, or perhaps in this case i++, so main know how far it's behind)

Last Edited: Fri. May 11, 2018 - 01:23 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I find hit hard to imagine that the SAM3 would not support re-entrant interrupts.

Why do you think this is not supported?

But there are a bunch of gotcha's with re-entrant code.

Are you sure it is not a bug in your code?

 

Another Idea:

You can put the second part, the "function" in a separate (lower priority) interrupt, and trigger that in software from the first interrupt.

Paul van der Hoeven.
Bunch of old projects with AVR's:
http://www.hoevendesign.com

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hello Paulvdh,

A further detail on why re-entry is not supported in Cortex M3 architectures:

I've tried a lot of things to get a re-entry to an ISR, but after failing miserably I went to read-and-learn mode. In essence for the SAM3U (Cortex M3) most of the work of saving pointers and the stack when going into and leaving an ISR is done by the HW to reduce the latency of IRQs. In the implementation, an ISR will not be pre-empted (immediately executed) unless it has a higher priority.

 

http://infocenter.arm.com/help/i...

 

I then tried to increase the priority of the IRQ inside it's IRS, which would allow me at least one re-entry, but it didn't work either. The reason (Look at the Dynamic Priority Change Support below): 

 

https://books.google.com/books?i...

 

So I' m back to hacking it.

 

The idea of "You can put the second part, the "function" in a separate (lower priority) interrupt, and trigger that in software from the first interrupt." sounds like my best option at this point, but the caveat is that the Interrupt Vector where the service functions are defined for the NVIC is limited to 30 registers. (AtmelDatasheet for SAM3U4, page 149). So now I'm stuck because the Atmel ASF framework (which I'm using for my development) has the 30 ISRs already defined for its peripherals and I don't want to trigger the interrupt of a peripheral to call my function.

 

Now, in page 158 of the Datasheet for SAM3U4, it talks about the Software Trigger Interrupt Register (STIR) and they mention you can use any number from 0-239 to specify an IRQ to be signaled. But the question is: how do I register a new ISR for say IRQ40? Is this even possible?

 

Thanks for your help.

 

Carlos.

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

This is why i suggested using a rtos. In the isr, the stack is manipulated so that the code you want to run happens when you exit the isr. Thus the isr is very short. By using a flag, the isr can test this to see if your task completed or simply keep a tick count. A RTOS like freeRtos does the magic for you straight out of the box.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Carloso wrote:
ASF has the 30 ISRs already defined for its peripherals and I don't want to trigger the interrupt of a peripheral to call my function.
Why not? I find it hard to believe you need them all for your appilcation. Just add some proper documentation and be done with it.

 

Have you done some kind of timing analysis of those functions? How much time do they actually need to complete?

If you have an interrupt triggered faster than it can be executed you are out of luck anyway.

 

It seems like you are on a path to beat your uC into something that is over it's head.

I see a few simple options:

1). Optimize those interrutpt so they can be made to guarantee to be executed in the alloted time.

2). Lower the interrupt rate to give an interrupt more time to execute.

3). Use a faster uC. How much development time / effort can you afford to avoid having to pay 50 cents more for each uC?

 

Simple is good. It makes you code easy to understand / debug and speeds up development.

It might even be a good idea to switch to a "faster" uC now to speed up development and go to market faster, and re-visit this issue if high-volume becomes important.

 

The infocenter.arm link you pointed to mentions "thread" and "handler" mode. You might be able to do something with that, but I'm not familiar enough to say anything about it.

But it just seems the wrong path to follow.

Paul van der Hoeven.
Bunch of old projects with AVR's:
http://www.hoevendesign.com

This reply has been marked as the solution. 
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hello Paul

Your are absolutely right:

"Why not? I find it hard to believe you need them all for your appilcation. Just add some proper documentation and be done with it."

 

Thanks to all of you for your help.

 

For anyone else in a similar situation, this is how I solved it:

 

I used the HSMCI_IRQn, which I'm not using, and just redirected it to have some peace of mind if we need to change the IRQ later on. My code looks like this:

 

/*

=============

The execution handler that I want to protect

from overlaps

=============

*/

 

#define EXECUTE_TICK_IRQn HSMCI_IRQn //It redefines this IRQn, since we are not using it

#define EXECUTE_TICK_Handler HSMCI_Handler //It redefines this handler, since we are not using it

 

static volatile bool tick_pending=false;

 

init()

{

NVIC_DisableIRQ(EXECUTE_TICK_IRQn);

NVIC_ClearPendingIRQ(EXECUTE_TICK_IRQn);

NVIC_SetPriority(EXECUTE_TICK_IRQn, 6);

NVIC_EnableIRQ(EXECUTE_TICK_IRQn);

}

 

ISR(EXECUTE_TICK_Handler)

{

//Execute the action

tick_pending=true;

... do what you need to do

tick_pending=false;

}

 

bool is_tick_pending(void)

{

return tick_pending;

}

 

 

And on the timer side, this is what I have in the ISR handler:

/*

=============

The timer handler

=============

*/

 

init()

{

// Enable the interrupt

NVIC_DisableIRQ(TICK_TIMER_TC_IRQn);

NVIC_ClearPendingIRQ(TICK_TIMER_TC_IRQn);

NVIC_SetPriority(TICK_TIMER_TC_IRQn, 4);

NVIC_EnableIRQ(TICK_TIMER_TC_IRQn);

tc_enable_interrupt(TICK_TIMER_TC, TICK_TIMER_TC_CHANNEL_CAPTURE, TC_IER_CPCS);

}

 

void TC0_Handler(void)

{

//read status register - this clears interrupt flags

if( tc_get_status( TICK_TIMER_TC, TICK_TIMER_TC_CHANNEL_CAPTURE ) > 0 )

{

// Check if we are busy

if ( is_tick_pending() )

{

report_error("The Timer handler was called but we haven't finished processing the last tick");

}

else

{

//Execute the action

NVIC_SetPendingIRQ(EXECUTE_TICK_IRQn);  //This executes EXECUTE_TICK_Handler()

}

}

}

 

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

A comment about commenting:

Comments like these are useless, even contraproductive, because they distract without adding anything:

// Execute the action
// Enable the interrupt
// read status register
// Check if we are busy
// execute the action

 

I particularly came to dislike such way of commenting when I worked with nucleus (a RTOS) code a long time ago.

That code was full of:

(empty line)
// Do something
(empty line)
Do_Something()

It was almost impossible to get any kind of overview of a function because 3/4 of all the lines had absolutely no meaning.

 

Instead, write comment on the code on ***  WHY *** you have written the code in the way you do.

Right in the interrupt handler for the "abused" interrupt write there that you use it only to offload the timer interrupt and it has nothing to do with "normal" use of that peripheral.

 

The idea is that if you want to change the code in a few years time (Or someone else who has no prior knowledge of your code) comments like that help in understanding the principles of the source code. If you need to modify your project and for example need that peripheral then, you are immediately reminded that you can move that code to some other unused interrupt.

Try to make a habit out of this. There is an art into writing good comments.

The difficult part is writing down the things that are now fresh in your memory, and so clear that they do not seem worth commenting, but will have forgotten in a few years time.

Try to imagine expaining to another programmer what your code does and how it works. Write those things down. Do not simply repeat code lines in the comment.

Paul van der Hoeven.
Bunch of old projects with AVR's:
http://www.hoevendesign.com