class UnsignedRippleCarryAdder(ArithmeticCircuit): """Class representing unsigned ripple carry adder. Unsigned ripple carry adder represents N-bit unsigned adder which is composed of N one bit adders, where first is a half adder and rest are full adders. Its downside is its long propagation delay the bigger the circuit is. ``` B3 A3 B2 A2 B1 A1 B0 A0 │ │ │ │ │ │ │ │ ┌─▼──▼─┐ ┌─▼──▼─┐ ┌─▼──▼─┐ ┌─▼──▼─┐ │ │ C3│ │ C2│ │ C1│ │ ┌──┤ FA │◄──┤ FA │◄──┤ FA │◄──┤ HA │ │ │ │ │ │ │ │ │ │ │ └──┬───┘ └──┬───┘ └──┬───┘ └──┬───┘ ▼ ▼ ▼ ▼ ▼ Cout S3 S2 S1 S0 ``` Description of the __init__ method. Args: a (Bus): First input bus. b (Bus): Second input bus. prefix (str, optional): Prefix name of unsigned rca. Defaults to "u_rca". """ 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())
class HalfAdder(TwoInputOneBitCircuit): """Class representing two input one bit half adder. ``` ┌──────┐ ───►│ ├─► Sum │ │ ───►│ ├─► Cout └──────┘ ``` Description of the __init__ method. Args: a (Wire, optional): First input wire. Defaults to Wire(name="a"). b (Wire, optional): Second input wire. Defaults to Wire(name="b"). prefix (str, optional): Prefix name of half adder. Defaults to "ha". """ def __init__(self, a: Wire = Wire(name="a"), b: Wire = Wire(name="b"), prefix: str = "ha"): super().__init__(a, b, prefix) # 2 wires for component's bus output (sum, cout) self.out = Bus(self.prefix + "_out", 2) # Sum # XOR gate for calculation of 1-bit sum obj_xor = XorGate(a, b, prefix=self.prefix + "_xor" + str(self.get_instance_num(cls=XorGate)), outid=0, parent_component=self) self.add_component(obj_xor) self.out.connect(0, obj_xor.out) # Cout # AND gate for calculation of 1-bit cout obj_and = AndGate(a, b, prefix=self.prefix + "_and" + str(self.get_instance_num(cls=AndGate)), outid=1, parent_component=self) self.add_component(obj_and) self.out.connect(1, obj_and.out)
class UnsignedCarryLookaheadAdder(ArithmeticCircuit): """Class representing unsigned carry-lookahead adder. Unsigned carry-lookahead adder represents faster adder circuit which is composed of more complex circuitry but provides much less propagation delay as opposed to rca. It is mainly composed of propagate/generate blocks and many AND/OR gates to calculate carries individually. Class enables to choose the size of composite inner cla blocks (default is 4). These cla blocks are then cascaded to form the final N bit adder. ``` B3 A3 B2 A2 B1 A1 B0 A0 │ │ │ │ │ │ │ │ ┌─▼──▼─┐ ┌─▼──▼─┐ ┌─▼──▼─┐ ┌─▼──▼─┐ │ PG │ │ PG │ │ PG │ │ PG │ │ block│ │ block│ │ block│ │ block│ │ │ │ │ │ │ │ │ └─┬┬┬──┘ └─┬┬┬──┘ └─┬┬┬──┘ └─┬┬┬──┘ │││G3P3S3 │││G2P2S2 │││G1P1S1 │││G0P0S0 ┌─▼▼▼────────▼▼▼────────▼▼▼────────▼▼▼──┐ │ Carry Lookahead logic │ │ │ └┬────┬───────┬──────────┬──────────┬───┘ │ │ │ │ │ ▼ ▼ ▼ ▼ ▼ Cout S3 S1 S0 S0 ``` Description of the __init__ method. Args: a (Bus): First input bus. b (Bus): Second input bus. cla_block_size (int, optional): Size of each composite cla adder block size. Defaults to 4. prefix (str, optional): Prefix name of unsigned cla. Defaults to "u_cla". """ def __init__(self, a: Bus, b: Bus, cla_block_size: int = 4, prefix: str = "u_cla"): 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: # Lists containing all propagate/generate wires self.propagate = [] self.generate = [] # Cin0 used as a first generate wire for obtaining next carry bits self.generate.append(cin) block_size = cla_block_size if N_wires >= cla_block_size else N_wires # Gradual addition of propagate/generate logic blocks and AND/OR gates for Cout bits generation, XOR gates for Sum bits generation for i in range(block_size): pg_block = PGLogicBlock( self.a.get_wire((N_blocks * cla_block_size) + i), self.b.get_wire((N_blocks * cla_block_size) + i), prefix=self.prefix + "_pg_logic" + str(self.get_instance_num(cls=PGLogicBlock))) self.propagate.append(pg_block.get_propagate_wire()) self.generate.append(pg_block.get_generate_wire()) self.add_component(pg_block) if i == 0 and N_blocks == 0: obj_sum_xor = XorGate( pg_block.get_sum_wire(), cin, prefix=self.prefix + "_xor" + str(self.get_instance_num(cls=XorGate)), parent_component=self) self.add_component(obj_sum_xor) self.out.connect(i + (N_blocks * cla_block_size), obj_sum_xor.out) # Carry propagation calculation obj_and = AndGate( self.propagate[(N_blocks * cla_block_size) + i], self.generate[(N_blocks * cla_block_size) + i], prefix=self.prefix + "_and" + str(self.get_instance_num(cls=AndGate)), parent_component=self) self.add_component(obj_and) # Carry bit generation obj_cout_or = OrGate( pg_block.get_generate_wire(), self.get_previous_component().out, prefix=self.prefix + "_or" + str( self.get_instance_num(cls=OrGate, count_disabled_gates=False)), parent_component=self) self.add_component(obj_cout_or) else: obj_sum_xor = XorGate( pg_block.get_sum_wire(), self.get_previous_component(2).out, prefix=self.prefix + "_xor" + str(self.get_instance_num(cls=XorGate)), parent_component=self) self.add_component(obj_sum_xor) self.out.connect(i + (N_blocks * cla_block_size), obj_sum_xor.out) # List of AND gates outputs that are later combined in a multi-bit OR gate composite_or_gates_inputs = [] for g_index in range(len(self.generate) - 1): composite_wires = [] # Getting a list of wires used for current bit position cout composite AND gate's generation # E.g. for Cout2 = G1 + G0·P1 C0·P0·P1 it gets a list containing [C0,P0,P1] then [G0,P1] composite_wires.append(self.generate[g_index]) for p_index in range( len(self.propagate) - 1, g_index - 1, -1): composite_wires.append(self.propagate[p_index]) # For each pg pair values algorithmically combine two input AND gates to replace multiple input gates (resolves fan-in issue) pg_wires = Bus(wires_list=composite_wires) multi_bit_and_gate = MultipleInputLogicGate( a=pg_wires, two_input_gate_cls=AndGate, prefix=self.prefix + "_and", parent_component=self) composite_or_gates_inputs.append( multi_bit_and_gate.out) # Final OR gates cascade using generated AND gates output wires composite_or_wires = Bus( wires_list=composite_or_gates_inputs) multi_bit_or_gate = MultipleInputLogicGate( a=composite_or_wires, two_input_gate_cls=OrGate, prefix=self.prefix + "_or", parent_component=self) # Carry bit generation obj_cout_or = OrGate( pg_block.get_generate_wire(), multi_bit_or_gate.out, prefix=self.prefix + "_or" + str( self.get_instance_num(cls=OrGate, count_disabled_gates=False)), parent_component=self) self.add_component(obj_cout_or) # 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 = obj_cout_or.out N_wires -= block_size N_blocks += 1 # Connection of final Cout self.out.connect(self.N, cin)
class SignedWallaceMultiplier(MultiplierCircuit): """Class representing signed wallace multiplier. Signed wallace multiplier represents fast N-bit multiplier which utilizes the functionality of wallace tree reduction algorithm proposed by Chris Wallace and uses Baugh-Wooley algorithm to perform signed multiplication. First partial products are calculated for each bit pair that form the partial product multiplication columns. At last the reduced pairs are inserted into chosen multi bit unsigned adder to execute their summation and obtain the final output bits, additional XOR gate serve the necessary sign extension. Wallace tree algorithm is described more in detail here: https://en.wikipedia.org/wiki/Wallace_tree It presents smaller circuit in area opposed to array multiplier but is slightly bigger then dadda because of less reduction stages. Description of the __init__ method. Args: a (Bus): First input bus. b (Bus): Second input bus. prefix (str, optional): Prefix name of signed wallace multiplier. Defaults to "s_wallace_cla". unsigned_adder_class_name (str, optional): Unsigned multi bit adder used to obtain final sums of products. Defaults to UnsignedCarryLookaheadAdder. """ def __init__(self, a: Bus, b: Bus, prefix: str = "s_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/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 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/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)) # 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/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)) 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()) 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)
class UnsignedPGRippleCarryAdder(ArithmeticCircuit): """Class representing unsigned ripple carry adder with propagate/generate logic. Unsigned ripple carry adder with PG logic represents slightly different rca implementation of N-bit unsigned adder which is composed of N one bit full adders with P/G logic. ``` B3 A3 B2 A2 B1 A1 B0 A0 │ │ │ │ │ │ │ │ ┌─▼──▼─┐ ┌─▼──▼─┐ ┌─▼──▼─┐ ┌─▼──▼─┐ │ PG │ C3 │ PG │ C2 │ PG │ C1 │ PG │ │ FA │◄────┐│ FA │◄──┐│ FA │◄──┐│ FA │◄──0 │ │ ││ │ ││ │ ││ │ └─┬──┬┬┘ │└─┬┬┬──┘ │└─┬┬┬──┘ │└─┬┬┬──┘ │ ││G3P3S3│ │││G2P2S2│ │││G1P1S1│ │││G0P0S0 │ ┌▼▼──────┴──▼▼▼──────┴──▼▼▼──────┴──▼▼▼──┐ │ │ Group PG logic │ │ │ │ │ └─┬───────┬──────────┬──────────┬────────┘ │ │ │ │ │ ┌─▼───▼───────▼──────────▼──────────▼────────┐ │ Sum logic │ │ │ └┬────┬───────┬──────────┬──────────┬────────┘ │ │ │ │ │ ▼ ▼ ▼ ▼ ▼ Cout S3 S1 S0 S0 ``` Description of the __init__ method. Args: a (Bus): First input bus. b (Bus): Second input bus. prefix (str, optional): Prefix name of unsigned P/G rca. Defaults to "u_pg_rca". """ def __init__(self, a: Bus, b: Bus, prefix: str = "u_pg_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): if input_index == 0: # First full adder with connected constant wire with value 0 as cin 0 obj_pg_fa = FullAdderPG(self.a.get_wire(input_index), self.b.get_wire(input_index), ConstantWireValue0(), prefix=self.prefix + "_pg_fa" + str(input_index)) else: obj_pg_fa = FullAdderPG(self.a.get_wire(input_index), self.b.get_wire(input_index), self.get_previous_component().out, prefix=self.prefix + "_pg_fa" + str(input_index)) self.add_component(obj_pg_fa) self.out.connect(input_index, obj_pg_fa.get_sum_wire()) obj_and = AndGate( self.get_previous_component().c, self.get_previous_component().get_propagate_wire(), prefix=self.prefix + "_and" + str(input_index), parent_component=self) obj_or = OrGate(obj_and.out, self.get_previous_component().get_generate_wire(), prefix=self.prefix + "_or" + str(input_index), parent_component=self) self.add_component(obj_and) self.add_component(obj_or) # Connecting last output bit to last cout if input_index == (self.N - 1): self.out.connect(self.N, obj_or.out)
class FullAdderPG(ThreeInputOneBitCircuit): """Class representing modified three input one bit full adder with propagate/generate logic. ``` ┌──────┐ ───►│ ├─► P ───►│ ├─► G ───►│ ├─► Sum └──────┘ ``` Description of the __init__ method. Args: a (Wire, optional): First input wire. Defaults to Wire(name="a"). b (Wire, optional): Second input wire. Defaults to Wire(name="b"). c (Wire, optional): Carry input wire. Defaults to Wire(name="cin"). prefix (str, optional): Prefix name of full adder with pg logic. Defaults to "pg_fa". """ def __init__(self, a: Wire = Wire(name="a"), b: Wire = Wire(name="b"), c: Wire = Wire(name="cin"), prefix: str = "pg_fa"): super().__init__(a, b, c, prefix) # 3 wires for component's bus output (sum, propagate, generate) self.out = Bus(self.prefix + "_out", 3) # PG logic propagate_xor = XorGate(a, b, prefix=self.prefix + "_xor" + str(self.get_instance_num(cls=XorGate)), outid=0, parent_component=self) self.add_component(propagate_xor) self.out.connect(0, propagate_xor.out) generate_and = AndGate(a, b, prefix=self.prefix + "_and" + str(self.get_instance_num(cls=AndGate)), outid=1, parent_component=self) self.add_component(generate_and) self.out.connect(1, generate_and.out) # Sum output sum_xor = XorGate(propagate_xor.out, c, prefix=self.prefix + "_xor" + str(self.get_instance_num(cls=XorGate)), outid=2, parent_component=self) self.add_component(sum_xor) self.out.connect(2, sum_xor.out) def get_propagate_wire(self): """Get output wire carrying propagate signal value. Returns: Wire: Return propagate wire. """ return self.out.get_wire(0) def get_generate_wire(self): """Get output wire carrying generate signal value. Returns: Wire: Return generate wire. """ return self.out.get_wire(1) def get_sum_wire(self): """Get output wire carrying sum value. Returns: Wire: Return sum wire. """ return self.out.get_wire(2)
class FullAdder(ThreeInputOneBitCircuit): """Class representing three input one bit full adder. ``` ┌──────┐ ───►│ ├─► Sum ───►│ │ ───►│ ├─► Cout └──────┘ ``` Description of the __init__ method. Args: a (Wire, optional): First input wire. Defaults to Wire(name="a"). b (Wire, optional): Second input wire. Defaults to Wire(name="b"). c (Wire, optional): Carry input wire. Defaults to Wire(name="cin"). prefix (str, optional): Prefix name of full adder. Defaults to "fa". """ def __init__(self, a: Wire = Wire(name="a"), b: Wire = Wire(name="b"), c: Wire = Wire(name="cin"), prefix: str = "fa"): super().__init__(a, b, c, prefix) # 2 wires for component's bus output (sum, cout) self.out = Bus(self.prefix + "_out", 2) # PG logic propagate_xor = XorGate(a, b, prefix=self.prefix + "_xor" + str(self.get_instance_num(cls=XorGate)), parent_component=self) self.add_component(propagate_xor) generate_and = AndGate(a, b, prefix=self.prefix + "_and" + str(self.get_instance_num(cls=AndGate)), parent_component=self) self.add_component(generate_and) # Sum # XOR gate for calculation of 1-bit sum obj_xor = XorGate(propagate_xor.out, c, prefix=self.prefix + "_xor" + str(self.get_instance_num(cls=XorGate)), outid=0, parent_component=self) self.add_component(obj_xor) self.out.connect(0, obj_xor.out) # Cout # AND gate for calculation of 1-bit cout obj_and = AndGate(propagate_xor.out, c, prefix=self.prefix + "_and" + str(self.get_instance_num(cls=AndGate)), parent_component=self) self.add_component(obj_and) obj_or = OrGate(generate_and.out, obj_and.out, prefix=self.prefix + "_or" + str(self.get_instance_num(cls=OrGate)), outid=1, parent_component=self) self.add_component(obj_or) self.out.connect(1, obj_or.out)
class FullSubtractor(ThreeInputOneBitCircuit): """Class representing three input one bit full subtractor. ``` ┌──────┐ ───►│ ├─► Difference ───►│ │ ───►│ ├─► Bout └──────┘ ``` Description of the __init__ method. Args: a (Wire, optional): First input wire. Defaults to Wire(name="a"). b (Wire, optional): Second input wire. Defaults to Wire(name="b"). c (Wire, optional): Input borrow wire. Defaults to Wire(name="bin"). prefix (str, optional): Prefix name of full subtractor. Defaults to "fs". """ def __init__(self, a: Wire = Wire(name="a"), b: Wire = Wire(name="b"), c: Wire = Wire(name="bin"), prefix: str = "fs"): super().__init__(a, b, c, prefix) # 2 wires for component's bus output (difference, bout) self.out = Bus(self.prefix + "_out", 2) # Difference xor_obj = XorGate(a=self.a, b=self.b, prefix=self.prefix + "_xor" + str(self.get_instance_num(cls=XorGate)), parent_component=self) self.add_component(xor_obj) not_obj = NotGate(a=self.a, prefix=self.prefix + "_not" + str(self.get_instance_num(cls=NotGate)), parent_component=self) self.add_component(not_obj) and_obj = AndGate(a=not_obj.out, b=self.b, prefix=self.prefix + "_and" + str(self.get_instance_num(cls=AndGate)), parent_component=self) self.add_component(and_obj) difference_xor = XorGate(a=self.c, b=xor_obj.out, prefix=self.prefix + "_xor" + str(self.get_instance_num(cls=XorGate)), outid=0, parent_component=self) self.add_component(difference_xor) self.out.connect(0, difference_xor.out) # Borrow out not_obj = NotGate(a=xor_obj.out, prefix=self.prefix + "_not" + str(self.get_instance_num(cls=NotGate)), parent_component=self) self.add_component(not_obj) and_obj = AndGate(a=not_obj.out, b=self.c, prefix=self.prefix + "_and" + str(self.get_instance_num(cls=AndGate)), parent_component=self) self.add_component(and_obj) borrow_out_or = OrGate(a=and_obj.out, b=self.get_previous_component(4).out, prefix=self.prefix + "_or" + str(self.get_instance_num(cls=OrGate)), outid=1, parent_component=self) self.add_component(borrow_out_or) self.out.connect(1, borrow_out_or.out) def get_difference_wire(self): """Get output wire carrying difference value. Returns: Wire: Return difference wire. """ return self.out.get_wire(0) def get_borrow_wire(self): """Get output wire carrying borrow out value. Returns: Wire: Return borrow out wire. """ return self.out.get_wire(1)
class TwoOneMultiplexer(ThreeInputOneBitCircuit): """Class representing two to one multiplexer (with select signal as its third input). ``` ┌──────┐ ───►│ │ │ ├─► ───►│ │ └───▲──┘ Sel ────┘ ``` Description of the __init__ method. Args: a (Wire, optional): First input wire. Defaults to Wire(name="d0"). b (Wire, optional): Second input wire. Defaults to Wire(name="d1"). c (Wire, optional): Select signal. Defaults to Wire(name="sel"). prefix (str, optional): Prefix name of two to one multiplexer. Defaults to "mux2to1". """ def __init__(self, a: Wire = Wire(name="d0"), b: Wire = Wire(name="d1"), c: Wire = Wire(name="sel"), prefix: str = "mux2to1"): super().__init__(a, b, c, prefix) # Represents select signal (self.c naming for proper unified generation) self.c = c # 1 wire for component's output bus self.out = Bus(self.prefix + "_out", 1) # 2:1MUX logic and_obj = AndGate(a=self.b, b=self.c, prefix=self.prefix + "_and" + str(self.get_instance_num(cls=AndGate)), parent_component=self) self.add_component(and_obj) not_obj = NotGate(a=self.c, prefix=self.prefix + "_not" + str(self.get_instance_num(cls=NotGate)), parent_component=self) self.add_component(not_obj) and_obj = AndGate(a=self.a, b=self.get_previous_component().out, prefix=self.prefix + "_and" + str(self.get_instance_num(cls=AndGate)), parent_component=self) self.add_component(and_obj) xor_obj = XorGate(a=self.get_previous_component(3).out, b=self.get_previous_component().out, prefix=self.prefix + "_xor" + str(self.get_instance_num(cls=XorGate)), parent_component=self) self.add_component(xor_obj) # Connection of MUX output wire self.out.connect(0, xor_obj.out)
class SignedArrayMultiplier(MultiplierCircuit): """Class representing signed array multiplier. Signed array multiplier represents N-bit multiplier composed of many AND/NAND gates and half/full adders to calculate partial products and gradually sum them. Downside is its rather big area because it is composed of many logic gates. ``` A3B0 A2B0 A1B0 A0B0 │ │ │ │ │ │ │ │ ┌▼─▼─┐ ┌▼─▼┐ ┌▼─▼┐ ┌▼─▼┐ │NAND│ │AND│ │AND│ │AND│ └┬───┘ └┬──┘ └┬──┘ └─┬─┘ A3B1 │ A2B1 │ A1B1 │ A0B1 │ ┌▼─▼─┐ │ ┌▼─▼┐ │ ┌▼─▼┐ │ ┌▼─▼┐ │ │NAND│ │ │AND│ │ │AND│ │ │AND│ │ 1 └┬───┘ │ └┬──┘ │ └┬──┘ │ └┬──┘ │ │ │ │ │ │ │ │ │ │ ┌▼──▼┐ ┌▼──▼┐ ┌▼──▼┐ ┌▼──▼┐ │ │ │ │ │ │ │ │ │ │ ┌───────┤ FA │◄──┤ FA │◄──┤ FA │◄──┤ HA │ │ │ │ │ │ │ │ │ │ │ │ │ └┬───┘ └┬───┘ └┬───┘ └─┬──┘ │ │ A3B2 │ A2B2 │ A1B2 │ A0B2 │ │ │ ┌▼─▼─┐ │ ┌▼─▼┐ │ ┌▼─▼┐ │ ┌▼─▼┐ │ │ │ │NAND│ │ │AND│ │ │AND│ │ │AND│ │ │ │ └┬───┘ │ └┬──┘ │ └┬──┘ │ └┬──┘ │ │ │ │ │ │ │ │ │ │ │ │ ┌▼──▼┐ ┌▼──▼┐ ┌▼──▼┐ ┌▼──▼┐ │ │ │ │ │ │ │ │ │ │ │ │ ┌───────┤ FA │◄──┤ FA │◄──┤ FA │◄──┤ HA │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ └┬───┘ └┬───┘ └┬───┘ └─┬──┘ │ │ │ A3B3 │ A2B3 │ A1B3 │ A0B3 │ │ │ │ ┌▼─▼┐ │ ┌▼─▼─┐ │ ┌▼─▼─┐ │ ┌▼─▼─┐ │ │ │ │ │AND│ │ │NAND│ │ │NAND│ │ │NAND│ │ │ │ 1 │ └┬──┘ │ └┬───┘ │ └┬───┘ │ └┬───┘ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ┌─▼──┐ ┌▼──▼┐ ┌▼──▼┐ ┌▼──▼┐ ┌▼──▼┐ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │XOR │◄──┤ FA │◄──┤ FA │◄──┤ FA │◄──┤ HA │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ └─┬──┘ └─┬──┘ └─┬──┘ └─┬──┘ └─┬──┘ │ │ │ │ │ │ │ │ │ │ │ ▼ ▼ ▼ ▼ ▼ ▼ ▼ ▼ P7 P6 P5 P4 P3 P2 P1 P0 ``` Description of the __init__ method. Args: a (Bus): First input bus. b (Bus): Second input bus. prefix (str, optional): Prefix name of signed array multiplier. Defaults to "s_arrmul". """ 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)
class UnsignedCarrySkipAdder(ArithmeticCircuit): """Class representing unsigned carry skip (bypass) adder composed of smaller carry bypass blocks of chosen size to reduce propagation delay. Unsigned carry skip (bypass) adder represents faster adder circuit which is composed of more complex circuitry but provides much less propagation delay as opposed to rca. Each carry bypass block is composed of these logic parts: Propagate XOR gates compute propagate signals of corresponding bit pairs, these signals are then combined in multiple input AND gate (cascaded two input gates). Half/full adder cascade represents basic ripple carry adder design for input carry to ripple through them, additionally these adders compute individual output sum bits. Finally multiplexer lies at the end of each carry bypass block and is used to propagate block's input carry if multiple input AND gate output, which serves as select signal, is 1 or to wait for rippling of cout from the block's adders if it is 0. ``` ┼ ┼ ┼ ┼ ┌───▼───▼───┐ ┌───▼───▼───┐ ┌────┤ Propagate │ ┌────┤ Propagate │ │SEL │ signals │ │SEL │ signals │ ┌────▼─┐ └───────────┘ ┌────▼─┐ └───────────┘ │ │ │ │ ┌──┤2:1MUX│◄────────────────┬─┤2:1MUX│◄────────────────┬─Cin │ │ │ │ │ │ │ │ └────▲─┘ ┼ ┼ │ └────▲─┘ ┼ ┼ │ │ │ ┌───▼───▼───┐ │ │ ┌───▼───▼───┐ │ │ │ │ Adders │◄─┘ │ │ Adders │◄─┘ │ └────┤ │ └────┤ │ │ └─────┬─────┘ └─────┬─────┘ │ │ │ ▼ ▼ ▼ Cout Sums Sums ``` Description of the __init__ method. Args: a (Bus): First input bus. b (Bus): Second input bus. bypass_block_size (int, optional): Size of each composite bypass adder block size. Defaults to 4. prefix (str, optional): Prefix name of unsigned cska. Defaults to "u_cska". """ 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)
class UnsignedDaddaMultiplier(MultiplierCircuit): """Class representing unsigned dadda multiplier. Unsigned dadda multiplier represents fast N-bit multiplier which utilizes the functionality of reduction algorithm proposed by Luigi Dadda. First partial products are calculated for each bit pair that form the partial product multiplication columns. At last the reduced pairs are inserted into chosen multi bit unsigned adder to execute their summation and obtain the final output bits. Dadda algorithm is described more in detail here: https://en.wikipedia.org/wiki/Dadda_multiplier It is composed of much less inner components (half/full adders, AND gates) as opposed to e.g. wallace and array multipliers. Description of the __init__ method. Args: a (Bus): First input bus. b (Bus): Second input bus. prefix (str, optional): Prefix name of unsigned dadda multiplier. Defaults to "u_dadda_cla". unsigned_adder_class_name (str, optional): Unsigned multi bit adder used to obtain final sums of products. Defaults to UnsignedCarryLookaheadAdder. """ def __init__(self, a: Bus, b: Bus, prefix: str = "u_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 gates matrix self.columns = self.init_column_heights() # 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 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 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 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)) ]
class ArrayDivider(ArithmeticCircuit): """Class representing array divider. Array divider performs division between two N bit numbers and stores their quotient in the output bus (remainder bits are not returned). Design is based on series of iterative subtractions. ``` 0 B3 0 B2 0 B1 A3 B0 │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ┌─▼──▼─┐ ┌─▼──▼─┐ ┌─▼──▼─┐ ┌─▼──▼─┐ │ │ │ │ │ │ │ │ ┌─┤ FS │◄─┤ FS │◄─┤ FS │◄─┤ FS │◄─ 0 │ │ │ │ │ │ │ │ │ │ └──────┘ └───┬──┘ └───┬──┘ └───┬──┘ │ │ │0 │ │0 │ │A3 │ ┌─▼───▼┐ ┌─▼───▼┐ ┌─▼───▼┐ ├────────────►│2:1MUX├─►│2:1MUX├─►│2:1MUX│ │ └───┬──┘ └───┬──┘ └───┬──┘ │ MX0│ B3 MX1│ B2 MX2│ B1 A2 B0 │ │ │ │ │ │ │ │ │ │ ┌─▼─▼──┐ ┌─▼─▼──┐ ┌─▼──▼─┐ ┌─▼──▼─┐ │ │ │ │ │ │ │ │ │ │ ┌─┤ FS │◄─┤ FS │◄─┤ FS │◄─┤ FS │◄─ 0 │ │ │ │ │ │ │ │ │ │ │ │ └──────┘ └───┬──┘ └───┬──┘ └───┬──┘ │ │ │ │MX1 │ │MX2 │ │A2 │ │ ┌─▼───▼┐ ┌─▼───▼┐ ┌─▼───▼┐ │ ├────────────►│2:1MUX├─►│2:1MUX│─►│2:1MUX│ │ │ └───┬──┘ └───┬──┘ └───┬──┘ │ │ MX3│ B3 MX4│ B2 MX5│ B1 A1 B0 │ │ │ │ │ │ │ │ │ │ │ │ ┌─▼─▼──┐ ┌─▼─▼──┐ ┌─▼──▼─┐ ┌─▼──▼─┐ │ │ │ │ │ │ │ │ │ │ │ │ ┌─┤ FS │◄─┤ FS │◄─┤ FS │◄─┤ FS │◄─ 0 │ │ │ │ │ │ │ │ │ │ │ │ │ │ └──────┘ └───┬──┘ └───┬──┘ └───┬──┘ │ │ │ │ │MX4 │ │MX5 │ │A1 │ │ │ ┌─▼───▼┐ ┌─▼───▼┐ ┌─▼───▼┐ │ │ │────────────►│2:1MUX├─►│2:1MUX│─►│2:1MUX│ │ │ │ └───┬──┘ └───┬──┘ └───┬──┘ │ │ │ MX6│ B3 MX7│ B2 MX8│ B1 A0 B0 │ │ │ │ │ │ │ │ │ │ │ │ │ │ ┌─▼─▼──┐ ┌─▼─▼──┐ ┌─▼──▼─┐ ┌─▼──▼─┐ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ┌─┤ FS │◄─┤ FS │◄─┤ FS │◄─┤ FS │◄─ 0 │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ └──────┘ └──────┘ └──────┘ └──────┘ │ │ │ │ │ │ │ │ ┌──▼──┐ ┌──▼──┐ ┌──▼──┐ ┌──▼──┐ │ │ │ │ │ │ │ │ │ NOT │ │ NOT │ │ NOT │ │ NOT │ │ │ │ │ │ │ │ │ └──┬──┘ └──┬──┘ └──┬──┘ └──┬──┘ ▼ ▼ ▼ ▼ Q3 Q2 Q1 Q0 ``` Description of the __init__ method. Args: a (Bus): First input bus. b (Bus): Second input bus. prefix (str, optional): Prefix name of array divider. Defaults to "arrdiv". """ def __init__(self, a: Bus, b: Bus, prefix: str = "arrdiv"): 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 quotient result self.out = Bus(self.prefix + "_out", self.N) # Performing series of iterative subtractions # Gradually shifting the divisor for a_index in reversed(range(self.N)): # Series of subtractions to obtain quotient bit for b_index in range(self.N): # First full subtractor is formed from divisor's LSB bit (b_index) and divident's MSB bit (a_index) if b_index == 0: adder_object = FullSubtractor( a=self.a.get_wire(a_index), b=self.b.get_wire(b_index), c=ConstantWireValue0(), prefix=self.prefix + "_fs" + str(self.get_instance_num(cls=FullSubtractor))) elif a_index == self.N - 1: adder_object = FullSubtractor( a=ConstantWireValue0(), b=self.b.get_wire(b_index), c=self.get_previous_component().get_borrow_wire(), prefix=self.prefix + "_fs" + str(self.get_instance_num(cls=FullSubtractor))) else: adder_object = FullSubtractor( a=self.get_previous_component(self.N + 1).out.get_wire(), b=self.b.get_wire(b_index), c=self.get_previous_component().get_borrow_wire(), prefix=self.prefix + "_fs" + str(self.get_instance_num(cls=FullSubtractor))) self.add_component(adder_object) # Don't generate multiplexers for divison remainders if a_index != 0: for mux_index in range(self.N - 1): mux_object = TwoOneMultiplexer( a=self.get_previous_component( self.N).get_difference_wire(), b=self.get_previous_component(self.N).a, c=self.get_previous_component( 1 + mux_index).get_borrow_wire(), prefix=self.prefix + "_mux2to1" + str(self.get_instance_num(cls=TwoOneMultiplexer))) self.add_component(mux_object) # Every borrow out obtained from each iteration of subtractions needs to be negated to represent the quotient output bit quotient = NotGate(a=adder_object.get_borrow_wire(), prefix=self.prefix + "_not" + str(self.get_instance_num(cls=NotGate)), parent_component=self) self.add_component(quotient) self.out.connect(a_index, quotient.out)
class PGLogicBlock(TwoInputOneBitCircuit): """Class representing two input one bit propagate/generate logic block. ``` ┌──────┐ ───►│ ├─► P │ ├─► G ───►│ ├─► S └──────┘ ``` Description of the __init__ method. Args: a (Wire, optional): First input wire. Defaults to Wire(name="a"). b (Wire, optional): Second input wire. Defaults to Wire(name="b"). prefix (str, optional): Prefix name of pg logic block. Defaults to "pg_logic". """ def __init__(self, a: Wire = Wire(name="a"), b: Wire = Wire(name="b"), prefix: str = "pg_logic"): super().__init__(a, b, prefix) # 3 wires for component's bus output (propagate, generate, sum) self.out = Bus(self.prefix + "_out", 3) # PG logic propagate_or = OrGate(a, b, prefix=self.prefix + "_or" + str(self.get_instance_num(cls=OrGate)), outid=0, parent_component=self) self.add_component(propagate_or) generate_and = AndGate(a, b, prefix=self.prefix + "_and" + str(self.get_instance_num(cls=AndGate)), outid=1, parent_component=self) self.add_component(generate_and) sum_xor = XorGate(a, b, prefix=self.prefix + "_xor" + str(self.get_instance_num(cls=XorGate)), outid=2, parent_component=self) self.add_component(sum_xor) self.out.connect(0, propagate_or.out) self.out.connect(1, generate_and.out) self.out.connect(2, sum_xor.out) def get_propagate_wire(self): """Get output wire carrying propagate signal value. Returns: Wire: Return propagate wire. """ return self.out.get_wire(0) def get_generate_wire(self): """Get output wire carrying generate signal value. Returns: Wire: Return generate wire. """ return self.out.get_wire(1) def get_sum_wire(self): """Get output wire carrying sum value. Returns: Wire: Return sum wire. """ return self.out.get_wire(2)
class HalfSubtractor(TwoInputOneBitCircuit): """Class representing two input one bit half subtractor. ``` ┌──────┐ ───►│ ├─► Difference │ │ ───►│ ├─► Bout └──────┘ ``` Description of the __init__ method. Args: a (Wire, optional): First input wire. Defaults to Wire(name="a"). b (Wire, optional): Second input wire. Defaults to Wire(name="b"). prefix (str, optional): Prefix name of half subtractor adder. Defaults to "hs". """ def __init__(self, a: Wire = Wire(name="a"), b: Wire = Wire(name="b"), prefix: str = "hs"): super().__init__(a, b, prefix) # 2 wires for component's bus output (difference, bout) self.out = Bus(self.prefix + "_out", 2) # Difference # XOR gate for calculation of 1-bit difference difference_xor = XorGate(a=self.a, b=self.b, prefix=self.prefix + "_xor" + str(self.get_instance_num(cls=XorGate)), outid=0, parent_component=self) self.add_component(difference_xor) self.out.connect(0, difference_xor.out) # Bout # NOT and AND gates for calculation of 1-bit borrow out not_obj = NotGate(a=self.a, prefix=self.prefix + "_not" + str(self.get_instance_num(cls=NotGate)), parent_component=self) self.add_component(not_obj) borrow_and = AndGate(a=not_obj.out, b=self.b, prefix=self.prefix + "_xor" + str(self.get_instance_num(cls=XorGate)), outid=1, parent_component=self) self.add_component(borrow_and) self.out.connect(1, borrow_and.out) def get_difference_wire(self): """Get output wire carrying difference value. Returns: Wire: Return difference wire. """ return self.out.get_wire(0) def get_borrow_wire(self): """Get output wire carrying borrow out value. Returns: Wire: Return borrow out wire. """ return self.out.get_wire(1)