def main(self, input): """ Args: input (DataValid): -1.0 ... 1.0 range, up to 18 bits Returns: DataValid: 18 bits(-1.0 ... 1.0 range) if transform size is up to 1024 points. Transforms over 1024 points start emphasizing smaller numbers e.g. 2048 would return a result with 18 bits but in -0.5 ... 0.5 range (one extra bit for smaller numbers) etc... """ var = input if self.INVERSE: var = DataValid(Complex(input.data.imag, input.data.real), input.valid) # execute stages for stage in self.stages: var = stage.main(var) if self.INVERSE: var = DataValid(Complex(var.data.imag, var.data.real), var.valid) # this part is active if transform is larger than 10 stages if self.POST_GAIN_CONTROL != 0: self.output.data = scalb(var.data, -self.POST_GAIN_CONTROL) else: self.output.data = var.data self.output.valid = var.valid return self.output
def __init__(self, fft_size, twiddle_bits=9, inverse=False, input_ordering='natural'): self._pyha_simulation_input_callback = NumpyToDataValid( dtype=default_complex) self.INPUT_ORDERING = input_ordering self.INVERSE = inverse self.FFT_SIZE = fft_size self.N_STAGES = int(np.log2(fft_size)) max_gain_control_stages = 10 self.POST_GAIN_CONTROL = max(self.N_STAGES - max_gain_control_stages, 0) self.stages = [ StageR2SDF(self.FFT_SIZE, i, twiddle_bits, inverse, input_ordering, allow_gain_control=i < max_gain_control_stages) for i in range(self.N_STAGES) ] self.output = DataValid( Complex(0, -self.POST_GAIN_CONTROL, -17 - self.POST_GAIN_CONTROL))
def test_all(input_power): dut = FFTPower() inp = (np.random.uniform(-1, 1, size=1280) + np.random.uniform(-1, 1, size=1280) * 1j) * input_power inp = [complex(Complex(x, 0, -17)) for x in inp] sims = simulate(dut, inp, pipeline_flush='auto', simulations=['MODEL', 'HARDWARE']) assert sims_close(sims, rtol=1e-20, atol=1e-20)
def __init__(self, cordic_iterations=14): """ :param cordic_iterations: """ self.cordic = Cordic(cordic_iterations, CordicMode.ROTATION) self.phase_acc = Sfix(0, 0, -17, wrap_is_ok=True) self.out = Complex(0, 0, -17, overflow_style='saturate') self.DELAY = self.cordic.ITERATIONS + 1 + 1 self.INIT_X = 1.0 / 1.646760 # gets rid of cordic gain, could use for amplitude modulation
def __init__(self, gain=1.0): """ :param gain: inverse of tx sensitivity """ self.gain = gain # components / registers precision = -35 self.conjugate = Complex(0.0 + 0.0j, 0, -17, overflow_style='saturate') self.mult = Complex(0.0 + 0.0j, 0, precision) self.angle = Angle(precision=precision) self.y = Sfix(0, 0, -17, overflow_style='saturate') # pi term gets us to -1 to +1 self.GAIN_SFIX = Sfix(gain * np.pi, 4, -13, round_style='round', overflow_style='saturate') self.DELAY = 1 + 1 + self.angle.DELAY
def main(self, c): """ :type c: Complex :rtype: Sfix """ self.conjugate = Complex(c.real, -c.imag) self.mult = c * self.conjugate # TODO: invalid data bug! angle = self.angle.main(self.mult) self.y = self.GAIN_SFIX * angle return self.y
def main(self, phase_inc): """ :param phase_inc: amount of rotation applied for next clock cycle, must be normalized to -1 to 1. :rtype: Complex """ self.phase_acc = self.phase_acc + phase_inc start_x = self.INIT_X start_y = Sfix(0.0, 0, -17) x, y, phase = self.cordic.main(start_x, start_y, self.phase_acc) self.out = Complex(x, y) return self.out
def test_nonstandard_input_size(): input_power = 0.0001 dut = FFTPower() dtype = Complex(0, -4, -21, round_style='round') dut._pyha_simulation_input_callback = NumpyToDataValid(dtype) inp = (np.random.uniform(-1, 1, size=64) + np.random.uniform(-1, 1, size=64) * 1j) * input_power inp = [complex(dtype(x)) for x in inp] sims = simulate(dut, inp, pipeline_flush='auto', conversion_path='/tmp/pyha_output') assert sims_close(sims, rtol=1e-20, atol=1e-20)
def __init__(self, window_length, window='hanning', coefficient_bits=18): self._pyha_simulation_input_callback = NumpyToDataValid( dtype=default_complex) self.WINDOW_LENGTH = window_length self.window_pure = get_window(window, window_length) self.WINDOW = [ Sfix(x, 0, -(coefficient_bits - 1), round_style='round', overflow_style='saturate') for x in self.window_pure ] self.output = DataValid(Complex(0, 0, -17, round_style='round')) self.index_counter = 1 self.coef = self.WINDOW[0]
def test_all(fft_size, avg_freq_axis, avg_time_axis, input_power): np.random.seed(0) input_size = (avg_time_axis) * fft_size if input_size < 1024: # must be atleast DC-removal size? input_size = 1024 orig_inp = (np.random.uniform(-1, 1, size=input_size) + np.random.uniform(-1, 1, size=input_size) * 1j) * input_power dut = Spectrogram(fft_size, avg_freq_axis, avg_time_axis) orig_inp_quant = np.vectorize(lambda x: complex(Complex(x, 0, -17)))( orig_inp) sims = simulate(dut, orig_inp_quant, pipeline_flush='auto', simulations=['MODEL', 'HARDWARE']) assert sims_close(sims, rtol=1e-7, atol=1e-7)
def __init__(self): self._pyha_simulation_input_callback = NumpyToDataValid(dtype=Complex( 0.0, 0, -11, overflow_style='saturate', round_style='round')) # components fft_size = 1024 * 8 avg_freq_axis = 16 avg_time_axis = 8 window_type = 'hamming' fft_twiddle_bits = 8 window_bits = 8 dc_removal_len = 1024 self.spect = Spectrogram(fft_size, avg_freq_axis, avg_time_axis, window_type, fft_twiddle_bits, window_bits, dc_removal_len) # TODO: could be unsigned! self.output = DataValid( Sfix(0.0, upper_bits=32) ) # no need to round because result is positive i.e. truncation = rounding
def test_complex(): class T(Hardware): def __init__(self, data): self.shr_new = ShiftRegister(data) self.shr_old = data def main(self, inp): self.shr_new.push_next(inp) self.shr_old = self.shr_old[1:] + [inp] return self.shr_new.peek(), self.shr_old[0] N = 128 dut = T([Complex(0.0 + 0.0j, 0, -17) for _ in range(N)]) inp = np.random.uniform(-1, 1, N * 2) + np.random.uniform(-1, 1, N * 2) * 1j sims = simulate(dut, inp, simulations=['HARDWARE', 'RTL', 'NETLIST']) assert sims['HARDWARE'][0] == sims['HARDWARE'][1] assert sims_close(sims)
def main(self, real, imag): self.out.data = scalb(Complex(real, imag), 4) self.out.valid = True return self.out
def resize(fix: Sfix, left=0, right=-17, size_res=None, overflow_style='wrap', round_style='truncate', wrap_is_ok=False, signed=None) -> Sfix: """ Resize fixed point number. :param fix: Sfix object to resize :param left: new left bound :param right: new right bound :param size_res: provide another Sfix object as size reference :param overflow_style: 'wrap' or 'saturate' :param round_style: 'truncate' or 'round' :param wrap_is_ok: silences logging about WRAP :return: New resized Sfix object >>> a = Sfix(0.89, left=0, right=-17) >>> a 0.8899993896484375 [0:-17] >>> b = resize(a, 0, -6) >>> b 0.890625 [0:-6] >>> c = resize(a, size_res=b) >>> c 0.890625 [0:-6] """ if signed is None: signed = fix.signed try: return fix.resize(left, right, size_res, overflow_style=overflow_style, round_style=round_style, wrap_is_ok=wrap_is_ok, signed=signed) except: # fix is int/float if size_res: left = size_res.left right = size_res.right try: return Sfix(fix, left, right, overflow_style=overflow_style, round_style=round_style, wrap_is_ok=wrap_is_ok, signed=signed) except: from pyha import Complex return Complex(fix, left, right, overflow_style=overflow_style, round_style=round_style, wrap_is_ok=wrap_is_ok, signed=signed)
def __init__(self): self.A = Complex()
def main(self, complex_in): """ Apply FIR filters to 'real' and 'imag' channels """ real = self.fir[0].main(complex_in.real) imag = self.fir[1].main(complex_in.imag) return Complex(real, imag)
def __init__(self): self.out = DataValid(Complex(0, 0, -17, overflow_style='saturate'))
def __init__(self): self._pyha_simulation_input_callback = NumpyToDataValid( dtype=Complex(0.0, 0, -11, overflow_style='saturate', round_style='round')) self.dc_removal = DCRemoval(window_len=2048) self.out = DataValid(Complex(0, 0, -15, round_style='round'))
def __init__(self, global_fft_size, stage_nr, twiddle_bits=18, inverse=False, input_ordering='natural', allow_gain_control=True): self._pyha_simulation_input_callback = NumpyToDataValid( dtype=default_complex) self.ALLOW_GAIN_CONTROL = allow_gain_control self.INVERSE = inverse self.GLOBAL_FFT_SIZE = global_fft_size self.STAGE_NR = stage_nr self.INPUT_ORDERING = input_ordering if input_ordering == 'bitreversed': self.IS_NATURAL_ORDER = False self.INPUT_STRIDE = 2**stage_nr # distance from butterfly input a to b self.LOCAL_FFT_SIZE = global_fft_size // self.INPUT_STRIDE self.CONTROL_MASK = (self.LOCAL_FFT_SIZE - 1) twid = [ W(i, self.LOCAL_FFT_SIZE) for i in range(self.LOCAL_FFT_SIZE // 2) ] twid = toggle_bit_reverse(twid) twid = np.roll(twid, 1, axis=0) self.TWIDDLES = [ Complex(x, 0, -(twiddle_bits - 1), overflow_style='saturate', round_style='round') for x in twid ] elif input_ordering == 'natural': self.IS_NATURAL_ORDER = True self.LOCAL_FFT_SIZE = global_fft_size // 2**stage_nr self.INPUT_STRIDE = self.LOCAL_FFT_SIZE // 2 self.CONTROL_MASK = (self.INPUT_STRIDE - 1) self.TWIDDLES = [ Complex(W(i, self.LOCAL_FFT_SIZE), 0, -(twiddle_bits - 1), overflow_style='saturate', round_style='round') for i in range(self.INPUT_STRIDE) ] self.IS_TRIVIAL_MULTIPLIER = len( self.TWIDDLES) == 1 # mult by 1.0, useless self.shr = ShiftRegister([Complex() for _ in range(self.INPUT_STRIDE)]) self.twiddle = self.TWIDDLES[0] self.stage1_out = Complex(0, 0, -17) self.stage2_out = Complex(0, 0, -17 - (twiddle_bits - 1)) self.output_index = 0 self.mode_delay = False self.control = 0 # replacing this with fixed-point counter saves no resources.. self.out = DataValid(Complex(0, 0, -17, round_style='round'), valid=False) self.start_counter = DownCounter(2 + self.INPUT_STRIDE)