RNG using MCU internal temp. sensor - behaviour of ATmega328P vs. ATmega32M1 - Page 1
Forum section: electronics / general

author: HwAoRrDk
author_rank: Super Contributor
timestamp: October 23, 2019, 03:27:40 pm
content: On an existing project of mine, a need came about to generate random values. However, as the hardware is already 'done', I can't really add anything specific to do the job (e.g. zener noise source), so I have to find a solution that makes use of what I've got.An approach that I have been experimenting with is using the microcontroller's internal temperature sensor in combination with theVon Neumann extractortechnique. I configure the ADC to read the channel for the internal temperature sensor, then take repeated readings (with a short delay in-between - 250 us) and extract the least-significant bit of the ADC value. For each pair of consecutive bits, when they differ (i.e. 0,1 or 1,0) I shift the first on to the output value. When enough bits have been gathered, the output value is returned.I first experimented with this on an Arduino Uno board that features an ATmega328P. It worked brilliantly, and the random output appeared to be sufficiently unbiased and uniform for my needs. I even did an experiment where I had it constantly extracting 8-bit random values, and it was able to continually do so at quite a fast rate. There were occasional slow-downs where it would drop to maybe 1-2 values per second, but warming the chip by pressing a finger on it for a couple of seconds, or cooling by blowing on it, had the extractor immediately whizzing away again.So, I proceeded to adapt this approach to my project's actual hardware, which uses an ATmega32M1. It initially appeared to work fine. I had it simply extracting a single 16-bit value upon start-up to use as an RNG seed. However, I soon hit upon a problem: triggering a reset of the MCU resulted in the start-up code just hanging, with it getting stuck not extracting enough (or any) bits. Letting it go for several minutes didn't result in anything, and even doing the same tricks of pressing a finger on or blowing at the chip had zero apparent effect.So what gives? Why does this work so well on an Arduino board, and not on my own hardware? Why does the ATmega32M1 produce results at power-on, but not on reset? And how can it be totally unaffected by warming or cooling?One thing that comes to mind is that perhaps the ADC reference voltage on the Arduino board has more noise than my hardware, so making the LSb of the temperature reading fluctuate more often. The Arduino board was powered via USB from my PC, and my ATmega32M1 board from a bench PSU. But then that doesn't explain the apparent immunity to thermal change on the '32M1.

author: magic
author_rank: Super Contributor
timestamp: October 23, 2019, 04:32:37 pm
content: Quote from: HwAoRrDk on October 23, 2019, 03:27:40 pmAre you absolutely sure the RNG loop really is running when the chip is locked up?

author: HwAoRrDk
author_rank: Super Contributor
timestamp: October 23, 2019, 05:22:31 pm
content: Quote from: magic on October 23, 2019, 04:32:37 pmYes, absolutely. As I said, exactly the same code is able to gather enough entropy on a cold power-up, but not on a reset. And I had also put in some debugging code to toggle an I/O pin every time through the loop, which I could observe on a logic analyser.Edit: Oh, and I almost forgot: when I additionally put in a maximum loop iterations limit, it would hit that and continue to start up normally, albeit having gathered no entropy.

author: mikerj
author_rank: Super Contributor
timestamp: October 23, 2019, 05:32:00 pm
content: Check for any uninitialised variables, this is exactly the kind of issue they can cause.

author: HwAoRrDk
author_rank: Super Contributor
timestamp: October 23, 2019, 06:25:08 pm
content: Here's the code:Code:void rand_init() {uint8_t bit_1, bit_2, seed_bits = 0;uint16_t seed = 0, iterations = 65535;ADMUX = _BV(REFS1) | _BV(REFS0) | _BV(ADLAR) | _BV(MUX3) | _BV(MUX1) | _BV(MUX0);ADCSRA = _BV(ADEN) | _BV(ADPS2) | _BV(ADPS0);ADCSRB = _BV(ADHSM);do {_delay_us(250);ADCSRA |= _BV(ADSC);loop_until_bit_is_clear(ADCSRA, ADSC);bit_1 = ADCH & 1;_delay_us(250);ADCSRA |= _BV(ADSC);loop_until_bit_is_clear(ADCSRA, ADSC);bit_2 = ADCH & 1;if(bit_1 != bit_2) {seed = (seed << 1) | bit_1;seed_bits++;}} while(seed_bits < (sizeof(seed) * 8) && --iterations > 0);srand(seed);}It's basically a copy-and-paste from the code I was using on the Arduino board, with the exception of changing the some of the initialisation values of the ADC due to chip differences (mux channel for temp sensor, etc).

