The basic idea would be to detect any loop that has no side effects (i.e.it only reads from the memory), and skip all the iterations of the loop until the next external event (timer firing / interrupt).
There are several factors that makes the implementation challenging:
Many instructions update the status register (SREG).
In many cases, the busy-waiting code does some calculations, and thus it has a local side effect of updating register values.
We still need to keep the clock cycle count correct, as if we actually executed the skipped instructions.
Currently, the avrInstruction() method advances the clock cycle count, and other peripherals (e.g. timers) synchronize with it by looking at the value of cpu.cycles. We will need to implement a new mechanism to keep track of the clock cycles and to synchronize the different peripherals to it.
As an example, here is the disassembly of the Arduino delay() function (as compiled by the Arduino CLI). It was obtained by compiling the "Blink" program and then running avr-objdump -S on the generated ELF file:
As you can see here, the code does some calculations that updates the registers r22 - r25, as well as the status register, and it also calls the micros() function. The call instruction pushes a value to the stack and updates the stack pointer, causing another temporary side effect (that is later cancelled out when we call ret in micros()).
This code also changes the status register (through the in and out instructions, and as a result of the arithmetic instructions), as well as registers r18, r20, and r22-r27.
There are many cases where the Arduino code is simply waiting for an external event. One example is calling
delay()
, which is implemented as a loop that repeatedly looks at the value ofmicros()
. Another example isSerial.write
, which waits for an interrupt or an external flag to be set when the TX buffer is full.The basic idea would be to detect any loop that has no side effects (i.e.it only reads from the memory), and skip all the iterations of the loop until the next external event (timer firing / interrupt).
There are several factors that makes the implementation challenging:
avrInstruction()
method advances the clock cycle count, and other peripherals (e.g. timers) synchronize with it by looking at the value ofcpu.cycles
. We will need to implement a new mechanism to keep track of the clock cycles and to synchronize the different peripherals to it.As an example, here is the disassembly of the Arduino
delay()
function (as compiled by the Arduino CLI). It was obtained by compiling the "Blink" program and then runningavr-objdump -S
on the generated ELF file:As you can see here, the code does some calculations that updates the registers
r22
-r25
, as well as the status register, and it also calls themicros()
function. Thecall
instruction pushes a value to the stack and updates the stack pointer, causing another temporary side effect (that is later cancelled out when we callret
inmicros()
).And the disassembly of
micros()
:This code also changes the status register (through the
in
andout
instructions, and as a result of the arithmetic instructions), as well as registersr18
,r20
, andr22-r27
.