1import os.path
2import numpy as np
3import itertools
4import Tools
5import math
6
7import numpy as np
8
9def q31accuracy(x):
10     return(np.round(1.0*x * (1<<31)))
11
12def q15accuracy(x):
13     return(np.round(1.0*x * (1<<15)))
14
15def q7accuracy(x):
16     return(np.round(1.0*x * (1<<7)))
17
18def Q31toF32(x):
19     return(1.0*x / 2**31)
20
21def Q15toF32(x):
22     return(1.0*x / 2**15)
23
24def Q7toF32(x):
25     return(1.0*x / 2**7)
26
27# Those patterns are used for tests and benchmarks.
28# For tests, there is the need to add tests for saturation
29
30# For benchmarks
31NBSAMPLES=256
32
33def cartesian(*somelists):
34   r=[]
35   for element in itertools.product(*somelists):
36       r.append(element)
37   return(r)
38
39# Fixed point division should not be called with a denominator of zero.
40# But if it is, it should return a saturated result.
41def divide(f,r):
42    e = 0
43    a,b=r
44
45    if f == Tools.Q31:
46        e = 1.0 / (1<<31)
47        a = 1.0*q31accuracy(a) / (2**31)
48        b = 1.0*q31accuracy(b) / (2**31)
49    if f == Tools.Q15:
50        e = 1.0 / (1<<15)
51        a = 1.0*q15accuracy(a) / (2**15)
52        b = 1.0*q15accuracy(b) / (2**15)
53    if f == Tools.Q7:
54        e = 1.0 / (1<<7)
55        a = 1.0*q7accuracy(a) / (2**7)
56        b = 1.0*q7accuracy(b) / (2**7)
57
58    if b == 0.0:
59        if a >= 0.0:
60           return(1.0,0)
61        else:
62           return(-1.0,0)
63
64    k = 0
65    while abs(a) > abs(b):
66       a = a / 2.0
67       k = k + 1
68    # In C code we don't saturate but instead generate the right value
69    # with a shift of 1.
70    # So this test is to ease the comparison between the Python reference
71    # and the output of the division algorithm in C
72    if abs(a/b) > 1 - e:
73       a = a / 2.0
74       k = k + 1
75
76    return(a/b,k)
77
78
79def initLogValues(format):
80    if format == Tools.Q15:
81        vals=np.linspace(np.float_power(2,-15),1.0,num=125)
82    elif format == Tools.F16:
83        vals=np.linspace(np.float_power(2,-10),1.0,num=125)
84    else:
85        vals=np.linspace(np.float_power(2,-31),1.0,num=125)
86
87
88    ref=np.log(vals)
89    if format==Tools.Q31 :
90        # Format must be Q5.26
91        ref = ref / 32.0
92    if format == Tools.Q15:
93        # Format must be Q4.11
94        ref = ref / 16.0
95    return(vals,ref)
96
97def normalizeToOne(x):
98    s = 0
99    while (abs(x)>1):
100        x = x /2.0
101        s = s + 1
102    return(int(s),x)
103
104def writeTests(config,format):
105
106    a1=np.array([0,math.pi/4,math.pi/2,3*math.pi/4,math.pi,5*math.pi/4,3*math.pi/2,2*math.pi-1e-6])
107    a2=np.array([-math.pi/4,-math.pi/2,-3*math.pi/4,-math.pi,-5*math.pi/4,-3*math.pi/2,-2*math.pi-1e-6])
108    a3 = a1 + 2*math.pi
109    angles=np.concatenate((a1,a2,a3))
110    refcos = np.cos(angles)
111    refsin = np.sin(angles)
112
113
114    vals=np.linspace(0.0,1.0,1024)
115    sqrtvals=np.sqrt(vals)
116
117    # Negative values in CMSIS are giving 0
118    vals[0] = -0.4
119    sqrtvals[0] = 0.0
120
121    if format != Tools.F64 and format != 0 and format != 16:
122        angles=np.concatenate((a1,a2,a1))
123        angles = angles / (2*math.pi)
124    config.writeInput(1, angles,"Angles")
125
126    config.writeInput(1, vals,"SqrtInput")
127    config.writeReference(1, sqrtvals,"Sqrt")
128
129    config.writeReference(1, refcos,"Cos")
130    config.writeReference(1, refsin,"Sin")
131
132
133    # For benchmarks
134    samples=np.random.randn(NBSAMPLES)
135    samples = np.abs(Tools.normalize(samples))
136    config.writeInput(1, samples,"Samples")
137
138    numerator=np.linspace(-0.9,0.9)
139    numerator=np.hstack([numerator,np.array([-1.0,1.0])])
140    denominator=np.linspace(-0.9,0.9)
141    denominator=np.hstack([denominator,np.array([-1.0,1.0])])
142
143    samples=cartesian(numerator,denominator)
144    numerator=[x[0] for x in samples]
145    denominator=[x[1] for x in samples]
146
147    result=[divide(format,x) for x in samples]
148
149    resultValue=[x[0] for x in result]
150    resultShift=[x[1] for x in result]
151
152    config.setOverwrite(False)
153    config.writeInput(1, numerator,"Numerator")
154    config.writeInput(1, denominator,"Denominator")
155    config.writeReference(1, resultValue,"DivisionValue")
156    config.writeReferenceS16(1, resultShift,"DivisionShift")
157    config.setOverwrite(False)
158
159
160    vals,ref=initLogValues(format)
161    config.writeInput(1, vals,"LogInput")
162    config.writeReference(1, ref,"Log")
163
164    config.setOverwrite(False)
165
166    # Testing of ATAN2
167    angles=np.linspace(0.0,2*math.pi,1000,endpoint=True)
168    angles=np.hstack([angles,np.array([math.pi/4.0])])
169    if format == Tools.Q31 or format == Tools.Q15:
170        radius=[1.0]
171    else:
172        radius=np.linspace(0.1,0.9,10,endpoint=True)
173    combinations = cartesian(radius,angles)
174    res=[]
175    yx = []
176    for r,angle in combinations:
177        x = r*np.cos(angle)
178        y = r*np.sin(angle)
179        res.append(np.arctan2(y,x))
180        yx.append(y)
181        yx.append(x)
182
183
184    config.writeInput(1, np.array(yx).flatten(),"Atan2Input")
185
186    # Q2.29 or Q2.13 to represent PI in the output
187    if format == Tools.Q31 or format == Tools.Q15:
188       config.writeReference(1, np.array(res)/4.0,"Atan2Ref")
189    else:
190       config.writeReference(1, np.array(res),"Atan2Ref")
191
192    config.setOverwrite(False)
193
194    if format == Tools.Q31 or format == Tools.Q15:
195
196       if format == Tools.Q31:
197          theInput=np.array([1.0-1e-6,0.6,0.5,0.3,0.25,0.1,1.0/(1<<31)])
198
199       if format == Tools.Q15:
200          theInput=np.array([1.0-1e-6,0.6,0.5,0.3,0.25,0.1,1.0/(1<<15)])
201
202       ref=1.0 / theInput
203       shiftAndScaled=np.array([normalizeToOne(x) for x in ref]).transpose()
204       shiftValues=shiftAndScaled[0].astype(np.int16)
205       scaledValues=shiftAndScaled[1]
206       #print(shiftAndScaled)
207
208
209       config.writeInput(1, np.array(theInput),"RecipInput")
210       config.writeReference(1, scaledValues,"RecipRef")
211       config.writeReferenceS16(1, shiftValues,"RecipShift")
212
213
214
215def tocint32(x):
216    if x < 0:
217        return((0x10000000000000000 + x) & 0xFFFFFFFF)
218    else:
219        return(x & 0xFFFFFFFF)
220
221# C and Python are not rounding the integer division
222# in the same way
223def cdiv(a,b):
224    sign = 1
225    if ((a<0) and (b>0)) or ((a>0) and (b<0)):
226        sign = -1
227
228    a= abs(a)
229    b = abs(b)
230
231    d = sign*(a // b)
232
233    return(d)
234
235def testInt64(config):
236    theInput=[0x1000000080000000,
237                 0x0000000080000000,
238                 0x0000000020000000,
239                 0x0000000000000000]
240
241    ref=[0x40000002,
242         0x40000000,
243         0x40000000,
244         0
245    ]
246    norms=[-30,-1,1,0]
247    config.writeInputU64(1,np.array(theInput),"Norm64To32_Input")
248    config.writeReferenceS16(1,norms,"RefNorm64To32_Norms")
249    config.writeReferenceS32(1,ref,"RefNorm64To32_Vals")
250
251    config.setOverwrite(False)
252
253    allCombinations=[(0x7FFFFFFFFFFFFFFF,2),
254    (-0x7FFFFFFFFFFFFFFF-1,2),
255    ( 0x4000000000000000,0x7FFFFFFF),
256    ( -0x4000000000000000,0x7FFFFFFF),
257    (  0x2000000000000000,0x7FFFFFFF),
258    ( -0x2000000000000000,0x7FFFFFFF),
259    (  0x1000000000000000,0x7FFFFFFF),
260    ( -0x1000000000000000,0x7FFFFFFF),
261    (  0x0000000080000000,2),
262    ( -0x0000000080000000,2),
263    (  0x0000000040000000,2),
264    ( -0x0000000080000000,2)
265    ]
266
267    res = [tocint32(cdiv(x,y))  for (x,y) in allCombinations]
268
269    allCombinations=np.array(allCombinations,dtype=np.int64).flatten()
270    config.writeInputS64(1,allCombinations[0::2],"DivDenInput")
271    config.writeInputS32(1,allCombinations[1::2],"DivNumInput")
272
273    config.writeReferenceU32(1, res,"DivRef")
274    config.setOverwrite(False)
275
276
277def writeTestsFloat(config,format):
278
279    writeTests(config,format)
280
281    data1 = np.random.randn(20)
282    data1 = np.abs(data1)
283    data1 = data1 + 1e-3 # To avoid zero values
284    data1 = Tools.normalize(data1)
285
286
287    samples=np.concatenate((np.array([0.0,1.0]),np.linspace(-0.4,0.4)))
288    config.writeInput(1, samples,"ExpInput")
289    v = np.exp(samples)
290    config.writeReference(1, v,"Exp")
291
292    # For benchmarks and other tests
293    samples=np.random.randn(NBSAMPLES)
294    samples = np.abs(Tools.normalize(samples))
295    config.writeInput(1, samples,"Samples")
296
297    v = 1.0 / samples
298    config.writeReference(1, v,"Inverse")
299
300
301
302
303
304def generatePatterns():
305    PATTERNDIR = os.path.join("Patterns","DSP","FastMath","FastMath")
306    PARAMDIR = os.path.join("Parameters","DSP","FastMath","FastMath")
307
308    configf64=Tools.Config(PATTERNDIR,PARAMDIR,"f64")
309    configf32=Tools.Config(PATTERNDIR,PARAMDIR,"f32")
310    configf16=Tools.Config(PATTERNDIR,PARAMDIR,"f16")
311    configq31=Tools.Config(PATTERNDIR,PARAMDIR,"q31")
312    configq15=Tools.Config(PATTERNDIR,PARAMDIR,"q15")
313
314    configq64=Tools.Config(PATTERNDIR,PARAMDIR,"q63")
315
316
317
318    configf64.setOverwrite(False)
319    configf32.setOverwrite(False)
320    configf16.setOverwrite(False)
321    configq31.setOverwrite(False)
322    configq15.setOverwrite(False)
323    configq64.setOverwrite(False)
324
325    writeTestsFloat(configf64,Tools.F64)
326    writeTestsFloat(configf32,0)
327    writeTestsFloat(configf16,16)
328    writeTests(configq31,31)
329    writeTests(configq15,15)
330
331    testInt64(configq64)
332
333
334if __name__ == '__main__':
335  generatePatterns()
336
337