From: Gordon H. <gor...@dr...> - 2009-11-12 09:12:56
|
... or what can't I do? Essentially I have a little real-time system which currently runs on an AVR chip (with GCC) which I'm porting to PIC18 and SDCC. So-far so good and SDCC is managing about 8000 lines of C very well. So before I end up shooting myself in the foot, I thought I'd ask a quickie here - This project has several interrupt sources, and while I could re-code it with just one ticker type interrupt and a big poll loop, it would make life easier if I can use multiple interrupts - however inside an interrupt routine I need to be able to call other functions and do some floating point math. I also need the main interrupt to be reentrant, so the longish bit of code that is doing the FP calculations which takes a handful of millisecs can be interrupted by something else. (e.g. button push, ADC conversion end, etc.) There is only one interrupt routine that'll be doing the floating point, but the 'main' program may also be doing floating point when it gets interrupted by the timer to go off and do FP calcs in the background. I've no issues with atomic access, locking, etc. to shared variables, etc. I'm just concerned that the interrupt code is going to stomp on some intermediate result being calculated elsewhere. There's scant documentation online for this sort of thing (that I've been able to find though!) so I'm wondering what the easiest way to go about this - will it "just work", or will I have to re-compile the math libraries to be re-entrant, or is there some other magic, or is it really just a bad idea! Thanks, Gordon |
From: Michel B. <mic...@bo...> - 2009-11-12 14:31:46
|
Hi, Le jeudi 12 novembre 2009, Gordon Henderson a écrit : > it would make life easier if I can use multiple interrupts - however > inside an interrupt routine I need to be able to call other functions and > do some floating point math. I also need the main interrupt to be > reentrant, so the longish bit of code that is doing the FP calculations > which takes a handful of millisecs can be interrupted by something else. > (e.g. button push, ADC conversion end, etc.) AFAIK, the PIC code generated by sdcc is *not* reentrant, so you'd better take care not to call functions recursively, or have an interrupt call a function while the main program has already called the same. What you could do is : - PIC18 can affect interrupts to 2 different (high/low) priorities, so you could affect you "longish bit of code" calling functions to a low priority int (or try to perform such calculations outside of an interrupt routine), and affect realtime, urgent interrupts to the high priority (that can interrupt the low priority). If you use in your main() some functions that the low-pri int calls as well, you should disable low-pri int (GIEL) before calling such function from main() and reenable GIEL immediately afterwards, so you're sure that your "shared" function will not be called by main() and by the isr() at the same time. > or will I have to re-compile the math > libraries to be re-entrant ...or you could do that. HTH. -- Michel Bouissou (OpenPGP ID 0xEB04D09C) |
From: Raphael N. <rn...@we...> - 2009-11-12 15:05:27
|
Hi, >> it would make life easier if I can use multiple interrupts - however >> inside an interrupt routine I need to be able to call other functions >> and do some floating point math. I also need the main interrupt to be >> reentrant, so the longish bit of code that is doing the FP calculations >> which takes a handful of millisecs can be interrupted by something else. >> (e.g. button push, ADC conversion end, etc.) > > AFAIK, the PIC code generated by sdcc is *not* reentrant, so you'd > better take care not to call functions recursively, or have an > interrupt call a function while the main program has already called > the same. I disagree. We do not require complete reentrancy, we only require properly nested execution of the code: If the main program is in the middle of any routine Rn (called from main (R0), R1, R2, ..., R{n-1}) when the interrupt fires, the interrupt routine can call each of these routines Ri (and all others) because we save the access bank registers in the function prologue and restore them in the epilogue. As long as the interrupt routines do not meddle with the stack pointer (i.e., switch to a different stack), properly nested execution of all the code generated by the PIC18 (-mpic16) target should not be a problem. >> or will I have to re-compile the math libraries to be re-entrant > > ...or you could do that. The PIC targets (-mpic14 and -mpic16) do not support any of the --int-long-reent or --float-reent switches and thus cannot be compiled with (full) reentrancy enabled. As I guess that this should not be required for the stated task, though, I don't think that this is a real problem. Context switching (meaning support for multiple threads with separate stacks and arbitrarily interleaved concurrent execution of code is not easily possible with the current code generation approach (using a large, unspecified number of register in the access bank for locals). Raphael |
From: Gordon H. <gor...@dr...> - 2009-11-12 16:36:49
|
On Thu, 12 Nov 2009, Raphael Neider wrote: > Hi, > >>> it would make life easier if I can use multiple interrupts - however >>> inside an interrupt routine I need to be able to call other functions >>> and do some floating point math. I also need the main interrupt to be >>> reentrant, so the longish bit of code that is doing the FP calculations >>> which takes a handful of millisecs can be interrupted by something else. >>> (e.g. button push, ADC conversion end, etc.) >> >> AFAIK, the PIC code generated by sdcc is *not* reentrant, so you'd >> better take care not to call functions recursively, or have an >> interrupt call a function while the main program has already called >> the same. > > I disagree. We do not require complete reentrancy, we only require properly > nested execution of the code: If the main program is in the middle of any > routine Rn (called from main (R0), R1, R2, ..., R{n-1}) when the interrupt > fires, the interrupt routine can call each of these routines Ri (and all > others) because we save the access bank registers in the function prologue > and restore them in the epilogue. As long as the interrupt routines do not > meddle with the stack pointer (i.e., switch to a different stack), properly > nested execution of all the code generated by the PIC18 (-mpic16) target > should not be a problem. This is good news! main() will never call any functions that are called from an interrupt, and there is no recursion either. I'm not fiddling with the stack pointer either. My concern is something in main() doing floating point, then being interrupted, the interrupt task also doing floating point, returning, then the result of the FP operating being carried out in main() being corrupted. >>> or will I have to re-compile the math libraries to be re-entrant >> >> ...or you could do that. > > The PIC targets (-mpic14 and -mpic16) do not support any of the > --int-long-reent or --float-reent switches and thus cannot be compiled > with (full) reentrancy enabled. We need more documentation on this - I may even volenteer after I've done my project, and even give examples... My experience of using the PIC16 variant of SDCC has been very good so-far, despite almost being put off by lots of warnings on various web sites - trouble is, they're all dated some years back! > As I guess that this should not be required for the stated task, though, > I don't think that this is a real problem. > Context switching (meaning support for multiple threads with separate > stacks and arbitrarily interleaved concurrent execution of code is > not easily possible with the current code generation approach (using > a large, unspecified number of register in the access bank for locals). Essentially what I have is an interrupt driven/assisted coroutine manager. I have a regular timer interrupt, and this scans a list of tasks, and calls them as needed (based on an abolute or relative time). However, it re-enables interrupts before it calls the task, so that if the task takes a time longer than the tick time, another task can be executed. (It will never call an active task twice). The tasks are not allowed to sit in a loop forever (well, one could, but only one, and then main() would never continue!) they must relinquish control in a timely manner (like coroutines) I've been running this tiny scheduller for a few different projects on AVR platforms, but now I have a nice PIC project to do and want to use the same framework. So from what you're said above it looks like it should work OK. I'll give it a go and let you know :) Thanks, Gordon |
From: Gordon H. <gor...@dr...> - 2009-11-24 17:05:20
|
On Thu, 12 Nov 2009, Gordon Henderson wrote: > I'll give it a go and let you know :) So a while back, I asked about interrupts, etc. and now I'm letting you know what happened... I see data corruption )-: Specifically during compiler generated calls to _mullong although I haven't tried any others yet. If I execute some code during the interrupt that has generated _mullong calls in it while doing code that also calls _mullong in the main task, then the result of the main task operations is corrupted. This code fragment: (I've removed the boring printing bits, and it gets called with a fixed value of 437, else it gets optimised out!) void mathTest (uint16_t x) { uint32_t y ; uint32_t i = 0 ; for (;;) { y = (uint32_t)x * 9668 + 500 ; if (y != 4225416L) { serialPuts ("Curruption @ ") ; ... } ++i ; } works fine with interrupts off, or with the math code in the interrupt routine commented out, but sees random coruptions with them on: Curruption @ 9912: 4744 Curruption @ 10761: 07279990 (Don't wory that this starts with 0.. Curruption @ 15990: 25649 Curruption @ 18438: 03028533 ... the field width isn't big enough) Curruption @ 19994: 17266 and so on. That's the first I've noticed int and I've not even dived into floating point land yet... So calling functions, manipulating data in my own code, etc. seems relatively well behaved, but the internal stuff not so... Any thoughts, anyone? Thanks, Gordon |
From: Michel B. <mic...@bo...> - 2009-11-24 17:24:03
|
Le mardi 24 novembre 2009, Gordon Henderson a écrit : > Any thoughts, anyone? As a workaround, you might disable (mask) interrupts in your main() code just before the instruction that calls _mullong, and re-enable them immediately after, so you're sure that an interrupt will never call _mullong while it's already being executed outside of the interrupt service routine... i.e while (INTCONbits.GIEL!=0) INTCONbits.GIEL=0; some_code_that_calls_mullong; INTCONbits.GIEL=1 (Use GIEL/GIEH or GIE/PEIE depending upon if you run with interrupt priorities enabled, or in compatibility mode) (See PIC app notes to see why you should "while()" test that interrupts are effectively disabled before continuing) HTH. -- Michel Bouissou (OpenPGP ID 0xEB04D09C) |
From: Gordon H. <gor...@dr...> - 2009-11-24 18:01:31
|
On Tue, 24 Nov 2009, Michel Bouissou wrote: > Le mardi 24 novembre 2009, Gordon Henderson a écrit : >> Any thoughts, anyone? > > As a workaround, you might disable (mask) interrupts in your main() code just > before the instruction that calls _mullong, and re-enable them immediately > after, so you're sure that an interrupt will never call _mullong while it's > already being executed outside of the interrupt service routine... > > i.e > > while (INTCONbits.GIEL!=0) INTCONbits.GIEL=0; > some_code_that_calls_mullong; > INTCONbits.GIEL=1 > > (Use GIEL/GIEH or GIE/PEIE depending upon if you run with interrupt priorities > enabled, or in compatibility mode) That's my current workaround for this case - which is rather trivial.. However I have lots and lots of floating point code to "guard" and that's going to need some consinderations > (See PIC app notes to see why you should "while()" test that interrupts are > effectively disabled before continuing) Intersting. I wasn't aware of this. Thanks. http://ww1.microchip.com/downloads/en/AppNotes/00576B.pdf for anyone else... fortunately, I have macros for this already, as I need to enable/disable at various points, so: #define DISABLE_INTS_IF_SET { uint8_t _intFlag = INTCONbits.GIE ; while (INTCONbits.GIE != 0) INTCONbits.GIE = 0 ; #define ENABLE_INTS_IF_SET INTCONbits.GIE = _intFlag ; } Email will line-wrap it, but you get the idea... Gordon |
From: Raphael N. <rn...@we...> - 2009-11-24 17:37:47
|
> Specifically during compiler generated calls to _mullong although I > haven't tried any others yet. A quick look at the generated .asm shows that sdcc replaces the local union-type variables x, y, t, t1, and t2 with global variables -- which are of course corrupted during the second (nested) execution of the interrupt routine. I will look into this and try to have them placed in the access bank like other locals. Raphael |
From: Gordon H. <gor...@dr...> - 2009-11-24 18:08:55
|
On Tue, 24 Nov 2009, Raphael Neider wrote: >> Specifically during compiler generated calls to _mullong although I >> haven't tried any others yet. > > A quick look at the generated .asm shows that sdcc replaces the local > union-type variables x, y, t, t1, and t2 with global variables -- which > are of course corrupted during the second (nested) execution of the > interrupt routine. OK - I did have a look at the C source - which seems like it ought to be all locals, but I guess the compiler doesn't like unions? > I will look into this and try to have them placed in the access bank > like other locals. When you want someone to test it, let me know! Thanks, Gordon |
From: Frieder F. <fri...@we...> - 2009-11-24 17:45:22
|
Hi Gordon, Gordon Henderson schrieb: > void mathTest (uint16_t x) > { > uint32_t y ; > uint32_t i = 0 ; > > for (;;) > { > > y = (uint32_t)x * 9668 + 500 ; > if (y != 4225416L) > { > serialPuts ("Curruption @ ") ; > ... > } > ++i ; > > } > > works fine with interrupts off, or with the math code in the interrupt > routine commented out, but sees random coruptions with them on: [..] > That's the first I've noticed int and I've not even dived into floating > point land yet... This does not really come as a surprise: The manual has a special section (3.9.1.4) Common interrupt pitfall which specifically mentions 16 bit and 32 bit arithmetics... > Any thoughts, anyone? First thought would be not to use math code within interrupts:) Second thought, if you just need a multiply within interrupt, you could (taking multiply with 9668 (= 0x25c4) and add with 500 as example) get to the intended y by: uint32_t xx = x; y = 500; xx <<= 2; // 0x0004 y += xx; xx <<= 3; // 0x0040 y += xx; xx <<= 1; // 0x0080 y += xx; xx <<= 1; // 0x0100 y += xx; xx <<= 2; // 0x0400 ... (bugs are mine, also check that the addition does not cause diving into a nonreentrant function) Greetings, Frieder |
From: Gordon H. <gor...@dr...> - 2009-11-24 17:55:16
|
On Tue, 24 Nov 2009, Frieder Ferlemann wrote: > Hi Gordon, > > Gordon Henderson schrieb: >> void mathTest (uint16_t x) >> { >> uint32_t y ; >> uint32_t i = 0 ; >> >> for (;;) >> { >> >> y = (uint32_t)x * 9668 + 500 ; >> if (y != 4225416L) >> { >> serialPuts ("Curruption @ ") ; >> ... >> } >> ++i ; >> >> } >> >> works fine with interrupts off, or with the math code in the interrupt >> routine commented out, but sees random coruptions with them on: > > [..] > >> That's the first I've noticed int and I've not even dived into floating >> point land yet... > > This does not really come as a surprise: > The manual has a special section (3.9.1.4) Common interrupt pitfall > which specifically mentions 16 bit and 32 bit arithmetics... Indeed - however, when I asked about this a week or so back, I got the impression that under the PIC16 port this was actually OK... >> Any thoughts, anyone? > > First thought would be not to use math code within interrupts:) Well, quite - however while I could code my application in a huge loop, checking sensors, doing "stuff" @ every exact time interval required of it, it will be so much easier under interrupts.. > Second thought, if you just need a multiply within interrupt, > you could (taking multiply with 9668 (= 0x25c4) and add with 500 as example) > get to the intended y by: Thanks - but this is just the tip of the iceberg in one isolated case - I need to do floating point. Lots of it... (And even if I could re-code it in fixed-point, I'd still need to do lots of long multiplys and divides...) Gordon |