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

Cortex-M4 interrupt occures right after it's disabled

Hi! I use STM32F417 with cortex-M4 core in my project. I found that a timer interrupt occures just after 2 instruction after disabled through peripherial register. How iis it possible? Assembler code (Interrupt disable regiter - is memory mapped peripherial register)

0x080016B4 49C0      LDR           r1,[pc,#768]  ; @0x080019B8
0x080016B6 8008      STRH          r0,[r1,#0x00] ;DISABLE INTERRUPT IN IER REGISTER
   146:     preemtp_disabled++; 
   147:     //TIM4->DIER |= TIM_DIER_UIE; 
0x080016B8 48BF      LDR           r0,[pc,#764]  ; @0x080019BC
0x080016BC 6800      LDR           r0,[r0,#0x00]
0x080016BE 1C40      ADDS          r0,r0,#1 ; <<<THIS IS A RETURT ADDRESS FROM INTERRUPT

May be it's a pipilene prefetching and reordering? Pipeline prefetsches 3 instructions, reorder them and advance pc counter, just in this time an interrupt occures. I was stopped at breakpoint in interrupt handler and interrupts has been disabled yet, and pc in exception frame points to 0x080016BE. I try to insert ISB instruction after clear interrupt disable bit, and after that cant reproduce this behavior any more. May be it works for me, or an error has not reproduced yet.. Thanks.

Parents
  • Ок,  Seems I found a solution in this article https://www.keil.com/support/docs/3928.htm

    Sometimes vendors incorporate an additional external, system-level write buffer 
    in their Cortex-M3 and Cortex-M4 designs for better performance. 
    But unfortunately, the core is not aware of this external write buffer 
    and cannot access it's status.
    ....
    Using the DSB instruction or __dsb(0) intrinsic before exiting will force a wait 
    for the internal write buffer to empty, but that instruction cannot 
    test the status of an optional system-level write buffer if 
    there happens to be one. To make sure the peripheral 
    interrupt register gets set properly, just perform another 
    memory write

    First of all i tried to add DSB and 6 NOPs after disable interrupt, but return address of isr sometimes points out of nops tail:

    inline void disable_preemption(void)
    {
        TIM4->DIER &= ~(TIM_DIER_UIE);
        __DSB(); //convay reload neaded, or inetrrup may be after we disabled it
        __NOP();
        __NOP();
        __NOP();
        __NOP();
        __NOP();
        __NOP();
        
        preemtp_disabled++;

    0x080015D6 8008      STRH          r0,[r1,#0x00] <<<<<DISABLE INTERRUPTS
       146:     __DSB(); //convay reload neaded, or inetrrup may be after we disabled it 
    0x080015D8 F3BF8F4F  DSB.W         
       147:     __NOP(); 
    0x080015DC BF00      NOP           
       148:     __NOP(); 
    0x080015DE BF00      NOP           
       149:     __NOP(); 
    0x080015E0 BF00      NOP           
       150:     __NOP(); 
    0x080015E2 BF00      NOP           
       151:     __NOP(); 
    0x080015E4 BF00      NOP           
       152:     __NOP(); 
       153:      
    0x080015E6 BF00      NOP           
       154:     preemtp_disabled++; 
       155:     //TIM4->DIER |= TIM_DIER_UIE; 
    0x080015E8 48B9      LDR           r0,[pc,#740]  ; @0x080018D0
    0x080015EA 6800      LDR           r0,[r0,#0x00] ;<<<< ISR POINTS HERE
    0x080015EC 1C40      ADDS          r0,r0,#1

    Then i add new dummy variable to increment it, as recommended in article above

    unsigned tmp=0;
    inline void disable_preemption(void)
    {
        TIM4->DIER &= ~(TIM_DIER_UIE);
        __DSB(); //convay reload neaded, or inetrrup may be after we disabled it
       tmp++;
        
        preemtp_disabled++;

    0x080015D6 8008      STRH          r0,[r1,#0x00] << DISABLE INTERRUPT
       146:     __DSB(); //convay reload neaded, or inetrrup may be after we disabled it 
    0x080015D8 F3BF8F4F  DSB.W         
       147:    tmp++; 
       148:      
    0x080015DC 48BB      LDR           r0,[pc,#748]  ; @0x080018CC
    0x080015DE 6800      LDR           r0,[r0,#0x00]
    0x080015E0 1C40      ADDS          r0,r0,#1
    0x080015E2 49BA      LDR           r1,[pc,#744]  ; @0x080018CC
    0x080015E4 6008      STR           r0,[r1,#0x00] <<<<RETURN FORM ISR
       149:     preemtp_disabled++; 
       150:     //TIM4->DIER |= TIM_DIER_UIE; 
    0x080015E6 48BA      LDR           r0,[pc,#744]  ; @0x080018D0
    0x080015E8 6800      LDR           r0,[r0,#0x00]

    in this case ISR ret  address never exceeded 0x080015E4.

    Now it's clear to me than M3-M4 are very tricky cores

Reply
  • Ок,  Seems I found a solution in this article https://www.keil.com/support/docs/3928.htm

    Sometimes vendors incorporate an additional external, system-level write buffer 
    in their Cortex-M3 and Cortex-M4 designs for better performance. 
    But unfortunately, the core is not aware of this external write buffer 
    and cannot access it's status.
    ....
    Using the DSB instruction or __dsb(0) intrinsic before exiting will force a wait 
    for the internal write buffer to empty, but that instruction cannot 
    test the status of an optional system-level write buffer if 
    there happens to be one. To make sure the peripheral 
    interrupt register gets set properly, just perform another 
    memory write

    First of all i tried to add DSB and 6 NOPs after disable interrupt, but return address of isr sometimes points out of nops tail:

    inline void disable_preemption(void)
    {
        TIM4->DIER &= ~(TIM_DIER_UIE);
        __DSB(); //convay reload neaded, or inetrrup may be after we disabled it
        __NOP();
        __NOP();
        __NOP();
        __NOP();
        __NOP();
        __NOP();
        
        preemtp_disabled++;

    0x080015D6 8008      STRH          r0,[r1,#0x00] <<<<<DISABLE INTERRUPTS
       146:     __DSB(); //convay reload neaded, or inetrrup may be after we disabled it 
    0x080015D8 F3BF8F4F  DSB.W         
       147:     __NOP(); 
    0x080015DC BF00      NOP           
       148:     __NOP(); 
    0x080015DE BF00      NOP           
       149:     __NOP(); 
    0x080015E0 BF00      NOP           
       150:     __NOP(); 
    0x080015E2 BF00      NOP           
       151:     __NOP(); 
    0x080015E4 BF00      NOP           
       152:     __NOP(); 
       153:      
    0x080015E6 BF00      NOP           
       154:     preemtp_disabled++; 
       155:     //TIM4->DIER |= TIM_DIER_UIE; 
    0x080015E8 48B9      LDR           r0,[pc,#740]  ; @0x080018D0
    0x080015EA 6800      LDR           r0,[r0,#0x00] ;<<<< ISR POINTS HERE
    0x080015EC 1C40      ADDS          r0,r0,#1

    Then i add new dummy variable to increment it, as recommended in article above

    unsigned tmp=0;
    inline void disable_preemption(void)
    {
        TIM4->DIER &= ~(TIM_DIER_UIE);
        __DSB(); //convay reload neaded, or inetrrup may be after we disabled it
       tmp++;
        
        preemtp_disabled++;

    0x080015D6 8008      STRH          r0,[r1,#0x00] << DISABLE INTERRUPT
       146:     __DSB(); //convay reload neaded, or inetrrup may be after we disabled it 
    0x080015D8 F3BF8F4F  DSB.W         
       147:    tmp++; 
       148:      
    0x080015DC 48BB      LDR           r0,[pc,#748]  ; @0x080018CC
    0x080015DE 6800      LDR           r0,[r0,#0x00]
    0x080015E0 1C40      ADDS          r0,r0,#1
    0x080015E2 49BA      LDR           r1,[pc,#744]  ; @0x080018CC
    0x080015E4 6008      STR           r0,[r1,#0x00] <<<<RETURN FORM ISR
       149:     preemtp_disabled++; 
       150:     //TIM4->DIER |= TIM_DIER_UIE; 
    0x080015E6 48BA      LDR           r0,[pc,#744]  ; @0x080018D0
    0x080015E8 6800      LDR           r0,[r0,#0x00]

    in this case ISR ret  address never exceeded 0x080015E4.

    Now it's clear to me than M3-M4 are very tricky cores

Children