1 //*****************************************************************************
2 //
3 //! @file am_hal_entropy.c
4 //!
5 //! @brief Functions for Generating True Random Numbers.
6 //!
7 //! @addtogroup entropy3 Entropy - Random Number Generation Functions
8 //! @ingroup apollo3_hal
9 //! @{
10 //
11 //*****************************************************************************
12
13 //*****************************************************************************
14 //
15 // Copyright (c) 2023, Ambiq Micro, Inc.
16 // All rights reserved.
17 //
18 // Redistribution and use in source and binary forms, with or without
19 // modification, are permitted provided that the following conditions are met:
20 //
21 // 1. Redistributions of source code must retain the above copyright notice,
22 // this list of conditions and the following disclaimer.
23 //
24 // 2. Redistributions in binary form must reproduce the above copyright
25 // notice, this list of conditions and the following disclaimer in the
26 // documentation and/or other materials provided with the distribution.
27 //
28 // 3. Neither the name of the copyright holder nor the names of its
29 // contributors may be used to endorse or promote products derived from this
30 // software without specific prior written permission.
31 //
32 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
33 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
34 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
35 // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
36 // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
37 // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
38 // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
39 // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
40 // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
41 // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
42 // POSSIBILITY OF SUCH DAMAGE.
43 //
44 // This is part of revision release_sdk_3_1_1-10cda4b5e0 of the AmbiqSuite Development Package.
45 //
46 //*****************************************************************************
47
48 #include <stdint.h>
49 #include <stdbool.h>
50
51 #include "am_mcu_apollo.h"
52
53 //******************************************************************************
54 //
55 // Entropy configuration
56 //
57 //******************************************************************************
58
59 // Note: To change this, you will also need to change the timer configuration below.
60 #define LFRC_FREQ 512
61
62 #define MEASURE_PERIOD_MS 10
63
64 #define LFRC_TICKS (((MEASURE_PERIOD_MS) * LFRC_FREQ) / 1000)
65 #define HFRC_FREQ 48000000
66
67 // Note: Done in this order to keep the numbers below 32-bit max integer. This
68 // should be okay, because HFRC_FREQ/LFRC_FREQ should be a whole number.
69 #define HFRC_TICKS_EXPECTED ((HFRC_FREQ / LFRC_FREQ) * LFRC_TICKS)
70
71 //******************************************************************************
72 //
73 //! Entropy collector structure.
74 //
75 //******************************************************************************
76 typedef struct am_hal_entropy_collector_s
77 {
78 uint8_t *pui8Data;
79 uint32_t ui32Length;
80 uint32_t ui32Index;
81 am_hal_entropy_callback_t pfnCallback;
82 void *pvContext;
83 }
84 am_hal_entropy_collector_t;
85
86 //******************************************************************************
87 //
88 //! Globals
89 //
90 //******************************************************************************
91 uint32_t am_hal_entropy_timing_error_count = 0;
92
93 static volatile uint32_t g_ui32LastSysTick = 0xFFFFFF;
94
95 static am_hal_entropy_collector_t g_sEntropyCollector;
96 static uint32_t g_ui32LastSleepCount = 0;
97
98 //******************************************************************************
99 //
100 // CTimer configuration.
101 //
102 //******************************************************************************
103 #if AM_HAL_ENTROPY_CTIMER_TIMERX == AM_HAL_CTIMER_TIMERA
104 am_hal_ctimer_config_t sENTROPYTimerConfig =
105 {
106 .ui32Link = 0,
107
108 .ui32TimerAConfig = (AM_HAL_CTIMER_FN_REPEAT |
109 AM_HAL_CTIMER_INT_ENABLE |
110 AM_HAL_CTIMER_LFRC_512HZ),
111
112 .ui32TimerBConfig = 0
113 };
114 #elif AM_HAL_ENTROPY_CTIMER_TIMERX == AM_HAL_CTIMER_TIMERB
115 am_hal_ctimer_config_t sENTROPYTimerConfig =
116 {
117 .ui32Link = 0,
118
119 .ui32TimerAConfig = 0,
120
121 .ui32TimerBConfig = (AM_HAL_CTIMER_FN_REPEAT |
122 AM_HAL_CTIMER_INT_ENABLE |
123 AM_HAL_CTIMER_LFRC_512HZ)
124
125 };
126 #endif
127
128 //*****************************************************************************
129 //! @brief CTIMER ISR
130 //!
131 //*****************************************************************************
132 static void
entropy_ctimer_isr(void)133 entropy_ctimer_isr(void)
134 {
135 uint32_t ui32RandomValue;
136 uint32_t ui32CurrentSysTick;
137 uint32_t ui32ElapsedTicks;
138
139 //
140 // Read the current time first.
141 //
142 ui32CurrentSysTick = am_hal_systick_count();
143
144 //
145 // Calculate the elapsed time.
146 //
147 // We should be able to do modular subtraction here as long as our sample
148 // window is less than (SYSTICK_MAX / HFRC_FREQUENCY). Sometimes the value
149 // will wrap, but it will still give the right answer unless we wrap twice.
150 //
151 // For an HFRC frequency of 48 MHz and a Systick max of 0xFFFFFF, we should
152 // be okay for ~350 ms.
153 //
154 ui32ElapsedTicks = (g_ui32LastSysTick - ui32CurrentSysTick) % 0x1000000;
155
156 //
157 // Update our "last seen" SysTick time.
158 //
159 g_ui32LastSysTick = ui32CurrentSysTick;
160
161 am_hal_ctimer_int_clear(AM_HAL_CTIMER_INT_TIMERA0);
162
163 //
164 // If the core has gone to sleep since the last time we ran this interrupt
165 // routine, we can't trust the random number we got. In that case, just
166 // skip this interrupt and wait for the next one.
167 //
168 if (g_ui32LastSleepCount != g_am_hal_sysctrl_sleep_count)
169 {
170 g_ui32LastSleepCount = g_am_hal_sysctrl_sleep_count;
171 return;
172 }
173 else
174 {
175 g_ui32LastSleepCount = g_am_hal_sysctrl_sleep_count;
176 }
177
178 //
179 // For informational purposes only.
180 //
181 // This is a tracking variable to make sure our HFRC time interval is
182 // reasonable. The variation in the HFRC should be far less than +/-50% in
183 // normal circumstances. If this error count increments, it suggests that
184 // something in the system is interfering with the timing of the entropy
185 // system.
186 //
187 // In practice, this might happen during a debugger halt or if CTIMER
188 // interrupt latency is too high.
189 //
190 if ((ui32ElapsedTicks > HFRC_TICKS_EXPECTED * 2) ||
191 (ui32ElapsedTicks < HFRC_TICKS_EXPECTED / 2))
192 {
193 am_hal_entropy_timing_error_count++;
194 }
195
196
197 //
198 // If we have an active Entropy context, we should start feeding random
199 // numbers into it.
200 //
201 if (g_sEntropyCollector.pui8Data)
202 {
203 if (g_sEntropyCollector.ui32Index < g_sEntropyCollector.ui32Length)
204 {
205 //
206 // Add the data to the buffer.
207 //
208 ui32RandomValue = ui32ElapsedTicks - (uint32_t) HFRC_TICKS_EXPECTED;
209 g_sEntropyCollector.pui8Data[g_sEntropyCollector.ui32Index++] = (ui32RandomValue & 0xFF);
210 }
211 else
212 {
213 //
214 // If we've captured all of the data we need, clear the global
215 // variables and call the callback.
216 //
217 g_sEntropyCollector.pui8Data = 0;
218 g_sEntropyCollector.ui32Length = 0;
219 g_sEntropyCollector.ui32Index = 0;
220
221 g_sEntropyCollector.pfnCallback(g_sEntropyCollector.pvContext);
222
223 g_sEntropyCollector.pfnCallback = 0;
224 g_sEntropyCollector.pvContext = 0;
225
226 }
227 }
228 }
229
230 //*****************************************************************************
231 //! @brief CTIMER initialization.
232 //
233 //*****************************************************************************
234 static void
entropy_ctimer_init(void)235 entropy_ctimer_init(void)
236 {
237 //
238 // Enable the LFRC.
239 //
240 am_hal_clkgen_control(AM_HAL_CLKGEN_CONTROL_LFRC_START, AM_HAL_ENTROPY_CTIMER);
241
242 //
243 // Set up timer A0.
244 //
245 am_hal_ctimer_clear(AM_HAL_ENTROPY_CTIMER, AM_HAL_ENTROPY_CTIMER_TIMERX);
246 am_hal_ctimer_config(AM_HAL_ENTROPY_CTIMER, &sENTROPYTimerConfig);
247 am_hal_ctimer_period_set(AM_HAL_ENTROPY_CTIMER, AM_HAL_ENTROPY_CTIMER_TIMERX, (LFRC_TICKS - 1), 0);
248 am_hal_ctimer_int_clear(AM_HAL_ENTROPY_CTIMER_INT);
249 }
250
251 //*****************************************************************************
252 // @brief Initialize the ENTROPY
253 //
254 //*****************************************************************************
255 void
am_hal_entropy_init(void)256 am_hal_entropy_init(void)
257 {
258 // Configure the timer
259 entropy_ctimer_init();
260
261 // Register our interrupt handler for the CTIMER interrupt.
262 am_hal_ctimer_int_register(AM_HAL_ENTROPY_CTIMER_INT, entropy_ctimer_isr);
263
264 // Enable interrupt for CTIMER
265 am_hal_ctimer_int_enable(AM_HAL_ENTROPY_CTIMER_INT);
266 NVIC_EnableIRQ(CTIMER_IRQn);
267 }
268
269 //*****************************************************************************
270 // @brief Start the entropy timers.
271 //
272 //*****************************************************************************
273 void
am_hal_entropy_enable(void)274 am_hal_entropy_enable(void)
275 {
276 //
277 // Reset our global error count.
278 //
279 am_hal_entropy_timing_error_count = 0;
280
281 //
282 // Make all of our timers are starting from a known-good state.
283 //
284 g_ui32LastSysTick = 0x00FFFFFF;
285 am_hal_systick_load(0x00FFFFFF);
286 am_hal_ctimer_clear(AM_HAL_ENTROPY_CTIMER, AM_HAL_ENTROPY_CTIMER_TIMERX);
287
288 //
289 // Start both SysTick and the CTIMER.
290 //
291 am_hal_systick_start();
292 am_hal_ctimer_start(AM_HAL_ENTROPY_CTIMER, AM_HAL_ENTROPY_CTIMER_TIMERX);
293 }
294
295 //*****************************************************************************
296 //
297 // @brief Stop the entropy timers.
298 //
299 //*****************************************************************************
300 void
am_hal_entropy_disable(void)301 am_hal_entropy_disable(void)
302 {
303 am_hal_systick_stop();
304 am_hal_ctimer_stop(AM_HAL_ENTROPY_CTIMER, AM_HAL_ENTROPY_CTIMER_TIMERX);
305 }
306
307 //*****************************************************************************
308 //
309 // @brief Place the next N values from the entropy collector into the output location
310 //
311 // @param pui8Output - where to put the data.
312 // @param ui32Length - how much data you want.
313 // @param pfnCallback - function to call when the data is ready.
314 // @param pvContext - will be passed to the callback. can be used for anything.
315 //
316 // @return uint32_t - generic satatus
317 //*****************************************************************************
318 uint32_t
am_hal_entropy_get_values(uint8_t * pui8Output,uint32_t ui32Length,am_hal_entropy_callback_t pfnCallback,void * pvContext)319 am_hal_entropy_get_values(uint8_t *pui8Output, uint32_t ui32Length,
320 am_hal_entropy_callback_t pfnCallback, void *pvContext)
321 {
322 uint32_t ui32RetValue = AM_HAL_STATUS_SUCCESS;
323
324 AM_CRITICAL_BEGIN;
325
326 if (g_sEntropyCollector.pui8Data == 0)
327 {
328 g_sEntropyCollector.pui8Data = pui8Output;
329 g_sEntropyCollector.ui32Length = ui32Length;
330 g_sEntropyCollector.pfnCallback = pfnCallback;
331 g_sEntropyCollector.pvContext = pvContext;
332 g_sEntropyCollector.ui32Index = 0;
333 }
334 else
335 {
336 ui32RetValue = AM_HAL_STATUS_FAIL;
337 }
338
339 AM_CRITICAL_END;
340
341 return ui32RetValue;
342 }
343 //*****************************************************************************
344 //
345 // End Doxygen group.
346 //! @}
347 //
348 //*****************************************************************************
349