// // Millis() Runtime Benchmarke // Code to determine the runtime in 'usec' of various millis() // function candidates. // #include #include #include // Include API-Headers extern "C" { // SDK functions for Arduino IDE access #include "osapi.h" #include "user_interface.h" #include "espconn.h" } //end, 'extern C' //--------------------------------------------------------------------------- // Globals const int LedBlu = 2, LedRed = 0, LedOff = 1, LedOn = 0; // For 64-bit usec ring counter static os_timer_t us_ovf_timer; static uint32_t us_cnt = 0; static uint32_t us_ovflow = 0; static uint32_t us_at_last_ovf = 0; static bool fixed_systime = false; // Testing vars static bool debugf = false; static uint32_t us_systime = 0; //--------------------------------------------------------------------------- // Set a fixed value of usec system time void set_systime ( uint32_t usec ) { us_systime = usec; } //set_systime // Wrapper to return a fixed system time uint32_t system_get_timeA ( void ) { return ( fixed_systime ? us_systime : system_get_time() ); } //system_get_timeA //--------------------------------------------------------------------------- // Interrupt code lifted directly from "cores/core_esp8266_wiring.c", // with some variable renaming //--------------------------------------------------------------------------- // Callback for usec counter overflow timer interrupt void us_overflow_tick ( void* arg ) { us_cnt = system_get_time(); // Check for usec counter overflow if ( us_cnt < us_at_last_ovf ) { ++us_ovflow; } //end-if us_at_last_ovf = us_cnt; } //us_overflow_tick //--------------------------------------------------------------------------- #define REPEAT 1 void us_count_init ( void ) { os_timer_setfn( &us_ovf_timer, (os_timer_func_t*)&us_overflow_tick, 0 ); os_timer_arm( &us_ovf_timer, 60000, REPEAT ); } //us_count_init //--------------------------------------------------------------------------- //--------------------------------------------------------------------------- // Print integer list as hex void viewhex( char *s, uint16_t *p, uint8_t n ) { Serial.printf( "% 6s: 0x", s ); for ( uint8_t i = 0; i < n; i++ ) { Serial.printf( "%04X ", p[ (n - 1) - i ] ); } Serial.println(); } //viewhex //--------------------------------------------------------------------------- // Original millis() function (1.0 x Orig) unsigned long ICACHE_RAM_ATTR millis_orig ( void ) { // Get usec system time, usec overflow conter uint32_t m = system_get_time(); uint32_t c = us_ovflow + ((m < us_at_last_ovf) ? 1 : 0); return ( (uint32_t)(c) * 4294967 + m / 1000 ); } //millis_orig //--------------------------------------------------------------------------- // Corrected millis(), 64-bit arithmetic gold standard // truncated to 32-bits by return unsigned long ICACHE_RAM_ATTR millis_corr_DEBUG( void ) { // Get usec system time, usec overflow conter uint32_t m = system_get_timeA(); // DEBUG uint32_t c = us_ovflow + ((m < us_at_last_ovf) ? 1 : 0); // DEBUG if ( debugf ) Serial.printf( "Corr m: 0x%08X c:0x%04X\n", m, c ); return ( ( (uint32_t)(c) * 4294967296 + m) / 1000 ); } //millis_corr //--------------------------------------------------------------------------- // Corrected millis(), 64-bit arithmetic gold standard // truncated to 32-bits by return unsigned long ICACHE_RAM_ATTR millis_corr( void ) { // Get usec system time, usec overflow conter uint32_t m = system_get_time(); uint32_t c = us_ovflow + ((m < us_at_last_ovf) ? 1 : 0); return ( (c * 4294967296 + m) / 1000 ); } //millis_corr //--------------------------------------------------------------------------- // millis() 'magic multiplier' approximation // Input: // 'm' - 32-bit usec counter, 0 <= m <= 0xFFFFFFFF // 'c' - 16-bit usec overflow counter 0 <= c < 0x00400000 // Output: // Returns milliseconds in modulo 0x10000000 ( 0 to 0xFFFFFFFF) // // A 'magic multiplier', equal to '(2^n) / const', is used to // approximate fixed-point division by a constant. For this // application, k = Ceiling[ 2^64 / 1000 ] (max. bits we can use) // // A distributed multiply with offset-summing is used find k( 2^32 c + m): // prd = (2^32 kh + kl) * ( 2^32 c + m ) // = 2^64 kh c + 2^32 kl c + 2^32 kh m + kl m // (d) (c) (b) (a) // // Important: ** Don't mess with (uint64_t) type castings ** #define MAGIC_1E3_wLO 0x4bc6a7f0 // LS part #define MAGIC_1E3_wHI 0x00418937 // MS part unsigned long ICACHE_RAM_ATTR millis_test_DEBUG ( void ) { uint32_t a[3]; // 96-bit accumulator, little endian a[2] = 0; // Zero high-acc // Get usec system time, usec overflow counter uint32_t m = system_get_timeA(); uint32_t c = us_ovflow + ((m < us_at_last_ovf) ? 1 : 0); // DEBUG if ( debugf ) Serial.printf( "Test m: 0x%08X c:0x%04X\n", m, c ); // (a) Init. low acc ((uint64_t *)(&a[0]))[0] = ( m * (uint64_t)MAGIC_1E3_wLO ); // DEBUG if( debugf ) viewhex( "m kl ", (uint16_t *)&a[0], 6 ); // (b) Offset sum, mid-acc ((uint64_t *)(&a[1]))[0] += ( m * (uint64_t)MAGIC_1E3_wHI ); // DEBUG if( debugf ) viewhex( "m kh ", (uint16_t *)&a[0], 6 ); // (c) Offset sum, mid-acc ((uint64_t *)(&a[1]))[0] += ( c * (uint64_t)MAGIC_1E3_wLO ); // DEBUG if( debugf ) viewhex( "c kl ", (uint16_t *)&a[0], 6 ); // (d) Truncated sum, high-acc ((uint32_t *)(&a[2]))[0] += (uint32_t)( c * (uint64_t)MAGIC_1E3_wHI ); // DEBUG if( debugf ) viewhex( "c kh ", (uint16_t *)&a[0], 6 ); return ( a[2] ); // Extract result, high-acc } //millis_test_DEBUG //--------------------------------------------------------------------------- unsigned long ICACHE_RAM_ATTR millis_test ( void ) { uint32_t a[3]; // 96-bit accumulator, little endian a[2] = 0; // Zero high-acc // Get usec system time, usec overflow counter uint32_t m = system_get_time(); uint32_t c = us_ovflow + ((m < us_at_last_ovf) ? 1 : 0); ((uint64_t *)(&a[0]))[0] = // (a) Init. low acc ( m * (uint64_t)MAGIC_1E3_wLO ); ((uint64_t *)(&a[1]))[0] += // (b) Offset sum, mid-acc ( m * (uint64_t)MAGIC_1E3_wHI ); ((uint64_t *)(&a[1]))[0] += // (c) Offset sum, mid-acc ( c * (uint64_t)MAGIC_1E3_wLO ); ((uint32_t *)(&a[2]))[0] += // (d) Truncated sum, high-acc (uint32_t)( c * (uint64_t)MAGIC_1E3_wHI ); return ( a[2] ); // Extract result, high-acc } //millis_test //--------------------------------------------------------------------------- //--------------------------------------------------------------------------- void Millis_RunTimes ( void ) { Serial.println(); Serial.println( "Millis RunTime Benchmark" ); uint32_t lc = 100000; // Samples float nsf = 1 / float(lc); // Normalization uint32_t bgn, cnto = 0, cntcx = 0, cntc = 0, cntfx = 0, cntf = 0; us_at_last_ovf = 0xFFFF0000; // Slightly less than max us_ovflow = 0x40000; for (uint32_t i = 0; i < lc; i++ ) { bgn = system_get_time(); millis_orig(); cnto += system_get_time() - bgn; bgn = system_get_time(); millis_corr_DEBUG(); cntcx += system_get_time() - bgn; bgn = system_get_time(); millis_corr(); cntc += system_get_time() - bgn; bgn = system_get_time(); millis_test_DEBUG(); cntfx += system_get_time() - bgn; bgn = system_get_time(); millis_test(); cntf += system_get_time() - bgn; yield(); } //end-for Serial.println(); Serial.println( " usec x Orig Comment" ); Serial.print( " Orig: " ); Serial.print( nsf * (float)cnto ); Serial.print( " " ); Serial.print( 1.00 ); Serial.println( " Original code" ); Serial.println(); Serial.print( " Corr: " ); Serial.print( nsf * (float)cntcx ); Serial.print( " " ); Serial.print( (float)cntcx / (float)cnto ); Serial.println( " 64-bit reference code, DEBUG" ); Serial.print( " Test: " ); Serial.print( nsf * (float)cntfx ); Serial.print( " " ); Serial.print( (float)cntfx / (float)cnto ); Serial.println( " 64-bit magic multiply, 4x32 DEBUG" ); Serial.println(); Serial.print( " Corr: " ); Serial.print( nsf * (float)cntc ); Serial.print( " " ); Serial.print( (float)cntc / (float)cnto ); Serial.println( " 64-bit reference code" ); Serial.print( " Test: " ); Serial.print( nsf * (float)cntf ); Serial.print( " " ); Serial.print( (float)cntf / (float)cnto ); Serial.println( " 64-bit magic multiply, 4x32" ); Serial.println(); Serial.println( F("*** End, Bench Test ***") ); } //Millis_RunTimes //--------------------------------------------------------------------------- //--------------------------------------------------------------------------- void setup () { pinMode(LedRed, OUTPUT); // Turn off LEDs pinMode(LedBlu, OUTPUT); // ... digitalWrite(LedRed, LedOff); // ... digitalWrite(LedBlu, LedOff); // ... Serial.begin(115200); while (!Serial) delay(250); // Wait until Arduino Serial Monitor opens Serial.println( " " ); Serial.println(F("Millis() Drift Correction / Runtime Estimates")); WiFi.mode( WIFI_OFF ); // DEBUG millis() test code uint32_t ix, msc, mstx; fixed_systime = true; us_ovflow = 0x400000; ix = 0xffffffff - (2 * 1000000); set_systime( ix ); us_at_last_ovf = ix - 1; fixed_systime = true; debugf = true; msc = millis_corr_DEBUG(); mstx = millis_test_DEBUG(); Serial.printf( "0x%08x 0x%08x 0x%08X %5d\n", ix, mstx, msc, (int32_t)(mstx - msc) ); debugf = false; fixed_systime = false; us_count_init(); Millis_RunTimes(); // Runtime benchmarks } //setup //--------------------------------------------------------------------------- void loop(void) { yield(); } //loop //--------------------------------------------------------------------------- //---------------------------------------------------------------------------