views:

167

answers:

4

I am writing the firmware for an embedded device in C using the Silicon Labs IDE and the SDCC compiler. The device architecture is based on the 8051 family. The function in question is shown below. The function is used to set the ports on my MCU to drive a stepper motor. It gets called in by an interrupt handler. The big switch statement just sets the ports to the proper value for the next motor step. The bottom part of the function looks at an input from a hall effect sensor and a number of steps moved in order to detect if the motor has stalled. The problem is, for some reason the second IF statement that looks like this if (StallDetector > (GapSize + 20)) { HandleStallEvent(); } always seems to get optimized out. If I try to put a breakpoint at the HandleStallEvent() call the IDE gives me a message saying "No Address Correlation to this line number". I am not really good enough at reading assembly to tell what it is doing but I have pasted a snippet from the asm output below. Any help would be much appreciated.

void OperateStepper(void)
{
    //static bit LastHomeMagState = HomeSensor;
    static bit LastPosMagState = PosSensor;
    if(PulseMotor)
    {
        if(MoveDirection == 1) // Go clockwise
        {
            switch(STEPPER_POSITION) 
            {
                case 'A': 
                     STEPPER_POSITION = 'B';
                     P1 = 0xFD;
                     break;
                case 'B':
                     STEPPER_POSITION = 'C';
                     P1 = 0xFF;
                     break;
                case 'C':
                     STEPPER_POSITION = 'D';
                     P1 = 0xFE;
                     break;
                case 'D':
                     STEPPER_POSITION = 'A';
                     P1 = 0xFC;
                     break; 
                default:
                     STEPPER_POSITION = 'A';
                     P1 = 0xFC;
            }   //end switch
        }
        else                // Go CounterClockwise
        {
            switch(STEPPER_POSITION) 
            {
                case 'A': 
                     STEPPER_POSITION = 'D';
                     P1 = 0xFE;
                     break;
                case 'B': 
                     STEPPER_POSITION = 'A';
                     P1 = 0xFC;
                     break;
                case 'C': 
                     STEPPER_POSITION = 'B';
                     P1 = 0xFD;
                     break;
                case 'D': 
                     STEPPER_POSITION = 'C';
                     P1 = 0xFF;
                     break; 
                default: 
                     STEPPER_POSITION = 'A';
                     P1 = 0xFE;
            }   //end switch
        }   //end else

        MotorSteps++;
        StallDetector++;

        if(PosSensor != LastPosMagState)
        {
            StallDetector = 0;

            LastPosMagState = PosSensor;
        }
        else
        {
            if (PosSensor == ON) 
            {
                if (StallDetector > (MagnetSize + 20))
                {
                    HandleStallEvent();
                }
            }
            else if (PosSensor == OFF) 
            {
                if (StallDetector > (GapSize + 20))
                {
                    HandleStallEvent();
                }
            }
        }

    }   //end if PulseMotor
}

... and the asm output for the the bottom part of this function...

;   C:\SiLabs\Optec Programs\HSFW_HID_SDCC_2\MotionControl.c:653: if(PosSensor != LastPosMagState)
    mov c,_P1_4
    jb  _OperateStepper_LastPosMagState_1_1,00158$
    cpl c
00158$:
    jc  00126$
    C$MotionControl.c$655$3$7 ==.
;   C:\SiLabs\Optec Programs\HSFW_HID_SDCC_2\MotionControl.c:655: StallDetector = 0;
    clr a
    mov _StallDetector,a
    mov (_StallDetector + 1),a
    C$MotionControl.c$657$3$7 ==.
;   C:\SiLabs\Optec Programs\HSFW_HID_SDCC_2\MotionControl.c:657: LastPosMagState = PosSensor;
    mov c,_P1_4
    mov _OperateStepper_LastPosMagState_1_1,c
    ret
00126$:
    C$MotionControl.c$661$2$8 ==.
;   C:\SiLabs\Optec Programs\HSFW_HID_SDCC_2\MotionControl.c:661: if (PosSensor == ON) 
    jb  _P1_4,00123$
    C$MotionControl.c$663$4$9 ==.
;   C:\SiLabs\Optec Programs\HSFW_HID_SDCC_2\MotionControl.c:663: if (StallDetector > (MagnetSize + 20))
    mov a,_MagnetSize
    mov r2,a
    rlc a
    subb    a,acc
    mov r3,a
    mov a,#0x14
    add a,r2
    mov r2,a
    clr a
    addc    a,r3
    mov r3,a
    clr c
    mov a,r2
    subb    a,_StallDetector
    mov a,r3
    subb    a,(_StallDetector + 1)
    jnc 00130$
    C$MotionControl.c$665$5$10 ==.
;   C:\SiLabs\Optec Programs\HSFW_HID_SDCC_2\MotionControl.c:665: HandleStallEvent();
    ljmp    _HandleStallEvent
00123$:
    C$MotionControl.c$668$2$8 ==.
