This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

pow function messes up program

Other Parts Discussed in Thread: MSP430G2553

Hello,

I made a simple application running on a MSP430G2553 (launchpad) which reads the position of a potentiometer on port p1.4 and then generates a shorter or longer impulse on P1.6 depending on the position of the potentiometer. The impulse is generated when button P1.3 is pressed. 

The sampling of P1.4 is triggered by Timer0output1; I am filtering 10 consecutive sampled values into filtratAD so I filter out the eventual "noise" given by an old potentiometer. The application works as expected if I use filtratAD as the impulse period. However if I try to call pow (filtratAD,0.7) and use the computation result as the variable impulse period, then I get wrong behaviour (random length impulses are generated). 

Here is the relevant code:

#include <msp430.h>

#include <math.h>

volatile int sumaAD=0;

int indexAD=0;

volatile int filtratAD=0;

volatile int sqrroot=0;

volatile int temp=0;

#pragma vector=ADC10_VECTOR;

__interrupt void ADC10(void)

{

if (indexAD>9) //filter 10 values

{

filtratAD=sumaAD/10;  //filtrat AD holds the filtered value of the last 10 inputs

indexAD=0;

sumaAD=0;

}else

{

indexAD++;

sumaAD=sumaAD+ADC10MEM;

}

ADC10CTL0&=~ENC; // wait for next sample trigger

ADC10CTL0|=ENC;

}

#pragma vector=TIMER1_A1_VECTOR;   //stinge sudura *** ce se scurge perioada de timp necesara

__interrupt void TIMER1A3(void)

{

P1OUT &= ~BIT6;  //falling edge generation on P1.6, end of pulse

TA1CTL &= ~(MC0|MC1); //turns off timer

TA1R=0; //resets timer

P1IFG=0; //clears all pending interrupts

P1IE|=BIT3; //reenables button interrupt (for new impulse generation)

int j=TA1IV; //acknowledge interrupt

}

#pragma vector=PORT1_VECTOR;

__interrupt void P13(void)

{

if (P1IFG&BIT3) //button pressed

{

sqrroot=(int)(pow(filtratAD,0.7));  //if i'm using pow on filtratAD, code is not working

//sqrroot=(int)(filtratAD); //if I am using filtratAD directly, code is working properly.

__delay_cycles(50000);   //button

if ((P1IN&BIT3)==0)      //debounce

{

P1IE&=~BIT3; //disable button interrupt until this impulse is generated

P1OUT |= BIT6; //impulse generation rising edge

TA1CCR0=100+sqrroot*2;  //timer1 counts up to TA1CCR0 value then generates falling edge

TA1CTL|=MC0; //start timer 1

}

P1IFG = 0; //clear interrupt flag

}else

{

P1IFG = 0; //clear interrupt flag

}

}

Main function contains nothing but peripheral intialization code and blocks in an endless loop, all things of use are done in the above interrupt service routines.

What I find strange with the debugger is that the line "sqrroot=(int)(pow(filtratAD,0.7));"  generates a correct result in sqrroot but also modifies the value of filtratAD. I do not know why this happens. Any advice greatly appreciated.

Best regards,

