This (above bold line) is simply RONG!
Ok, I made simple program to test it:
a=0x55;
result = PINB&1;
b=0xAA;
and the result is here:
000005a2 <.LBB125>:
5a2: 85 e5 ldi r24, 0x55 ; 85
5a4: 80 93 21 01 sts 0x0121, r24 ; 0x800121 <a>
000005a8 <.Loc.295>:
5a8: 83 b1 in r24, 0x03 ; 3
5aa: 81 70 andi r24, 0x01 ; 1
000005ac <.Loc.297>:
5ac: 80 93 20 01 sts 0x0120, r24 ; 0x800120 <result>
000005b0 <.Loc.298>:
5b0: 8a ea ldi r24, 0xAA ; 170
5b2: 80 93 1f 01 sts 0x011F, r24 ; 0x80011f <b>
000005b6 <.Loc.300>:
So it takes 4 cycles
in r24, 0x03 ;# 1 cycles, 2 bytes
andi r24, 0x01 ;# 1 cycles, 2 bytes
sts 0x0120, r24 ;# 2 cycles, 4 bytes
In the case of the AVR, it's not the interrupt latency of the chip that is the limiting factor (It's a bit complicated to figure out, since there is both some hardware latency for the external interrupt synchronization logic AND the SW interrupt latency of the CPU (the CPU logic takes 4 to 7 cycles, and a return takes 4.)
The big consumer of cycles comes from the compiler and its choice of interfaces, and the Arduino attachInterrupt() is especially expensive. Minimally, C thinks it has to save the status register and ensure that the "known zero" register is zero:
252: 1f 92 push r1
254: 0f 92 push r0
256: 0f b6 in r0, 0x3f ; 63
258: 0f 92 push r0
25a: 11 24 eor r1, r1
That's 8 cycles (and a similar number on exit from the ISR.) It'll also need to save any registers that the ISR function itself actually uses. (6 registers or 12cycles for the Uno timer interrupt, for example.)
But for ISRs that call other C or C++ code, it also has to save all of the registers that C allows to be modified by the called function, and there are 12 of them (24 cycles.) And then attachInterrupt has to load up the actual destination and do an indirect call. (7 more cycles.)
So that's about 4+7+30+7 or 48 cycles from the time you wiggle a pin till the time the function you supplied starts executing, and perhaps 40 cycles to exit the ISR.
IMPLEMENT_ISR(INT0_vect, EXTERNAL_INT_0)
IMPLEMENT_ISR(INT1_vect, EXTERNAL_INT_1)
2e6: 1f 92 push r1
2e8: 0f 92 push r0
2ea: 0f b6 in r0, 0x3f ; 63
2ec: 0f 92 push r0
2ee: 11 24 eor r1, r1
2f0: 2f 93 push r18
2f2: 3f 93 push r19
2f4: 4f 93 push r20
2f6: 5f 93 push r21
2f8: 6f 93 push r22
2fa: 7f 93 push r23
2fc: 8f 93 push r24
2fe: 9f 93 push r25
300: af 93 push r26
302: bf 93 push r27
304: ef 93 push r30
306: ff 93 push r31
308: e0 91 02 01 lds r30, 0x0102 ; 0x800102 <__data_start+0x2>
30c: f0 91 03 01 lds r31, 0x0103 ; 0x800103 <__data_start+0x3>
310: 09 95 icall
312: ff 91 pop r31
314: ef 91 pop r30
316: bf 91 pop r27
318: af 91 pop r26
31a: 9f 91 pop r25
31c: 8f 91 pop r24
31e: 7f 91 pop r23
320: 6f 91 pop r22
322: 5f 91 pop r21
324: 4f 91 pop r20
326: 3f 91 pop r19
328: 2f 91 pop r18
32a: 0f 90 pop r0
32c: 0f be out 0x3f, r0 ; 63
32e: 0f 90 pop r0
330: 1f 90 pop r1
332: 18 95 reti
Add the annoyance of probably having to do that for each "bounce" of your switch contact...
Well, at least two cycles (if "result" is a function-local register.)
There's a single "test bit in IO register and skip if 0/1" instruction that you could use if you're just TESTING the bit, but it takes up to 3 cycles (depending on whether it skips or not, and whether the instruction is skips is one or two words.)
Lots slower if your use digitalRead() to read the pin ![]()
But that's a bit of a red herring. The "latency" to your function isn't dependent on the timing of the instruction that checks the bit, it's dependent on EVERYTHING ELSE that happens in between the times that you check the bit. Ie, if you poll in loop(), the latency is dependent on the length of all the code that executes in loop. If your loop() takes 1000 cycles (which I don't think is unusual), then the ~100 cycles of overhead for an ISR starts to seem pretty reasonable. OTOH, if your loop doesn't do anything but wait for the ISR to set a variable, polling looks more attractive (OTTH, if your loop isn't doing anything, who cares how fast it's going?)
By "interrupt latency", I simply understand the time/cycles (16 clock cycles for a 4 cycle duration ISR) being taken by the AVR MCU during automatic execution of the following tasks once triggered by an (external) interrupting signal. In other words: iterrupt latency refers to the absolute time difference between exiting MLP (Main Line Program) and resuming MLP in an interrupt process.
- Finishing the current instruction, (1 cycle)
- Disabling global interrupt flag, (1 cycle)
- Saving return address onto stack (not executing push). (3/4 clock cycle)
- Loading the ISR address into PC, (1 clock cycle)
- Finishing the ISR, (variable: say, max 4 clock cycles for a very short ISR)
- Enabling global interrupt flag, and (1 clock cycles)
- Retrieving the return address from stack and loads into PC to resume
the interrupted program (the Main Line Program). (4 clock cycles)
In between three and four is another JMP instruction, since the "ISR Address" is a 4byte space in the vector table (usually contain a "JMP ISRn." If you're not using many interrupts, you can sometimes get away with positioning the entire ISR in the vector table, but that's considered pretty questionable.)
And then all the status and register saving I mentioned. The AVR hardware doesn't save ANY state other than the PC.
I think manipulation of the Global Interrupt flag happens concurrently with other stuff - RET and RETI both take 4 cycles. And the other interrupt flag clearing that happens "when an ISR is executed" isn't documented to add any additional time, either.
In ATmega328P AVR, the flash/code memory is word (16-bit) organized. Is it possible for the CPU to read 16-bit code in one cycle like 8086 which can read code from both banks (odd/evn) in one machine cycle?
Yes, AVR have most instruction 2bytes=1word and does them in 1 cycle, 4bytes=2words instructions does usually in 2 cycles.
Yes, code fetches are 16bits at a time and take one cycle (normally overlapped with execution of the previous instruction, giving you one cycle per instruction unless you break the pipeline (branch and skip instructions.))
However, there are no instructions for fetching 16bit DATA from flash...