|
|
View previous topic :: View next topic |
Author |
Message |
mpfj
Joined: 09 Sep 2003 Posts: 95 Location: UK
|
Calling functions within an interrupt routine ... |
Posted: Wed Jan 21, 2004 5:58 am |
|
|
I have encountered an "issue" (not a bug !!) when calling functions within an interrupt routine.
Since the compiler is not stack based, all function parameters are stored in a scratchpad area. The compiler uses a call tree to work out which functions are mutually exclusive, and hence when the scratchpad area can be re-used.
Now I know some of you will say I should be keeping my isr to a minimum, but I have the following situation :-
bitmap() displays a bitmap on an LCD.
rda_isr() deals with incoming serial characters, and queues them using ...
queue() which adds a character to a queue.
When bitmap() is running (and it takes a relatively long time), and I transmit a character to the PIC, rda_isr() is called, when in turn calls queue().
The problem I have is that the compiler thinks that bitmap() is mutually exclusive to queue(), and hence re-uses the scratchpad area. This corrupts the variables used by bitmap(), so that when the rda_isr() finishes, the bitmap() function fails big time !!
I have fixed the problem by adding a dummy call to queue() in the bitmap() function, hence forcing the compiler to realise that the two functions are no mutually exclusive.
*BUT* this is not a good solution !! I have no idea where or when this might happen again, so does anyone know how I should fix the situation ?
(1) I could simply force the queue() function not to take parameters, but to use global variables. However, queue() *is* used elsewhere, so I would have to rewrite a whole stack of code.
(2) Don't call queue() in rda_isr(), just set a flag and call queue() elsewhere. But I don't know when rda_isr() will happen, so I'd have to place "queue test" logic in all my functions !!
Anyone know what I might do ?
Mark |
|
|
Mark
Joined: 07 Sep 2003 Posts: 2838 Location: Atlanta, GA
|
|
Posted: Wed Jan 21, 2004 7:00 am |
|
|
Make sure that you don't enable interrupts from any function that is called from an isr. You can call the queue() function from the main loop. Not sure exactly what your function does but if you are just recieving data, then why not just create your own software buffer. Your isr would then only need to get the byte and put it in the buffer. The main loop can look at the buffer and see if any data has arrived. There are many examples of this already posted on this board. Ttelmah has posted some I know. You can search on his name and find them. |
|
|
mpfj
Joined: 09 Sep 2003 Posts: 95 Location: UK
|
|
Posted: Wed Jan 21, 2004 9:21 am |
|
|
Agreed, I could locally store any incoming characters and only call the queue() function from the main loop, but this does seem rather a backward step ...
Also this still doesn't get over the fact that functions called from an interrupt routine could use the same parameter scratchpad locataions as the function that was interrupted !!
I can't believe that CCS aren't expeecting anyone to call a function in a isr !?! If they *are*, then this should be clearly stated !! |
|
|
Mark
Joined: 07 Sep 2003 Posts: 2838 Location: Atlanta, GA
|
|
Posted: Wed Jan 21, 2004 9:23 am |
|
|
Well it is legal to call a function and the compiler does figure the call path and does create a separate scratch pad. |
|
|
mpfj
Joined: 09 Sep 2003 Posts: 95 Location: UK
|
|
Posted: Wed Jan 21, 2004 10:27 am |
|
|
I agree that it's legel, but the compiler doesn't associate the call path with the function that was interrupted.
I'm quickly coming to the conclusion that you can't (or rather shouldn't) use functions in an isr !! |
|
|
Ttelmah Guest
|
|
Posted: Wed Jan 21, 2004 10:57 am |
|
|
mpfj wrote: | I agree that it's legel, but the compiler doesn't associate the call path with the function that was interrupted.
I'm quickly coming to the conclusion that you can't (or rather shouldn't) use functions in an isr !! |
You don't say what compiler version?.
The scratch area, is normally saved by the global interrupt handler. If you look at the disassembly of the normal code, it copies to contents of SCRATCH, SCRATCH+1, and SCRATCH+2, into an area reserved for the interrupt handler, after saving the working registers, and the table pointers.
The 'exception', would be if you are using your own INT_GLOBAL (in which case it is your responsibility to do this). Also some old compilers did have probelms in this area.
What you are describing, should not be happening with any recent release, unless you are doing something 'unexpected'.
Would you care to post the interrupt routine, declarations, and 'queue', so we can see what is happening.
Adding 'queue' to bitmap, forces the second protection mechanism to 'trigger', which is that if a routine is called both in an ISR, and outside, interrupts will be disabled around the routine in the main code.
Best Wishes |
|
|
mpfj
Joined: 09 Sep 2003 Posts: 95 Location: UK
|
|
Posted: Wed Jan 21, 2004 1:33 pm |
|
|
I'm using compiler version 3.184 on a PIC18F8720.
I can see that the SCRATCH area 0x000-0x004 are saved and restored. However, this is *not* the scratch area I am talking about.
What I am concerned about is the scratchpad area used by all the functions ... e.g. from my .SYM file
Code: | B6E queue_in.c
B6E display_show_bitmap.@SCRATCH
B6F lcd_set_ptr.@SCRATCH
B6F display_show_bitmap.@SCRATCH
B6F queue_in.@SCRATCH
|
Now let us assume that display_show_bitmap() is currently running. This is a lengthy routine, so it is possible to receive several characters via the serial port while it is running.
For each serial character, the display_show_bitmap() function is interrupted by INT_RDA, and the isr is called.
The isr receives each character, calls queue_in(), and exits (in this case back to the display_show_bitmap() routine).
This appears perfectly reasonable to me, but you can see that the scratchpad location 0xB6F is used by both queue_in() and display_show_bitmap(). Hence there is a high probability that, when the isr returns, the data held in 0xB6F that's being used by display_show_bitmap() will have been changed by queue_in().
Of course when this happens, display_show_bitmap() is likely to go wrong ... and it does !!
The compiler thinks it can re-use the scratchpad area because display_show_bitmap() doesn't directly call queue_in(). Hence there is no link in the call tree.
So what I'd like to know is how do I fix this problem ?
How can I prevent this incorrect re-sue of the scratchpad area ? |
|
|
Ttelmah Guest
|
|
Posted: Wed Jan 21, 2004 4:16 pm |
|
|
mpfj wrote: | I'm using compiler version 3.184 on a PIC18F8720.
I can see that the SCRATCH area 0x000-0x004 are saved and restored. However, this is *not* the scratch area I am talking about.
What I am concerned about is the scratchpad area used by all the functions ... e.g. from my .SYM file
Code: | B6E queue_in.c
B6E display_show_bitmap.@SCRATCH
B6F lcd_set_ptr.@SCRATCH
B6F display_show_bitmap.@SCRATCH
B6F queue_in.@SCRATCH
|
Now let us assume that display_show_bitmap() is currently running. This is a lengthy routine, so it is possible to receive several characters via the serial port while it is running.
For each serial character, the display_show_bitmap() function is interrupted by INT_RDA, and the isr is called.
The isr receives each character, calls queue_in(), and exits (in this case back to the display_show_bitmap() routine).
This appears perfectly reasonable to me, but you can see that the scratchpad location 0xB6F is used by both queue_in() and display_show_bitmap(). Hence there is a high probability that, when the isr returns, the data held in 0xB6F that's being used by display_show_bitmap() will have been changed by queue_in().
Of course when this happens, display_show_bitmap() is likely to go wrong ... and it does !!
The compiler thinks it can re-use the scratchpad area because display_show_bitmap() doesn't directly call queue_in(). Hence there is no link in the call tree.
So what I'd like to know is how do I fix this problem ?
How can I prevent this incorrect re-sue of the scratchpad area ? |
Though it sounds daft, try an older compiler!. 3.148, was largely 'good', and I have dozens of routines using exactly this layout, and it is correctly allocating a different 'scratch' for this type of operation, for the interrupt routines. 3.184, has definately got some issues, and this may be one of them...
If it still behaves the same, then it becomes necessary to look at why the compiler is not seeing the queue function as being part of an interrupt.
Are you actually seeing corruption?. One reason I ask, is that for some routines, this type of space is apparently allocated, but not used!. For instance, I have a declaration, that shows:
277-278 mymemcpy.source
277 I_EEPROM_PUT.@SCRATCH
277 BCDINC.@SCRATCH
With 'I_EEPROM_PUT", occuring inside an interrupt (in the event of a powerfailure signal), _but_, the routine, does not use the address at all. It accesses 273, 274, 275, 276, and 279, but never addresses 277!.
Best Wishes |
|
|
adrian
Joined: 08 Sep 2003 Posts: 92 Location: Glasgow, UK
|
|
Posted: Thu Jan 22, 2004 8:01 am |
|
|
I would be very interested in your results if you re ran them with v3.148.
I am currently developing a product using v3.148 with a 12F629 with the internal oscillator, using Timer 1 and #INT_RB for int_on_change_GP05(). I too am suffering from corrupted variables - but mine are global variables. I do not call any functions from my interrupt routines - apart from printf. I have to use printf to debug as I am on a shoestring and havn't got an ICD plus the special required ICD chip.
I have found that moving the code around or moving the print statement around in the same function cured the problem. My concern is when I rebuild the software for the application, removing the printf's, will I still get the corruption - and how will I know? |
|
|
mpfj
Joined: 09 Sep 2003 Posts: 95 Location: UK
|
|
Posted: Thu Jan 22, 2004 8:14 am |
|
|
One nagging thought still in my head is that this all has something to do with queue_in() (and some of the other functions called by the interrupt routines) are also called *outside* the interrupt routines.
e.g. the queue_in() function is a generic function that handles all my queues (receive buffer, transmit buffer, keyboard buffer, barcode reading buffer, etc).
Similar can be said for queue_empty() and queue_out() and a few others I haven't mentioned.
Is this the cause of my problems ?
I don't get any warnings about "disabling interrupts to prevent recursive calls" that I've seen elsewhere on this forum.
If I were to create functions *only* called by the interrupt routines, but that did the same thing, would that cure my problem ?
i.e. for queue_in(), I would actually have the original queue_in() and also queue_in_isr(), and any interrupt would have to use the "isr" version.
Or would I even have to have seperate queue_in() routines for each interrupt routine as well as one for non-interrupt functions ? |
|
|
Ttelmah Guest
|
|
Posted: Thu Jan 22, 2004 10:08 am |
|
|
mpfj wrote: | One nagging thought still in my head is that this all has something to do with queue_in() (and some of the other functions called by the interrupt routines) are also called *outside* the interrupt routines.
e.g. the queue_in() function is a generic function that handles all my queues (receive buffer, transmit buffer, keyboard buffer, barcode reading buffer, etc).
Similar can be said for queue_empty() and queue_out() and a few others I haven't mentioned.
Is this the cause of my problems ?
I don't get any warnings about "disabling interrupts to prevent recursive calls" that I've seen elsewhere on this forum.
If I were to create functions *only* called by the interrupt routines, but that did the same thing, would that cure my problem ?
i.e. for queue_in(), I would actually have the original queue_in() and also queue_in_isr(), and any interrupt would have to use the "isr" version.
Or would I even have to have seperate queue_in() routines for each interrupt routine as well as one for non-interrupt functions ? |
That is exactly what 'I_EEPROM_PUT' is in my code. Since I write occasionally to the EEPROM, when changing configuration data, and also want to write a small number of bytes inside the 'power fail' interrupt, I have a seperate routine for the latter. Obviously, you will have to be very careful, in your use of any 'common' variables (addresses for data in the buffer for instance).
Best Wishes |
|
|
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
Powered by phpBB © 2001, 2005 phpBB Group
|