This discussion has been locked.
You can no longer post new replies to this discussion. If you have a question you can start a new discussion

Can keil optimize the interrupt routines?

When I set optimize(9,size) to a interrupt function, which should be optimize for "Common Block Subroutines", but it wasn't!
All interrupt funtions contain following duplicate blocks(see BOLD):


    PUSH    ACC
    PUSH    B
    PUSH    DPH
    PUSH    DPL
    PUSH    DPH1
    PUSH    DPL1
    PUSH    DPS
    MOV     DPS,#00H
    PUSH    PSW


    MOV     PSW,#010H ;Depend on using.
    LCALL   MyFunc


    POP     PSW
    POP     DPS
    POP     DPL1
    POP     DPH1
    POP     DPL
    POP     DPH
    POP     B
    POP     ACC
    RETI


Because I have so many interrupt functions. The optimize could save some code space, isn't it?

Parents Reply Children
  • I hope the compiler could optimize the code like:

    LB1:
        PUSH ACC
        PUSH B
        ...
        RET
    
    LB2:
        ...
        POP B
        POP ACC
        RETI
    
    ;void INT0 (void) intterupt x using y
        LCALL LB1
    
        MOV     PSW,#010H ;Depend on using.
        LCALL MyFunc ;
    
        LJMP LB2
    

    I don't see any problems on preceding codes. Am I right?

  • Funtionally, you are quite right - no reason why what you suggest will not work. However, interrupts are generally time critical so it may be that the optimiser leaves this bit of common code alone for the sake of speed.

    By-the-way, if you do have many interrupts, you may get better performance by turning off dual data pointers. memcpy() etc will run a little slower, but interrupts willl have fewer registers to save/restore.

  • The optimize could save some code space, isn't it?
    I believe that if you made my_func inline the optimizer would have a better idea of what to save. E.g if my_func uses the dptr it must be saved, if not no need, but with a call it is probablyu more that the optimizer can handle to figure out if dptr is used..

    Erik

  • Hi,
    No - your example will not work. Look again on your suggestion:

    LB1:
        PUSH ACC
        PUSH B
        ...
        RET
    Now could you imagine, where the MCU jumps to when executes RET? The return address will not be at stack pointer because there were some PUSHes before and no same numbers of POPs! Be carefull when play with stack area.
    Good days!

  • >erik: I believe that if you made my_func inline..
    The interrupt function must locate on common area. My idea is to minimize the size of common area, so I did my real jobs on another function which could be located on normal banks (Bank0,1,..). I can save many code sizes, because my_func's codes will not duplicate on every bank (common area).

    >Oleg: Now could you imagine, where the MCU jumps to when executes RET?
    I could imagine, because everything is under control. You have point out that we should play carefully with stack area, and as I know, the preceding codes should be fine.

  • Sorry, I forgot reponse to Graham.
    1. I did tell the compiler my wish: optimize(9,size). Maybe it's keil's policy for interrupt function as you mention.

    2. Since my chip support dual pointer, and keil's documents said it will improve the performance of may library functions, so I use it! In fact, I will use NOMODDP2 on those ISR functions (I have verify those ISRs won't call any improved functions), then, the extra code will be "PUSH DPS, and MOVE DPS,#0" which takes only 5 machin cycles. The extra latency is fine for my project.

  • No, it will not work in the whole. In your example, when RET command of LB1() executes, MCU jumps to random address (in fact it goes to address given from two last bytes on the stack). See once again: LCALL LB1 puts return address (address of the next command after LCALL) on the top of stack. Inside your LB1() routine there are some PUSHes which increase stack pointer for each byte held on stack area. The last command of LB1() is RET. It takes two bytes from the top of stack and does jump on address constructed from these bytes. So if the end of your LB1() looks like:

    LB1:
        ....
        PUSH    DPL1
        PUSH    DPS
        RET
    ...then after RET command MCU does not return to command after LCALL LB1 but goes to address (DSP<<8 +DPL1) instead! Based on it I ask you again: could you even imagine? (=
    Okay, there are many ways to do it by right anyway. For example (just one of many solutions):
    ;void INT0 (void) intterupt x using y
    	PUSH	DPL
    	PUSH	DPH
    	MOV	DPTR,#MyFunc
    	CALL	SafeRun   ; <- common for all the interrupts
    	POP	DPH
    	POP	DPL
    	RET
    
    ; Common function which saves resisters,
    goes to subroutine with address in DPTR
    and retrives registers back
    SafeRun:
    	PUSH	PSW
    	PUSH	ACC
    ;	....  push rest registers except DPTR (mark1)
    	CALL	SafeRun_Go
    ;	.... pop previous pushed registers (mark1)
    	POP	ACC
    	POP	PSW
    	RET
    SafeRun_Go:
    	CLR	A
    	JMP	@A+DPTR
    Good days!

  • Yes, you are right! The precdeing codes can't work correctly. I am so embarrassing to say such words: "everything is under control".

    So, it seems only LB2 could be optimize.

  • Your solution is good. Thank you again.

  • "keil's documents said it will improve the performance of may [many?] library functions"

    Actually, the manual says, "Using additional data pointers can improve the performance of the following library functions: memcpy, memmove, memcmp, strcpy, and strcmp." (my ephasis).

    So that's only five library functions, and no guarantee that it will actually help!

    You need to examine whether any improvement in these few library functions is actually worthwhile in your application, considering the impact of the extra saves on your interrupt performance - which seems, from your post, to be your major concern?