register storing/loading problem in interrupt handling functions

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

register storing/loading problem in interrupt handling functions

Jump to solution
2,970 Views
honzin
Contributor I

Hi,

 

While developing a program for a Coldifre 5212 microcontroller I noticed strange behavior of the prog. After some investigation I found that interrupt handling functions do not store all registers which are used inside the interrupt. They store registers used in the functions themselves  but ignore all registers used in functions called inside the interrupts. This corrupts code execution.

In the forum I have found no working solution.

1) pragmas NO_ENTRY, NO_EXIT etc. do not work in CW at all.

2) I wrote an interrupt handler in assembler and manually stored/loaded all registers (except a7 and a6 in case of a6 stack frames :smileyhappy: ) but there is a problem with A6 stack frames checkbox (each situation needs different instructions and handling). I cannot find out how to check actual value of a6frames checkbox and compile corresponding code lines.

 

Does anyone know how to solve this problem (rewrite interrupt handlers not to call any function is not possible) and force compiler store all (desired) registers in interrupt handlers? 

Labels (1)
0 Kudos
Reply
1 Solution
1,711 Views
bkatt
Contributor IV

In CodeWarrior, A0,A1,D0,D1, and D2 are scratch registers. Normal functions do not save them. Interrupt functions save all scratch registers (if they call other functions).

 

Any function which uses a non-scratch register must save and restore it. A7 (stack pointer) and A6 (frame pointer) are preserved by being used in the normal way.

 

In m68k-elf-gcc, D2 is not a scratch register.

 

The compilers actually get this right, so if registers get clobbered, it may be due to assembly code which breaks the rules. For example, freescale distributes an "asm exception handler" that does not preserve D2. They get away with it only because the interrrupt never returns (it eventually halts). 

View solution in original post

0 Kudos
Reply
7 Replies
1,712 Views
bkatt
Contributor IV

In CodeWarrior, A0,A1,D0,D1, and D2 are scratch registers. Normal functions do not save them. Interrupt functions save all scratch registers (if they call other functions).

 

Any function which uses a non-scratch register must save and restore it. A7 (stack pointer) and A6 (frame pointer) are preserved by being used in the normal way.

 

In m68k-elf-gcc, D2 is not a scratch register.

 

The compilers actually get this right, so if registers get clobbered, it may be due to assembly code which breaks the rules. For example, freescale distributes an "asm exception handler" that does not preserve D2. They get away with it only because the interrrupt never returns (it eventually halts). 

0 Kudos
Reply
1,711 Views
honzin
Contributor I

Hi,

 

The routine is quite long, so I cannot place it here. But registers d0-d2/a0-a1 are stored in the interrupt routines only, no other function stores them.

 

"The compilers actually get this right, so if registers get clobbered, it may be due to assembly code which breaks the rules."

Thanks for advice. When it comes to assembler I will have to investigate all the subroutines which could be called from interrupt. There should be no assembler at all but I might have overlooked something.

 

I have found one C function which uses registers otside scratch registers without storing them and restoring at the end. Registers are not used before/after calling of the function - so no parameters are passed to/from them. The assembler code looks a bit strange, there is a jump somewhere before these instructions and I have no idea what it means. 

 

