From 53337296441f7b962e6a424996f2deb3f95e51c9 Mon Sep 17 00:00:00 2001 From: Robert May Date: Wed, 18 Nov 2015 15:50:44 +0000 Subject: [PATCH] Revert "Cchange random() to use libc srand()/rand()" This reverts commit db5227872affd9cca252a1b39551d239212b9680. return to using our own random implementation. --- inc/MicroBit.h | 6 ++--- source/MicroBit.cpp | 63 ++++++++++++++++++++++++++++++--------------- 2 files changed, 45 insertions(+), 24 deletions(-) diff --git a/inc/MicroBit.h b/inc/MicroBit.h index 9056492..417b300 100644 --- a/inc/MicroBit.h +++ b/inc/MicroBit.h @@ -90,6 +90,7 @@ class MicroBit private: void seedRandom(); + uint32_t randomValue; public: @@ -213,9 +214,8 @@ class MicroBit /** * Generate a random number in the given range. - * We use libc's rand() which is sufficient for our applications and much - * more lightweight than the hardware random number generator built into - * the processor, which takes a long time and uses a lot of energy. + * We use the NRF51822 in built random number generator here + * TODO: Determine if we want to, given its relatively high power consumption! * * @param max the upper range to generate a number for. This number cannot be negative * @return A random, natural number between 0 and the max-1. Or MICROBIT_INVALID_PARAMETER if max is <= 0. diff --git a/source/MicroBit.cpp b/source/MicroBit.cpp index cedcefa..0e74061 100644 --- a/source/MicroBit.cpp +++ b/source/MicroBit.cpp @@ -1,5 +1,4 @@ #include "MicroBit.h" -#include /* rand(),srand() */ char MICROBIT_BLE_DEVICE_NAME[] = "BBC micro:bit [xxxxx]"; @@ -284,11 +283,13 @@ int MicroBit::sleep(int milliseconds) /** * Generate a random number in the given range. - * We use libc's rand() which is sufficient for our applications and much - * more lightweight than the hardware random number generator built into - * the processor, which takes a long time and uses a lot of energy. + * We use a simple Galois LFSR random number generator here, + * as a Galois LFSR is sufficient for our applications, and much more lightweight + * than the hardware random number generator built int the processor, which takes + * a long time and uses a lot of energy. * - * KIDS: You shouldn't use this is the real world to generate cryptographic keys though... + * KIDS: You shouldn't use this is the real world to generte cryptographic keys though... + * have a think why not. :-) * * @param max the upper range to generate a number for. This number cannot be negative * @return A random, natural number between 0 and the max-1. Or MICROBIT_INVALID_VALUE (defined in ErrorNo.h) if max is <= 0. @@ -300,38 +301,60 @@ int MicroBit::sleep(int milliseconds) */ int MicroBit::random(int max) { - unsigned long num_bins, num_rand, bin_size, defect; - int result; + uint32_t m, result; //return MICROBIT_INVALID_VALUE if max is <= 0... if(max <= 0) return MICROBIT_INVALID_PARAMETER; - num_bins = (unsigned long) max; - num_rand = (unsigned long) RAND_MAX + 1; - bin_size = num_rand / num_bins; - defect = num_rand % num_bins; + // Our maximum return value is actually one less than passed + max--; do { - result = rand(); - } while ((num_rand - defect) <= (unsigned long)result); // CARE: avoid overflow + m = (uint32_t)max; + result = 0; + while(m >>= 1) { + // Cycle the LFSR (Linear Feedback Shift Register). + // We use an optimal sequence with a period of 2^32-1, as defined by Bruce Schneider here (a true legend in the field!), + // For those interested, it's documented in his paper: + // "Pseudo-Random Sequence Generator for 32-Bit CPUs: A fast, machine-independent generator for 32-bit Microprocessors" + // https://www.schneier.com/paper-pseudorandom-sequence.html + // Avoid interupts (and hence fibre context switch) while we are doing this + + __disable_irq(); + + randomValue = ((((randomValue >> 31) + ^ (randomValue >> 6) + ^ (randomValue >> 4) + ^ (randomValue >> 2) + ^ (randomValue >> 1) + ^ randomValue) + & 0x0000001) + << 31 ) + | (randomValue >> 1); + + __enable_irq(); + + result = ((result << 1) | (randomValue & 0x00000001)); + } + } while (result > (uint32_t)max); - return result/bin_size; + return result; } /** * Seed our a random number generator (RNG). - * We use the NRF51822 in built cryptographic random number generator to seed rand(). + * We use the NRF51822 in built cryptographic random number generator to seed a Galois LFSR. * We do this as the hardware RNG is relatively high power, and use the the BLE stack internally, - * with a less than optimal application interface. rand() is sufficient for our + * with a less than optimal application interface. A Galois LFSR is sufficient for our * applications, and much more lightweight. */ void MicroBit::seedRandom() { - unsigned int seed = 0; - + randomValue = 0; + // Start the Random number generator. No need to leave it running... I hope. :-) NRF_RNG->TASKS_START = 1; @@ -343,13 +366,11 @@ void MicroBit::seedRandom() // Wait for a number ot be generated. while ( NRF_RNG->EVENTS_VALRDY == 0); - seed = (seed << 8) | ((int) NRF_RNG->VALUE); + randomValue = (randomValue << 8) | ((int) NRF_RNG->VALUE); } // Disable the generator to save power. NRF_RNG->TASKS_STOP = 1; - - srand(seed); }