def __init__(self, a: Bus, b: Bus, prefix: str = "u_rca"):
        super().__init__()
        self.N = max(a.N, b.N)
        self.prefix = prefix
        self.a = Bus(prefix=a.prefix, wires_list=a.bus)
        self.b = Bus(prefix=b.prefix, wires_list=b.bus)

        # Bus sign extension in case buses have different lengths
        self.a.bus_extend(N=self.N, prefix=a.prefix)
        self.b.bus_extend(N=self.N, prefix=b.prefix)

        # Output wires for N sum bits and additional cout bit
        self.out = Bus(self.prefix + "_out", self.N + 1)

        # Gradual addition of 1-bit adder components
        for input_index in range(self.N):
            # First adder is a half adder
            if input_index == 0:
                obj_adder = HalfAdder(self.a.get_wire(input_index),
                                      self.b.get_wire(input_index),
                                      prefix=self.prefix + "_ha")
            # Rest adders are full adders
            else:
                obj_adder = FullAdder(
                    self.a.get_wire(input_index),
                    self.b.get_wire(input_index),
                    self.get_previous_component().get_carry_wire(),
                    prefix=self.prefix + "_fa" + str(input_index))

            self.add_component(obj_adder)
            self.out.connect(input_index, obj_adder.get_sum_wire())
            if input_index == (self.N - 1):
                self.out.connect(self.N, obj_adder.get_carry_wire())
    def __init__(self, a: Bus, b: Bus, bypass_block_size: int = 4, prefix: str = "u_cska"):
        super().__init__()
        self.N = max(a.N, b.N)
        self.prefix = prefix
        self.a = Bus(prefix=a.prefix, wires_list=a.bus)
        self.b = Bus(prefix=b.prefix, wires_list=b.bus)

        # Bus sign extension in case buses have different lengths
        self.a.bus_extend(N=self.N, prefix=a.prefix)
        self.b.bus_extend(N=self.N, prefix=b.prefix)

        # Output wires for N sum bits and additional cout bit
        self.out = Bus(self.prefix+"_out", self.N+1)

        # To signify current number of blocks and number of bits that remain to be added into function blocks
        N_blocks = 0
        N_wires = self.N
        cin = ConstantWireValue0()

        while N_wires != 0:
            propagate_wires = []
            block_size = bypass_block_size if N_wires >= bypass_block_size else N_wires

            for i in range(block_size):
                # Generate propagate wires for corresponding bit pairs
                propagate_xor = XorGate(a=self.a.get_wire((N_blocks*bypass_block_size)+i), b=self.b.get_wire((N_blocks*bypass_block_size)+i), prefix=self.prefix+"_xor"+str(self.get_instance_num(cls=XorGate)), parent_component=self)
                self.add_component(propagate_xor)
                propagate_wires.append(propagate_xor.out)

                if N_blocks == 0 and i == 0:
                    obj_adder = HalfAdder(a=self.a.get_wire((N_blocks*bypass_block_size)+i), b=self.b.get_wire((N_blocks*bypass_block_size)+i), prefix=self.prefix+"_ha"+str(self.get_instance_num(cls=HalfAdder)))
                else:
                    obj_adder = FullAdder(a=self.a.get_wire((N_blocks*bypass_block_size)+i), b=self.b.get_wire((N_blocks*bypass_block_size)+i), c=cout, prefix=self.prefix+"_fa"+str(self.get_instance_num(cls=FullAdder)))

                cout = obj_adder.get_carry_wire()
                self.add_component(obj_adder)
                # Connecting adder's output sum bit to its proper position within the described circuit's output bus
                self.out.connect(i+(N_blocks*bypass_block_size), obj_adder.get_sum_wire())

            # ANDing of propagate wires, gate's output serves as select signal into 2:1 multiplexer and signifies whether block's input carry should be propagated (thus reducing delay) or not
            propagation_and = MultipleInputLogicGate(a=Bus(prefix=self.prefix+f"_propagate_signal{N_blocks}", N=len(propagate_wires), wires_list=propagate_wires), two_input_gate_cls=AndGate, parent_component=self, prefix=self.prefix+f"_and_propagate{N_blocks}")

            mux = TwoOneMultiplexer(a=cout, b=cin, c=propagation_and.out, prefix=self.prefix+"_mux2to1"+str(self.get_instance_num(cls=TwoOneMultiplexer)))
            self.add_component(mux)

            # Updating cin for the the next bypass block
            # Also updating cout value which is used as cin for the first adder of the next block
            cin = mux.out.get_wire()
            cout = mux.out.get_wire()
            N_wires -= block_size
            N_blocks += 1

        # Connection of final Cout
        self.out.connect(self.N, cin)