;  307: void FilterLength( const unsigned char typ, int* lA, int* lB )
;  308: {
0x00000000  0x4E560000               link     a6,#0
;  310:     switch ( typ )
;  311:      {
;  312:          case IIR_FILTER_ACCELERATION_p0063:
;  313:          {
0x00000004  0x7400                   moveq    #0,d2
0x00000006  0x142E0008               move.b   8(a6),d2
0x0000000A  0x7204                   moveq    #4,d1
0x0000000C  0xB481                   cmp.l    d1,d2
0x0000000E  0x6E62                   bgt.s    *+100                 ; 0x00000072
0x00000010  0x7000                   moveq    #0,d0
0x00000012  0xB480                   cmp.l    d0,d2
0x00000014  0x6D5C                   blt.s    *+94                  ; 0x00000072
0x00000016  0x343B2A08               move.w   (8,pc,d2.l*2),d2
0x0000001A  0x48C2                   ext.l    d2
0x0000001C  0x4EFB2A00               jmp      (pc,d2.l*2)
0x00000020  0x0006000D               ori.b    #0xd,d6               ; '.'
0x00000024  0x0014001B               ori.b    #0x1b,(a4)            ; '.'
0x00000028  0x0022206E               ori.b    #0x6e,-(a2)           ; 'n'
;  314:             *lA = IIR_FILTER_ACCELRATION_LENp0063;
0x0000002C  0x000A2081               ori.b    #0x81,a2              ; '.'
;  315:             *lB = IIR_FILTER_ACCELRATION_LENp0063;
0x00000030  0x206E0010               movea.l  16(a6),a0
0x00000034  0x2081                   move.l   d1,(a0)
;  316:             break;
;  317:            }
;  318:            case IIR_FILTER_ACCELERATION_p0125:
;  319:            {
0x00000036  0x6046                   bra.s    *+72                  ; 0x0000007e
;  320:             *lA = IIR_FILTER_ACCELRATION_LENp0125;
0x00000038  0x206E000A               movea.l  10(a6),a0
0x0000003C  0x2081                   move.l   d1,(a0)
;  321:             *lB = IIR_FILTER_ACCELRATION_LENp0125;
0x0000003E  0x206E0010               movea.l  16(a6),a0
0x00000042  0x2081                   move.l   d1,(a0)
;  322:             break;
;  323:            }
;  324:            case IIR_FILTER_ACCELERATION_p025:
;  325:            {
0x00000044  0x6038                   bra.s    *+58                  ; 0x0000007e
;  326:             *lA = IIR_FILTER_ACCELRATION_LENp025;
0x00000046  0x206E000A               movea.l  10(a6),a0
0x0000004A  0x2081                   move.l   d1,(a0)
;  327:             *lB = IIR_FILTER_ACCELRATION_LENp025;
0x0000004C  0x206E0010               movea.l  16(a6),a0
0x00000050  0x2081                   move.l   d1,(a0)
;  328:             break;
;  329:            }
;  331:            case IIR_FILTER_ACCELERATION_p05:
;  332:            {
0x00000052  0x602A                   bra.s    *+44                  ; 0x0000007e
;  333:             *lA = IIR_FILTER_ACCELRATION_LENp05;
0x00000054  0x206E000A               movea.l  10(a6),a0
0x00000058  0x2081                   move.l   d1,(a0)
;  334:             *lB = IIR_FILTER_ACCELRATION_LENp05;
0x0000005A  0x206E0010               movea.l  16(a6),a0
0x0000005E  0x2081                   move.l   d1,(a0)
;  335:             break;
;  336:            }
;  337:            case IIR_FILTER_ACCELERATION_p1HI:
;  338:            {
0x00000060  0x601C                   bra.s    *+30                  ; 0x0000007e
;  339:             *lA = IIR_FILTER_ACCELRATION_LENp1HI;
0x00000062  0x206E000A               movea.l  10(a6),a0
0x00000066  0x7007                   moveq    #7,d0
0x00000068  0x2080                   move.l   d0,(a0)
;  340:             *lB = IIR_FILTER_ACCELRATION_LENp1HI;
;  341:                break;
;  342:            }
;  343:            default:
;  344:            {
0x0000006A  0x206E0010               movea.l  16(a6),a0
0x0000006E  0x2080                   move.l   d0,(a0)
;  350: }
0x00000070  0x600C                   bra.s    *+14                  ; 0x0000007e
;  345:             *lA = 0;
0x00000072  0x206E000A               movea.l  10(a6),a0
0x00000076  0x4290                   clr.l    (a0)
;  346:             *lB = 0;             
;  347:                   return;     
;  348:               }
;  349:        }
0x00000078  0x206E0010               movea.l  16(a6),a0
0x0000007C  0x4290                   clr.l    (a0)
;  350: }
0x0000007E  0x4E5E                   unlk     a6
0x00000080  0x4E75                   rts     
 

 

0 Kudos
Reply
1,711 Views
bkatt
Contributor IV

"jmp  (pc,d2.l*2)" is a computed goto. The next four double words will eventually contain the addresses of the case labels (filled in by the linker). Your disassembler doesn't understand this and is mistakenly showing them as "ori" instructions. The function changes only scratch registers and appears to be compiled correctly.

 

Note that the stack is used for saving registers, frame pointers, return addresses, and local variables. If a local variable is an array, or some code takes a pointer to a local, there could be a buffer overflow or other undesired damage to the stack contents that could look like a register being clobberred.

 

0 Kudos
Reply
1,710 Views
honzin
Contributor I

Hi,

 

Finally I found several lines with mac operations and so I stored mac registers too. Everything seems to be OK now. Thanks for help and comments.

0 Kudos
Reply
1,711 Views
mjbcswitzerland
Specialist V

Hi

 

