tinygo.org/x/drivers@v0.27.1-0.20240509133757-7dbca2a54349/delay/sleep.c (about) 1 #include <stdint.h> 2 #include <stdbool.h> 3 4 // Loop the given times, where one loop takes four CPU cycles. 5 bool tinygo_drivers_sleep(uint32_t cycles) { 6 // In this function, a [n] comment indicates the number of cycles an 7 // instruction or a set of instructions take. This is typically 1 for most 8 // arithmetic instructions, and a bit more for branches. 9 #if __ARM_ARCH_6M__ || __ARM_ARCH_7M__ || __ARM_ARCH_7EM__ 10 // Inline assembly for Cortex-M0/M0+/M3/M4/M7. 11 // The Cortex-M0 (but not M0+) takes one more cycle, so is off by 12.5%. 12 // Others should be basically cycle-accurate (with a slight overhead to 13 // calculate the number of cycles). Unfortunately, there doesn't appear to 14 // be a preprocessor macro to detect the Cortex-M0 specifically (although we 15 // could rely on macros like NRF51). 16 17 // Each loop takes 8 cycles (5 nops, 1 sub, and 2 for the branch). 18 uint32_t loops = (cycles + 7) / 8; 19 __asm__ __volatile__( 20 "1:\n\t" 21 "nop\n\t" // [5] nops 22 "nop\n\t" 23 "nop\n\t" 24 "nop\n\t" 25 "nop\n\t" 26 "subs %[loops], #1\n\t" // [1] 27 "bne 1b" // [1-4], at least 2 cycles if taken 28 : [loops]"+r"(loops) 29 ); 30 return true; 31 #elif __XTENSA__ 32 // Inline assembly for Xtensa. 33 // I don't know exactly how many cycles a branch takes, so I've taken a 34 // conservative guess and assume it takes only one cycle. In practice, it's 35 // probably more than that. 36 uint32_t loops = (cycles + 7) / 8; 37 __asm__ __volatile__( 38 "1:\n\t" 39 "nop\n\t" // [6] nops 40 "nop\n\t" 41 "nop\n\t" 42 "nop\n\t" 43 "nop\n\t" 44 "nop\n\t" 45 "addi %[loops], %[loops], -1\n\t" // [1] 46 "bnez %[loops], 1b" // [1?] 47 : [loops]"+r"(loops) 48 ); 49 return true; 50 #else 51 // Unknown architecture, so fall back to time.Sleep. 52 return false; 53 #endif 54 }