author: magic
author_rank: Super Contributor
timestamp: October 23, 2019, 06:54:09 pm
content: Disable ADLAR and read ADCL&1 followed by a dummy read from ADCH. Now you are reading bit 2 of the result instead of bit 0.Also, double-check if all MUX and REF bits are correct for this MCU model. Maybe you are reading some I/O pin by accident or using reference voltage out of range.

author: iMo
author_rank: Super Contributor
timestamp: October 23, 2019, 07:47:38 pm
content: With ADLAR set the ADCH contains the 8 most significant bits of the ADC result (as "magic" wrote above). Handy when you want read the ADC in 8bit mode.Btw the temperature sensor does (DS) typically 312mV diff for 125C temp diff. With ambient diff=5C and the 8bit mode and 2.56V reference the ADC diff would be something like 1.2LSB..PS: provided your ambient is 26C and you "press your finger" against the package (the finger temp with a live person is around 31C) the ADCH value change will be 1, imho..

author: capt bullshot
author_rank: Super Contributor
timestamp: October 23, 2019, 08:52:40 pm
content: I'd set up some debug code to output the raw ADC values e.g. through an UART. Looking at these data can easily reveal to root cause or give at least some hints.

author: HwAoRrDk
author_rank: Super Contributor
timestamp: October 24, 2019, 09:57:43 am
content: Quote from: magic on October 23, 2019, 06:54:09 pmAaagh, stupid me!Yes, ADLAR doesn't work how I thought it did, and I was reading bit 2 of the ADC result. I was thinking with ADLAR it was discarding the two MSb, not just swapping the H/L registers. Thank you for spotting my stupid mistake.I changed the code to disable ADLAR and read both ADCL & ADCH, and it's working just fine now on the ATmega32M1.I wonder why on the Arduino board bit 2 of the ADC value fluctuates so much more frequently (or rather, at all) than on my board given the same environmental conditions.

author: iMo
author_rank: Super Contributor
timestamp: October 24, 2019, 11:04:20 am
content: Quote from: HwAoRrDk on October 24, 2019, 09:57:43 amBecause the Vref of the 32M1 is 2.56V and the Vref of the 328p is 1.1V.

author: HwAoRrDk
author_rank: Super Contributor
timestamp: October 24, 2019, 12:06:34 pm
content: Quote from: imo on October 24, 2019, 11:04:20 amI don't see how that affects anything, because the temperature sensors of both have output that is proportional to their reference voltage. The datasheets say the '328P is 1 mV/°C and the '32M1 is 2.5 mV/°C. So unless I've worked things out wrong, they both have approx. 1°C per LSb.

author: capt bullshot
author_rank: Super Contributor
timestamp: October 24, 2019, 12:11:59 pm
content: Quote from: HwAoRrDk on October 24, 2019, 12:06:34 pmNoise amplitude stays roughly constant, so more noise affects your result with lower Vref. Additionally, external layout and supply plus internally coupled digital noise can have large influence on the noise seen on the ADC output. Temperature fluctuation alone wouldn't give you this fast output of randomness, so you need the electrical noise here, and real "white" noise is better than coupled / EMI noise since this can be coherent to e.g. the external power supply and spoil the randomness.

author: HwAoRrDk
author_rank: Super Contributor
timestamp: October 24, 2019, 12:48:06 pm
content: Ah, I see what you mean. With a smaller Vref, the noise will be larger relative to the Vref.Maybe my initial thought about differences in how the boards are powered (USB for Arduino, bench PSU for my board) is indeed a factor. IIRC, the Arduino Uno uses VBUS directly as VCC, and maybe my PC's USB power is 'dirty'. But on the other hand, on my board, as I wasn't expecting to use the ADC, the analog supply filtering/decoupling is fairly sub-par - only a single 0.1uF cap on AVCC and no inductor (unlike the Arduino), and no cap at all on AREF.Maybe there's some difference in noise susceptibility of DIP vs. LQFP and/or their pin arrangements.

