1 /*
2 * Copyright (c) 2024 Nordic Semiconductor ASA
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 /*
8 * CRACEN (T/ND)RNG - (True/Non-Deterministic) Random Number Generator
9 *
10 * Notes:
11 * * Conditioning is not modeled beyond the throughput difference.
12 * The model will produce pseudorandom data of the same quality with it enabled or not.
13 * The KEY registers are ignored.
14 *
15 * * The test LFSR is not modeled. Setting CONTROL.LFSREn has no effect. The RNG produces the same data either way,
16 * which will not match what the HW LFSR would produce.
17 *
18 * * DisableOsc has no effect, the produced entropy is the same either way
19 *
20 * * The TestData register is ignored. It is not possible to hand feed to the logic entropy values with it. TestDataBusy is never set.
21 *
22 * * CONFIG.ForceRun is ignored (assumed always off)
23 * * CONFIG.FifoWriteStartUp is ignored (assumed always off)
24 *
25 * * The model does not include the real AIS31 test logic, and therefore its associated registers are ignored (AIS31*)
26 * Enabling or not the AIS31Bypass has no effect and so does CONTROL.AIS31TestSel.
27 * AIS31Status is never set.
28 *
29 * * The model does not include the real "health" monitoring logic. CONTROL.HealthTestSel is ignored and so are the Rep & PropThresh
30 * registers.
31 *
32 * * The model includes test logic to trigger a startup, AIS31 or Health monitoring failure.
33 * See nhw_CRACEN_RNG_fake_test_error()
34 *
35 * * The model does not properly support changing configuration on the fly (you should disable the peripheral first)
36 *
37 * * The model timing is a bit faster than the real HW (up to 25% faster depending on configuration),
38 * and when conditioning is on, it pushes to the FIFO 4 words at a time, while the real HW pushes one at a time in sets of 4.
39 *
40 * * Regarding FIFOLEVEL, writing to this register (in HW or the model) only clears the full status flag.
41 * The register content itself remains untouched. note that if the level is still full while writing to this
42 * register, the status flag and interrupt will be set back right away => Fix spec.
43 *
44 * * Regarding FIFOThresh, the spec seems to be incorrect, in that the actual threshold is
45 * FIFOLEVEL < (FIFOThresh + 1)*4 (note the +1) => Fix spec
46 */
47
48 #include <stdint.h>
49 #include <stdbool.h>
50 #include <string.h>
51 #include "NHW_common_types.h"
52 #include "NHW_config.h"
53 #include "NHW_peri_types.h"
54 #include "NHW_templates.h"
55 #include "NHW_xPPI.h"
56 #include "NHW_CRACEN_RNG.h"
57 #include "NHW_CRACEN_wrap.h"
58 #include "irq_ctrl.h"
59 #include "nsi_tasks.h"
60 #include "nsi_hws_models_if.h"
61 #include "bs_rand_main.h"
62 #include "bs_tracing.h"
63
64 extern NRF_CRACEN_Type NRF_CRACEN_regs;
65 extern NRF_CRACENCORE_Type NRF_CRACENCORE_regs;
66
67 static NRF_CRACENCORE_RNGCONTROL_Type *RNG_regs;
68
69 bs_time_t Timer_CRACEN_NDRNG;
70 static uint Timer_rem_clocks;
71
72 static struct rng_status {
73 enum status_t {rng_reset = 0, rng_startup, rng_idle_ron, rng_idle_roff, rng_filling, rng_error} status;
74 bool enabled;
75 uint fifo_size; //Size of FIFO in 32bit words
76 uint fifo_rptr; //Offset of the first available word to read
77 uint fifo_wptr; //Offset of the first available empty word
78 uint fifo_level; //Number of words uses in the fifo
79 uint32_t fifo[1 << NHW_CRACEN_RNG_G_log2fifodepth];
80 uint queued_words; //Number of 32bit words ready to go into FIFO
81
82 bool error_ais31_noise;
83 bool error_ais31_prenoise;
84 bool error_prop_test;
85 bool error_rep_test;
86 bool error_startup_test;
87
88 bool pend_error_ais31_noise;
89 bool pend_error_ais31_prenoise;
90 bool pend_error_prop_test;
91 bool pend_error_rep_test;
92 bool pend_error_startup_test;
93 } rng_st;
94
95 static void generate_more_data(void);
96 static void startup(void);
97 static inline void update_state(int state);
98 static void check_interrupts(void);
99 static void check_errors(void);
100
nhw_CRACEN_soft_reset(void)101 static void nhw_CRACEN_soft_reset(void) {
102 rng_st.queued_words = 0;
103 rng_st.fifo_level = 0;
104 RNG_regs->FIFOLEVEL = 0;
105 rng_st.fifo_wptr = 0;
106 rng_st.fifo_rptr = 0;
107
108 rng_st.status = rng_reset;
109 update_state(CRACENCORE_RNGCONTROL_STATUS_STATE_RESET);
110
111 rng_st.error_ais31_noise = false;
112 rng_st.error_ais31_prenoise = false;
113 rng_st.error_prop_test = false;
114 rng_st.error_rep_test = false;
115 rng_st.error_startup_test = false;
116 rng_st.pend_error_ais31_noise = false;
117 rng_st.pend_error_ais31_prenoise = false;
118 rng_st.pend_error_prop_test = false;
119 rng_st.pend_error_rep_test = false;
120 rng_st.pend_error_startup_test = false;
121
122 check_interrupts();
123 }
124
nhw_CRACEN_RNG_init(void)125 void nhw_CRACEN_RNG_init(void) {
126 RNG_regs = (NRF_CRACENCORE_RNGCONTROL_Type *)&NRF_CRACENCORE_regs.RNGCONTROL;
127
128 RNG_regs->CONTROL = CRACENCORE_RNGCONTROL_CONTROL_ResetValue;
129 RNG_regs->FIFOTHRESHOLD = CRACENCORE_RNGCONTROL_FIFOTHRESHOLD_ResetValue;
130 RNG_regs->FIFODEPTH = CRACENCORE_RNGCONTROL_FIFODEPTH_ResetValue;
131 RNG_regs->REPEATTHRESHOLD = CRACENCORE_RNGCONTROL_REPEATTHRESHOLD_ResetValue;
132 RNG_regs->PROPTHRESHOLD = CRACENCORE_RNGCONTROL_PROPTHRESHOLD_ResetValue;
133 RNG_regs->INITWAITVAL = CRACENCORE_RNGCONTROL_INITWAITVAL_ResetValue;
134 RNG_regs->SWOFFTMRVAL = CRACENCORE_RNGCONTROL_SWOFFTMRVAL_ResetValue;
135 RNG_regs->AIS31CONF0 = CRACENCORE_RNGCONTROL_AIS31CONF0_ResetValue;
136 RNG_regs->AIS31CONF1 = CRACENCORE_RNGCONTROL_AIS31CONF1_ResetValue;
137 RNG_regs->AIS31CONF2 = CRACENCORE_RNGCONTROL_AIS31CONF2_ResetValue;
138 RNG_regs->HWCONFIG = CRACENCORE_RNGCONTROL_HWCONFIG_ResetValue;
139
140 rng_st.fifo_size = 1 << NHW_CRACEN_RNG_G_log2fifodepth;
141
142 Timer_CRACEN_NDRNG = TIME_NEVER;
143
144 nhw_CRACEN_soft_reset();
145 }
146
check_interrupts(void)147 static void check_interrupts(void) {
148 bool new_int_line = false;
149
150 RNG_regs->STATUS &= ~( CRACENCORE_RNGCONTROL_STATUS_REPFAIL_Msk
151 | CRACENCORE_RNGCONTROL_STATUS_PROPFAIL_Msk
152 | CRACENCORE_RNGCONTROL_STATUS_FULLINT_Msk
153 | CRACENCORE_RNGCONTROL_STATUS_PREINT_Msk
154 | CRACENCORE_RNGCONTROL_STATUS_ALMINT_Msk );
155
156 #define CHECK_SET_FAILINT(statusm, intm) \
157 RNG_regs->STATUS |= CRACENCORE_RNGCONTROL_STATUS_##statusm##_Msk; \
158 if (RNG_regs->CONTROL & CRACENCORE_RNGCONTROL_CONTROL_INTEN##intm##_Msk) { \
159 new_int_line = true; \
160 }
161
162 if (rng_st.error_startup_test) { //Does not have an int line, only status bit
163 RNG_regs->STATUS |= CRACENCORE_RNGCONTROL_STATUS_STARTUPFAIL_Msk;
164 }
165 if (rng_st.fifo_level == rng_st.fifo_size) {
166 CHECK_SET_FAILINT(FULLINT, FULL)
167 }
168 if (rng_st.error_rep_test) {
169 CHECK_SET_FAILINT(REPFAIL, REP)
170 }
171 if (rng_st.error_prop_test) {
172 CHECK_SET_FAILINT(PROPFAIL, PROP)
173 }
174 if (rng_st.error_ais31_prenoise) {
175 CHECK_SET_FAILINT(PREINT, PRE)
176 }
177 if (rng_st.error_ais31_noise) {
178 CHECK_SET_FAILINT(ALMINT, ALM)
179 }
180 nhw_CRACEN_toggle_RNG_intline(new_int_line);
181 }
182
183 /**
184 * Check if an error has "occurred"
185 */
check_errors(void)186 void check_errors(void) {
187 bool error = false;
188
189 #define CHECK_SET_PEND_ERROR(err_n) \
190 if (rng_st.pend_error_##err_n) { \
191 rng_st.error_##err_n = true; \
192 error = true; \
193 }
194
195 if (rng_st.status == rng_startup) {
196 CHECK_SET_PEND_ERROR(startup_test)
197 }
198 if (rng_st.status == rng_filling) {
199 CHECK_SET_PEND_ERROR(rep_test)
200 CHECK_SET_PEND_ERROR(prop_test)
201 CHECK_SET_PEND_ERROR(ais31_prenoise)
202 CHECK_SET_PEND_ERROR(ais31_noise)
203 }
204
205 if (error) {
206 rng_st.status = rng_error;
207 update_state(CRACENCORE_RNGCONTROL_STATUS_STATE_ERROR);
208 Timer_CRACEN_NDRNG = TIME_NEVER;
209 nhw_CRACEN_update_timer();
210 check_interrupts();
211 }
212 }
213
fifo_push(uint32_t data)214 static void fifo_push(uint32_t data) {
215 rng_st.fifo[rng_st.fifo_wptr] = data;
216 rng_st.fifo_level +=1; /* no need to check for overflow */
217 RNG_regs->FIFOLEVEL = rng_st.fifo_level;
218 rng_st.fifo_wptr = (rng_st.fifo_wptr + 1) % rng_st.fifo_size;
219
220 check_interrupts();
221 }
222
fifo_pop(void)223 static uint32_t fifo_pop(void) {
224 uint32_t value = rng_st.fifo[rng_st.fifo_rptr];
225 if (rng_st.fifo_level > 0) {
226 rng_st.fifo_level -=1;
227 RNG_regs->FIFOLEVEL = rng_st.fifo_level;
228 rng_st.fifo_rptr = (rng_st.fifo_rptr + 1) % rng_st.fifo_size;
229 } else {
230 bs_trace_warning_time_line("CRACEN RNG pop from empty FIFO\n");
231 }
232
233 if (rng_st.fifo_level < (RNG_regs->FIFOTHRESHOLD + 1)* 4) {
234 if (rng_st.status == rng_idle_roff) {
235 startup();
236 } else if (rng_st.status == rng_idle_ron) { //Note: The condition is the same from idle_roff or idle_ron
237 generate_more_data();
238 }
239 }
240 check_interrupts();
241
242 return value;
243 }
244
update_state(int state)245 static inline void update_state(int state) {
246 RNG_regs->STATUS &= ~CRACENCORE_RNGCONTROL_STATUS_STATE_Msk;
247 RNG_regs->STATUS |= state << CRACENCORE_RNGCONTROL_STATUS_STATE_Pos;
248 }
249
get_NB128BITBLOCKS(void)250 static inline int get_NB128BITBLOCKS(void) {
251 return (RNG_regs->CONTROL & CRACENCORE_RNGCONTROL_CONTROL_NB128BITBLOCKS_Msk)
252 >> CRACENCORE_RNGCONTROL_CONTROL_NB128BITBLOCKS_Pos;
253 }
254
generate_more_data(void)255 static void generate_more_data(void) {
256 bs_time_t clocks_next;
257
258 rng_st.status = rng_filling;
259 update_state(CRACENCORE_RNGCONTROL_STATUS_STATE_FILLFIFO);
260
261 if (RNG_regs->CONTROL & CRACENCORE_RNGCONTROL_CONTROL_CONDBYPASS_Msk) {
262 clocks_next = 32*(RNG_regs->CLKDIV+1);
263 rng_st.queued_words = 1;
264 } else {
265 clocks_next = 128*get_NB128BITBLOCKS()*(RNG_regs->CLKDIV+1);
266 rng_st.queued_words = 4;
267 }
268
269 clocks_next+=Timer_rem_clocks;
270 Timer_CRACEN_NDRNG = clocks_next/NHW_CRACEN_FREQ_MHZ;
271 Timer_rem_clocks = clocks_next - (Timer_CRACEN_NDRNG*NHW_CRACEN_FREQ_MHZ);
272 Timer_CRACEN_NDRNG += nsi_hws_get_time();
273 nhw_CRACEN_update_timer();
274 check_errors();
275 }
276
nhw_CRACEN_RNG_timer_triggered(void)277 void nhw_CRACEN_RNG_timer_triggered(void) {
278
279 if (rng_st.status == rng_idle_ron) {
280 rng_st.status = rng_idle_roff;
281 update_state(CRACENCORE_RNGCONTROL_STATUS_STATE_IDLEROFF);
282 Timer_CRACEN_NDRNG = TIME_NEVER;
283 nhw_CRACEN_update_timer();
284 return;
285
286 } else if ((rng_st.status == rng_filling)) {
287 while ((rng_st.queued_words > 0) && (rng_st.fifo_level < rng_st.fifo_size)) {
288 fifo_push(bs_random_uint32());
289 rng_st.queued_words--;
290 }
291 rng_st.queued_words = 0; //Discard a possible remainder
292 }
293
294 if (rng_st.fifo_level < rng_st.fifo_size) {
295 generate_more_data();
296 } else {
297 rng_st.status = rng_idle_ron;
298 update_state(CRACENCORE_RNGCONTROL_STATUS_STATE_IDLERON);
299 Timer_CRACEN_NDRNG = nsi_hws_get_time() + RNG_regs->SWOFFTMRVAL/NHW_CRACEN_FREQ_MHZ;
300 nhw_CRACEN_update_timer();
301 }
302 }
303
startup(void)304 static void startup(void) {
305 rng_st.status = rng_startup;
306 update_state(CRACENCORE_RNGCONTROL_STATUS_STATE_STARTUP);
307
308 Timer_CRACEN_NDRNG = nsi_hws_get_time() + RNG_regs->INITWAITVAL/NHW_CRACEN_FREQ_MHZ + NHW_CRACEN_STARTUPTEST_DUR; //TODO: do start up tests add any delay?
309 nhw_CRACEN_update_timer();
310 check_errors();
311 }
312
nhw_CRACEN_RNG_regw_sideeffects_CONTROL(void)313 void nhw_CRACEN_RNG_regw_sideeffects_CONTROL(void) {
314 bool enabled = RNG_regs->CONTROL & CRACENCORE_RNGCONTROL_CONTROL_ENABLE_Msk;
315
316 if (RNG_regs->CONTROL & CRACENCORE_RNGCONTROL_CONTROL_SOFTRST_Msk) {
317 nhw_CRACEN_soft_reset();
318 enabled = false;
319 }
320
321 if (!enabled) {
322 rng_st.enabled = false;
323 Timer_CRACEN_NDRNG = TIME_NEVER;
324 nhw_CRACEN_update_timer();
325 rng_st.status = rng_reset;
326 update_state(CRACENCORE_RNGCONTROL_STATUS_STATE_RESET);
327 return;
328 }
329
330 if ((NRF_CRACEN_regs.ENABLE & CRACEN_ENABLE_RNG_Msk) == 0) {
331 bs_trace_warning_time_line("Attempting to enable CRACEN RNG while the CRACEN wrap logic is off\n");
332 }
333
334 check_interrupts();
335
336 if (rng_st.enabled == true) { //Already enabled
337 return; //Nothing more to do (we don't handle too properly on the fly CONFIG changes)
338 }
339
340 rng_st.enabled = true;
341
342 startup();
343 }
344
nhw_CRACEN_RNG_regr_sideeffects_FIFO(void)345 uint32_t nhw_CRACEN_RNG_regr_sideeffects_FIFO(void) {
346 if ((rng_st.status == rng_reset) && (rng_st.fifo_level == 0)) {
347 RNG_regs->STATUS |= CRACENCORE_RNGCONTROL_STATUS_FIFOACCFAIL_Msk;
348 }
349
350 RNG_regs->FIFO[0] = fifo_pop();
351 return RNG_regs->FIFO[0];
352 }
353
nhw_CRACEN_RNG_regw_sideeffects_FIFOLEVEL(void)354 void nhw_CRACEN_RNG_regw_sideeffects_FIFOLEVEL(void) {
355 RNG_regs->STATUS &= ~CRACENCORE_RNGCONTROL_STATUS_FULLINT_Msk;
356 RNG_regs->FIFOLEVEL = rng_st.fifo_level;
357 check_interrupts();
358 }
359
nhw_CRACEN_RNG_fake_test_error(uint32_t mask)360 void nhw_CRACEN_RNG_fake_test_error(uint32_t mask) {
361 if (mask & CRACEN_RNG_FAKE_STARTUP_ERROR) {
362 rng_st.pend_error_startup_test = true;
363 }
364 if (mask & CRACEN_RNG_FAKE_REP_TEST_ERROR) {
365 rng_st.pend_error_rep_test = true;
366 }
367 if (mask & CRACEN_RNG_FAKE_PROP_TEST_ERROR) {
368 rng_st.pend_error_prop_test = true;
369 }
370 if (mask & CRACEN_RNG_FAKE_AIS31_PRENOISE_ERROR) {
371 rng_st.pend_error_ais31_prenoise = true;
372 }
373 if (mask & CRACEN_RNG_FAKE_AIS31_NOISE_ERROR) {
374 rng_st.pend_error_ais31_noise = true;
375 }
376 check_errors();
377 }
378