1 /*
2 * Copyright (c) 2014 Mellanox Technologies, Inc.
3 * All rights reserved.
4 * Copyright (c) 2016 Research Organization for Information Science
5 * and Technology (RIST). All rights reserved.
6 * $COPYRIGHT$
7 *
8 * Additional copyrights may follow
9 *
10 * $HEADER$
11 */
12
13 #include "opal_config.h"
14
15 #include <string.h>
16
17 #include "alfg.h"
18
19 /* Mask corresponding to the primitive polynomial
20 *---------------------------------------------------
21 *
22 * p(x) = 1 + x^25 + x^27 + x^29 + x^30 + x^31 + x^32
23 *
24 *---------------------------------------------------
25 */
26 #define MASK 0x80000057U
27
28 /* Additive lagged Fibonacci parameters:
29 *---------------------------------------------------
30 *
31 * x_n = (x_(n - TAP1) + x_(n - TAP2) ) mod M
32 *
33 *---------------------------------------------------
34 */
35 #define TAP1 127
36 #define TAP2 97
37 #define CBIT 21 /* Canonical bit */
38
39
40 /**
41 * @brief Galois shift register: Used to seed the ALFG's
42 * canonical rectangle
43 *
44 * @param[in] unsigned int *seed: used to seed the Galois register
45 * @param[out] uint32_t lsb: least significant bit of the Galois
46 * register after shift
47 */
48 static uint32_t galois(unsigned int *seed){
49
50 uint32_t lsb;
51 lsb = (*seed & 1) ? 1 : 0;
52 *seed >>= 1;
53 /* tap it with the mask */
54 *seed = *seed ^ (lsb*MASK);
55
56 return lsb;
57 }
58
59 /* OPAL global rng buffer */
60 static opal_rng_buff_t alfg_buffer;
61
62 /**
63 * @brief Routine to seed the ALFG register
64 *
65 * @param[in] uint32_t seed
66 * @param[out] opal_rng_buff_t *buff: handle to ALFG buffer state
67 */
68 int opal_srand(opal_rng_buff_t *buff, uint32_t seed) {
69
70 int i, j;
71 uint32_t seed_cpy = seed;
72 buff->tap1 = TAP1 - 1;
73 buff->tap2 = TAP2 - 1;
74
75 /* zero out the register */
76 for( i = 0; i < TAP1; i++){
77 buff->alfg[i] = 0;
78 }
79 /* set the canonical bit */
80 buff->alfg[CBIT] = 1;
81
82 /* seed the ALFG register by blasting
83 * the canonical rectangle with bits
84 */
85 for ( j = 1; j < TAP1; j++){
86 for( i = 1; i < 32; i++){
87 buff->alfg[j] = buff->alfg[j] ^ ((galois(&seed_cpy))<<i);
88 }
89 }
90 /* copy the ALFG to the global buffer */
91 memcpy(&alfg_buffer, buff, sizeof(alfg_buffer));
92
93 return 1;
94
95 }
96
97 /**
98 * @brief The additive lagged Fibonnaci PRNG
99 *
100 * @param[in] opal_rng_buff_t *buff: handle to ALFG buffer state
101 * @param[out] 32-bit unsigned random integer
102 */
103
104 uint32_t opal_rand(opal_rng_buff_t *buff){
105
106 int *tap1 = &(buff->tap1);
107 int *tap2 = &(buff->tap2);
108 uint64_t overflow;
109 uint32_t temp;
110
111 /* prevent overflow */
112 overflow = (uint64_t) buff->alfg[*tap1] + (uint64_t) buff->alfg[*tap2];
113 /* circular buffer arithmetic */
114 temp = (*tap1 + 1) == TAP1 ? 0 : (*tap1 + 1);
115 /* Division modulo 2^32 */
116 buff->alfg[temp] = (uint32_t) ( overflow & ((1ULL<<32) -1));
117
118 /* increment tap points */
119 *tap1 = (*tap1 + 1)%TAP1;
120 *tap2 = (*tap2 + 1)%TAP1;
121
122 return buff->alfg[temp];
123
124 }
125
126 /**
127 * @brief A wrapper for opal_rand() with our global ALFG buffer;
128 *
129 * @param[in] none
130 * @param[out] int, the same as normal rand(3)
131 */
132 int opal_random(void){
133 /* always return a positive int */
134 return (int)(opal_rand(&alfg_buffer) & 0x7FFFFFFF);
135 }