1 /*
2 * Copyright (c) 2017 Oticon A/S
3 * Copyright (c) 2023 Nordic Semiconductor ASA
4 *
5 * SPDX-License-Identifier: Apache-2.0
6 */
7
8 /*
9 * RNG - Random number generator
10 * https://infocenter.nordicsemi.com/topic/ps_nrf52833/rng.html?cp=5_1_0_5_18
11 * https://infocenter.nordicsemi.com/topic/ps_nrf5340/rng.html?cp=4_0_0_6_26
12 *
13 * Compatible with 52833 & 5340
14 *
15 * Note:
16 * 1. The delay to produce a value is constant (though bias correction increases time)
17 *
18 * 2. The spec says that the delay is unpredictable but it does not provide any indication
19 * of what kind of random distribution to expect => The model just assumes the value is
20 * always the average(?) the spec provides
21 *
22 * 3. The produced random value is always "good enough".
23 * The bias correction has no effect on the random value quality
24 */
25
26 #include <string.h>
27 #include <stdbool.h>
28 #include "NHW_config.h"
29 #include "NHW_peri_types.h"
30 #include "NHW_common_types.h"
31 #include "NHW_templates.h"
32 #include "NHW_RNG.h"
33 #include "NHW_xPPI.h"
34 #include "nsi_hw_scheduler.h"
35 #include "irq_ctrl.h"
36 #include "bs_rand_main.h"
37 #include "nsi_tasks.h"
38 #include "nsi_hws_models_if.h"
39
40 NRF_RNG_Type NRF_RNG_regs;
41 static bs_time_t Timer_RNG = TIME_NEVER; //Time when the next random number will be ready
42
43 static bool RNG_hw_started = false;
44 static bool RNG_INTEN = false; //interrupt enable
45
46 #if (NHW_HAS_DPPI)
47 /* Mapping of peripheral instance to DPPI instance */
48 static uint nhw_RNG_dppi_map[NHW_RNG_TOTAL_INST] = NHW_RNG_DPPI_MAP;
49 #endif
50
51 /**
52 * Initialize the RNG model
53 */
nhw_rng_init(void)54 static void nhw_rng_init(void) {
55 memset(&NRF_RNG_regs, 0, sizeof(NRF_RNG_regs));
56 RNG_hw_started = false;
57 RNG_INTEN = false;
58 Timer_RNG = TIME_NEVER;
59 }
60
61 NSI_TASK(nhw_rng_init, HW_INIT, 100);
62
nhw_rng_schedule_next(bool first_time)63 static void nhw_rng_schedule_next(bool first_time){
64 bs_time_t delay = 0;
65
66 if (first_time) {
67 delay = NHW_RNG_tRNG_START;
68 }
69
70 //See Note 1.
71 if (NRF_RNG_regs.CONFIG & RNG_CONFIG_DERCEN_Msk){ //Bias correction enabled
72 delay += NHW_RNG_tRNG_BC;
73 } else {
74 delay += NHW_RNG_tRNG_RAW;
75 }
76 Timer_RNG = nsi_hws_get_time() + delay;
77
78 nsi_hws_find_next_event();
79 }
80
nhw_RNG_eval_interrupt(uint inst)81 static void nhw_RNG_eval_interrupt(uint inst) {
82 static bool rng_int_line[NHW_RNG_TOTAL_INST]; /* Is the RNG currently driving its interrupt line high */
83 /* Mapping of peripheral instance to {int controller instance, int number} */
84 static struct nhw_irq_mapping nhw_rng_irq_map[NHW_RNG_TOTAL_INST] = NHW_RNG_INT_MAP;
85 bool new_int_line = false;
86
87 if (NRF_RNG_regs.EVENTS_VALRDY && (RNG_INTEN & RNG_INTENCLR_VALRDY_Msk)){
88 new_int_line = true;
89 }
90
91 hw_irq_ctrl_toggle_level_irq_line_if(&rng_int_line[inst],
92 new_int_line,
93 &nhw_rng_irq_map[inst]);
94 }
95
96 /**
97 * TASK_START triggered handler
98 */
nhw_RNG_TASK_START(void)99 void nhw_RNG_TASK_START(void) {
100 if (RNG_hw_started) {
101 return;
102 }
103 RNG_hw_started = true;
104 nhw_rng_schedule_next(true);
105 }
106
107 /**
108 * TASK_STOP triggered handler
109 */
nhw_RNG_TASK_STOP(void)110 void nhw_RNG_TASK_STOP(void) {
111 RNG_hw_started = false;
112 Timer_RNG = TIME_NEVER;
113 nsi_hws_find_next_event();
114 }
115
NHW_SIDEEFFECTS_TASKS_si(RNG,START)116 NHW_SIDEEFFECTS_TASKS_si(RNG, START)
117 NHW_SIDEEFFECTS_TASKS_si(RNG, STOP)
118
119 #if (NHW_HAS_DPPI)
120 NHW_SIDEEFFECTS_SUBSCRIBE_si(RNG, START)
121 NHW_SIDEEFFECTS_SUBSCRIBE_si(RNG, STOP)
122 #endif /* NHW_HAS_DPPI */
123
124 NHW_SIDEEFFECTS_INTSET_si(RNG, NRF_RNG_regs., RNG_INTEN)
125 NHW_SIDEEFFECTS_INTCLR_si(RNG, NRF_RNG_regs., RNG_INTEN)
126
127 NHW_SIDEEFFECTS_EVENTS(RNG)
128
129 static NHW_SIGNAL_EVENT_si(RNG, VALRDY)
130
131 static void nhw_RNG_signal_VALRDY(uint periph_inst) {
132 if (NRF_RNG_regs.SHORTS & RNG_SHORTS_VALRDY_STOP_Msk) {
133 nhw_RNG_TASK_STOP();
134 }
135
136 nhw_RNG_signal_EVENTS_VALRDY(periph_inst);
137 }
138
139 /**
140 * Time has come when a new random number is ready
141 */
nhw_rng_timer_triggered(void)142 static void nhw_rng_timer_triggered(void) {
143 //We generate a proper random number even if CONFIG is not set to correct the bias:
144 NRF_RNG_regs.VALUE = bs_random_uint32();
145
146 nhw_rng_schedule_next(false);
147
148 nhw_RNG_signal_VALRDY(0);
149 }
150
151 NSI_HW_EVENT(Timer_RNG, nhw_rng_timer_triggered, 50);
152