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