import os.path import numpy as np import itertools import Tools import math import numpy as np def q31accuracy(x): return(np.round(1.0*x * (1<<31))) def q15accuracy(x): return(np.round(1.0*x * (1<<15))) def q7accuracy(x): return(np.round(1.0*x * (1<<7))) def Q31toF32(x): return(1.0*x / 2**31) def Q15toF32(x): return(1.0*x / 2**15) def Q7toF32(x): return(1.0*x / 2**7) # Those patterns are used for tests and benchmarks. # For tests, there is the need to add tests for saturation # For benchmarks NBSAMPLES=256 def cartesian(*somelists): r=[] for element in itertools.product(*somelists): r.append(element) return(r) # Fixed point division should not be called with a denominator of zero. # But if it is, it should return a saturated result. def divide(f,r): e = 0 a,b=r if f == Tools.Q31: e = 1.0 / (1<<31) a = 1.0*q31accuracy(a) / (2**31) b = 1.0*q31accuracy(b) / (2**31) if f == Tools.Q15: e = 1.0 / (1<<15) a = 1.0*q15accuracy(a) / (2**15) b = 1.0*q15accuracy(b) / (2**15) if f == Tools.Q7: e = 1.0 / (1<<7) a = 1.0*q7accuracy(a) / (2**7) b = 1.0*q7accuracy(b) / (2**7) if b == 0.0: if a >= 0.0: return(1.0,0) else: return(-1.0,0) k = 0 while abs(a) > abs(b): a = a / 2.0 k = k + 1 # In C code we don't saturate but instead generate the right value # with a shift of 1. # So this test is to ease the comparison between the Python reference # and the output of the division algorithm in C if abs(a/b) > 1 - e: a = a / 2.0 k = k + 1 return(a/b,k) def initLogValues(format): if format == Tools.Q15: vals=np.linspace(np.float_power(2,-15),1.0,num=125) elif format == Tools.F16: vals=np.linspace(np.float_power(2,-10),1.0,num=125) else: vals=np.linspace(np.float_power(2,-31),1.0,num=125) ref=np.log(vals) if format==Tools.Q31 : # Format must be Q5.26 ref = ref / 32.0 if format == Tools.Q15: # Format must be Q4.11 ref = ref / 16.0 return(vals,ref) def writeTests(config,format): 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]) 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]) a3 = a1 + 2*math.pi angles=np.concatenate((a1,a2,a3)) refcos = np.cos(angles) refsin = np.sin(angles) vals=np.linspace(0.0,1.0,1024) sqrtvals=np.sqrt(vals) # Negative values in CMSIS are giving 0 vals[0] = -0.4 sqrtvals[0] = 0.0 if format != Tools.F64 and format != 0 and format != 16: angles=np.concatenate((a1,a2,a1)) angles = angles / (2*math.pi) config.writeInput(1, angles,"Angles") config.writeInput(1, vals,"SqrtInput") config.writeReference(1, sqrtvals,"Sqrt") config.writeReference(1, refcos,"Cos") config.writeReference(1, refsin,"Sin") # For benchmarks samples=np.random.randn(NBSAMPLES) samples = np.abs(Tools.normalize(samples)) config.writeInput(1, samples,"Samples") numerator=np.linspace(-0.9,0.9) numerator=np.hstack([numerator,np.array([-1.0,1.0])]) denominator=np.linspace(-0.9,0.9) denominator=np.hstack([denominator,np.array([-1.0,1.0])]) samples=cartesian(numerator,denominator) numerator=[x[0] for x in samples] denominator=[x[1] for x in samples] result=[divide(format,x) for x in samples] resultValue=[x[0] for x in result] resultShift=[x[1] for x in result] config.setOverwrite(True) config.writeInput(1, numerator,"Numerator") config.writeInput(1, denominator,"Denominator") config.writeReference(1, resultValue,"DivisionValue") config.writeReferenceS16(1, resultShift,"DivisionShift") config.setOverwrite(False) vals,ref=initLogValues(format) config.writeInput(1, vals,"LogInput") config.writeReference(1, ref,"Log") config.setOverwrite(False) # Testing of ATAN2 angles=np.linspace(0.0,2*math.pi,1000,endpoint=True) angles=np.hstack([angles,np.array([math.pi/4.0])]) if format == Tools.Q31 or format == Tools.Q15: radius=[1.0] else: radius=np.linspace(0.1,0.9,10,endpoint=True) combinations = cartesian(radius,angles) res=[] yx = [] for r,angle in combinations: x = r*np.cos(angle) y = r*np.sin(angle) res.append(np.arctan2(y,x)) yx.append(y) yx.append(x) config.writeInput(1, np.array(yx).flatten(),"Atan2Input") # Q2.29 or Q2.13 to represent PI in the output if format == Tools.Q31 or format == Tools.Q15: config.writeReference(1, np.array(res)/4.0,"Atan2Ref") else: config.writeReference(1, np.array(res),"Atan2Ref") config.setOverwrite(False) def writeTestsFloat(config,format): writeTests(config,format) data1 = np.random.randn(20) data1 = np.abs(data1) data1 = data1 + 1e-3 # To avoid zero values data1 = Tools.normalize(data1) samples=np.concatenate((np.array([0.0,1.0]),np.linspace(-0.4,0.4))) config.writeInput(1, samples,"ExpInput") v = np.exp(samples) config.writeReference(1, v,"Exp") # For benchmarks and other tests samples=np.random.randn(NBSAMPLES) samples = np.abs(Tools.normalize(samples)) config.writeInput(1, samples,"Samples") v = 1.0 / samples config.writeReference(1, v,"Inverse") def generatePatterns(): PATTERNDIR = os.path.join("Patterns","DSP","FastMath","FastMath") PARAMDIR = os.path.join("Parameters","DSP","FastMath","FastMath") configf64=Tools.Config(PATTERNDIR,PARAMDIR,"f64") configf32=Tools.Config(PATTERNDIR,PARAMDIR,"f32") configf16=Tools.Config(PATTERNDIR,PARAMDIR,"f16") configq31=Tools.Config(PATTERNDIR,PARAMDIR,"q31") configq15=Tools.Config(PATTERNDIR,PARAMDIR,"q15") configf64.setOverwrite(False) configf32.setOverwrite(False) configf16.setOverwrite(False) configq31.setOverwrite(False) configq15.setOverwrite(False) writeTestsFloat(configf64,Tools.F64) writeTestsFloat(configf32,0) writeTestsFloat(configf16,16) writeTests(configq31,31) writeTests(configq15,15) if __name__ == '__main__': generatePatterns()