f407-rng README¶
Project: f407-rng
Created: Sun 14 Mar 2021 20:53:47 AEDT
Author Copyright 2021 by t.j.porter <terry@tjporter.com.au>
Purpose: Demonstrate the Random Number Generator and the 168MHz clock setup
Intended Audience: Forth users
MCU: STM32F407
Board: STM32F407 Discovery
Core: Mecrisp-Stellaris RA 2.5.4 for STM32F407 by Matthias Koch
REQUIRED: SWDCOM https://mecrisp-stellaris-folkdoc.sourceforge.io/swdcom.html
Recommended:
Based on:
References: STM32F407 Reference manual February 2019 RM0090 Rev 18
f407-rng URL:
license: MIT, please see COPYING
Warning
To run the RNG with a serial terminal you need a different project, please see:
Intro¶
Note
The code below is designed to explain every relevent hardware detail because I’m a technician and I believe this is VITAL in embedded code for understanding and maintennance. This code would be a lot smaller if I only used “Magic Numbers” everywhere, but it would also be unreadable.
Generating random numbers on the STM32F407 Random Number Generator at first glance seems like a trivial Forth program to write given the very few registers that need to be configured.
RNG Registers¶
$50060800 constant RNG ( Random number generator )
RNG $0 + constant RNG_CR ( read-write ) \ control register
RNG $4 + constant RNG_SR ( ) \ status register
RNG $8 + constant RNG_DR ( read-only ) \ data register
Simple RNG program¶
Initially it appears that one just enables the RNG clock and the RNG and starts reading random numbers, however there is a problem.
: RCC_AHB2ENR_RNGEN ( -- x addr ) 6 bit RCC_AHB2ENR ; \ RCC_AHB2ENR_RNGEN, Random number generator clock enable
: RNG_CR_RNGEN ( -- x addr ) 2 bit RNG_CR ; \ RNG_CR_RNGEN, Random number generator enable
: RNG_SR_DRDY? ( -- x addr ) 0 bit RNG_SR bit@ ; \ RNG_SR_DRDY, Random data ready yet ?
: RNG_DR_RNDATA? ( -- x ) RNG_DR @ ; \ RNG_DR_RNDATA, Random data output register
: init.rng ( -- )
RCC_AHB2ENR_RNGEN bis! \ Enable Random number generator clock
RNG_CR_RNGEN bis! \ Enable Random number generator
;
: print-random ( -- )
begin RNG_SR_DRDY? until \ Is a random number ready ?
RNG_DR_RNDATA? u. ; \ yes, Print it
init.rng
print-random 1066052044 ok.
print-random 3903797255 ok.
print-random 233178905 ok.
Warning
The above code only works because the MCU PLL clock is set up and running. See below for more.
Why isn’t the RNG quite so simple ?¶
Item |
Comment |
---|---|
Analog random number generator |
Requires a clock derived from the PLL which is not trivial to configure. |
Error checking required to: |
eliminate duplicates, detect bad seed and detect clock problems |
Thanks To:¶
My thanks to Matthias Koch, Jan Bramkamp and Igor de om1zz whose 168MHz code I shamelessly copied and referred to constantly while writing this article..
168 Mhz¶
Setting up the F407 clock for 168 Mhz isn’t trivial but the MCU does run at 8Mhz MSI with no clock code at all. However the default 8MHz clock wont run the RNG in this default case.
So if we are going to use the RNG, we may as well set up the MCU clock for 168 Mhz ?
I’ve made a clock speed calculator, stm32F407.f.vco.clock cr ( pllp pll plln pllm – f.vco ) f(VCO clock) = (f(PLL clock input) × (PLLN / PLLM)) /PLLP which is in the code below. It uses s31.32 fixed point numbers and probably doesn’t need them but I couldn’t resist the exercise and it can’t hurt ?
Running The Code¶
Upload the code to your F407 Disco and run it, it should work fine giving something like :
f.vco.clock = 168,00 MHz
print-random 1405158109 ok.
print-random 1160931903 ok.
print-random 1641587307 ok
Then comment out the clock line like so, and watch the RNG fail:
: init ( -- )
\ 168mhz
\ init.calltrace \ not required for the clock of rng, but recommended for general development.
init.rng
;
The Full Program¶
\ Program Name: rnd-f407.fs
\ Project: f407-rng
\ Created: Sun 14 Mar 2021 20:53:47 AEDT
\ Author Copyright 2021 by t.j.porter <terry@tjporter.com.au>
\ Purpose: Demonstrate the RNG
\ Intended Audience:
\ MCU: STM32F407
\ Board: F4 Disco
\ Core: Mecrisp-Stellaris RA 2.5.4 for STM32F407 by Matthias Koch
\ Required: swdcom https://mecrisp-stellaris-folkdoc.sourceforge.io/swdcom.html or see below re Usart2
\ Recommended:
\ Based on:
\ References:
\ f407-rng URL:
\ license: MIT, please see COPYING
\ note: HSE clock = 8Mhz
compiletoram
\ Uncomment any of these memory maps not previously loaded
\ $40023800 constant RCC ( Reset and clock control )
\ RCC $34 + constant RCC_AHB2ENR ( read-write ) \ AHB2 peripheral clock enable register
\ RCC $0 + constant RCC_CR ( ) \ clock control register
\ RCC $4 + constant RCC_PLLCFGR ( read-write ) \ PLL configuration register
\ RCC $8 + constant RCC_CFGR ( ) \ clock configuration register
\ $40023C00 constant FLASH ( FLASH )
\ FLASH $0 + constant FLASH_ACR ( ) \ Flash access control register
\ $50060800 constant RNG ( Random number generator )
\ RNG $0 + constant RNG_CR ( read-write ) \ control register
\ RNG $4 + constant RNG_SR ( ) \ status register
\ RNG $8 + constant RNG_DR ( read-only ) \ data register
\ bit ( u -- u ) 1 swap lshift1-foldable ;
\ ------------------------------------ MCU Clock ------------------------------------------- \
: RCC_CR_HSEON ( -- x addr ) 16 bit RCC_CR ; \ HSE clock enable
: RCC_CR_HSERDY? ( -- 1|0 ) 17 bit RCC_CR bit@ ; \ HSE clock ready flag
: RCC_CR_PLLON ( -- x addr ) 24 bit RCC_CR ; \ Main PLL PLL enable
: RCC_CR_PLLRDY? ( -- 1|0 ) 25 bit RCC_CR bit@ ; \ Main PLL PLL clock ready flag
: RCC_PLLCFGR_PLLQ ( 2 <= x <= 15 -- addr ) 24 lshift RCC_PLLCFGR ; \ PLL division factor for random number generator clocks
: RCC_PLLCFGR_PLLP ( %bb -- addr ) 17 lshift RCC_PLLCFGR ; \ Main PLL PLL division factor for main system clock
: RCC_PLLCFGR_PLLN ( 50 <= x <= 432 -- addr ) 6 lshift RCC_PLLCFGR ; \ Main PLL PLL multiplication factor for VCO
: RCC_PLLCFGR_PLLM ( 2 <= x 63 <= -- x addr ) 0 lshift RCC_PLLCFGR ; \ Division factor for the main PLL
: RCC_PLLCFGR_PLLSRC ( -- x addr ) 22 bit RCC_PLLCFGR ; \ Bit 22, PLLSRC, 0: HSI clock selected as PLL, 1: HSE oscillator clock selected as PLL
: RCC_CFGR_PPRE2 ( %bbb -- x addr ) 13 lshift RCC_CFGR ; \ APB high-speed prescaler APB2
: RCC_CFGR_PPRE1 ( %bbb -- x addr ) 10 lshift RCC_CFGR ; \ APB Low speed prescaler APB1
: RCC_CFGR_SW ( %bb -- x addr ) 0 lshift RCC_CFGR ; \ System clock switch,
: FLASH_ACR_LATENCY ( %bbb -- x addr ) FLASH_ACR ; \ Latency
: FLASH_ACR_PRFTEN ( -- x addr ) 8 bit FLASH_ACR ; \ Prefetch enable
: FLASH_ACR_ICEN ( -- x addr ) 9 bit FLASH_ACR ; \ Instruction cache enable
: FLASH_ACR_DCEN ( -- x addr ) 10 bit FLASH_ACR ; \ Data cache enable
%101 constant 5wait-states
: >s31.32 ( u -- s31.32 ) 0 swap ; \ convert to a s31.32 number
: ART-5WS ( -- ) \ Enable data cache, instruction and prefetching, 5 wait states
5wait-states FLASH_ACR_LATENCY bis!
FLASH_ACR_PRFTEN bis!
FLASH_ACR_ICEN bis!
FLASH_ACR_DCEN bis!
;
: PLLCFGR-CNF ( -- ) \ PLL output = 336,00 MHz divided by RCC_PLLCFGR_PLLP of 2 = 168MHz
RCC_PLLCFGR_PLLSRC bis! \ HSE oscillator clock selected as PLL clock entry
336 RCC_PLLCFGR_PLLN bis! \ 50 <= x <= 432 ; RCC_PLLCFGR_PLLN0, Main PLL PLL multiplication factor for VCO
8 RCC_PLLCFGR_PLLM bis! \ 2 <= x 63 ; RCC_PLLCFGR_PLLM0, Division factor for the main PLL
7 RCC_PLLCFGR_PLLQ bis! \ 2 <= x <= 15 ; PLL division factor for random number generator clocks. 336 MHz / 8 = 48 MHz
%11 RCC_PLLCFGR_PLLP bic! \ 00: PLLP = 2
;
: RCC_CFGR-CNF ( -- )
%10 RCC_CFGR_SW bis! \ %10: PLL selected as system clock, = 168 MHz
%100 RCC_CFGR_PPRE2 bis! \ %100: AHB clock divided by 2. APB High speed prescaler APB2 (max 84 MHz) = 168/2 MHz = 84 MHz
%101 RCC_CFGR_PPRE1 bis! \ %101: AHB clock division by 3. APB Low speed prescaler APB1 (max 42 MHz) = 168/4 MHz = 42 MHz.
;
: hse-on ( -- ) RCC_CR_HSEON bis! begin RCC_CR_HSERDY? until ;
: pll-on ( -- ) RCC_CR_PLLON bis! begin RCC_CR_PLLRDY? until ;
: flash-168mhz ( -- ) ART-5WS ;
: 168mhz ( -- ) hse-on flash-168mhz PLLCFGR-CNF pll-on RCC_CFGR-CNF ;
: stm32f407.systemcoreclock cr ( pllp in_clk plln pllm -- systemcoreclock ) \ SystemCoreClock = ((INPUT_CLOCK (HSE_OR_HSI_IN_HZ) / PLL_M) * PLL_N) / PLL_P
swap >s31.32
rot >s31.32
f/
rot >s31.32 2swap
f*
rot >s31.32 2swap
2swap
f/
." SystemCoreClock = " 2 f.n ." MHz " cr
;
\ Uncomment the two lines below if you use the F407 Disco Usart2 instead of swdcom
\ $40004408 constant USART2_BRR
\ $16d USART2_BRR ! \ Set Baud rate divider for 115200 Baud at 42 MHz. 22.786
\ ----------------------------- RNG ------------------------- \
0 variable rnd-sample
0 variable rnd-flag
: RCC_AHB2ENR_RNGEN ( -- x addr ) 6 bit RCC_AHB2ENR ; \ RCC_AHB2ENR_RNGEN, Random number generator clock enable
: RNG_CR_RNGEN ( -- x addr ) 2 bit RNG_CR ; \ RNG_CR_RNGEN, Random number generator enable
: RNG_SR_DRDY? ( -- x addr ) 0 bit RNG_SR bit@ ; \ RNG_SR_DRDY, Random data ready yet ?
: RNG_SR_SECS? ( -- 1|0 ) 2 bit RNG_SR bit@ ; \ RNG_SR_SECS, Seed error current status
: RNG_SR_CECS? ( -- 1|0 ) 1 bit RNG_SR bit@ ; \ RNG_SR_CECS, Clock error current status
: RNG_DR_RNDATA? ( -- x ) RNG_DR @ ; \ RNG_DR_RNDATA, Random data output register
: init.rng ( -- )
RCC_AHB2ENR_RNGEN bis! \ Enable Random number generator clock
RNG_CR_RNGEN bis! \ Enable Random number generator
;
: rng-check-errors ( -- )
RNG_SR_SECS? if ." There has been a seed error, reinitializing the RNG " cr
RNG_CR_RNGEN bic!
RNG_CR_RNGEN bis!
0 rnd-flag !
then
RNG_SR_CECS? if ." There is a RNG CLOCK problem, see page 768 of RM0090 Rev 18" cr exit
then
;
: print-random ( -- ) \ but check it's valid first.
rng-check-errors
RNG_DR_RNDATA? \ get the new random number
rnd-flag @ 0= if \ is this the first random number generated since RNG_CR_RNGEN ?
RNG_DR_RNDATA? rnd-sample ! \ yes, save it
then \ no, proceed to testing the new number against the old
begin
begin RNG_SR_DRDY? until \ Is a new random number ready ?
RNG_DR_RNDATA? dup rnd-sample @ - 0= if \ compare old and new random numbers
rnd-sample ! \ same! save new as old, delete new
else drop then \ different, drop the copy
dup \ valid RN on the stack, make a copy for final printing
0<> \ is it greater than zero ? ( question: will the RNG generate 0 as a valid RN ?)
until
u. \ yes, Print it
;
\ ----------------------------------------------------------- \
: init ( -- )
168mhz
\ init.calltrace \ not required for the clock of rng, but recommended for general development.
init.rng
;
init
2 8 336 8 stm32F407.f.vco.clock ( pllp pll plln pllm -- f.vco ) \ Calculate the MCU clock
print-random
print-random
print-random
Debugging¶
If you use SVD2FORTH https://sourceforge.net/projects/mecrisp-stellaris-folkdoc/files/svd2forth-v3-stm32-20.03.21-F7a01.zip you can compare what you get to my working results below.
RCC_CR.
RCC_CR. $40023800
3|3|2|2|2|2|2|2|2|2|2|2|1|1|1|1|1|1|1|1|1|1|
1|0|9|8|7|6|5|4|3|2|1|0|9|8|7|6|5|4|3|2|1|0|9|8|7|6|5|4|3|2|1|0
0 1 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 1 1 1 0 0 0 0 0 0 0 0 0 0 0
RCC_PLLCFGR.
RCC_PLLCFGR. RW $40023804
3|3|2|2|2|2|2|2|2|2|2|2|1|1|1|1|1|1|1|1|1|1|
1|0|9|8|7|6|5|4|3|2|1|0|9|8|7|6|5|4|3|2|1|0|9|8|7|6|5|4|3|2|1|0
0 1 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 1 1 1 0 0 0 0 0 0 0 0 1 0 0
RCC_CFGR.
RCC_CFGR. $40023808
3|3|2|2|2|2|2|2|2|2|2|2|1|1|1|1|1|1|1|1|1|1|
1|0|9|8|7|6|5|4|3|2|1|0|9|8|7|6|5|4|3|2|1|0|9|8|7|6|5|4|3|2|1|0
0 1 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 1 1 1 0 0 0 0 0 0 0 1 0 0 0
FLASH_ACR.
FLASH_ACR. $40023C00
3|3|2|2|2|2|2|2|2|2|2|2|1|1|1|1|1|1|1|1|1|1|
1|0|9|8|7|6|5|4|3|2|1|0|9|8|7|6|5|4|3|2|1|0|9|8|7|6|5|4|3|2|1|0
0 1 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 1 1 1 1 0 0 0 0 0 0 0 0 0 0
RCC_AHB2ENR.
RCC_AHB2ENR. RW $40023834
3|3|2|2|2|2|2|2|2|2|2|2|1|1|1|1|1|1|1|1|1|1|
1|0|9|8|7|6|5|4|3|2|1|0|9|8|7|6|5|4|3|2|1|0|9|8|7|6|5|4|3|2|1|0
0 1 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 1 1 1 0 0 0 0 0 1 1 0 1 0 0
RNG_CR.
RNG_CR. RW $50060800
3|3|2|2|2|2|2|2|2|2|2|2|1|1|1|1|1|1|1|1|1|1|
1|0|9|8|7|6|5|4|3|2|1|0|9|8|7|6|5|4|3|2|1|0|9|8|7|6|5|4|3|2|1|0
0 1 0 1 0 0 0 0 0 0 0 0 0 1 1 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0
RNG_SR.
RNG_SR. $50060804
3|3|2|2|2|2|2|2|2|2|2|2|1|1|1|1|1|1|1|1|1|1|
1|0|9|8|7|6|5|4|3|2|1|0|9|8|7|6|5|4|3|2|1|0|9|8|7|6|5|4|3|2|1|0
0 1 0 1 0 0 0 0 0 0 0 0 0 1 1 0 0 0 0 0 1 0 0 0 0 0 0 0 0 1 0 0