diff --git a/b_asic/sfg_generators.py b/b_asic/sfg_generators.py index 2b693af3dedb9cb6dc51b6ec65b6673833488981..b9ca6a4fbb8681d242229a99ccfcb211e45db1ba 100644 --- a/b_asic/sfg_generators.py +++ b/b_asic/sfg_generators.py @@ -10,6 +10,7 @@ import numpy as np from b_asic.core_operations import ( Addition, + Butterfly, ConstantMultiplication, Name, SymmetricTwoportAdaptor, @@ -371,3 +372,87 @@ def direct_form_2_iir( output = Output() output <<= add return SFG([input_op], [output], name=Name(name)) + + +def radix_2_dif_fft( + points: int, + mult_properties: Optional[Union[Dict[str, int], Dict[str, Dict[str, int]]]] = None, + add_properties: Optional[Union[Dict[str, int], Dict[str, Dict[str, int]]]] = None, +) -> SFG: + if points < 0: + raise ValueError("Points must be positive number.") + if points & (points - 1) != 0: + raise ValueError("Points must be a power of two.") + + inputs = [] + for i in range(points): + inputs.append(Input(name=f"Input: {i}")) + + ports = inputs + number_of_stages = int(np.log2(points)) + + twiddles = _generate_twiddles(points, number_of_stages) + print(twiddles) + + for stage in range(number_of_stages): + ports = _construct_dif_fft_stage(ports, number_of_stages, stage) + + ports = _get_bit_reversed_ports(ports) + outputs = [] + for i, port in enumerate(ports): + outputs.append(Output(port, name=f"Output: {i}")) + + return SFG(inputs=inputs, outputs=outputs) + + +def _construct_dif_fft_stage(ports_from_previous_stage, number_of_stages, stage): + ports = ports_from_previous_stage.copy() + number_of_butterflies = len(ports) // 2 + number_of_groups = 2 ** (stage) + group_size = number_of_butterflies // number_of_groups + + for group_index in range(number_of_groups): + for bf_index in range(group_size): + input1_index = group_index * 2 * group_size + bf_index + input2_index = input1_index + group_size + + input1 = ports[input1_index] + input2 = ports[input2_index] + + butterfly = Butterfly(input1, input2, name=f"bf: {stage*4+bf_index}") + output1, output2 = butterfly.outputs + + ports[input1_index] = output1 + ports[input2_index] = output2 + + return ports + + +def _get_bit_reversed_number(number, number_of_bits) -> int: + reversed_number = 0 + for i in range(number_of_bits): + # mask out the current bit + current_bit = (number >> i) & 1 + # compute the position of the current bit in the reversed string + reversed_pos = number_of_bits - 1 - i + # place the current bit in that position + reversed_number |= current_bit << reversed_pos + return reversed_number + + +def _get_bit_reversed_ports(ports): + num_of_ports = len(ports) + bits = int(np.log2(num_of_ports)) + return [ports[_get_bit_reversed_number(i, bits)] for i in range(num_of_ports)] + + +def _generate_twiddles(points, number_of_stages): + twiddles = [] + for stage in range(1, number_of_stages + 1): + stage_twiddles = [] + for k in range(points // 2 ** (stage)): + twiddle = np.exp(-1j * 2 * np.pi * stage * k / points) + print("STAGE:", stage, "k:", k, "TW:", twiddle) + stage_twiddles.append(twiddle) + twiddles.append(stage_twiddles) + return twiddles