Skip to content

Conversation

@nponsard
Copy link
Contributor

@nponsard nponsard commented Jan 5, 2026

This PR adds a CryptoCell RNG driver for the nrf91 series. I built it following the RNG driver for the other nrf chips, an example has been added for the nrf9151 to show how to use it.

It needs an updated nrf-pac, PR here: embassy-rs/nrf-pac#14

I tested the example on the nrf9160dk and Thingy:91 X (nrf9151).

Concerning the activation of the CryptoCell (Cryptocell::enable()), should this be set up by the user or the RNG driver ?

Copy link
Member

@lulf lulf left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For now I think the rng driver can do it, ideally if it can check if it's enabled already first.

Also, maybe the peripheral and types could be named RNG and Rng? It aligns with existing peripherals and doesn't conflict with any other rng for those chips.

@nponsard
Copy link
Contributor Author

nponsard commented Jan 5, 2026

Also, maybe the peripheral and types could be named RNG and Rng? It aligns with existing peripherals and doesn't conflict with any other rng for those chips.

It would become a conflict if it gets extended to the nrf52840 that has both RNG and CryptoCell peripherals.

EGU5_S as EGU5,
FICR_S as FICR,
FPU_S as FPU,
FPU_NS as FPU,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why this change?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It came with the new generated PAC, FPU_S doesn't exist anymore. I saw that FPU_NS was used even in s mode on the nrf9120 so I made the same modification as a fix.

@nponsard nponsard force-pushed the cryptocell branch 2 times, most recently from 9958880 to f85f144 Compare January 6, 2026 15:21
@nponsard
Copy link
Contributor Author

nponsard commented Jan 6, 2026

Alright, squashed my fixup commits, managed to get nrf52840 supported and tested (copying cryptocell_rng from examples/nrf9151/s/, ran on the nrf52840 of my nrf9160dk).

Is there any more changes needed ?

@nponsard nponsard requested a review from lulf January 6, 2026 15:23
@nponsard
Copy link
Contributor Author

nponsard commented Jan 6, 2026

Oh also I changed the nrf-pac commit to be the one in main since the svd update got merged.

@nponsard
Copy link
Contributor Author

nponsard commented Jan 6, 2026

Ah I just thought about the naming, should the struct be named CcRng, CCRng or Ccrng ?
I think the last one matches more UpperCamelCase


let on_drop = OnDrop::new(|| {
self.stop();
self.disable_irq();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

disable_irq should come before stop, since stop stops the RNG_CLK and "The registers of a cryptographic engine are only accessible when its clock is enabled."

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't think about that, this should be fixed now.

Comment on lines 120 to 131
fn enable_irq(&self) {
pac::CC_HOST_RGF
.imr()
.write(|w| w.set_rng_mask(pac::cc_host_rgf::vals::RngMask::IRQENABLE));
self.r
.rng_imr()
.write(|w| w.set_ehr_valid_mask(pac::cc_rng::vals::EhrValidMask::IRQENABLE));
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure if this is considered a pac bug or not, but the default value of IMR from nrf-pac is 0, which means enable all the interrupts. (According to the datasheet and SVD file, the reset value is all 1's.). You could use modify instead. Same issue exists in disable_irq.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah my bad, I used write too often when experimenting. Should be good now.

Comment on lines 129 to 142
fn disable_irq(&self) {
self.r.rng_icr().write(|w| w.set_ehr_valid_clear(true));
pac::CC_HOST_RGF.icr().write(|w| w.set_rng_clear(true));
self.r
.rng_imr()
.write(|w| w.set_ehr_valid_mask(pac::cc_rng::vals::EhrValidMask::IRQDISABLE));
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

did you forget CC_HOST_RGF.IMR here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, I now added the instruction to mask the rng interrupt


// https://docs.nordicsemi.com/bundle/ps_nrf9151/page/cryptocell.html#ariaid-title96

pac::CRYPTOCELL.enable().write(|w| w.set_enable(true));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this still necessary?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No 👍

// without calling its destructor.

for i in 0..6 {
let bytes = r.ehr_data(i).read().to_be_bytes();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why to_be_bytes? I saw you use from_ne_bytes elsewhere.

Copy link
Contributor Author

@nponsard nponsard Jan 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

from_ne_bytes comes from the rng driver on main, I tend to use to_be_bytes by default with no real reason. It shouldn't matter as it's random bits right ? Is there a performance impact ?

I can change it to to_ne_bytes if you want.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is at least a size impact, it's one extra instruction.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed to_be_bytes occurrences to to_ne_bytes

@0e4ef622
Copy link
Contributor

0e4ef622 commented Jan 8, 2026

Mentioned on matrix earlier, but I tested this on an nrf5340 and blocking api works, but async doesn't. The CRYPTOCELL interrupt never fires for some reason. But if I read some registers, like CRYPTOCELL.ENABLE or the RNG_ISR register, then the interrupt fires, might be an undocumented erratum.

Interestingly, I found a library within TF-M for using the cryptocell, and it seems like they always busy wait for RNG interrupts specifically.

@nponsard
Copy link
Contributor Author

nponsard commented Jan 8, 2026

I can try to add nrf5340 support.

What's the best way to busy wait ? Is it a good idea to make the task sleep for short durations using Timer::after_millis(1).await; so other tasks can be executed ?

@Dirbaio
Copy link
Member

Dirbaio commented Jan 8, 2026

Is it a good idea to make the task sleep for short durations using Timer::after_millis(1).await; so other tasks can be executed ?

it's better to do loop { yield_now().await }. It still lets other tasks run but loops as fast as possible.

Sleeping adds ltency, plus embassy-time is an optional dep.

@0e4ef622
Copy link
Contributor

0e4ef622 commented Jan 8, 2026

I'd also be fine with excluding the async functionality from nrf53.

@nponsard
Copy link
Contributor Author

nponsard commented Jan 9, 2026

I made it so only the async driver of the nrf5340 CryptoCell is behind a async-ccrng-nrf5340 feature. Makes a bit of a mess in the imports that I tried to mitigate in e6118b2

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.

4 participants