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.

TMS320F280039: Calculation error

Part Number: TMS320F280039
Other Parts Discussed in Thread: LAUNCHXL-F280039C, C2000WARE

Tool/software:

I encountered some calculation errors.

static int16_t LvBswPwm_Period_Cnt=2400;
static uint16_t test[6];

test[0]=(1.0f/6.0f*LvBswPwm_Period_Cnt);
test[1]=(2.0f/6.0f*LvBswPwm_Period_Cnt);
test[2]=(3.0f/6.0f*LvBswPwm_Period_Cnt);
test[3]=(4.0f/6.0f*LvBswPwm_Period_Cnt);
test[4]=(5.0f/6.0f*LvBswPwm_Period_Cnt);
test[5]=(6.0f/6.0f*LvBswPwm_Period_Cnt);

All the calculation results are correct except for 5/6, it would be 0 but not 2000.

Some configurations are as follows:

Some solutions have tried:

1- change Optimization Level to off, 5/6 result will be right;

2-(5.0f*LvBswPwm_Period_Cnt)/6.0f, the result will be right;

I would like to know what caused this.

  • Hi Bo,

    Since the test variable is of type uint16_t, can you please type cast the multiplication and see if that works. You have some floating point calculations and casting the result to 16-bit might make a difference.

    Regards,

    Ozino

  • No,it does't work. I have some more information about this issue. It may have some relationship with __fmax and __ max function;

    Total code is as follow:

        /*1 - Local Variables*/
        static int16_t LvBswPwm_Period_Cnt=0;
        static uint16_t test[6];
    
        //int16_t LvBswPwm_Period_Cnt=0;
        int16_t LvBswPwm_Duty_Cnt[2]={0,0};
        int16_t LvBswPwm_DeadTime_Cnt[2]={0,0};
    
        /*2 - Value Calculation and Limit*/
        //Period Calculation
        LvBswPwm_Period_Cnt     = (uint16_t) ( KvBswPwm_PWMCLK_kHz / Utility_floatValLimitRange(pPwmInput->Freq_kHz,KvBswPwm_FREQMIN_kHz,KvBswPwm_FREQMAX_kHz) );
    
        //Duty Calculation
        if(pPwmInput->PriDuty_U < KvBswPwm_DUTYMIN_U)
            LvBswPwm_Duty_Cnt[0]=0;
        else if(pPwmInput->PriDuty_U > KvBswPwm_DUTYMAX_U)
            LvBswPwm_Duty_Cnt[0]=(uint16_t)(KvBswPwm_DUTYMAX_U * LvBswPwm_Period_Cnt);
        else
            LvBswPwm_Duty_Cnt[0]=(uint16_t)(pPwmInput->PriDuty_U * LvBswPwm_Period_Cnt);
    
        if(pPwmInput->SecDuty_U < KvBswPwm_DUTYMIN_U)
            LvBswPwm_Duty_Cnt[1]=0;
        else if(pPwmInput->SecDuty_U > KvBswPwm_DUTYMAX_U)
            LvBswPwm_Duty_Cnt[1]=(uint16_t)(KvBswPwm_DUTYMAX_U * LvBswPwm_Period_Cnt);
        else
            LvBswPwm_Duty_Cnt[1]=(uint16_t)(pPwmInput->SecDuty_U * LvBswPwm_Period_Cnt);
    
        // DeadTime Calculation and Limit
        LvBswPwm_DeadTime_Cnt[0]= __max(LvBswPwm_Period_Cnt/2 - LvBswPwm_Duty_Cnt[0] +1,KvBswPwm_DBMIN_U);
        LvBswPwm_DeadTime_Cnt[1]= __max(LvBswPwm_Period_Cnt/2 - LvBswPwm_Duty_Cnt[1] +1,KvBswPwm_DBMIN_U);
        
        test[0]=(1.0f/6.0f*LvBswPwm_Period_Cnt);
        test[1]=(2.0f/6.0f*LvBswPwm_Period_Cnt);
        test[2]=(3.0f/6.0f*LvBswPwm_Period_Cnt);
        test[3]=(4.0f/6.0f*LvBswPwm_Period_Cnt);
        test[4]=(5.0f/6.0f*LvBswPwm_Period_Cnt);
        test[5]=(6.0f/6.0f*LvBswPwm_Period_Cnt);

    1 - If I define the variable LvBswPwm_Period_Cnt and test as volatile, it will be ok.

    2-  If I calculate test[0]-test[5] just after Line 11, it will be ok.

    3- If I change __max to __fmax,it will be ok.

    4-  If I calculate  test[0]-test[5] just as the code above, there will be one result wrong, sometimes it will be test[4](5/6),some times it wll be test[1](2/6),but other results are right.  I think the compiler generating code like this,  __max changed CPU registers, but LvBswPwm_Period_Cnt value is not got from RAM again.Why the compiler will generate code like this?

  • Hi Bo,

    I'm having the question rerouted to the compiler group for further assistance.

    Regards,

    Ozino

  • LvBswPwm_Period_Cnt value is not got from RAM again.Why the compiler will generate code like this?

    The variable LvBswPwm_Period_Cnt is assigned to one time.  When that assignment occurs, the register copied to LvBswPwm_Period_Cnt can be viewed as holding the same value.  Later reads of LvBswPwm_Period_Cnt probably use that register instead of reading from memory again.  Why is this a problem?  Is it possible for LvBswPwm_Period_Cnt to change in a way other than shown in this C code?

    If I calculate  test[0]-test[5] just as the code above, there will be one result wrong, sometimes it will be test[4](5/6),some times it wll be test[1](2/6),but other results are right.

    I presume this change in behavior occurs happens on multiple runs of the same code.  If that is right, that almost always means there is some problem with the hardware, and not the compiler or the software.

    Thanks and regards,

    -George

  • 1 - I didn't change the value of LvBswPwm_Period_Cnt after code line 11,as you said later reads of LvBswPwm_Period_Cnt probably use that register instead of reading from memory again. But the test value calculated with LvBswPwm_Period_Cnt is not right. If  I define the variable as volatile type, variable test calculation will be right. I think __max function below change the register, but when calculating test, LvBswPwm_Period_Cnt is not got from ram again.

    LvBswPwm_DeadTime_Cnt[0]= __max(LvBswPwm_Period_Cnt/2 - LvBswPwm_Duty_Cnt[0] +1,KvBswPwm_DBMIN_U);
    LvBswPwm_DeadTime_Cnt[1]= __max(LvBswPwm_Period_Cnt/2 - LvBswPwm_Duty_Cnt[1] +1,KvBswPwm_DBMIN_U);

    2 -  I don't think it has relationship with hardware, if I change the code above to __fmax, it will get the right results all the time, even if i define the variable LvBswPwm_Period_Cnt without volatile.

  • Please change the code so that ...

    the test value calculated with LvBswPwm_Period_Cnt is not right

    For the source file that contains the problem statements, please follow the directions in the article How to Submit a Compiler Test Case.  

    You have stated several ways of changing the code so it works.  Pick the one that seems closest to the problem variant.  For this second source file, also follow the directions in the article How to Submit a Compiler Test Case.

    I'll build both test cases down to assembly code, and compare them.

    Regarding ...

    when calculating test, LvBswPwm_Period_Cnt is not got from ram again

    I don't understand. Please explain why you say that.  Exactly how do you see it?  What do you expect to see instead?

    Thanks and regards,

    -George

  • Problems  may be from idiv_support.

    1) I build a new project from driverlib empty project and run it on LAUNCHXL-F280039C. Project configuration is the same as first question in this issue.

    Code is uploaded as attachment.

        Demo Project Loaction: x:\ti\C2000Ware_5_04_00_00\driverlib\f28003x\examples\empty_projects

    2) I write the code in the main.c as follows

    #define Utility_floatValLimitRange(x,MinVal,MaxVal)      ( __fmax(__fmin(x,MaxVal),MinVal) )
    #define Utility_intValLimitRange(x,MinVal,MaxVal)        ( __max(__min(x,MaxVal),MinVal) )
    
    int16_t Period=0;
    int16_t Duty[2]={0,0};
    int16_t Dead[2]={0,0};
    
    float32_t test[6];
    
    
    
    
    
    Period     =  (120.0e3f) / Utility_floatValLimitRange(50.0f,50.0f,70.0f);
    
    Duty[0] = 0;
    Duty[1] = 0;
    
    Dead[0] = __max(Period/2 - Duty[0] + 1 , 18);
    Dead[1] = __max(Period/2 - Duty[1] + 1 , 18);
    
    test[0] = 1.0f/6.0f*(float32_t)Period;
    test[1] = 2.0f/6.0f*(float32_t)Period;
    test[2] = 3.0f/6.0f*(float32_t)Period;
    test[3] = 4.0f/6.0f*(float32_t)Period;
    test[4] = 5.0f/6.0f*(float32_t)Period;
    test[5] = 6.0f/6.0f*(float32_t)Period;
    

    3) The datasheet of F280039C says that it supports for Fast Integer Division (FINTDIV), so I changed  idiv_support=idiv0, and get the results as follows:

    test[1] and Dead can not get the right value;

    4) I changed  idiv_support  to empty, this time it will get the right results as follows:

    5) This is the Test Code

    4428.empty_driverlib_project.rar

  • Some Supplements, project comfiguration with  idiv_support=idiv0.

    1) if I change 

    Period =  (120.0e3f) / Utility_floatValLimitRange(50.0f,50.0f,70.0f); 

    to Period= 2400; The results will be right;

    2) If I delete this 2 lines:

    Duty[0] = 0;
    Duty[1] = 0;

    The results will be right also.

    3) If I change the 2 lines below:

    Dead[0] = __max(Period/2 - Duty[0] + 1 , 18);
    Dead[1] = __max(Period/2 - Duty[1] + 1 , 18);

    to

    Dead[0] = __max(Period/2  + 1 , 18);
    Dead[1] = __max(Period/2  + 1 , 18);

    The results will be right also.

    4) If I change the 2 lines below:

    Dead[0] = __max(Period/2 - Duty[0] + 1 , 18);
    Dead[1] = __max(Period/2 - Duty[1] + 1 , 18);

    to

    Dead[0] = __fmax(Period/2.0f - Duty[0] + 1.0f , 18.0f);
    Dead[1] = __fmax(Period/2.0f - Duty[1] + 1.0f , 18.0f);

    The results will be right also.

    5) if I change the calculation:

    test[n] = x.0f*6.0f/(float32_t)Period;

    to
    test[n] = x.0f*(float32_t)Period/6.0f;

    The results will be wrong in another way.

    6) I tried on chip 280025 with the same configuration and code, I can get the same phenomenon.

  • Would you kindly let me know if there's any update or if additional information is needed from my side?

  • Bo, sorry, still discussing it internally. I'll get back to you soon.

  • Bo, we have been able to reproduce this internally. A compiler bug will be filed and this will be investigated.

    Thanks,

    Sira

  • After further investigation, we have another request.  Please look at the final overall result.  Is it correct?  Set a breakpoint on the return instruction of the problem function, step a few more instructions, then look.  Is everything computed correctly?

    Thanks and regards,

    -George