Hacker News new | ask | show | jobs
by friendzis 2086 days ago
Poor man's sleeps (various NOP loops) are just that. In all cases you should try and utilize on-chip sleep instruments, timers most probably.

On a Cortex-sized MCU that is most probably as simple as installing appropriate interrupt handler and launching the timer by writing appropriate data to some register(s).

2 comments

I have to support this advice. Poor man's sleep is not really sleep as it is still executing NOPs. The on-chip sleep mechanism puts various parts of the silicon into an off-state. The target of sleep is to reduce current consumption when the hardware is idle.
Sleep is a generic term. In this context of an arduino the sleep term is perfectly applicable. On a bigger processor with pipelining/memorycontroller/tlbs I'd agree with you.
But the example here is to simply delay, not to sleep for power consumption.
It might be fine if it is just for delay, then you run into problem of portability to the system with different instruction cycle times.

Not saying it is bad, but technical debt is high from several angles.

But a delay that uses less power still is preferred. Busy-looping should be avoided whenever possible (I wouldn’t know whether that’s the case here)
The delay is just 10ms and this is a bootloader code that runs only once at bootup. As a bootloader for an MCU, a less error-prone way with less code size is more preferrable.
I can agree that justification to use nop loops somewhere along the lines of "core starts executing code when, judging by real-world testing, hardware peripherals do not guarantee stable state. Thorough testing suggests that 8ms+safety margin delay mitigates issues related to hardware readiness. 2500 cycle nop-loop guarantees required 10ms delay on fastest clock speeds and 50ms delay on slowest core speeds without causing observable issues. 50ms is hereby deemed acceptable." would acceptable in most commercial design documents.
> In all cases

Aren't you overgeneralizing a little? There is nothing wrong with using loops when execution time is known and constant (like an Arduino). It saves the effort of setting up interrupts, and they can be used with interrupts disabled.

They won't be so accurate: time spent in interrupts will not be counted, and loop setup/teardown won't as well, but it's rare to need such precision over short scales, e.g. when delaying a LED blink.

Never use those when execution time is not guaranteed though, like in emulation or binaries executing on different MCUs.

> > In all cases you should try

Yes, in all cases you should try to avoid nop loops, similarly like in high level applications you should try and avoid blocking IO in favor of non-blocking IO. And it does not only mean "avoid nop-loops in code". It also means design your thing in a way where random waits (nop loops are random waits) are unnecessary in the first place. Wait on readiness flag, use timers. Sometimes you have shitty hardware that either does not indicate readiness or indicates readiness falsely, happens. There definitely are situations where nop loops are the only sane available solution.

> using loops when execution time is known and constant

Um... Cortex and larger cores normally can run at multiple clock speeds (e.g. via PLL or clock multiplier config), so this speed is constant only as long as you do not change core clock configuration. Furthermore, if the core in question is even more advanced and employs dynamic PLL, nop-loop execution time is not constant.

> (like an Arduino)

Yes, if you run on small cores like ATmega this can be more or less true.

> It saves the effort of setting up interrupts, and they can be used with interrupts disabled.

Sure and that is one of the main advantages of nop-loops. However, unless you have all "userland" interrupts disabled (all except fault handlers), you run inherently concurrent code which by definition cannot have constant real world run time. To be fair, in the context of a bootloader application flow can be controlled and you can avoid most concurrency issues.

> Never use those when execution time is not guaranteed though

This is the core issue. Execution time is most probably not guaranteed. Nop loops can work during development, hobby project or when you need just a tiny little bit of delay and hardware sync is unavailable.

I agree. Using timer is not as deterministic as precisely-calculated NOP loop.

And why even bother setting timer peripherals if it is to delay for 10ms once in the entire program?