What does ",ISR_NAKED" do?

Can someone please explain what the exact difference between a normal ISR and a naked ISR is? I understand that a naked one leaves out some instructions the compiler would actually add, but what instructions are these exactly? I know a naked one triggers faster, about 1us from the triggering event until ISR code begins executing rather than about 4us for a normal ISR, but don't know what a normal one does that adds to a whole 3us more than a naked one.

I'd like to be able to have an interrupt trigger (time from event until my ISR code begins) as fast as is possible, but don't mind adding extra arcane register like stuff at the end of it and having a slow exit, so long as the start is very quick.

Also is there a difference in trigger-to-start time between interrupts defined like:

attachInterrupt(digitalPinToInterrupt(pin), ISR_func, mode)

void ISR_func(){
  //code here
}

vs

PCMSKX |= bit (PCINTX);  // pin 
PCIFR  |= bit (PCIFX);   // clear any outstanding interrupts
PCICR  |= bit (PCIEX);   // enable pin change interrupts for pins
ISR (PCINT0_vect ){
  //code here
}

Thank you

P.S. This is in regards to the ATMEGA328P and other AVR microcontrollers

What did google say ? :thinking:

First hit: avr-libc: <avr/interrupt.h>: Interrupts - near the bottom of the page

See also the section, " Manually defined ISRs"

1 Like

Already saw avr-libc: <avr/interrupt.h>: Interrupts

but it didn't say anything beyond that some code is missing from a NAKED one doesn't say which.

EDIT: didn't see the "Manually defined ISRs" section earlier though. Looks like it has a bit more info, but still doesn't seem to fully list everything which is missing from a naked but included in a normal. It discusses SREG * , but surely some SREG copyng doesn't add up to the huge time difference between a normal and naked ISR's triggering time?

*interrupts are disabled unless you make SREG calls to ensure otherwise in a normal ISR, and this applies automatically within even a naked ISR doesn't it? So saving and restoring SREG, the way you would around a piece of code in the loop() function which wanted to pull several variables from the ISR without the ISR running in the middle of them, isn't usually needed for an ISR unless the ISR makes explicit SREG or cli(); calls?

EDIT2:
" compiler-implied assumption of __zero_reg__ always being 0 could be wrong"
And what would that mean in practice? Would it only affect anything where I'd explicitly call
__zero_reg__
in something, or do a lot of functions use it behind the scenes? Would this be compensated for perhaps by having a
__zero_reg__=0;
line somewhere after the most time critical parts of the ISR have happened but before anything in the ISR than needs __zero_reg__ happens?

Thanks

EDIT3:
Is there a sample of code somewhere showing what functions needing adding to a naked ISR for it to be the same as a normal one. That would be the easiest way to recognise the differences in full, I could perhaps then write an ISR which enters as a naked one, but then runs the same code as for a non-naked one after the most time critical bits of it are completed.
Thanks

You will only see a diference between ISR and ISR_NAKED if you examine the assembly code.
However I don't know how to make the Arduino IDE output assembly if it can.
You may be able to do it using the CLI.

1 Like

Yes. Nick Gammon presents a good analysis of interrupt response time in the section "How long does it take to execute an ISR?"
http://www.gammon.com.au/interrupts

He also advises against using the NAKED_ISR here

My advice is: don't try to out-think the compiler

https://forum.arduino.cc/t/interrupt-response-time/109372/13

1 Like

If you compile your code in the Wokwi simulator, you can get an assembly listing with "F1 / View compiled assembly code listing"

@DaveX Thanks!

Why? Often there is more than one way to solve a problem.

1 Like

With "Naked", the compiler omits ALL of the standard context save and restore that it would have inserted on an ISR. How much context that is is dependent on exactly what code your ISR contains. If the ISR is a "leaf" function (doesn't call any other functions), the context saved includes R0, R1, the status register, and whatever registers the code actually uses.

ISR(PCINT1_vect) {
 2d2:   1f 92           push    r1
 2d4:   0f 92           push    r0
 2d6:   0f b6           in      r0, 0x3f        ; Status register
 2d8:   0f 92           push    r0
 2da:   11 24           eor     r1, r1         ;r1 must be a "known zero" for C code.

This has actually been improved in more recent versions of the compiler (not currently used by Arduino), so that if you don't use R0, R1, or instructions that change the SR, they won't be saved.
Of course, there is matching "restore" code at the end of the ISR.

if the ISR calls additional code, it also has to save whatever registers are defined as "caller modifiable" by the C api. Thisi is ... a lot, but is somewhat mitigated by the use of link time optimization...

IMPLEMENT_ISR(INT1_vect, EXTERNAL_INT_1)
 186:   1f 92           push    r1
 188:   0f 92           push    r0
 18a:   0f b6           in      r0, 0x3f        ; status register
 18c:   0f 92           push    r0
 18e:   11 24           eor     r1, r1          ; known zero
 190:   2f 93           push    r18             ; registers that C code does not have to preserve.
 192:   3f 93           push    r19             ;  :
 194:   4f 93           push    r20
 196:   5f 93           push    r21
 198:   6f 93           push    r22
 19a:   7f 93           push    r23
 19c:   8f 93           push    r24
 19e:   9f 93           push    r25
 1a0:   af 93           push    r26
 1a2:   bf 93           push    r27
 1a4:   ef 93           push    r30
 1a6:   ff 93           push    r31
   :

Also is there a difference in trigger-to-start time between interrupts defined like:
attachInterrupt(digitalPinToInterrupt(pin), ISR_func, mode)

Yes. if you use a bare ISR, it's address is a compile-time constant, so it gets inserted directly into the AVR vector table. attachInterrupt allows the vector to be changed at runtime, so it has an additional level of indirection (load the ISR address from a RAM location, and then do an icall), IN ADDITION to invoking that "have to save a lot because I don't know what the C code called will do" described above.

 ;;;  All that other context save from above
 1a6:   ff 93           push    r31
 1a8:   e0 91 02 01     lds     r30, 0x0102     ; Load provided function address
 1ac:   f0 91 03 01     lds     r31, 0x0103     ;  (which is two bytes)
 1b0:   09 95           icall                   ; indirect call
 1b2:   ff 91           pop     r31             ; start restoring state.
;; more restore state

I believe "naked" also omits the "iret" instruction at the end of the ISR...
Usually, a "naked" ISR will consist entirely of inline asm statements.

Indeed:

I'd recommend using a separate assembler module - rather than do battle with all the arcane vagaries of inline assembler ...

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.