|
|
View previous topic :: View next topic |
Author |
Message |
sebdey
Joined: 11 Sep 2003 Posts: 17 Location: Switzerland
|
#inline and variables allocation |
Posted: Mon Jan 29, 2007 4:12 am |
|
|
Hello, I have the following question/problem, maybe is it simply a C understanding problem from my side... I use 18F4620, PCWH 3.235
I have a project where I use #inline functions to be able to call some 'generic' functions from the main() and from interrupts() without having to disable interrupts to prevent reentrancy... However I just realized that the variables used IN the #inline functions (I mean variables declared and initialized in the #inline function) are not declared separately each time I the function is '#inlined'. I figured this out by looking at the RAM map of my compiled project which showed me that a local vaiable of an #inline function used in ~10 different places was only using a single RAM address...
In other terms, if I call let's say #inline function_A() from main() and that this function declares, intialize and change the variable function_A_var. During this call an interrupt occurs, and the interrupt handler also uses an #inlined version of function_A(). My problem is that the interrupt version of the #inlined function WILL also use the function_A_var, and as the compiler doesn't allocate another memory area for this variable, it will corrupt the function_A_var used in the main() version of the #inlined function...
I thought that '#inlined' functions were completely separated in terms of variable memory allocation, is there a way to work around this problem.
S.Dey |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Mon Jan 29, 2007 12:52 pm |
|
|
Post a short test program that shows the problem, and we'll come up
with a solution. Make the program be as short as possible.
The program should be complete, so that we can copy and paste it
into MPLAB (or the CCS IDE) and punch the compile button and it
will compile with no errors (and no editing).
Also, make sure you disable HTML when you post the code.
There's a tickbox to do this just below the editing window. |
|
|
sebdey
Joined: 11 Sep 2003 Posts: 17 Location: Switzerland
|
#inline etc... |
Posted: Tue Jan 30, 2007 1:51 am |
|
|
Hi PCM,
I won't be able to post a test program, I found this 'problem' in an almost finished project, no chance to extract a part of it to be able to test it.. To show you how I found that variables were at the same memory address, you'll find below a part of the lst file showing the #inlined functions and the ram allocation showing the address given to the variables...
.lst file:
Code: |
.................... ///////////////////////////////////////////////////////////////////////////////
.................... #inline
.................... void setInternal(int signalId, short int state)
.................... {
.................... short intNeeded;
.................... intNeeded=bit_test(GIE);
*
1896: BCF xAC.0
1898: BTFSC FF2.7
189A: BSF xAC.0
*
1942: BCF xAC.0
1944: BTFSC FF2.7
1946: BSF xAC.0
*
(...)
|
Here you see that the 'intNeeded' variable used in the #inlined function is always at the same memory address...
Ram map:
Code: |
(...)
0AC-0AD @MUL1616.P2
0AC.0 setInternal.intNeeded
0AD @DIV88.P1
(...)
|
Here you see that the variable 'intNeeded' used in the 'setInterna(...)' function is only declared once (I searched the whole ram map, no other declaration for setInternal.intNeeded...) Thus leading into the problem I described in my first post...
For me it's maybe not a bug of the compiler, but an undocumented 'feature' which has to be known... |
|
|
ferrumvir
Joined: 01 Feb 2006 Posts: 64 Location: England
|
|
Posted: Tue Jan 30, 2007 2:52 am |
|
|
Hi,
The CCS compiler allocates a location in memory for all the variables in your program. Unlike a PC it does not allocate memory for function variables when functions are called. Therefore when you use functions in interrupts you have to be very careful.
The #inline statment only really unwraps the function call and function return statements, gaining a little speed.
There are two workarounds, don't call functions from isr routines or have two identical functions Function_A and Function_A_ISR and call the correct one from the appropriate location. I personally would go with first option but I understand that a certain amount of encapsulation can be gained from the later.
Hope that helps a little.
Cheers Scott |
|
|
ckielstra
Joined: 18 Mar 2004 Posts: 3680 Location: The Netherlands
|
|
Posted: Tue Jan 30, 2007 3:11 am |
|
|
I did a small test in v3.249.
Code: | #include <18F458.h>
#fuses HS, NOWDT, PUT, NOLVP
#use delay(clock=4000000)
#use rs232(baud=9600, xmit=PIN_C6, rcv=PIN_C7)
void MyDummyFunc()
{
int8 Temp;
Temp = 1;
Temp += Read_ADC();
}
#INT_RDA
void isr_RDA(void)
{
char c;
MyDummyFunc();
c = fgetc();
}
void main(void)
{
MyDummyFunc();
while (1)
;
} |
Using a _normal_ function both inside and outside an interrupt causes the compiler to generate the warning 'Interrupts disabled during call to prevent re-entrancy' and code is generated to disable interrupts during execution of this function. This is correct behaviour and has prevented many programmer's errors.
Changing the MyDummyFunc() to #inline causes the warning to disappear and the function code is generated inline for each function call. So far, all as to be expected. Unexpected is that all inlined functions use the same memory location for the local Temp variable, in the combined use with interrupt routines this causes problems.
I'm not sure whether to call this a compiler bug or not, but I tend to think it is a bug. At least a compiler warning would have been appropriate.
For functions not returning a value a possible workaround is to change the inline function into a macro:
Code: | #define MyDummyFunc() \
{ \
int8 Temp; \
\
Temp = 1; \
Temp += Read_ADC(); \
} |
|
|
|
Ttelmah Guest
|
|
Posted: Tue Jan 30, 2007 4:01 am |
|
|
OK.
Key is to understand that in variable terms, a 'inline' function, behaves exactly like any other function. It only affects the location of the code, not the overlap or otherwise of the variables.
The way to get a separate set of variables declared, is to declare a separate function. For example, when I need a routine to do an eeprom write and want this to also be used in an interrupt, without the potential for one to cause problems for the other, I simply declare two copies:
Code: |
//Standard routine to write block of data to the eeprom
void EEPROM_PUT(int *ptr,int num,int addr)
{
int count;
for (count=0;count<num;count++)
{
INTOFF;
WRT_EEPROM(addr+count,*ptr++);
INTON;
}
}
//Version used inside the interrupt - remember the time delay involved....
void I_EEPROM_PUT(int *ptr,int num,int addr)
{
static int count;
for (count=0;count<num;count++)
{
I_WRT_EEPROM(addr+count,*ptr++);
}
}
|
The internal subroutines, are also duplicated, so all variables are distinct. The reasons for using 'inline', are that it avoids the overhead of another call on the stack, both in terms of stack space, and time to setup the data transfers. It does not give unique local variables.
One solution, is to code local variables yourself. For instance:
Code: |
int8 state=0;
int8 silly_routine() {
static int8 val[4];
++val[state];
return val[state];
}
void main(void) {
int8 count,test;
state=1;
for (count=0;count<8;count++) test=silly_routine();
state=2;
for (count=0;count<15;count++) test=silly_routine();
state=0;
test=silly_routine();
//Here test should be _0_, since 'val' bing static will be initialised
//to zero.
state=1;
test=silly_routine();
//Here test is now 9, since the 'state1' variable, was incremented
//eight times in the for loop above.
state=2;
test=silly_routine();
//Now 16.
}
|
All you do, is have a 'global' variable, specifying which set of internal variables you want to use (I have called it 'state'), and before calling the routine, set this to address the required set. You can even setup the setting of the variable, and the call, as a named macro.
Best Wishes |
|
|
sebdey
Joined: 11 Sep 2003 Posts: 17 Location: Switzerland
|
|
Posted: Tue Jan 30, 2007 4:57 am |
|
|
Thanks everyone,
I will go with the Quote: | (...)have two identical functions Function_A and Function_A_ISR and call the correct one from the appropriate location(...) | solution (which I already implemented and tested, and works the way it should...).
Nervertheless, the way the compiler allocates #inlined functions' variables should be better documented to avoid this type of conflicts. I spent a LOT of time on my code to find this silly problem...
Have a good day/night/evening ! |
|
|
Ttelmah Guest
|
|
Posted: Tue Jan 30, 2007 5:31 am |
|
|
I don't see that there is any problem with the documentation. You only have a single function. The #inline declaration, only affects how the code is stored (and says so), it does not change the fundamental behaviour of 'C'....
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
|