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