author: iMo
author_rank: Super Contributor
timestamp: October 24, 2019, 01:01:33 pm
content: Quote from: HwAoRrDk on October 24, 2019, 12:06:34 pmMind that calc is for 10bit ADC output. With 8bit as you had before it was aprox 4C/LSB.Unless you design the pcb and the stuff around with care the noise with ADC input itself could be 2-3LSBits p-p easily.The temperature of the die changes with what the CPU is doing. With cold start the rate of change is large (the chip was "cold"), with reset the temp change is much smaller or none (the chip is "hot" already).

author: SiliconWizard
author_rank: Super Contributor
timestamp: October 24, 2019, 01:45:29 pm
content: Yeah.I find this approach creative, but I'm not sure I would rely on it for a product. It's based on assumptions you may not be able to prove correct in all use cases.If the device fails to start in some particular condition, what are you going to tell your customers? "Oh wait, put the thing in your fridge for a while, then get it out. It should start. If it's still reluctant, blow some hot air with your hair dryer on it. We appreciate your business."

author: iMo
author_rank: Super Contributor
timestamp: October 24, 2019, 02:02:49 pm
content: The 32M1 includes 3 "ADC preamplifiers" with 5-40x amplification. An external zener with a transistor + 40x ADC amplification may generate some noise for your random generator, imho.For example a 2.7V zener in the base of an NPN (beta 400) could generate, say, 5mVpp noise. Amplified 40x you get 200mVpp noise.I would set the R1 such the DC at collector is 2.5V. You may also experiment with capacitor's values, the larger the values the more low freq fluctuations you get handy..

author: magic
author_rank: Super Contributor
timestamp: October 24, 2019, 02:31:04 pm
content: I think temperature is a red herring here, it doesn't change often enough to be useful. The real noise source is the ADC itself and the thermal sensor only serves to bias its input to some semi-constant voltage which is guaranteed not to saturate the ADC. Equally well it could measure ground, but then there is risk the reading would be solid zero due to offset voltage.

author: cv007
author_rank: Frequent Contributor
timestamp: October 24, 2019, 02:49:50 pm
content: You can use ram as a seed.https://github.com/cv007/ATTiny416XplainedNano/blob/master/Util.hppIts not going to win a /random/ contest, but its probably good enough for most simple uses when wanting to at least get some starting point that is not always the same.I don't know how this holds up among mcu's (I can't imagine why not- they all follow the same laws), but testing with various avr's it seems the ram bits are pre-disposed to a certain state on power up, but there are still a number of them that can't decide what they want to be. Even though it looks like a lot of ram bits like to be in a certain state on power up, there are still plenty around that can't decide. Even when you do a non-power reset, the stack has already changed quite a bit and you still get a 'random' seed.I also played around with the adc techniques as described, but it ends up with more code and you can waste a lot of time waiting it out in some cases.

author: iMo
author_rank: Super Contributor
timestamp: October 24, 2019, 03:00:04 pm
content: The C compilers usually clean the entire ram, afaik. Thus xoring through its content at certain stage will always give you the same number, I bet (unless you tell linker to reserve some part of it).

author: SiliconWizard
author_rank: Super Contributor
timestamp: October 24, 2019, 03:25:24 pm
content: Quote from: imo on October 24, 2019, 03:00:04 pmNope, just the .bss section, normally. Not the entire RAM.Problem is, all the non-initialized global variables will be set to 0, and all the initiliazed ones to their initial value... so the only part of RAM that is untouched upon startup is that which is NOT statically allocated from your code. Typically, the heap and the stack.A reasonable (but not fail-safe) assumption would be that dynamic allocation on the heap will never get zero-ed out (with malloc()), unless you use calloc().So you could just malloc() a RAM block and assume its whole content will be pretty much random. Problem with that is that it's probably going to be "random" after a "cold" start, but you have no guarantee of that upon a "warm" start (no power cycling), unless you are certain this RAM area will never get allocated for subsequent allocations (you'd have to never free() the block - a bit wasteful.) Reserving it through a linker option (linker script) would also work, but it would do the same: make this RAM block unusable for any other purpose...And still, the "randomness" of SRAM content upon power-on is probably debatable.

