Skip to content

Commit 285f82f

Browse files
committed
[Silicon Labs] Add TRNG support
Adding support for the TRNG peripheral present on Series 1 Configuration 2 devices.
1 parent bdf13cb commit 285f82f

File tree

5 files changed

+675
-2
lines changed

5 files changed

+675
-2
lines changed

targets/TARGET_Silicon_Labs/TARGET_EFM32/common/objects.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,12 @@ struct flash_s {
151151
};
152152
#endif
153153

154+
#if DEVICE_TRNG
155+
struct trng_s {
156+
TRNG_TypeDef *instance;
157+
};
158+
#endif
159+
154160
#ifdef __cplusplus
155161
}
156162
#endif
Lines changed: 373 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,373 @@
1+
/*
2+
* True Random Number Generator (TRNG) driver for Silicon Labs devices
3+
*
4+
* Copyright (C) 2016, Silicon Labs, http://www.silabs.com
5+
* SPDX-License-Identifier: Apache-2.0
6+
*
7+
* Licensed under the Apache License, Version 2.0 (the "License"); you may
8+
* not use this file except in compliance with the License.
9+
* You may obtain a copy of the License at
10+
*
11+
* http://www.apache.org/licenses/LICENSE-2.0
12+
*
13+
* Unless required by applicable law or agreed to in writing, software
14+
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
15+
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
* See the License for the specific language governing permissions and
17+
* limitations under the License.
18+
*/
19+
#include "sl_trng.h"
20+
21+
#if defined(TRNG_PRESENT)
22+
#include "em_cmu.h"
23+
#include "em_common.h"
24+
#include <string.h>
25+
26+
#define FIFO_LEVEL_RETRY (1000)
27+
#define TEST_WORDS_MIN (257)
28+
29+
#define TEST_VECTOR_CONDITIONING_KEY_SIZE (4)
30+
static const uint32_t
31+
test_vector_conditioning_key[TEST_VECTOR_CONDITIONING_KEY_SIZE] =
32+
{0x16157E2B, 0xA6D2AE28, 0x8815F7AB, 0x3C4FCF09};
33+
34+
#define TEST_VECTOR_CONDITIONING_INPUT_SIZE (16)
35+
static const uint32_t
36+
test_vector_conditioning_input[TEST_VECTOR_CONDITIONING_INPUT_SIZE] =
37+
{0xE1BCC06B, 0x9199452A, 0x1A7434E1, 0x25199E7F,
38+
0x578A2DAE, 0x9CAC031E, 0xAC6FB79E, 0x518EAF45,
39+
0x461CC830, 0x11E45CA3, 0x19C1FBE5, 0xEF520A1A,
40+
0x45249FF6, 0x179B4FDF, 0x7B412BAD, 0x10376CE6};
41+
42+
#define TEST_VECTOR_CONDITIONING_OUTPUT_SIZE (4)
43+
static const uint32_t
44+
test_vector_conditioning_output[TEST_VECTOR_CONDITIONING_OUTPUT_SIZE] =
45+
{0xA1CAF13F, 0x09AC1F68, 0x30CA0E12, 0xA7E18675};
46+
47+
#define TRNG_STARTUP_TEST_WAIT_RETRY (10000)
48+
49+
typedef struct {
50+
TRNG_TypeDef *instance;
51+
CMU_Clock_TypeDef clock;
52+
} sl_trng_device_t;
53+
54+
static const sl_trng_device_t sl_trng_devices[TRNG_COUNT] =
55+
{
56+
#if defined(TRNG0)
57+
{
58+
TRNG0,
59+
cmuClock_TRNG0
60+
},
61+
#endif
62+
};
63+
64+
static CMU_Clock_TypeDef sl_trng_get_clock( TRNG_TypeDef *device )
65+
{
66+
for(int i = 0; i < TRNG_COUNT; i++) {
67+
if(sl_trng_devices[i].instance == device) {
68+
return sl_trng_devices[i].clock;
69+
}
70+
}
71+
return cmuClock_TRNG0;
72+
}
73+
74+
void sl_trng_init( TRNG_TypeDef *device )
75+
{
76+
int i;
77+
78+
/* Enable the TRNG's clock. */
79+
CMU_ClockEnable( sl_trng_get_clock(device), true );
80+
81+
device->CONTROL =
82+
TRNG_CONTROL_ENABLE |
83+
TRNG_CONTROL_REPCOUNTIEN |
84+
TRNG_CONTROL_APT64IEN |
85+
TRNG_CONTROL_APT4096IEN |
86+
TRNG_CONTROL_PREIEN |
87+
TRNG_CONTROL_ALMIEN;
88+
89+
/* Apply software reset */
90+
sl_trng_soft_reset(device);
91+
92+
/* Wait for TRNG to complete startup tests and start filling the FIFO. */
93+
for (i=0; (device->FIFOLEVEL == 0) && (i<TRNG_STARTUP_TEST_WAIT_RETRY); i++);
94+
EFM_ASSERT(i<TRNG_STARTUP_TEST_WAIT_RETRY);
95+
}
96+
97+
void sl_trng_free( TRNG_TypeDef *device )
98+
{
99+
/* Disable TRNG. */
100+
device->CONTROL = 0;
101+
102+
/* Disable the TRNG clock. */
103+
CMU_ClockEnable( sl_trng_get_clock(device), false );
104+
}
105+
106+
void sl_trng_soft_reset( TRNG_TypeDef *device )
107+
{
108+
uint32_t ctrl = device->CONTROL;
109+
110+
ctrl |= TRNG_CONTROL_SOFTRESET;
111+
112+
device->CONTROL = ctrl;
113+
114+
ctrl &= ~TRNG_CONTROL_SOFTRESET;
115+
device->CONTROL = ctrl;
116+
}
117+
118+
static inline
119+
void sl_trng_write_test_data( TRNG_TypeDef *device, uint32_t data )
120+
{
121+
/* Wait for TESTDATA register to become ready for next word. */
122+
while (device->STATUS & TRNG_STATUS_TESTDATABUSY);
123+
device->TESTDATA = data;
124+
}
125+
126+
static void sl_trng_clear_fifo( TRNG_TypeDef *device )
127+
{
128+
volatile uint32_t val32;
129+
130+
/* Empty FIFO */
131+
while ( device->FIFOLEVEL )
132+
{
133+
val32 = device->FIFO;
134+
(void)val32;
135+
}
136+
}
137+
138+
int sl_trng_set_key( TRNG_TypeDef *device, const unsigned char *key )
139+
{
140+
uint32_t *_key = (uint32_t*) key;
141+
142+
sl_trng_clear_fifo(device);
143+
144+
/* Program key in KEY registers of the TRNG. */
145+
device->KEY0 = *_key++;
146+
device->KEY1 = *_key++;
147+
device->KEY2 = *_key++;
148+
device->KEY3 = *_key++;
149+
150+
return 0;
151+
}
152+
153+
int sl_trng_check_conditioning( TRNG_TypeDef *device )
154+
{
155+
uint32_t val32;
156+
int i, ret=0;
157+
uint32_t ctrl = device->CONTROL;
158+
159+
/* Setup control register */
160+
device->CONTROL = TRNG_CONTROL_ENABLE | TRNG_CONTROL_TESTEN |
161+
TRNG_CONTROL_BYPNIST | TRNG_CONTROL_BYPAIS31;
162+
163+
/* Apply software reset */
164+
sl_trng_soft_reset(device);
165+
166+
/* Write test vector to the key register. */
167+
sl_trng_set_key(device,
168+
(const unsigned char*)test_vector_conditioning_key);
169+
170+
/* Write test vector to the TESTDATA register */
171+
for (i=0; i<TEST_VECTOR_CONDITIONING_INPUT_SIZE; i++)
172+
{
173+
sl_trng_write_test_data(device,
174+
test_vector_conditioning_input[i]);
175+
}
176+
177+
for (i=0; i<TEST_VECTOR_CONDITIONING_OUTPUT_SIZE; i++)
178+
{
179+
/* Wait for data to become available in the FIFO. */
180+
while ( 0 == device->FIFOLEVEL );
181+
/* Read output from the conditioning function */
182+
val32 = device->FIFO;
183+
/* Compare with expected test vector. */
184+
if (val32 != test_vector_conditioning_output[i])
185+
{
186+
/*
187+
mbedtls_printf("Conditioning test failed. "
188+
"Test output word %d 0x%lx. Expected 0x%lx\n",
189+
i, val32, test_vector_conditioning_output[i]);
190+
*/
191+
ret = SL_TRNG_ERR_CONDITIONING_TEST_FAILED;
192+
}
193+
}
194+
195+
/* Restore initial value of control register */
196+
device->CONTROL = ctrl;
197+
198+
return ret;
199+
}
200+
201+
static int sl_trng_check_status( TRNG_TypeDef *device )
202+
{
203+
uint32_t status = device->STATUS;
204+
205+
if ( (status & (TRNG_STATUS_PREIF
206+
| TRNG_STATUS_REPCOUNTIF
207+
| TRNG_STATUS_APT64IF
208+
| TRNG_STATUS_APT4096IF
209+
| TRNG_STATUS_ALMIF)) == 0 )
210+
{
211+
/* No errors */
212+
return 0;
213+
}
214+
215+
if ( status & TRNG_STATUS_PREIF )
216+
{
217+
/* On a preliminary noise alarm we clear the FIFO and clear
218+
* the alarm. The preliminary noise alarm is not critical. */
219+
status &= ~TRNG_STATUS_PREIF;
220+
device->STATUS = status;
221+
sl_trng_clear_fifo(device);
222+
return SL_TRNG_ERR_PRELIMINARY_NOISE_ALARM;
223+
}
224+
else
225+
{
226+
/* Clear alarm conditions by doing a TRNG soft reset. */
227+
sl_trng_soft_reset( device );
228+
if ( status & TRNG_STATUS_REPCOUNTIF )
229+
{
230+
return SL_TRNG_ERR_REPETITION_COUNT_TEST_FAILED;
231+
}
232+
if ( status & TRNG_STATUS_APT64IF )
233+
{
234+
return SL_TRNG_ERR_ADAPTIVE_PROPORTION_TEST_64_FAILED;
235+
}
236+
if ( status & TRNG_STATUS_APT4096IF )
237+
{
238+
return SL_TRNG_ERR_ADAPTIVE_PROPORTION_TEST_4096_FAILED;
239+
}
240+
if ( status & TRNG_STATUS_ALMIF )
241+
{
242+
return SL_TRNG_ERR_NOISE_ALARM;
243+
}
244+
}
245+
246+
return 0;
247+
}
248+
249+
int sl_trng_check_entropy( TRNG_TypeDef *device )
250+
{
251+
volatile uint32_t val32;
252+
int i, ret = 0;
253+
uint32_t ctrl = device->CONTROL;
254+
255+
/* Setup control register */
256+
device->CONTROL =
257+
TRNG_CONTROL_ENABLE |
258+
TRNG_CONTROL_REPCOUNTIEN |
259+
TRNG_CONTROL_APT64IEN |
260+
TRNG_CONTROL_APT4096IEN |
261+
TRNG_CONTROL_PREIEN |
262+
TRNG_CONTROL_ALMIEN;
263+
264+
/* Apply software reset */
265+
sl_trng_soft_reset(device);
266+
267+
/* Check FIFO level is non-zero . */
268+
for (i=0; i<FIFO_LEVEL_RETRY; i++)
269+
{
270+
if ( device->FIFOLEVEL )
271+
{
272+
break;
273+
}
274+
}
275+
/* Check for no data within timeout (max retry count) */
276+
if (i>=FIFO_LEVEL_RETRY)
277+
{
278+
ret = SL_TRNG_ERR_NO_DATA;
279+
}
280+
else
281+
{
282+
/* Read at least 4097x2 bits (~257 x 32 bits) in order for the longest
283+
test to complete (adaptive proportion test of 4096 samples). */
284+
for (i=0; i<TEST_WORDS_MIN; i++)
285+
{
286+
val32 = device->FIFO;
287+
(void)val32;
288+
}
289+
290+
/* Check in status register for errors. */
291+
ret = sl_trng_check_status( device );
292+
}
293+
294+
/* Restore initial value of control register */
295+
device->CONTROL = ctrl;
296+
297+
return ret;
298+
}
299+
300+
static void sl_trng_read_chunk( TRNG_TypeDef *device,
301+
unsigned char *output,
302+
size_t len )
303+
{
304+
uint32_t * out32 = (uint32_t *) output;
305+
uint32_t tmp;
306+
307+
/* Read known good available data. */
308+
while ( len >= 4)
309+
{
310+
*out32++ = device->FIFO;
311+
len -= 4;
312+
}
313+
314+
/* Handle the case where len is not a multiple of 4. */
315+
if ( len < 4 )
316+
{
317+
tmp = device->FIFO;
318+
memcpy((uint8_t *)out32, (const uint8_t *) &tmp, len);
319+
}
320+
}
321+
322+
int sl_trng_poll( TRNG_TypeDef *device,
323+
unsigned char *output,
324+
size_t len,
325+
size_t *olen )
326+
{
327+
size_t output_len = 0;
328+
size_t count = 0;
329+
size_t available;
330+
int ret = 0;
331+
332+
while (len > 0)
333+
{
334+
available = device->FIFOLEVEL * 4;
335+
if (available == 0)
336+
{
337+
break;
338+
}
339+
340+
#if !defined(SL_TRNG_IGNORE_ALL_ALARMS)
341+
/* Check status for current data in FIFO
342+
* and handle any error conditions. */
343+
ret = sl_trng_check_status( device );
344+
#if defined(SL_TRNG_IGNORE_NOISE_ALARMS)
345+
/* Ignore noise alarms by returning 0 (OK) if they occur and
346+
* keeping the already generated random data. */
347+
if ( (ret == SL_TRNG_ERR_PRELIMINARY_NOISE_ALARM) ||
348+
(ret == SL_TRNG_ERR_NOISE_ALARM) )
349+
{
350+
ret = 0;
351+
break;
352+
}
353+
#endif
354+
/* Alarm has been signaled so we throw the generated data away. */
355+
if (ret != 0)
356+
{
357+
output_len = 0;
358+
break;
359+
}
360+
#endif
361+
362+
count = SL_MIN(len, available);
363+
sl_trng_read_chunk(device, output, count);
364+
output += count;
365+
output_len += count;
366+
len -= count;
367+
}
368+
369+
*olen = output_len;
370+
return ret;
371+
}
372+
373+
#endif /* TRNG_PRESENT */

0 commit comments

Comments
 (0)