diff --git a/ariths_gen/core/arithmetic_circuits/general_circuit.py b/ariths_gen/core/arithmetic_circuits/general_circuit.py index 2061ee5..76142a2 100644 --- a/ariths_gen/core/arithmetic_circuits/general_circuit.py +++ b/ariths_gen/core/arithmetic_circuits/general_circuit.py @@ -18,7 +18,7 @@ class GeneralCircuit(): The __init__ method fills some mandatory attributes concerning arithmetic circuit that are later used for generation into various representations. """ - def __init__(self, prefix: str, name: str, out_N: int, inner_component: bool = False, inputs: list = [], one_bit_circuit: bool = False, signed: bool = False, outname=None, **kwargs): + def __init__(self, prefix: str, name: str, out_N: int, inner_component: bool = False, inputs: list = [], one_bit_circuit: bool = False, signed: bool = False, signed_out = None, outname=None, **kwargs): if prefix == "": self.prefix = name else: @@ -41,13 +41,17 @@ class GeneralCircuit(): if not outname: outname = self.prefix+"_out" - self.out = Bus(outname, out_N, out_bus=True, signed=signed) + + if signed_out is None: + signed_out = signed + self.out = Bus(outname, out_N, out_bus=True, signed=signed_out) self.components = [] self._prefixes = [] # TODO rename to fullname and add distinct attr for prefix, name, suffix self.circuit_gates = [] self.circuit_wires = [] self.signed = signed + self.signed_out = signed_out self.c_data_type = "int64_t" if self.signed is True else "uint64_t" self.pyc = None # Python compiled function self.kwargs = kwargs diff --git a/ariths_gen/multi_bit_circuits/subtractors/__init__.py b/ariths_gen/multi_bit_circuits/subtractors/__init__.py new file mode 100644 index 0000000..9f9e86a --- /dev/null +++ b/ariths_gen/multi_bit_circuits/subtractors/__init__.py @@ -0,0 +1,3 @@ +from ariths_gen.multi_bit_circuits.subtractors.ripple_carry_subtractor import ( + UnsignedRippleCarrySubtractor, SignedRippleCarrySubtractor +) diff --git a/ariths_gen/multi_bit_circuits/subtractors/ripple_carry_subtractor.py b/ariths_gen/multi_bit_circuits/subtractors/ripple_carry_subtractor.py new file mode 100644 index 0000000..619e4bb --- /dev/null +++ b/ariths_gen/multi_bit_circuits/subtractors/ripple_carry_subtractor.py @@ -0,0 +1,139 @@ +from ariths_gen.wire_components import ( + Bus +) +from ariths_gen.core.arithmetic_circuits import ( + GeneralCircuit +) +from ariths_gen.one_bit_circuits.one_bit_components import ( + HalfAdder, + FullAdder +) +from ariths_gen.one_bit_circuits.logic_gates import ( + XorGate, NotGate +) +from ariths_gen.wire_components.wires import ConstantWireValue1 + + +class UnsignedRippleCarrySubtractor(GeneralCircuit): + """Class representing unsigned ripple carry adder. + + Unsigned ripple carry adder represents N-bit unsigned adder which is composed of + N one bit fulladders. The first has carry-in set to one and the B input is inverted. + ``` + ── ── ── ── + B3 A3 B2 A2 B1 A1 B0 A0 + │ │ │ │ │ │ │ │ + ┌─▼──▼─┐ ┌─▼──▼─┐ ┌─▼──▼─┐ ┌─▼──▼─┐ + │ │ C3│ │ C2│ │ C1│ │ 1 + ┌──┤ 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 rcs. Defaults to "". + name (str, optional): Name of unsigned rcs. Defaults to "u_rcs". + """ + def __init__(self, a: Bus, b: Bus, prefix: str = "", name: str = "u_rcs", **kwargs): + self.N = max(a.N, b.N) + super().__init__(inputs=[a, b], prefix=prefix, name=name, out_N=self.N + 1, signed_out=True, **kwargs) + + # 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) + + + + # Gradual addition of 1-bit adder components + for input_index in range(self.N): + b_not = self.add_component(NotGate(self.b.get_wire(input_index), prefix=self.prefix+"_not"+str(input_index))) + # First adder is a half adder + if input_index == 0: + obj_adder = FullAdder(self.a.get_wire(input_index), b_not.out, ConstantWireValue1(), prefix=self.prefix+"_ha") + # Rest adders are full adders + else: + obj_adder = FullAdder(self.a.get_wire(input_index), b_not.out, obj_adder.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()) + + # invert the last carry wire + self.add_component(NotGate(obj_adder.get_carry_wire(), prefix=self.prefix+"_not_c"+str(self.N-1))) + self.out.connect(self.N, self.get_previous_component().out) + + +class SignedRippleCarrySubtractor(GeneralCircuit): + """Class representing signed ripple carry subtractor. + + Signed ripple carry adder represents N-bit signed adder which is composed of + N one bit adders, where first is a half adder and rest are full adders. + At last XOR gates are used to ensure proper sign extension. + + Its downside is its long propagation delay the bigger the circuit is. + + ``` + __ __ __ __ __ __ + B3 A3 B3 A3 B3 A3 B2 A2 B1 A1 B0 A0 + │ │ │ │ │ │ │ │ │ │ │ │ + ┌─▼──▼─┐ ┌─▼──▼─┐ ┌─▼──▼─┐ ┌─▼──▼─┐ ┌─▼──▼─┐ ┌─▼──▼─┐ + │ SIGN │ C4│ SIGN │ C4│ │ C3│ │ C2│ │ C1│ │ + │Extend│◄──│Extend│◄──┤ FA │◄──┤ FA │◄──┤ FA │◄──┤ HA │ + │ 2 │ │ │ │ │ │ │ │ │ │ │ + └──┬───┘ └──┬───┘ └──┬───┘ └──┬───┘ └──┬───┘ └──┬───┘ + ▼ ▼ ▼ ▼ ▼ ▼ + S5 S4 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 signed rca. Defaults to "". + name (str, optional): Name of signed rca. Defaults to "s_rcs". + """ + def __init__(self, a: Bus, b: Bus, prefix: str = "", name: str = "u_rcs", **kwargs): + self.N = max(a.N, b.N) + super().__init__(inputs=[a, b], prefix=prefix, name=name, out_N=self.N + 2, signed=True, **kwargs) + + assert self.a.N == self.b.N, "Both buses must have the same length" + # Bus sign extension in case buses have different lengths - don't know if works + self.a.bus_extend(N=self.N, prefix=a.prefix) + self.b.bus_extend(N=self.N, prefix=b.prefix) + + + + # Gradual addition of 1-bit adder components + for input_index in range(self.N): + b_not = self.add_component(NotGate(self.b.get_wire(input_index), prefix=self.prefix+"_not"+str(input_index))) + # First adder is a half adder + if input_index == 0: + obj_adder = FullAdder(self.a.get_wire(input_index), b_not.out, ConstantWireValue1(), prefix=self.prefix+"_ha") + # Rest adders are full adders + else: + obj_adder = FullAdder(self.a.get_wire(input_index), b_not.out, obj_adder.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()) + + # replicate the last (sign) bit + obj_adder = FullAdder(self.a.get_wire(self.N - 1), b_not.out, obj_adder.get_carry_wire(), prefix=self.prefix+"_fa_repl_"+str(input_index)) + self.add_component(obj_adder) + self.out.connect(self.N, self.get_previous_component().get_sum_wire()) + + + + obj_adder = FullAdder(self.a.get_wire(self.N - 1), b_not.out, obj_adder.get_carry_wire(), prefix=self.prefix+"_fa_repl2_"+str(input_index)) + self.add_component(obj_adder) + + self.out.connect(self.N+1, self.get_previous_component().get_sum_wire()) + + # invert the last carry wire + #onot = self.add_component(NotGate(obj_adder.get_carry_wire(), prefix=self.prefix+"_not_c"+str(self.N-1))) + #self.out.connect(self.N + 1, onot.out) \ No newline at end of file diff --git a/tests/test_all.py b/tests/test_all.py index 9e0c93e..51caec8 100644 --- a/tests/test_all.py +++ b/tests/test_all.py @@ -44,6 +44,10 @@ from ariths_gen.multi_bit_circuits.adders import ( SignedCarryIncrementAdder ) +from ariths_gen.multi_bit_circuits.subtractors import ( + UnsignedRippleCarrySubtractor, SignedRippleCarrySubtractor +) + from ariths_gen.multi_bit_circuits.multipliers import ( UnsignedDaddaMultiplier, UnsignedArrayMultiplier, @@ -282,6 +286,23 @@ def test_unsigned_add(): np.testing.assert_array_equal(expected, r) + +def test_unsigned_sub(): + """ Test unsigned subtractor """ + N = 9 + a = Bus(N=N, prefix="a") + b = Bus(N=N, prefix="b") + av = np.arange(2**N) + bv = av.reshape(-1, 1) + expected = av - bv + + # Non configurable multi-bit subtractors + for c in [UnsignedRippleCarrySubtractor]: + sub = c(a, b) + r = sub(av, bv) + np.testing.assert_array_equal(expected, r) + + def test_signed_add(): """ Test signed adders """ N = 9 @@ -311,6 +332,20 @@ def test_signed_add(): r = add(av, bv) np.testing.assert_array_equal(expected, r) +def test_signed_sub(): + """ Test signed subtractor """ + N = 4 + a = Bus(N=N, prefix="a") + b = Bus(N=N, prefix="b") + av = np.arange(-(2**(N-1)), 2**(N-1)) + bv = av.reshape(-1, 1) + expected = av - bv + + # Non configurable multi-bit adders + for c in [SignedRippleCarrySubtractor]: + sub = c(a, b) + r = sub(av, bv) + np.testing.assert_array_equal(expected, r) def test_mac(): class MAC(GeneralCircuit):