author: RoGeorge
author_rank: Super Contributor
timestamp: October 24, 2019, 03:29:19 pm
content: Since the bits are de-biased with Von Newmann's algorithm, that will mathematicallyguarantee50/50 distribution of 0's and 1's. I don't see how to get better than this.ADC's last bit fluctuations here comes from thermal noises, so it's true randomness, all OK.Throughput can be increased in many ways (e.g. conecting a bipolar transistor BE reverse polarized junction noise at each ADC input, or using other bits too from each conversion, not only the LSB, or memorize and reuse the dropped identical pairs of bits as simple 0/1 bits in the de-biasing algorithm again, etc.), but since speed is not required, why bother?All it needs to be safe as a true RNG is to mark when a new stream of 16 bits is ready, so other function won't use by mistake the same 16 bits over and over.

author: iMo
author_rank: Super Contributor
timestamp: October 24, 2019, 03:44:00 pm
content: Quote from: RoGeorge on October 24, 2019, 03:29:19 pmThat would be true if you take a resistor and amplify its 4kTRdF..

author: coppice
author_rank: Super Contributor
timestamp: October 24, 2019, 03:59:59 pm
content: Quote from: RoGeorge on October 24, 2019, 03:29:19 pmA 50:50 balance is just one of many tests you would need to pass to be able to describe a sequence as reasonably random. Try applying the FIPS tests. If you pass those you might still not have a very good level of randomness, but its not a bad start.Quote from: RoGeorge on October 24, 2019, 03:29:19 pmThe LSB of an ADC may not change much at all, if things are pretty quiet. If there is enough thermal noise you may have a fairly good level of randomness in the LSB, but you will probably have some structured digital noise mixed with it. Digital noise will often swamp the thermal noise, especially if the board is poorly laid out and the grounding and decoupling are not great. Anything with some entropy can be used to derive a good random sequence, so a mixture of thermal noise and structured digital noise is usable, but it takes some work to produce a sequence depending only on the entropy, rejecting anything pattern like.

author: cv007
author_rank: Frequent Contributor
timestamp: October 24, 2019, 04:01:46 pm
content: >And still, the "randomness" of SRAM content upon power-on is probably debatable.Try it out, its easy enough to test- just dump the ram on power up. Over and over. Its either good enough or a bad idea. I think it works pretty good for a random-enough seed. Even when just continually getting a new seed (not using the lfsr or other method you seeded) its hard to pick out any patterns unless the mcu is not doing anything. I just use the top 64 bytes on the stack. I don't think we are talking about gaming industry or cryptography random here.Pick the top of stack and use some number of bytes below it. On power up, there better not be any initialized data close to the stack so you get whatever the ram bits are at when the mcu starts up (out of 64 bytes, there seems to be at least a couple dozen random bits). When you need a new seed on the next reset, the stack has already been used billions and billions of time (or less) and is probably pretty random for anything that doesn't require a TRNG.The biggest benefit is probably the fixed time it takes to run through the chosen set of ram. You will get some seed in x amount of time. No need to hope the adc doesn't decide to get all stable on you, and no external parts needed to coax the adc into a little activity to make sure.

author: coppice
author_rank: Super Contributor
timestamp: October 24, 2019, 04:06:20 pm
content: Quote from: SiliconWizard on October 24, 2019, 03:25:24 pmDebatable? Most SRAMs power up to a highly repeatable pattern.

author: iMo
author_rank: Super Contributor
timestamp: October 24, 2019, 04:57:16 pm
content: He/she may try to create a temperature gradient by heating up the chip shortly, and then generate the bits during the cool down.Like (for example an 300ohm resistor against Vcc on pinX):1. cold/reset2. in a loop make 100000x3. set pinX low for 1us (or something like that)4. set pinX high for 1us (or something like that)5. end loop6. make random bitsPS: you may try with capacitor, instead of the resistor. The heat generates mostly during 0/1 and 1/0 transitions.

author: cv007
author_rank: Frequent Contributor
timestamp: October 24, 2019, 05:21:49 pm
content: >Debatable? Most SRAMs power up to a highly repeatable pattern.Yes, but not exactly the same every time. There are enough bits that are not repeatable, and you just have to decide how many bytes to look at to get what you need (I just xor through the chosen set of bytes on the top of stack). Since power up happens relatively infrequently, it would take quite a while to discern any pattern or even figure out you had less random than the 16bits you intended to have. If you power up a thousand times and each time gets you a different number, that is probably random enough and you cannot get more random (for a seed number). Power up a million times and you may find a pattern. Reset a thousand times, and your stack is going to be different every time, so your seed will be also. Once you have the seed, now you are back to the known random (rand/lfsr/whatever), where you no longer have to worry about it as you are now getting one of each in some order.Eventually TRNG's will be on most micros, and we will have no more need to discuss random.

