CCS C Software and Maintenance Offers
FAQFAQ   FAQForum Help   FAQOfficial CCS Support   SearchSearch  RegisterRegister 

ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

CCS does not monitor this forum on a regular basis.

Please do not post bug reports on this forum. Send them to CCS Technical Support

#inline and variables allocation

 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
sebdey



Joined: 11 Sep 2003
Posts: 17
Location: Switzerland

View user's profile Send private message

#inline and variables allocation
PostPosted: Mon Jan 29, 2007 4:12 am     Reply with quote

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

View user's profile Send private message

PostPosted: Mon Jan 29, 2007 12:52 pm     Reply with quote

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

View user's profile Send private message

#inline etc...
PostPosted: Tue Jan 30, 2007 1:51 am     Reply with quote

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

View user's profile Send private message Visit poster's website

PostPosted: Tue Jan 30, 2007 2:52 am     Reply with quote

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

View user's profile Send private message

PostPosted: Tue Jan 30, 2007 3:11 am     Reply with quote

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







PostPosted: Tue Jan 30, 2007 4:01 am     Reply with quote

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

View user's profile Send private message

PostPosted: Tue Jan 30, 2007 4:57 am     Reply with quote

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







PostPosted: Tue Jan 30, 2007 5:31 am     Reply with quote

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
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion All times are GMT - 6 Hours
Page 1 of 1

 
Jump to:  
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