0

I have some code where interrupt jitter is deal killer. There are just one interrupt source and that need to be as precise as possible. For ATMEGA the max interrupt response is the length of current instruction + call to interrupt routine.
But my code behave erratic. The interrupt delay seems much larger. Finally I find where trouble lies. The GCC disables the interrupt by itself!!! So here is simple example of that. No interrupt usage at all in source code.

#include <avr/io.h>

void DoJob(void){
    uint8_t buf[256];
    for (uint8_t i = 0; i < 255; i++){
        PORTB=buf[i];
        buf[i]=PORTB;
    }
}

int main(void){
    for(;;){
        DoJob();
    }
}

Now in assembler listing there is surprise. There are CLI instruction to disable the interrupts.

000000cc <DoJob>:
  cc:   cf 93           push    r28
  ce:   df 93           push    r29
  d0:   cd b7           in  r28, 0x3d   ; 61
  d2:   de b7           in  r29, 0x3e   ; 62
  d4:   da 95           dec r29
  d6:   0f b6           in  r0, 0x3f    ; 63

  d8:   f8 94           cli

  da:   de bf           out 0x3e, r29   ; 62
  dc:   0f be           out 0x3f, r0    ; 63
  de:   cd bf           out 0x3d, r28   ; 61
  e0:   fe 01           movw    r30, r28
  e2:   31 96           adiw    r30, 0x01   ; 1
  e4:   ce 01           movw    r24, r28
  e6:   93 95           inc r25
  e8:   21 91           ld  r18, Z+
  ea:   25 b9           out 0x05, r18   ; 5
  ec:   25 b1           in  r18, 0x05   ; 5
  ee:   df 01           movw    r26, r30
  f0:   11 97           sbiw    r26, 0x01   ; 1
  f2:   2c 93           st  X, r18
  f4:   e8 17           cp  r30, r24
  f6:   f9 07           cpc r31, r25
  f8:   b9 f7           brne    .-18        ; 0xe8 <DoJob+0x1c>
  fa:   d3 95           inc r29
  fc:   0f b6           in  r0, 0x3f    ; 63

  fe:   f8 94           cli

 100:   de bf           out 0x3e, r29   ; 62
 102:   0f be           out 0x3f, r0    ; 63
 104:   cd bf           out 0x3d, r28   ; 61
 106:   df 91           pop r29
 108:   cf 91           pop r28
 10a:   08 95           ret

0000010c <main>:
 10c:   0e 94 66 00     call    0xcc    ; 0xcc <DoJob>
 110:   fd cf           rjmp    .-6         ; 0x10c <main>

In this example the solution is simple as just declaring buffer to be global solve the problem. But in real code can't declare all buffers and variables global as there is no space. So how to avoid that and is it there some other situations where compiler WITHOUT KNOWING DISABLE INTERRUPTS? At least warning that compiler do that should be welcome. My system misbehave maybe once per day and it take really long time to find problem.

4
  • I was able to reproduce this on Compiler Explorer, and the assembly code is more readable there because it contains the register names: godbolt.org/z/6E4YeTrvc Commented May 11, 2024 at 19:53
  • When that code to write to SP is "unexpected"; what code DO you expect? Commented May 16, 2024 at 8:02
  • Now as I know that is easy. But find why code throw error once per day because of that additional 3 cycle latency is not easy. I did except some warning if code blocks interrupt on it's own purpose. Commented May 16, 2024 at 15:07
  • There's no way the compiler can write to SP in any reasonable way without disabling IRQs for some cycles. You could write your own allocation routines that don't need a write to SP, or use some XMEGA device which can write atomically to SP without disabling IRQs. Commented May 16, 2024 at 19:42

1 Answer 1

1

The interrupt lock is required because the setting of SP must be atomic. The lock is active until OUT 0x3d.

Without the lock you may get violated SP because IRQ triggers in the middle of writing to SP.

The compiler only disables IRQs for 3 instructions and when it is absolutely required, which is when writing to a multi-byte SP on non-Xmega devices.

You'll have jitter nonetheless from slow instructions like LPM, CALL, RET, etc.

Sign up to request clarification or add additional context in comments.

5 Comments

I understand why interrupt is disabled. But is there some option to show when this is necessary? Now I know if I have some buffer in sub then probably this will happend. But even if there are only few variables in sub this can happend.
for now I search ASM output and look if CLI is somewhere. Can be that automated?
Searching asm for (grep) can be automated of course. With -save-temps -dp you also get the gcc's insn name.
Also it's more about the accumulated size of auto variables than about the number of such variables. The one array in your example allocates 256 bytes already.
I got warning if have wrong variable type and need to cast. I do except warning if compiler disable interrupts on it own as this lead program to behave different as expected.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.