Skip to content

Implement pseudorandom number generator#14

Open
JesseDeMeulemeester wants to merge 5 commits intoproteus-core:mainfrom
JesseDeMeulemeester:rng
Open

Implement pseudorandom number generator#14
JesseDeMeulemeester wants to merge 5 commits intoproteus-core:mainfrom
JesseDeMeulemeester:rng

Conversation

@JesseDeMeulemeester
Copy link
Contributor

@JesseDeMeulemeester JesseDeMeulemeester commented Jan 9, 2026

This PR implements a pseudorandom number generator. It provides an RngService that can be called in any component that requires random numbers. We use unrolled Bivium [1] to generate a continuous random stream, where the Bivium core produces one 32-bit (64-bit) random value every cycle for RV32 (RV64). To improve latency, each component that requires random numbers holds a small buffer to hold the random values. The RNG core itself is shared between all components that register a buffer with the RNG component. The RNG component will fill these buffers with the values produced by the core in Round Robin fashion, and can provide up to one random number every clock cycle.

Initialization

The (80-bit) key for the Bivium core can be set at runtime through CSRs. By default, the RNG component will not generate any random numbers until the seed has been updated. This behavior can be disabled by setting the allowUninitializedRng flag when initializing the RNG. The RNG core will then use a hard-coded key instead.
The RNG component can also be disabled through a control CSR. If the disable bit is set, timings will remain the same, but the RNG component will only return zero.

The different CSRs that are used by this component are as follows:

CSR ID Description
0x863 RNG control
0x880 Bivium key(32 downto 0)
0x881 Bivium key(64 downto 32)
0x882 Bivium key(80 downto 64)

RNG Control CSR

bit Description
32:2 reserved
1 Update key: Writing 1 causes the RNG to load the values from CSRs 0x880-0x882 as the key for the Bivium core.
0 Disable RNG: Writing 1 causes the RNG to replace all output from the Bivium core with 0.

Note that both of these actions can only be performed once, i.e., the RNG component will only respond the first time these bits are set.

Usage

The usage is very similar to CSRs:
You add the RNG plugin (note that this plugin has to be added after any other plugin that requires it):

pipeline.addPlugins(
  Seq(
    ...
    new Rng(),
    ...
  ) ++ extraPlugins
)

You register a buffer with the RngService:

val rngService = pipeline.service[RngService]
rngBufferIndex = rngService.registerRngBuffer(new RngFifo())

By default, the RngFifo has a depth of 2, but this can be changed by setting the queueDepth parameter when instantiating the RngFifo.

You can then get random numbers from this buffer:

val rngService = pipeline.service[RngService]
private val rng = slave(new RngIo)
rng <> rngService.getRngBuffer(rngBufferIndex)

val (rngValid, rngValue) = rng.get()
when (rngValid) {
  ...
}

JesseDeMeulemeester and others added 5 commits January 9, 2026 17:13
Co-authored-by:  Quinten Norga <43999019+qnorga1@users.noreply.github.com>
* Replaced AES core with Bivium core for more efficient random number generation. This reduces area usage and improves performance to 1 random number per clock cycle.
* Refactored the connection between the RngCore and the RngFifos to remove unnecessary buffers
@martonbognar
Copy link
Member

Thank you and sorry for the late response! If I understand correctly, the implementation has changed since you wrote the original message, could you update this so that it can serve as a good reference for what was merged?

@JesseDeMeulemeester
Copy link
Contributor Author

Yes, we swapped out the AES core with Bivium, which offers better throughput/area. And we also fixed some of the minor issues. I have updated the main comment.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants