Revert "Cchange random() to use libc srand()/rand()"

This reverts commit db5227872a.

return to using our own random implementation.
This commit is contained in:
Robert May 2015-11-18 15:50:44 +00:00
parent db5227872a
commit 5333729644
2 changed files with 45 additions and 24 deletions

View file

@ -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.

View file

@ -1,5 +1,4 @@
#include "MicroBit.h"
#include <stdlib.h> /* 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);
}