;   C:\SiLabs\Optec Programs\HSFW_HID_SDCC_2\MotionControl.c:668: else if (PosSensor == OFF) 
    jnb _P1_4,00130$
    C$MotionControl.c$670$4$11 ==.
;   C:\SiLabs\Optec Programs\HSFW_HID_SDCC_2\MotionControl.c:670: if (StallDetector > (GapSize + 20))
    mov a,#0x14
    add a,_GapSize
    mov r2,a
    clr a
    addc    a,(_GapSize + 1)
    mov r3,a
    clr c
    mov a,r2
    subb    a,_StallDetector
    mov a,r3
    subb    a,(_StallDetector + 1)
    jnc 00130$
    C$MotionControl.c$672$5$12 ==.
;   C:\SiLabs\Optec Programs\HSFW_HID_SDCC_2\MotionControl.c:672: HandleStallEvent();
    C$MotionControl.c$678$2$1 ==.
    XG$OperateStepper$0$0 ==.
    ljmp    _HandleStallEvent
00130$:
    ret

It looks to me like the compiler is NOT optimizing out this second if statement from the looks of the asm but if that is the case why does the IDE not allow me so set a breakpoint there? Maybe it's just a dumb IDE!

+1  A: 

You can usually tweak the optimizer with #pragma statements. I don't know the exact syntax for your compiler, but you should be able to find it in the docs which come with your compiler/ide.

Something like this

#pragma optimize( "", off )
//now the function which should not be optimized
#pragma optimize( "", on )
Stefan
volatile foo(void)?
Tom
+2  A: 

The IF statement is not being optimized out. This is the code for it.

    C$MotionControl.c$670$4$11 ==.
;   C:\SiLabs\Optec Programs\HSFW_HID_SDCC_2\MotionControl.c:670: if (StallDetector > (GapSize + 20))
    mov a,#0x14       ; r2,r3 = 20 + GapSize
    add a,_GapSize    ; (adding a 16-bit number in two 8-bit steps)
    mov r2,a
    clr a
    addc    a,(_GapSize + 1)
    mov r3,a
    clr c
    mov a,r2          ; subtracting in two 8-bit steps
    subb    a,_StallDetector
    mov a,r3
    subb    a,(_StallDetector + 1)
    jnc 00130$        ; jump if carry not set (fall through if carry set)
    C$MotionControl.c$672$5$12 ==.
;   C:\SiLabs\Optec Programs\HSFW_HID_SDCC_2\MotionControl.c:672: HandleStallEvent();
    C$MotionControl.c$678$2$1 ==.
    XG$OperateStepper$0$0 ==.
    ljmp    _HandleStallEvent    ; it knows HandleStallEvent does not return!
00130$:                            ; or rather, it knows this handler cannot return, so there's no need to call.
    ret

I notice that in the 3rd-to-last line, something funny is happening. It is not doing a function call to HandleStallEvent. It is doing a long-jump, so it evidently knows that HandleStallEvent cannot return. I also see that in the two lines above that, it is defining assembler symbols that relate the line number to the jump instruction. So, it has a symbol for line 678. If the IDE won't let you set a breakpoint at line 678, perhaps you can get the hex address of line 678, and set it at the hex address. Another thing you might try could be to insert a local variable definition like int breakhere = 1 before that line, and see if that gives you some instructions you can break at.

BTW, you can see that the CPU thinks in terms of 8-bit numbers, so if you can use char instead of short, it will save instructions. Whether the time saved is worth it depends on what percent of time the machine is in this code.

BTW2, if you want to squeeze performance out of this puppy, what I relied on when I was doing embedded work was randomly halting the IDE (or the Intel "Blue Box" ICE). Here's something about that.

Mike Dunlavey
Mike, I answered this below, but I'll comment as well.This is called "tail call optimization". The compiler sees that nothing is done after the calls to HandleStallEvent(), so it knows that no useful work will be accomplished by having HandleStallEvent() return to OperateStepper().
John R. Strohm
@John R. Strohm: Duh. I should have seen that.
Mike Dunlavey
+1  A: 

How optimizations can be configured is compiler dependent. The manual for SDCC has its optimization options listed under section 3.28. You can use both command line options or pragmas at the source code level. Try out disabling optimizations globally to see if you get the same effect. Normally stepping through code in a debugger with optimizations disabled will eliminate the problem of not being able to set a breakpoint. If this works fine you can try disabling suspect optimizations at the function level with pragmas to see which one might be causing the problem.

waffleman
+3  A: 

It is called "tail call optimization".

OperateStepper() doesn't do anything after the call to HandleStallEvent(), so there is no point in returning to it. You'd just be doing a RET to a RET, which is a waste of an instruction AND a stack slot.

Read the "Lambda: The Ultimate ..." MIT AI Lab memos for more details.

Set your breakpoint on the HandleStallEvent() routine, rather than the call.

John R. Strohm
++ I should have seen that. Thanks for noticing.
Mike Dunlavey