mirror of
https://github.com/esp8266/Arduino.git
synced 2025-07-29 05:21:37 +03:00
* Fix device test environment variables Device tests were not connecting properly to WiFi because the environment variables were not set when WiFi.connect was called. This would result in tests sometimes working *if* the prior sketch run on the ESP saved WiFi connection information and auto-connect was enabled. But, in most cases, the tests would simply never connect to any WiFi and fail. getenv() works only after BS_RUN is called (because BS_RUN handles the actual parsing of environment variables sent from the host). Add a "pretest" function to all tests which is called by the host test controller only after all environment variables are set. Move all WiFi/etc. operations that were in each separate test's setup() into it. So the order of operations for tests now is: ESP: setup() -> Set serial baud -> Call BS_RUN() HOST: Send environment Send "do pretest" ESP: pretest() -> Set Wifi using env. ariables, etc. return "true" on success HOST: Send "run test 1" ESP: Run 1st test, return result HOST: Send "run test 2" ESP: Run 2nd test, return result <and so forth> If nothing is needed to be set up, just return true from the pretest function. All tests now run and at least connect to WiFi. There still seem to be some actual test errors, but not because of the WiFi/environment variables anymore. * Remove unneeded debug prints * Silence esptool.py output when not in V=1 mode Esptool-ck.exe had an option to be silent, but esptool.py doesn't so the output is very chatty and makes looking a the run logs hard (60 lines of esptool.py output, 3 lines of actual test reports). Redirect esptool.py STDOUT to /dev/null unless V=1 to clear this up. * Speed up builds massively by removing old JSON arduino-builder checks the build.options.json file and then goes off and pegs my CPU at 100% for over a minute on each test compile checking if files have been modified. Simply deleting any pre-existing options.json file causes this step to be skipped and a quick, clean recompile is done in siginificantly less time. * Enable compile warnings, fix any that show up Enable all GCC warnings when building the tests and fix any that came up (mostly signed/unsigned, unused, and deprecated ones). * Fix UMM_MALLOC printf crash, umm_test Printf can now handle PROGMEM addresses, so simplify and correct the debug printouts in umm_info and elsewhere.
499 lines
16 KiB
C++
499 lines
16 KiB
C++
//
|
|
// Millis() Runtime Benchmarke
|
|
//
|
|
// Code to determine the runtime in 'usec' of various millis()
|
|
// functions.
|
|
//
|
|
|
|
#include <Arduino.h>
|
|
#include <ESP8266WiFi.h>
|
|
#include <stdio.h>
|
|
#include <BSTest.h>
|
|
|
|
// Include API-Headers
|
|
extern "C" { // SDK functions for Arduino IDE access
|
|
#include "osapi.h"
|
|
#include "user_interface.h"
|
|
} //end, 'extern C'
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Globals
|
|
//---------------------------------------------------------------------------
|
|
|
|
// Here // In 'core_esp8266_wiring.c'
|
|
static os_timer_t us_ovf_timer; // 'micros_overflow_timer'
|
|
static uint32_t us_cnt = 0; // 'm' in 'micros_overflow_tick()'
|
|
static uint32_t us_ovflow = 0; // 'micros_overflow_count'
|
|
static uint32_t us_at_last_ovf = 0; // 'micros_at_last_overflow_tick'
|
|
|
|
static bool fixed_systime = false; // Testing vars
|
|
static bool debugf = false;
|
|
static uint32_t us_systime = 0;
|
|
static float nsf = 0; // Normalization factor
|
|
static uint32_t cntref = 0; // Ref. comparision count
|
|
|
|
//---------------------------------------------------------------------------
|
|
// 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 )
|
|
{
|
|
(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
|
|
#define ONCE 0
|
|
|
|
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
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Wrapper(s) for our benchmark
|
|
//---------------------------------------------------------------------------
|
|
// 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
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Functions to be tested
|
|
//---------------------------------------------------------------------------
|
|
// Print integer list as hex
|
|
void viewhex( uint16_t *p, uint8_t n )
|
|
{
|
|
Serial.print( "0x" );
|
|
for ( uint8_t i = 0; i < n; i++ )
|
|
{
|
|
Serial.printf( "%04X ", p[ (n - 1) - i ] );
|
|
}
|
|
|
|
} //viewhex
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Support routine for 'millis_test_DEBUG()'
|
|
// Print accumulator value along interm summed into it
|
|
void view_accsum ( const char *desc, uint16_t *acc, uint16_t *itrm )
|
|
{
|
|
Serial.print( "acc: " );
|
|
viewhex( acc, 4 );
|
|
Serial.printf( " %s = ", desc );
|
|
viewhex( itrm, 4 );
|
|
Serial.println();
|
|
|
|
} //view_accsum
|
|
|
|
//---------------------------------------------------------------------------
|
|
// FOR BENCHTEST
|
|
// Original millis() function
|
|
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 ( (c * 4294967) + m / 1000 );
|
|
|
|
} //millis_orig
|
|
|
|
//---------------------------------------------------------------------------
|
|
// FOR DEBUG
|
|
// 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);
|
|
|
|
return ( (c * 4294967296 + m) / 1000 );
|
|
|
|
} //millis_corr_DEBUG
|
|
|
|
//---------------------------------------------------------------------------
|
|
// FOR BENCHMARK
|
|
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
|
|
|
|
//---------------------------------------------------------------------------
|
|
// FOR DEBUG
|
|
// millis() 'magic multiplier' approximation
|
|
//
|
|
// This function corrects the cumlative (296us / usec overflow) drift
|
|
// seen in the orignal 'millis()' function.
|
|
//
|
|
// Input:
|
|
// 'm' - 32-bit usec counter, 0 <= m <= 0xFFFFFFFF
|
|
// 'c' - 32-bit usec overflow counter 0 <= c < 0x00400000
|
|
// Output:
|
|
// Returns milliseconds in modulo 0x1,0000,0000 (0 to 0xFFFFFFFF)
|
|
//
|
|
// Notes:
|
|
//
|
|
// 1) This routine approximates the 64-bit integer division,
|
|
//
|
|
// quotient = ( 2^32 c + m ) / 1000,
|
|
//
|
|
// through the use of 'magic' multipliers. A slow division is replaced by
|
|
// a faster multiply using a scaled multiplicative inverse of the divisor:
|
|
//
|
|
// quotient =~ ( 2^32 c + m ) * k, where k = Ceiling[ 2^n / 1000 ]
|
|
//
|
|
// The precision difference between multiplier and divisor sets the
|
|
// upper-bound of the dividend which can be successfully divided.
|
|
//
|
|
// For this application, n = 64, and the divisor (1000) has 10-bits of
|
|
// precision. This sets the dividend upper-bound to (64 - 10) = 54 bits,
|
|
// and that of 'c' to (54 - 32) = 22 bits. This corresponds to a value
|
|
// for 'c' = 0x0040,0000 , or +570 years of usec counter overflows.
|
|
//
|
|
// 2) 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)
|
|
//
|
|
// Graphically, the offset-sums align in little endian like this:
|
|
// LS -> MS
|
|
// 32 64 96 128
|
|
// | a[-1] | a[0] | a[1] | a[2] |
|
|
// | m kl | 0 | 0 | a[-1] not needed
|
|
// | | m kh | |
|
|
// | | c kl | | a[1] holds the result
|
|
// | | | c kh | a[2] can be discarded
|
|
//
|
|
// As only the high-word of 'm kl' and low-word of 'c kh' contribute to the
|
|
// overall result, only (2) 32-bit words are needed for the accumulator.
|
|
//
|
|
// 3) As C++ does not intrinsically test for addition overflows, one must
|
|
// code specifically to detect them. This approximation skips these
|
|
// overflow checks for speed, hence the sum,
|
|
//
|
|
// highword( m kl ) + m kh + c kl < (2^64-1), MUST NOT OVERFLOW.
|
|
//
|
|
// To meet this criteria, not only do we have to pick 'k' to achieve our
|
|
// desired precision, we also have to split 'k' appropriately to avoid
|
|
// any addition overflows.
|
|
//
|
|
// 'k' should be also chosen to align the various products on byte
|
|
// boundaries to avoid any 64-bit shifts before additions, as they incur
|
|
// major time penalties. The 'k' chosen for this specific division by 1000
|
|
// was picked primarily to avoid shifts as well as for precision.
|
|
//
|
|
// For the reasons list above, this routine is NOT a general one.
|
|
// Changing divisors could break the overflow requirement and force
|
|
// picking a 'k' split which requires shifts before additions.
|
|
//
|
|
// ** Test THOROUGHLY after making changes **
|
|
//
|
|
// 4) Results of time benchmarks run on an ESP8266 Huzzah feather are:
|
|
//
|
|
// usec x Orig Comment
|
|
// Orig: 3.18 1.00 Original code
|
|
// Corr: 13.21 4.15 64-bit reference code
|
|
// Test: 4.60 1.45 64-bit magic multiply, 4x32
|
|
//
|
|
// The magic multiplier routine runs ~3x faster than the reference. Execution
|
|
// times can vary considerably with the numbers being multiplied, so one
|
|
// should derate this factor to around 2x, worst case.
|
|
//
|
|
// Reference function: corrected millis(), 64-bit arithmetic,
|
|
// truncated to 32-bits by return
|
|
// unsigned long ICACHE_RAM_ATTR millis_corr_DEBUG( void )
|
|
// {
|
|
// // Get usec system time, usec overflow conter
|
|
// ......
|
|
// return ( (c * 4294967296 + m) / 1000 ); // 64-bit division is SLOW
|
|
// } //millis_corr
|
|
//
|
|
// 5) See this link for a good discussion on magic multipliers:
|
|
// http://ridiculousfish.com/blog/posts/labor-of-division-episode-i.html
|
|
//
|
|
|
|
#define M_USEC_MAX 0xFFFFFFFF
|
|
#define C_COUNT_MAX 0x00400000
|
|
|
|
#define MAGIC_1E3_wLO 0x4bc6a7f0 // LS part
|
|
#define MAGIC_1E3_wHI 0x00418937 // MS part, magic multiplier
|
|
|
|
unsigned long ICACHE_RAM_ATTR millis_test_DEBUG ( void )
|
|
{
|
|
union {
|
|
uint64_t q; // Accumulator, 64-bit, little endian
|
|
uint32_t a[2]; // ..........., 32-bit segments
|
|
} acc;
|
|
acc.a[1] = 0; // Zero high-acc
|
|
|
|
uint64_t prd; // Interm product
|
|
|
|
// 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: Show input vars
|
|
if ( debugf )
|
|
Serial.printf( "Test m: 0x%08X c: 0x%08X\n", m, c );
|
|
|
|
// (a) Init. low-acc with high-word of 1st product. The right-shift
|
|
// falls on a byte boundary, hence is relatively quick.
|
|
|
|
acc.q = ( (prd = (uint64_t)( m * (uint64_t)MAGIC_1E3_wLO )) >> 32 );
|
|
|
|
// DEBUG: Show both accumulator and interm product
|
|
if( debugf )
|
|
view_accsum( "m kl", (uint16_t *)&acc.q, (uint16_t *)&prd );
|
|
|
|
// (b) Offset sum, low-acc
|
|
acc.q += ( prd = ( m * (uint64_t)MAGIC_1E3_wHI ) );
|
|
|
|
// DEBUG: Show both accumulator and interm product
|
|
if( debugf )
|
|
view_accsum( "m kh", (uint16_t *)&acc.q, (uint16_t *)&prd );
|
|
|
|
// (c) Offset sum, low-acc
|
|
acc.q += ( prd = ( c * (uint64_t)MAGIC_1E3_wLO ) );
|
|
|
|
// DEBUG: Show both accumulator and interm product
|
|
if( debugf )
|
|
view_accsum( "c kl", (uint16_t *)&acc.q, (uint16_t *)&prd );
|
|
|
|
// (d) Truncated sum, high-acc
|
|
acc.a[1] += (uint32_t)( prd = ( c * (uint64_t)MAGIC_1E3_wHI ) );
|
|
|
|
// DEBUG: Show both accumulator and interm product
|
|
if( debugf )
|
|
view_accsum( "c kh", (uint16_t *)&acc.q, (uint16_t *)&prd );
|
|
|
|
return ( acc.a[1] ); // Extract result, high-acc
|
|
|
|
} //millis_test_DEBUG
|
|
|
|
//---------------------------------------------------------------------------
|
|
// FOR BENCHTEST
|
|
unsigned long ICACHE_RAM_ATTR millis_test ( void )
|
|
{
|
|
union {
|
|
uint64_t q; // Accumulator, 64-bit, little endian
|
|
uint32_t a[2]; // ..........., 32-bit segments
|
|
} acc;
|
|
acc.a[1] = 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);
|
|
|
|
// (a) Init. low-acc with high-word of 1st product. The right-shift
|
|
// falls on a byte boundary, hence is relatively quick.
|
|
|
|
acc.q = ( (uint64_t)( m * (uint64_t)MAGIC_1E3_wLO ) >> 32 );
|
|
|
|
// (b) Offset sum, low-acc
|
|
acc.q += ( m * (uint64_t)MAGIC_1E3_wHI );
|
|
|
|
// (c) Offset sum, low-acc
|
|
acc.q += ( c * (uint64_t)MAGIC_1E3_wLO );
|
|
|
|
// (d) Truncated sum, high-acc
|
|
acc.a[1] += (uint32_t)( c * (uint64_t)MAGIC_1E3_wHI );
|
|
|
|
return ( acc.a[1] ); // Extract result, high-acc
|
|
|
|
} //millis_test
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Execution time benchmark
|
|
//---------------------------------------------------------------------------
|
|
|
|
// Print benchmark result
|
|
void millis_rtms_print ( const char *pream, uint32_t cntx, const char *desc )
|
|
{
|
|
Serial.print( pream );
|
|
Serial.print( nsf * (float)cntx );
|
|
Serial.print( " " );
|
|
Serial.print( (float)cntx / (float)cntref );
|
|
Serial.print( " " );
|
|
Serial.println( desc );
|
|
|
|
} //millis_rtms_print
|
|
|
|
//---------------------------------------------------------------------------
|
|
void Millis_RunTimes ( void )
|
|
{
|
|
Serial.println();
|
|
Serial.println( "Millis() RunTime Benchmarks" );
|
|
|
|
uint32_t lc = 100000; // Samples
|
|
nsf = 1 / float(lc); // Normalization (global)
|
|
|
|
uint32_t bgn;
|
|
uint32_t cnto = 0, cntv = 0;
|
|
uint32_t cntcx = 0, cntc = 0;
|
|
uint32_t cntfx = 0, cntf = 0;
|
|
|
|
// Setup timer values
|
|
fixed_systime = true;
|
|
us_ovflow = C_COUNT_MAX;
|
|
us_at_last_ovf = (M_USEC_MAX - (20 * 1000000)); // Max. less 20 sec
|
|
|
|
// No printing, systime active
|
|
debugf = false; fixed_systime = false;
|
|
|
|
for (uint32_t i = 0; i < lc; i++ )
|
|
{
|
|
bgn = system_get_time();
|
|
millis_orig();
|
|
cnto += system_get_time() - bgn;
|
|
|
|
bgn = system_get_time();
|
|
millis();
|
|
cntv += 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
|
|
|
|
cntref = cnto; // Set global ref. count
|
|
|
|
Serial.println();
|
|
Serial.println( " usec x Orig Comment" );
|
|
|
|
millis_rtms_print( " Orig: ", cntref, "Original code" );
|
|
millis_rtms_print( " Core: ", cntv, "Current core" );
|
|
Serial.println();
|
|
|
|
millis_rtms_print( " Ref: ", cntcx, "64-bit reference code, DEBUG" );
|
|
millis_rtms_print( " Test: ", cntfx, "64-bit magic multiply, 4x32 DEBUG" );
|
|
Serial.println();
|
|
|
|
millis_rtms_print( " Ref: ", cntc, "64-bit reference code" );
|
|
millis_rtms_print( " Test: ", cntf, "64-bit magic multiply, 4x32" );
|
|
Serial.println();
|
|
|
|
Serial.println( F("*** End, Bench Test ***") );
|
|
Serial.println();
|
|
|
|
} //Millis_RunTimes
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Debug millis_test()
|
|
//---------------------------------------------------------------------------
|
|
|
|
bool Debug_Millis_Test ( void )
|
|
{
|
|
uint32_t m, msc, mstx;
|
|
int32_t diff;
|
|
|
|
// Switch over to fixed system time, enable printing
|
|
fixed_systime = true; debugf = true;
|
|
|
|
us_ovflow = C_COUNT_MAX;
|
|
m = (M_USEC_MAX - (0 * 1000000));
|
|
set_systime( m );
|
|
us_at_last_ovf = m - 1; // Disables 'c' bump
|
|
|
|
// Millis() comparison, test vs. reference
|
|
Serial.println();
|
|
mstx = millis_test_DEBUG();
|
|
msc = millis_corr_DEBUG();
|
|
diff = (int32_t)(mstx - msc);
|
|
|
|
Serial.println();
|
|
Serial.println( " m Test Reference Difference" );
|
|
Serial.printf( "0x%08x 0x%08x 0x%08X %9d\n", m, mstx, msc, diff );
|
|
Serial.println();
|
|
|
|
// No printing, variable systime
|
|
debugf = false; fixed_systime = false;
|
|
|
|
return( (bool)( diff == 0 ) ); // Good test, matches reference
|
|
|
|
} //Debug_Millis_Test
|
|
|
|
//---------------------------------------------------------------------------
|
|
//---------------------------------------------------------------------------
|
|
|
|
BS_ENV_DECLARE();
|
|
|
|
void setup ()
|
|
{
|
|
Serial.begin(115200);
|
|
WiFi.mode( WIFI_OFF );
|
|
BS_RUN(Serial);
|
|
} //setup
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
bool pretest ()
|
|
{
|
|
us_count_init(); // Start up timer overflow sampling
|
|
return true;
|
|
} //pretest
|
|
|
|
//---------------------------------------------------------------------------
|
|
void loop(void)
|
|
{
|
|
yield();
|
|
} //loop
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Test cases
|
|
//---------------------------------------------------------------------------
|
|
TEST_CASE( "Millis RunTime Benchmarks", "[bs]" )
|
|
{
|
|
Millis_RunTimes();
|
|
} //testcase1
|
|
|
|
TEST_CASE( "Debug 'millis_test()' Code", "[bs]" )
|
|
{
|
|
bool ok = Debug_Millis_Test();
|
|
CHECK( ok );
|
|
} //testcase2
|
|
|
|
//---------------------------------------------------------------------------
|
|
//---------------------------------------------------------------------------
|