Exemplo n.º 3
0
    def __init__(self,
                 a: Bus,
                 b: Bus,
                 prefix: str = "u_wallace_cla",
                 unsigned_adder_class_name: str = UnsignedCarryLookaheadAdder):
        super().__init__()
        self.N = max(a.N, b.N)
        self.prefix = prefix
        self.a = Bus(prefix=a.prefix, wires_list=a.bus)
        self.b = Bus(prefix=b.prefix, wires_list=b.bus)

        # Bus sign extension in case buses have different lengths
        self.a.bus_extend(N=self.N, prefix=a.prefix)
        self.b.bus_extend(N=self.N, prefix=b.prefix)

        # Output wires for multiplication product
        self.out = Bus(self.prefix + "_out", self.N * 2)

        # Initialize all columns partial products forming AND gates matrix
        self.columns = self.init_column_heights()

        # Perform reduction until all columns have 2 or less bits in them
        while not all(height <= 2 for (height, *_) in self.columns):
            col = 0
            while col < len(self.columns):
                # If column has exactly 3 bits in height and all previous columns has maximum of 2 bits in height, combine them in a half adder
                if self.get_column_height(col) == 3 and all(
                        height <= 2
                        for (height, *_) in self.columns[0:col - 1]):
                    # Add half adder and also AND gates if neccesarry (via add_column_wire invocation) into list of circuit components
                    obj_adder = HalfAdder(
                        self.add_column_wire(column=col, bit=0),
                        self.add_column_wire(column=col, bit=1),
                        prefix=self.prefix + "_ha" +
                        str(self.get_instance_num(cls=HalfAdder)))
                    self.add_component(obj_adder)

                    # Update the number of current and next column wires
                    self.update_column_heights(curr_column=col,
                                               curr_height_change=-1,
                                               next_column=col + 1,
                                               next_height_change=1)

                    # Update current and next column wires arrangement
                    #   add ha's generated sum to the bottom of current column
                    #   add ha's generated cout to the top of next column
                    self.update_column_wires(
                        curr_column=col,
                        next_column=col + 1,
                        adder=self.get_previous_component(1))

                # If column has more than 3 bits in height, combine them in a full adder
                elif self.get_column_height(col) > 3:
                    # Add full adder and also AND gates if neccesarry (via add_column_wire invocation) into list of circuit components
                    obj_adder = FullAdder(
                        self.add_column_wire(column=col, bit=0),
                        self.add_column_wire(column=col, bit=1),
                        self.add_column_wire(column=col, bit=2),
                        prefix=self.prefix + "_fa" +
                        str(self.get_instance_num(cls=FullAdder)))
                    self.add_component(obj_adder)

                    # Update the number of current and next column wires
                    self.update_column_heights(curr_column=col,
                                               curr_height_change=-2,
                                               next_column=col + 1,
                                               next_height_change=1)

                    # Update current and next column wires arrangement
                    #   add fa's generated sum to the bottom of current column
                    #   add fa's generated cout to the top of next column
                    self.update_column_wires(
                        curr_column=col,
                        next_column=col + 1,
                        adder=self.get_previous_component(1))
                col += 1

        # Output generation
        # First output bit from single first pp AND gate
        self.out.connect(0, self.add_column_wire(column=0, bit=0))
        # Final addition of remaining bits
        # 1 bit multiplier case
        if self.N == 1:
            self.out.connect(1, ConstantWireValue0())
        # 2 bit multiplier case
        elif self.N == 2:
            obj_ha = HalfAdder(self.add_column_wire(column=1, bit=0),
                               self.add_column_wire(column=1, bit=1),
                               prefix=self.prefix + "_ha" +
                               str(self.get_instance_num(cls=HalfAdder)))
            self.add_component(obj_ha)
            self.out.connect(1, obj_ha.get_sum_wire())

            obj_ha = HalfAdder(self.get_previous_component().get_carry_wire(),
                               self.add_column_wire(column=2, bit=0),
                               prefix=self.prefix + "_ha" +
                               str(self.get_instance_num(cls=HalfAdder)))
            self.add_component(obj_ha)
            self.out.connect(2, obj_ha.get_sum_wire())
            self.out.connect(3, obj_ha.get_carry_wire())
        # Final addition of remaining bits using chosen unsigned multi bit adder
        else:
            # Obtain proper adder name with its bit width (columns bit pairs minus the first alone bit)
            adder_prefix = self.prefix + "_" + unsigned_adder_class_name(
                a=a, b=b).prefix + str(len(self.columns) - 1)

            adder_a = Bus(prefix=f"{adder_prefix}_a",
                          wires_list=[
                              self.add_column_wire(column=col, bit=0)
                              for col in range(1, len(self.columns))
                          ])
            adder_b = Bus(prefix=f"{adder_prefix}_b",
                          wires_list=[
                              self.add_column_wire(column=col, bit=1)
                              for col in range(1, len(self.columns))
                          ])
            final_adder = unsigned_adder_class_name(a=adder_a,
                                                    b=adder_b,
                                                    prefix=adder_prefix)
            self.add_component(final_adder)

            [
                self.out.connect(o,
                                 final_adder.out.get_wire(o - 1),
                                 inserted_wire_desired_index=o - 1)
                for o in range(1, len(self.out.bus))
            ]
    def __init__(self, a: Bus, b: Bus, prefix: str = "u_arrmul"):
        super().__init__()
        self.N = max(a.N, b.N)
        self.prefix = prefix
        self.a = Bus(prefix=a.prefix, wires_list=a.bus)
        self.b = Bus(prefix=b.prefix, wires_list=b.bus)

        # Bus sign extension in case buses have different lengths
        self.a.bus_extend(N=self.N, prefix=a.prefix)
        self.b.bus_extend(N=self.N, prefix=b.prefix)

        # Output wires for multiplication product
        self.out = Bus(self.prefix + "_out", self.N * 2)

        # Gradual generation of partial products
        for b_multiplier_index in range(self.N):
            for a_multiplicand_index in range(self.N):
                # AND gates generation for calculation of partial products
                obj_and = AndGate(self.a.get_wire(a_multiplicand_index),
                                  self.b.get_wire(b_multiplier_index),
                                  prefix=self.prefix + "_and" +
                                  str(a_multiplicand_index) + "_" +
                                  str(b_multiplier_index))
                self.add_component(obj_and)

                if b_multiplier_index != 0:
                    previous_product = self.components[
                        a_multiplicand_index +
                        b_multiplier_index].out if b_multiplier_index == 1 else self.get_previous_partial_product(
                            a_index=a_multiplicand_index,
                            b_index=b_multiplier_index)
                    # HA generation for first 1-bit adder in each row starting from the second one
                    if a_multiplicand_index == 0:
                        obj_adder = HalfAdder(
                            self.get_previous_component().out,
                            previous_product,
                            prefix=self.prefix + "_ha" +
                            str(a_multiplicand_index) + "_" +
                            str(b_multiplier_index))
                        self.add_component(obj_adder)
                        # Product generation
                        self.out.connect(b_multiplier_index,
                                         obj_adder.get_sum_wire())

                    # HA generation, last 1-bit adder in second row
                    elif a_multiplicand_index == self.N - 1 and b_multiplier_index == 1:
                        obj_adder = HalfAdder(
                            self.get_previous_component().out,
                            self.get_previous_component(
                                number=2).get_carry_wire(),
                            prefix=self.prefix + "_ha" +
                            str(a_multiplicand_index) + "_" +
                            str(b_multiplier_index))
                        self.add_component(obj_adder)

                    # FA generation
                    else:
                        obj_adder = FullAdder(
                            self.get_previous_component().out,
                            previous_product,
                            self.get_previous_component(
                                number=2).get_carry_wire(),
                            prefix=self.prefix + "_fa" +
                            str(a_multiplicand_index) + "_" +
                            str(b_multiplier_index))
                        self.add_component(obj_adder)

                # PRODUCT GENERATION
                if a_multiplicand_index == 0 and b_multiplier_index == 0:
                    self.out.connect(a_multiplicand_index, obj_and.out)

                    # 1 bit multiplier case
                    if a_multiplicand_index == self.N - 1:
                        self.out.connect(a_multiplicand_index + 1,
                                         ConstantWireValue0)

                elif b_multiplier_index == self.N - 1:
                    self.out.connect(b_multiplier_index + a_multiplicand_index,
                                     obj_adder.get_sum_wire())

                    if a_multiplicand_index == self.N - 1:
                        self.out.connect(self.out.N - 1,
                                         obj_adder.get_carry_wire())
    def __init__(self, a: Bus, b: Bus, prefix: str = "s_arrmul"):
        super().__init__()
        self.c_data_type = "int64_t"
        self.N = max(a.N, b.N)
        self.prefix = prefix
        self.a = Bus(prefix=a.prefix, wires_list=a.bus)
        self.b = Bus(prefix=b.prefix, wires_list=b.bus)

        # Bus sign extension in case buses have different lengths
        self.a.bus_extend(N=self.N, prefix=a.prefix)
        self.b.bus_extend(N=self.N, prefix=b.prefix)

        # Output wires for multiplication product
        self.out = Bus(self.prefix + "_out", self.N * 2)

        # Gradual generation of partial products
        for b_multiplier_index in range(self.N):
            for a_multiplicand_index in range(self.N):
                # AND and NAND gates generation for calculation of partial products and sign extension
                if (b_multiplier_index == self.N - 1
                        and a_multiplicand_index != self.N - 1) or (
                            b_multiplier_index != self.N - 1
                            and a_multiplicand_index == self.N - 1):
                    obj_nand = NandGate(self.a.get_wire(a_multiplicand_index),
                                        self.b.get_wire(b_multiplier_index),
                                        prefix=self.prefix + "_nand" +
                                        str(a_multiplicand_index) + "_" +
                                        str(b_multiplier_index),
                                        parent_component=self)
                    self.add_component(obj_nand)
                else:
                    obj_and = AndGate(self.a.get_wire(a_multiplicand_index),
                                      self.b.get_wire(b_multiplier_index),
                                      prefix=self.prefix + "_and" +
                                      str(a_multiplicand_index) + "_" +
                                      str(b_multiplier_index),
                                      parent_component=self)
                    self.add_component(obj_and)

                if b_multiplier_index != 0:
                    previous_product = self.components[
                        a_multiplicand_index +
                        b_multiplier_index].out if b_multiplier_index == 1 else self.get_previous_partial_product(
                            a_index=a_multiplicand_index,
                            b_index=b_multiplier_index)
                    # HA generation for first 1-bit adder in each row starting from the second one
                    if a_multiplicand_index == 0:
                        obj_adder = HalfAdder(
                            self.get_previous_component().out,
                            previous_product,
                            prefix=self.prefix + "_ha" +
                            str(a_multiplicand_index) + "_" +
                            str(b_multiplier_index))
                        self.add_component(obj_adder)
                        # Product generation
                        self.out.connect(b_multiplier_index,
                                         obj_adder.get_sum_wire())

                    # FA generation
                    else:
                        # Constant wire with value 1 used at the last FA in second row (as one of its inputs) for signed multiplication (based on Baugh Wooley algorithm)
                        if a_multiplicand_index == self.N - 1 and b_multiplier_index == 1:
                            previous_product = ConstantWireValue1()

                        obj_adder = FullAdder(
                            self.get_previous_component().out,
                            previous_product,
                            self.get_previous_component(
                                number=2).get_carry_wire(),
                            prefix=self.prefix + "_fa" +
                            str(a_multiplicand_index) + "_" +
                            str(b_multiplier_index))
                        self.add_component(obj_adder)

                # PRODUCT GENERATION
                if a_multiplicand_index == 0 and b_multiplier_index == 0:
                    self.out.connect(a_multiplicand_index, obj_and.out)

                    # 1 bit multiplier case
                    if a_multiplicand_index == self.N - 1:
                        obj_nor = NorGate(ConstantWireValue1(),
                                          self.get_previous_component().out,
                                          prefix=self.prefix +
                                          "_nor_zero_extend",
                                          parent_component=self)
                        self.add_component(obj_nor)

                        self.out.connect(a_multiplicand_index + 1, obj_nor.out)

                elif b_multiplier_index == self.N - 1:
                    self.out.connect(b_multiplier_index + a_multiplicand_index,
                                     obj_adder.get_sum_wire())

                    if a_multiplicand_index == self.N - 1:
                        obj_xor = XorGate(
                            self.get_previous_component().get_carry_wire(),
                            ConstantWireValue1(),
                            prefix=self.prefix + "_xor" +
                            str(a_multiplicand_index + 1) + "_" +
                            str(b_multiplier_index),
                            parent_component=self)
                        self.add_component(obj_xor)

                        self.out.connect(self.out.N - 1, obj_xor.out)
    def __init__(self,
                 a: Bus,
                 b: Bus,
                 prefix: str = "s_dadda_cla",
                 unsigned_adder_class_name: str = UnsignedCarryLookaheadAdder):
        super().__init__()
        self.N = max(a.N, b.N)
        self.prefix = prefix
        self.a = Bus(prefix=a.prefix, wires_list=a.bus)
        self.b = Bus(prefix=b.prefix, wires_list=b.bus)

        # Bus sign extension in case buses have different lengths
        self.a.bus_extend(N=self.N, prefix=a.prefix)
        self.b.bus_extend(N=self.N, prefix=b.prefix)

        # Output wires for multiplication product
        self.out = Bus(self.prefix + "_out", self.N * 2)

        # Get starting stage and maximum possible column height
        self.stage, self.d = self.get_maximum_height(
            initial_value=min(self.a.N, self.b.N))
        # Initialize all columns partial products forming AND/NAND gates matrix based on Baugh-Wooley multiplication
        self.columns = self.init_column_heights(signed=True)

        # Not used for 1 bit multiplier
        if self.N != 1:
            # Adding constant wire with value 1 to achieve signedness based on Baugh-Wooley multiplication algorithm
            # (adding constant value bit to last column (with one bit) to combine them in XOR gate to get the correct final multplication output bit at the end)
            self.columns[self.N].insert(1, ConstantWireValue1())
            self.update_column_heights(curr_column=self.N,
                                       curr_height_change=1)

        # Perform reduction until stage 0
        for stage in range(self.stage, 0, -1):
            col = 0
            while col < len(self.columns):
                if self.get_column_height(col) == self.d + 1:
                    # Add half adder and also AND/NAND gates if neccesarry (via add_column_wire invocation) into list of circuit components
                    obj_adder = HalfAdder(
                        self.add_column_wire(column=col, bit=0),
                        self.add_column_wire(column=col, bit=1),
                        prefix=self.prefix + "_ha" +
                        str(self.get_instance_num(cls=HalfAdder)))
                    self.add_component(obj_adder)

                    # Update the number of current and next column wires
                    self.update_column_heights(curr_column=col,
                                               curr_height_change=-1,
                                               next_column=col + 1,
                                               next_height_change=1)

                    # Update current and next column wires arrangement
                    #   add ha's generated sum to the bottom of current column
                    #   add ha's generated cout to the top of next column
                    self.update_column_wires(
                        curr_column=col,
                        next_column=col + 1,
                        adder=self.get_previous_component(1))
                elif self.get_column_height(col) > self.d:
                    # Add full adder and also AND/NAND gates if neccesarry (via add_column_wire invocation) into list of circuit components
                    obj_adder = FullAdder(
                        self.add_column_wire(column=col, bit=0),
                        self.add_column_wire(column=col, bit=1),
                        self.add_column_wire(column=col, bit=2),
                        prefix=self.prefix + "_fa" +
                        str(self.get_instance_num(cls=FullAdder)))
                    self.add_component(obj_adder)

                    # Update the number of current and next column wires
                    self.update_column_heights(curr_column=col,
                                               curr_height_change=-2,
                                               next_column=col + 1,
                                               next_height_change=1)

                    # Update current and next column wires arrangement
                    #   add fa's generated sum to the bottom of current column
                    #   add fa's generated cout to the top of next column
                    self.update_column_wires(
                        curr_column=col,
                        next_column=col + 1,
                        adder=self.get_previous_component(1))
                    # Next iteration with same column in case there is need for further reduction
                    col -= 1
                col += 1
            # Update maximum possible column height
            _, self.d = self.get_maximum_height(stage)

        # Output generation
        # First output bit from single first pp AND gate
        self.out.connect(0, self.add_column_wire(column=0, bit=0))
        # Final addition of remaining bits
        # 1 bit multiplier case (no sign extension)
        if self.N == 1:
            self.out.connect(1, ConstantWireValue0())
            return
        # 2 bit multiplier case
        elif self.N == 2:
            obj_ha = HalfAdder(self.add_column_wire(column=1, bit=0),
                               self.add_column_wire(column=1, bit=1),
                               prefix=self.prefix + "_ha" +
                               str(self.get_instance_num(cls=HalfAdder)))
            self.add_component(obj_ha)
            self.out.connect(1, obj_ha.get_sum_wire())

            obj_fa = FullAdder(self.get_previous_component().get_carry_wire(),
                               self.add_column_wire(column=2, bit=0),
                               self.add_column_wire(column=2, bit=1),
                               prefix=self.prefix + "_fa" +
                               str(self.get_instance_num(cls=FullAdder)))
            self.add_component(obj_fa)
            self.out.connect(2, obj_fa.get_sum_wire())
            self.out.connect(3, obj_fa.get_carry_wire())

        # Final addition of remaining bits using chosen unsigned multi bit adder
        else:
            # Obtain proper adder name with its bit width (columns bit pairs minus the first alone bit)
            adder_prefix = self.prefix + "_" + unsigned_adder_class_name(
                a=a, b=b).prefix + str(len(self.columns) - 1)

            adder_a = Bus(prefix=f"{adder_prefix}_a",
                          wires_list=[
                              self.add_column_wire(column=col, bit=0)
                              for col in range(1, len(self.columns))
                          ])
            adder_b = Bus(prefix=f"{adder_prefix}_b",
                          wires_list=[
                              self.add_column_wire(column=col, bit=1)
                              for col in range(1, len(self.columns))
                          ])
            final_adder = unsigned_adder_class_name(a=adder_a,
                                                    b=adder_b,
                                                    prefix=adder_prefix)
            self.add_component(final_adder)

            [
                self.out.connect(o,
                                 final_adder.out.get_wire(o - 1),
                                 inserted_wire_desired_index=o - 1)
                for o in range(1, len(self.out.bus))
            ]

        # Final XOR to ensure proper sign extension
        obj_xor = XorGate(ConstantWireValue1(),
                          self.out.get_wire(self.out.N - 1),
                          prefix=self.prefix + "_xor" +
                          str(self.get_instance_num(cls=XorGate)),
                          parent_component=self)
        self.add_component(obj_xor)
        self.out.connect(self.out.N - 1, obj_xor.out)