author: SiliconWizard
author_rank: Super Contributor
timestamp: October 24, 2019, 05:27:57 pm
content: Quote from: cv007 on October 24, 2019, 05:21:49 pmYep. This was clearly a debatable topic.There will also be a difference depending on how long the IC has stayed unpowered. So that's not something very easy/robust to rely on.

author: coppice
author_rank: Super Contributor
timestamp: October 24, 2019, 05:33:29 pm
content: Quote from: cv007 on October 24, 2019, 05:21:49 pmThat is by no means certain, as companies don't want the potential liabilities associated with their TRNG proving to be less random than expected. Also, no TRNG is truly random. There is always bias, so the debate will never go away. People will always be looking for better randomness.

author: cv007
author_rank: Frequent Contributor
timestamp: October 24, 2019, 06:13:21 pm
content: >People will always be looking for better randomness.Then I guess we need a Quad-Core TRNG peripheral.>There will also be a difference depending on how long the IC has stayed unpowered. So that's not something very easy/robust to rely on.Well, in that case it will be retaining its last state to a certain point, which is quite random as it is the stack. Power down for an amount of time that gets its to be itself again and you now are back to your startup random bits. Power up, changing stack, whatever- all good.Maybe I'm misunderstanding- I'm assuming only a seed is wanted so some other 'random' generator like an lfsr can appear less predictable. I'm not sure I would want to be testing my (as in me) random theories when an lfsr is a known thing that suits many purposes. I'm quite sure if I started to use an adc, I would surely get something wrong and end up with with something worse than an lfsr type thing. That is why I am using ram, I just wanted to produce some 'random' starting point for the lfsr. Anyhow, it seems to work pretty good.It is pretty easy to test, and is interesting to see what the ram state is on power up. I had thought it would be more random than what it actually is, but you can also see which bits are the undecided ones.

author: iMo
author_rank: Super Contributor
timestamp: October 24, 2019, 08:44:30 pm
content: Always think as an attacker. I want to know more about your LFSR. So I will cool the chip down with LN2 for example, or heat it up to 150deg, where the ram bits will most probably will change their randomness dramatically. An I will put a current probe into the Vcc to see what is going on, etc. etc.The same with the temperature sensor. The "TRNG on the chip" topic is rather old, there is a lot of papers on it you may find on the web.

author: SiliconWizard
author_rank: Super Contributor
timestamp: October 24, 2019, 08:55:07 pm
content: Yes. Obviously it all depends on what you're going to do with your RNG.If it's for security algorithms - then you're completely right.Now there are many other uses for good RNGs that are not that critical. Some pseudo-random algorithmic generators are very good as generators, and if you don't mind the sequences being always the same upon startup, as long as the sequence itself has a nice uniform distribution, you can even always use the same seed(s). Depends on what your use is.In one project (not security related though!), I've used one of Knuth's random generators. I seeded it with a sequence of numbers from the standard C rand() function. The generator itself was WAY better than rand().

author: cv007
author_rank: Frequent Contributor
timestamp: October 24, 2019, 10:09:38 pm
content: >I want to know more about your LFSRI posted a link to what I am using. Its the kind of random when you want something better than checking a timer, but have no need to communicate with military satellites flying overhead. The sram part is so the random blinking led's don't look exactly the same every time it powers up. The other alternative is just close your eyes when it powers on, and after about 10 seconds you will no longer be able to recognize a pattern.Since you now have the sequence to my little avr mcu, there will be no need for heat and cold. You just have to figure out what those ram bits will be so you can know where the sequence starts. Then you will be able to know what the blinking led's will do before they do it, and then... I don't know what, I guess you win a prize or somethingLFSR's are neat little things.Maybe there should be different categories of random. I think I'm talking Cat1 random. Cat5 random would be when you don't even know your in Cat5.

author: iMo
author_rank: Super Contributor
timestamp: October 24, 2019, 10:15:36 pm
content: Here is an interesting paper from Xilinx - atable with LFSR feedback taps- up to N=168. You may generate a pseudorandom sequence at xxMB/s data rate for XXXXXXXXX millenniaPS: at 50MHz clock 1.2E35 years..