STM32L432KC hardware random number generator

The STM32L432KC has an RNG (Random Number Generator) to allow you to generate “true” random numbers. More importantly from my perspective is that it does it fast. I will be using the LL (low-level) library of CubeIDE because I don’t think there’s any advantage to using the high-level ones.

CubeIDE setup

In the pinout section, go to category Security, and select RNG. In the Mode section, check the Activated button.

If you want an interrupt to be triggered every time a number is generated, then go to the Configuration Section, NVIC Settings tab, and check the Enabled button for RNG Global Interrupt.

The clock is badly configured by default, so go to Clock Configuration section. Click the button Resolve Clock Issues.

The save the project.

Using the RNG

The function MX_RNG_Init() is generated for you to configure the RNG. It’s quite a small function, and looks straightforward.

If you want an interrupt to trigger every time a number is generated, you’ll need to perform a one-time initialisation before your while() loop:

RNG->CR |= RNG_CR_IE; // enable interrupts

I’ve just used CMSIS in this example, because it’s so dirt-simple.

To use the interrupt, look in stm32l4xx_it.c, and navigate until you find the function RNG_IRQHandler(). Put whatever code you require in there. There is no need to set any flags.

It takes a certain amount of time for the mcu to generate a random number, so be a little careful about how often you need them.

I decided to toggle a pin to see how long it took:

void RNG_IRQHandler(void)
{
  /* USER CODE BEGIN RNG_IRQn 0 */
  LL_GPIO_TogglePin(GPIOB, LL_GPIO_PIN_3);

  /* USER CODE END RNG_IRQn 0 */
  /* USER CODE BEGIN RNG_IRQn 1 */
  //volatile uint32_t val = RNG->DR;

  /* USER CODE END RNG_IRQn 1 */
}

I ran the project, and used a logic analyser to see how fast the interrupt was being called. About once every 0.7us is what I found.

That’s plenty enough for audio work. It basically means that I don’t really need interrupts, put can just poll for a random number whenever I feel like it. to actually get a value:

volatile uint32_t val = RNG->DR; // dunno if "volatile" is strictly req'd

Wrapping up

So that’s about it for RNG. Nice and simple, except for the complication of clock compatibility.

Hope that helps.

Update 1

I tried using an empty RNG_IRQHandler() function, and that seemed to screw up the mcu operation. I’m not sure what the problem is. You should probably at least read the RNG->DR in the interrupt (otherwise what’s the point anyway?). Something like:

void RNG_IRQHandler(void)
{
  /* USER CODE BEGIN RNG_IRQn 0 */
  /* USER CODE END RNG_IRQn 0 */
  /* USER CODE BEGIN RNG_IRQn 1 */
  //volatile uint32_t val = RNG->DR;
	RNG->DR; // at minimum. But you should probably set a variable

  /* USER CODE END RNG_IRQn 1 */
}

The alternative is not to use interrupts for RNG. When I used this function:

void RNG_IRQHandler(void)
{
  /* USER CODE BEGIN RNG_IRQn 0 */
	LL_GPIO_TogglePin(GPIOB, LL_GPIO_PIN_3);

  /* USER CODE END RNG_IRQn 0 */
  /* USER CODE BEGIN RNG_IRQn 1 */
  //volatile uint32_t val = RNG->DR;
	RNG->DR;

  /* USER CODE END RNG_IRQn 1 */
}

This seemed to toggle the the pin once every 1.3us, effectively halving the rate or RNG generation.

I’m inclined to advise against using the RNG IRQ unless you have some significant reason for doing so.

Note that the RNG->SR status bit contains a bit DRDY to signify when data is ready. It is a read-only field. It also generates the interrupt.

As an alternative, you could poll for the DRDY bit to see if a new random number is ready. This is probably overkill for most applications. If you want a random number at most once every 1.5us, then you are likely best to just do the simplest thing that works and read the RNG->DR register whenever you feel like it. If you need it significantly more frequently than that, then you’ve got your work cut out for you! Remember, though, there’s unlikely to be any magical shortcuts to making the chip work faster. Notwithstanding that, it may be possible to tinker around with clock settings to speed things up.

Update 2

I will note further that interrupts always involve a context switching cost. So, to reiterate on my previous update: yeah, you probably don’t want to use the RNG interrupt, just read the data register directly. You may be able to squeeze more than one reading every 1.5us in that case. I presume that nearly everyone aren’t going to get close to the edge on that, though.

About mcturra2000

Computer programmer living in Scotland.
This entry was posted in Uncategorized. Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s