I don't use the 5212 and so can't exclude something special with it (Codewarrior questions are better posted in the Codewarrior forum: http://forums.freescale.com/freescale/board?board.id=CWCOMM).

 

But I believe that it is generally not necessary for the interrupt handler to store any more registers than it actually uses itself. If there are sub-routines called from it, it is the sub-routines that need to store any registers that will be used there.

 

Taking a look at an example interrupt handler and a called routine I get the following (on a V2 device):

 

static __interrupt__ void _Interrupt(void)

{

0x00000000  0x40E746FC2700           strldsr  #0x2700
0x00000006  0x4FEFFFDC               lea      -36(a7),a7
0x0000000A  0x48EF43870008           movem.l  d0-d2/d7/a0-a1/a6,8(a7)

 ..

0x00000092  0x7204                   moveq    #4,d1
0x00000094  0x4EB900000000           jsr      _subroutine_x

   subroutine_x(_ACTIVATE);

...

0x000001F6  0x4CEF43870008           movem.l  8(a7),d0-d2/d7/a0-a1/a6
0x000001FC  0x4FEF0028               lea      40(a7),a7
0x00000200  0x4E73                   rte  

}

 

In the subroutine:

 

extern void subroutine_x(unsigned char ucSetState) 

{

0x00000000  0x4FEFFFEC               lea      -20(a7),a7
0x00000004  0x48D750E0               movem.l  d5-d7/a4/a6,(a7)
0x00000008  0x2E01                   move.l   d1,d7

...

0x0000007C  0x4CD750E0               movem.l  (a7),d5-d7/a4/a6
0x00000080  0x4FEF0014               lea      20(a7),a7
0x00000084  0x4E75                   rts   

}

 

Here the subroutine is first saving all of the registers that it will use too. On exit it then restores them. The interrupt also restores the ones that it used and so the interrupted code should be able to continue with a full set of registers with the same content as before the interrupt occurred.

 

If your registers do not have the content that they had when entering the interrupt the problem is more likely to be in the subroutine - an interrupt handler which saves all registers may mask a problem elsewhere and is doing unnecessary extra work. If the interrupt subroutine's disassembled code shows that it is indeed saving and restoring correctly (use right click on particular file in Codewarrior and then command disassemble to show disassembly of a particular file) it is more likely to be that the subroutine is corrupting the stack where the registers were saved to. Another possible problem is that the subroutine is being called with an incorrect prototype (check that the C/C++ language setting is with "Require Function Prototypes" to exclude this being possible) which could also cause corruption if the wrong registers are used.

 

Finally check that both files are compiled with the same compiler settings - if libraries are involved this is especially relevant - to ensure that they are using the same register passing conventions (which again could leave a particular register in a unrestored condition if mismatching).

 

Regards

 

Mark

 

 

www.uTasker.com

 

 

 

0 Kudos
Reply
1,711 Views
honzin
Contributor I

Hi,

 

The problem is that the subroutine does not store registers it uses. In fact from that subroutine there are more subroutines called. None of them stores its registers. I tried to set "Require Function Prototypes" to checked but it did not help. All the routines are written in C language (no assembler). And I am using no libraries and no pragmas to change options of compiler in the sourcecodes.

Situation is as follows:

 

timer.c:

    extern void TimerInterrupt(void);

    declspec(interrupt) void Timer_INT(void)

    {

        // uses and stores d0-d2/a0-a1

        PCSR0|= 0x0004;

        TimerInterrupt();// routine+subroutines (entirely written in C) use but do not store d0-d3,d6,d7/a0-a3

        // restores d0-d2/a0-a1

    }

 

And I cannot force compiler to store registers in all subroutines called from the interrupt.

The easiest way is to store d0-d7/a0-a6 or a5 and restore in the end. I can do it eg. by:

 

asm void Timer_INT(void) // without a6 stack frames, would be a bit different with them

{

    strldsr    #0x2700

    lea         -60(a7),a7

    movem.l  d0-d7/a0-a6,(a7)

    jsr          TimerInterruptOrig  // original Timer_INT without declspec(interrupt)

    movem.l   (a7),d0-d7/a0-a6

    lea          64(a7),a7

    rte

}

This safely stores ant restores all the registers (except mac regs). The problem is that I am unable to check a6 stack frames presence and compile correspondent verison of asm Timer_INT.

 

Maybe I did something wrong that subroutines do not store registers but I do not know what it was :smileysad:

0 Kudos
Reply
1,711 Views
mjbcswitzerland
Specialist V

Hi

 

Can you show your  subroutine which doesn't save registers: as C-code and as disassembled code (eg. by attaching the two files or cut outs of the relevant parts)?

 

Does this happen with routines not called from interrupts?

Does this happen when the routine is 'also' called from code which is not interrupt code?

 

Regards

 

Mark

 

 

www.uTasker.com

0 Kudos
Reply