Florin

  • Yes, it is pretty clear to me now what is happening but I don't know why and couldn't find a workaround so far.

    When I'm doing sqrroot=(int)(pow(filtratAD,0.7));  /the result is correct in sqrroot, but also filtratAD is modified with a (huge) random value. Therefore, when the user presses the button next time to generate an impulse, if the AD routine did not overwrite filtratAD again with a correct aquired value, then a random length impulse will be generated. So my problem seems to be that filtratAD, which is an input argument of pow() is overwritten. I tried to save filtratAD to a temporary variable before calling function pow and then restore it but it didn't work, probably because of a compile optimisation since I was not using the temp variable anywhere. I'm sure I will be able to find an workaround to save the variable but still the problem stands, why is the input argument of pow being overridden? Are there any known issues with pow()?

    Thank for your time,

    Florin 

  • I can only afford KickStart. The pow function (both the fast and the accurate versions) works without the side-effect you found.

  • The following program when compiled with CCS 5.2 and MSP430 compiler 4.1.1 also misbehaves:



    #include <msp430.h>

    #include <math.h>

    volatile int sumaAD=0;

    int indexAD=0;

    volatile int filtratAD=0;

    volatile int sqrroot=0;

    volatile int temp=0;

    void main(void) {

       WDTCTL = WDTPW + WDTHOLD;
       while (1)
       {
          for (filtratAD = 1; filtratAD < 100; filtratAD += 3)
          {
            temp = filtratAD;
            sqrroot=(int)(pow(temp,0.7)); //if i'm using pow on filtratAD, code is not working
          }
      }
    }

    When single stepping in a MSP430G2553 the temp and filtratAD variables get corrupted after the call to pow. Not sure if the problem is pow, or the implicit integer / floating point conversions.

  • I looked at the static call graph generated by IAR, and it shows a stack overflow when calling pow(double,double) with the default stack size (80 or 0x50 bytes). May explain the mysterious "corruptions" you are seeing. (I suspect the pow implementation on CCS would also be similarly stack heavy)

    Tony

  • A lot of stack space is needed indeed. But the simplified code posted by Chester shouldn't blow up its stack. 94 bytes is needed under KickStart.

  • old_cow_yellow said:

    A lot of stack space is needed indeed. But the simplified code posted by Chester shouldn't blow up its stack. 94 bytes is needed under KickStart.

    So I compiled Chester's code with CCS 5.2 and debugged it on a MSP430G2553, and there indeed was a stack overflow which overrode the global volatile variables. Surprisingly, filtratAD was located at 0x344, which means that roughly 300 bytes of memory had gone to the runtime, which appears to include some EABI stuff and static variables used within pow(). It's quite bizarre; apparently the C standard requires "exceptions" to be "thrown" when a floating-point error occurs.

    This is the memory allocated by the C runtime in symbolic format:

    Denorm
    0001    0000    0000    0000    0000    0000    0000    0000
    Eps
    0000    0000    0000    3C90    0000    0000    0000    0000
    Hugeval
    0000    0000    0000    7FF0    0000    0000    0000    0000
    Inf
    0000    0000    0000    7FF0    0000    0000    0000    0000
    Nan
    0000    0000    0000    7FF8    0000    0000    0000    0000
    Snan
    0001    0000    0000    7FF0    0000    0000    0000    0000
    Rteps
    0000    0000    0000    3E40    0000    0000    0000    0000
    Xbig
    0000    0000    0000    4032
    Zero
    0000    0000    0000    0000
    FDenorm
    0001    0000    0000    0000    0000    0000    0000    0000
    FEps
    0000    3300    0000    0000    0000    0000    0000    0000
    FInf
    0000    7F80    0000    0000    0000    0000    0000    0000
    FNan
    0000    7FC0    0000    0000    0000    0000    0000    0000
    FSnan
    0001    7F80    0000    0000    0000    0000    0000    0000
    FRteps
    0000    3980    0000    0000    0000    0000    0000    0000
    FXbig
    0000    4100
    FZero
    0000    0000
    sigtable
    0000    0000    0000    0000    0000    0000 [... 34 bytes of zeros ...] 0A11    D5E8    D596    6666

    Tony

  • Thank you guys for your help. I forgot to mention that I was getting the problem on CSS 5.2 and compiler 4.1 . I tried to increase the stack size to 140 from the default 80 but still the same problem occurred, I can not increase it much higher without getting an "out of memory" error. 

    Fortunately using pow now is not important for me, I posted this only because I am new to the TI compiler and wanted to learn. For my purpose I will just use a logarithmic potentiometer or some other trick to allow the user more fine grained control on a portion of the potentiometer track. It would be great though if there were a lighter version of pow, eventually one with less precision. 

    Best regards,

    Florin.

  • Florin Tufescu said:

    I tried to increase the stack size to 140 from the default 80 but still the same problem occurred

    This is because the stack allocates "down" the memory address, while statics and the heap allocate "up", so if an overflowing stack already crosses into statics and heap then increasing the stack size will not help, because there's no "free" space between the stack and statics/heap to begin with.

    I hope you find a suitable fixed-point solution to your current problem; depending on the distribution of the "noise" an alternative algorithm may work adequately, maybe majority vote? This is not my forte though so you should probably listen to someone else :)

    Tony

  • Florin Tufescu said:
    When I'm doing sqrroot=(int)(pow(filtratAD,0.7));  /the result is correct in sqrroot, but also filtratAD is modified with a (huge) random value.

    Teh G2553 has only 512 bytes of ram. Part of it (bottom up) is used to store your global variabels, part of it (top-down) is used for the stack.

    Now I don't know what else your program does, and which amount of local variables your main funciton will define (which all go onto the stack).
    But your observation seems to show that yoU're getting a stack overflow. Float variables (at least the standard version) take 10 bytes. As much is required for passing float parameters to a float math function, the function itself also might require some stack space, the return address for the function, the return data for the ISR, the registers that ISR uses internally, all this is pushed on the stack. At the end, the stack grows so much that the data pushed on it will land on the memory area that is used by your global variables, overwriting them. A classical stack overflow. No flaw with the function, just the program requires more ressources than available on the used system.

    How to fix this? Simple: don't waste so much ram. Don't declare more local variable sthan absolutely necessary. Don't declare individual local variables for each loop counter just 'because different names are more telling". Re-use a single one, if possible. Don't use recursions. Maybe block interrupts when youre calling a function that requires lots of local variables, so the ISR data won't be put on top of them.

    P.s.: 'setting' the stack size in the project setting has no influence on the binary. The stack grows as the stack needs. Always. No limitation possible (and what should the processor do if the stack limit is reached and it needs to put a return address on stack for an interrupt? Cry for help? It couldn't even call the function that emits the cry!)

    The 'setting' only produces a link error if after placing all global variables less than the 'set' amount is free. Apparently you most of your ram already for global variables, except for 140 bytes. These have to be sufficient for any local variables and return addresses and parameter calling. Apparently they aren't.

  • TonyKao said:
    So I compiled Chester's code with CCS 5.2 and debugged it on a MSP430G2553, and there indeed was a stack overflow which overrode the global volatile variables. Surprisingly, filtratAD was located at 0x344, which means that roughly 300 bytes of memory had gone to the runtime, which appears to include some EABI stuff and static variables used within pow().

    The program was compiled with the CCS 5.2 default project settings. Running the call_graph program from Code Generation Tools XML Processing Scripts reported that "at least" 252 bytes of stack were required. I say at least since I didn't attempt to resolve these warnings from the call_graph program:

    The following functions are known to contain indirect function calls, but
    do not contain any information about those indirect calls in the configuration
    file specified with --i_cfg=file. Run "perldoc call_graph.pl" for more
    information.
    ======================================================================

    _auto_init
    feraiseexcept
    raise
    signal

    With this program there is insufficient RAM to fit a stack size of 252 bytes into the MSP430G2553.

    (the call_graph warning about indirect function calls means more stack space may be required depending upon which other functions get called indirectly, e.g. by function pointers upon exception handling)

    The CCS 5.2 default Output Format is eabi (ELF) which supports double, which means pow takes double arguments. As the inputs and output are only integer, double precision isn't necessary.

    Changing from:

      sqrroot=(int)(pow(temp,0.7));

    To:

     sqrroot=(int)(powf(temp,0.7));

    Means float arguments are used. call_graph now reports at least 196 bytes of stack space are required. With a MSP430G2553 can link with the stack space set to 196, and now runs without corrupting the global variables.

  • Chester Gillon said:

    Changing from:

      sqrroot=(int)(pow(temp,0.7));

    To:

     sqrroot=(int)(powf(temp,0.7));

    Means float arguments are used. call_graph now reports at least 196 bytes of stack space are required. With a MSP430G2553 can link with the stack space set to 196, and now runs without corrupting the global variables.

    [/quote]

    Hi Chester, your solution does seem to work. However the function itself and the assorted "support" the linker brings in take up more than 10 KB of flash, which is far more than that I find acceptable just to get the base of a power, and certainly not for least squares smoothing of an ADC input.

    The function is also likely as slow as it is big, and in the original program pow() was called in an ISR, a double whammy; I'm surprised the stacked PC wasn't corrupted in the original, or maybe it was but just not noticed.

    Anyway sorry for the bit of rant; it's not you, it's the standard FP library, so let's just be friends ;)

    Tony

  • I have to vote Chester's solution the best, regardless of any criticism. It worked perfectly for me in MSP430G2553. Below is the snippet of my code.

        NoteN={1,2,3,.....,87,88};
        Freq = 440*powf(2,((NoteN-49)/12));
        TA0CCR0 = (int)(1000000/(2*Freq))-1;

    Thanks Chester!

**Attention** This is a public forum