From b88c502343d031e6fbe75176d4910ca3ec7009a0 Mon Sep 17 00:00:00 2001 From: honzastor Date: Wed, 22 Mar 2023 17:57:51 +0100 Subject: [PATCH 01/35] Addition of MUX2x1 PDK support and optimization of hierarchical Verilog code generation when using PDK modules (the gates and wires associated with native ArithsGen implementation are not generated). --- .../arithmetic_circuits/general_circuit.py | 20 ++++-- .../three_input_one_bit_components.py | 66 +++++++++++++++++-- .../two_input_one_bit_components.py | 19 ++++-- ariths_gen/pdk.py | 1 + 4 files changed, 88 insertions(+), 18 deletions(-) diff --git a/ariths_gen/core/arithmetic_circuits/general_circuit.py b/ariths_gen/core/arithmetic_circuits/general_circuit.py index 232774c..4e12819 100644 --- a/ariths_gen/core/arithmetic_circuits/general_circuit.py +++ b/ariths_gen/core/arithmetic_circuits/general_circuit.py @@ -86,19 +86,24 @@ class GeneralCircuit(): else: return sum(isinstance(c, cls) for c in self.components) - def get_circuit_gates(self): + def get_circuit_gates(self, verilog_output: bool = False): """Gets a list of all the logic gates in circuit that should be generated. + Args: + verilog_output (bool): Specifies whether the call has been invoked by a verilog output generation method. Returns: list: List of composite logic gates. """ gates = [] for c in self.components: if isinstance(c, TwoInputLogicGate): - if c.disable_generation is False: + if c.disable_generation is False and (verilog_output is False or (hasattr(self, "use_verilog_instance") and self.use_verilog_instance is False)) or hasattr(self, "use_verilog_instance") is False: gates.append(c) else: - gates.extend((c.get_circuit_gates())) + # Check whether it is necessary to use gates for the Verilog component + # description (ArithsGen internally defined comp) or not (technology specific instance) + if verilog_output is False or (hasattr(c, "use_verilog_instance") and c.use_verilog_instance is False) or hasattr(c, "use_verilog_instance") is False: + gates.extend((c.get_circuit_gates(verilog_output))) return gates def get_one_bit_components(self): @@ -150,15 +155,17 @@ class GeneralCircuit(): else: return list({type(c): c for c in components}.values()) - def get_component_types(self): + def get_component_types(self, verilog_output: bool = False): """Retrieves a list of all the unique types of subcomponents composing the circuit. Returning list consists of only the unique types of logic gates, one bit circuits and multi bit circuits. + Args: + verilog_output (bool): Specifies whether the call has been invoked by a verilog output generation method. Returns: list: List of unique component types describing the circuit. """ - gate_comps = self.get_unique_types(components=self.get_circuit_gates()) + gate_comps = self.get_unique_types(components=self.get_circuit_gates(verilog_output)) one_bit_comps = self.get_unique_types( components=self.get_one_bit_components()) multi_bit_comps = self.get_unique_types( @@ -371,7 +378,6 @@ class GeneralCircuit(): # Obtain proper circuit name with its bit width circuit_prefix = self.__class__( a=Bus("a"), b=Bus("b")).prefix + str(self.N) - print(self._parent_kwargs) circuit_block = self.__class__(a=Bus(N=self.N, prefix="a"), b=Bus( N=self.N, prefix="b"), name=circuit_prefix, **self._parent_kwargs) return f"{circuit_block.get_circuit_c()}\n\n" @@ -509,7 +515,7 @@ class GeneralCircuit(): str: Hierarchical Verilog code of all subcomponents function blocks description. """ # Retrieve all unique component types composing this circuit and add them kwargs from the parent circuit to allow propagatation of config settings for subcomponents - self.component_types = self.get_component_types() + self.component_types = self.get_component_types(verilog_output=True) for c in self.component_types: c._parent_kwargs = self.kwargs return "".join([c.get_function_block_v() for c in self.component_types]) diff --git a/ariths_gen/one_bit_circuits/one_bit_components/three_input_one_bit_components.py b/ariths_gen/one_bit_circuits/one_bit_components/three_input_one_bit_components.py index 3e93b21..069d38b 100644 --- a/ariths_gen/one_bit_circuits/one_bit_components/three_input_one_bit_components.py +++ b/ariths_gen/one_bit_circuits/one_bit_components/three_input_one_bit_components.py @@ -76,13 +76,12 @@ class FullAdder(ThreeInputOneBitCircuit): return " " + self.use_verilog_instance.format( **{ "unit": self.prefix, - "wirea": self.a.prefix, - "wireb": self.b.prefix, - "wirec": self.c.prefix, + "wirea": f"1'b{self.a.value}" if self.a.is_const() else self.a.name, + "wireb": f"1'b{self.b.value}" if self.b.is_const() else self.b.name, + "wirec": f"1'b{self.c.value}" if self.c.is_const() else self.c.name, "wireys": self.get_sum_wire().prefix, "wireyc": self.get_carry_wire().prefix, - } - ) + ";\n" + }) + ";\n" def get_self_init_v_hier(self): """ support of custom PDK """ @@ -93,7 +92,8 @@ class FullAdder(ThreeInputOneBitCircuit): for o in self.out.bus: unique_out_wires.append(o.name+"_outid"+str(self.out.bus.index(o))) if o.is_const() or o.name in [self.a.name, self.b.name] else unique_out_wires.append(o.name) - return " " + self.use_verilog_instance.format(**{ + return " " + self.use_verilog_instance.format( + **{ "unit": self.prefix, "wirea": self.a.name, "wireb": self.b.name, @@ -102,6 +102,15 @@ class FullAdder(ThreeInputOneBitCircuit): "wireyc": unique_out_wires[1], }) + ";\n" + def get_circuit_v(self): + """ support of custom PDK """ + if not self.use_verilog_instance: + return super().get_circuit_v() + + return f"{self.get_prototype_v_hier()}" + \ + f"{self.get_self_init_v_hier()}" + \ + f"endmodule" + class FullAdderP(FullAdder, ThreeInputOneBitCircuit): """Class representing three input one bit full adder with additional output wire for P signal. @@ -264,6 +273,8 @@ class TwoOneMultiplexer(ThreeInputOneBitCircuit): c (Wire, optional): Select signal. Defaults to Wire(name="sel"). prefix (str, optional): Prefix name of two to one multiplexer. Defaults to "mux2to1". """ + use_verilog_instance = False + 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) @@ -295,6 +306,49 @@ class TwoOneMultiplexer(ThreeInputOneBitCircuit): """ return self.out.get_wire(0) + def get_init_v_flat(self): + """ support of custom PDK """ + if not self.use_verilog_instance: + return super().get_init_v_flat() + + neg_out_w_name = f"neg_{self.out.get_wire(0).name}" + return f" wire {neg_out_w_name};\n " + self.use_verilog_instance.format( + **{ + "unit": self.prefix, + "wirea": self.a.name, + "wireb": self.b.name, + "wires": self.c.name, + "wirey": neg_out_w_name, + }) + ";\n" + f" assign {self.out.get_wire(0).name} = ~{neg_out_w_name};\n" + + def get_self_init_v_hier(self): + """ support of custom PDK """ + if not self.use_verilog_instance: + return super().get_self_init_v_hier() + + unique_out_wires = [] + for o in self.out.bus: + unique_out_wires.append(o.name+"_outid"+str(self.out.bus.index(o))) if o.is_const() or o.name in [self.a.name, self.b.name] else unique_out_wires.append(o.name) + + neg_out_w_name = f"neg_{unique_out_wires[0]}" + return f" wire {neg_out_w_name};\n " + self.use_verilog_instance.format( + **{ + "unit": self.prefix, + "wirea": self.a.name, + "wireb": self.b.name, + "wires": self.c.name, + "wirey": neg_out_w_name + }) + ";\n" + f" assign {unique_out_wires[0]} = ~{neg_out_w_name};\n" + + def get_circuit_v(self): + """ support of custom PDK """ + if not self.use_verilog_instance: + return super().get_circuit_v() + + return f"{self.get_prototype_v_hier()}" + \ + f"{self.get_self_init_v_hier()}" + \ + f"endmodule" + class FullSubtractor(ThreeInputOneBitCircuit): """Class representing three input one bit full subtractor. diff --git a/ariths_gen/one_bit_circuits/one_bit_components/two_input_one_bit_components.py b/ariths_gen/one_bit_circuits/one_bit_components/two_input_one_bit_components.py index 835b545..d6bb318 100644 --- a/ariths_gen/one_bit_circuits/one_bit_components/two_input_one_bit_components.py +++ b/ariths_gen/one_bit_circuits/one_bit_components/two_input_one_bit_components.py @@ -64,12 +64,11 @@ class HalfAdder(TwoInputOneBitCircuit): return " " + self.use_verilog_instance.format( **{ "unit": self.prefix, - "wirea": self.a.prefix, - "wireb": self.b.prefix, + "wirea": f"1'b{self.a.value}" if self.a.is_const() else self.a.name, + "wireb": f"1'b{self.b.value}" if self.b.is_const() else self.b.name, "wireys": self.get_sum_wire().prefix, "wireyc": self.get_carry_wire().prefix, - } - ) + ";\n" + }) + ";\n" def get_self_init_v_hier(self): """ support of custom PDK """ @@ -80,7 +79,8 @@ class HalfAdder(TwoInputOneBitCircuit): for o in self.out.bus: unique_out_wires.append(o.name+"_outid"+str(self.out.bus.index(o))) if o.is_const() or o.name in [self.a.name, self.b.name] else unique_out_wires.append(o.name) - return " " + self.use_verilog_instance.format(**{ + return " " + self.use_verilog_instance.format( + **{ "unit": self.prefix, "wirea": self.a.name, "wireb": self.b.name, @@ -88,6 +88,15 @@ class HalfAdder(TwoInputOneBitCircuit): "wireyc": unique_out_wires[1], }) + ";\n" + def get_circuit_v(self): + """ support of custom PDK """ + if not self.use_verilog_instance: + return super().get_circuit_v() + + return f"{self.get_prototype_v_hier()}" + \ + f"{self.get_self_init_v_hier()}" + \ + f"endmodule" + class PGLogicBlock(TwoInputOneBitCircuit): """Class representing two input one bit propagate/generate logic block. diff --git a/ariths_gen/pdk.py b/ariths_gen/pdk.py index fab62ed..41d3a12 100644 --- a/ariths_gen/pdk.py +++ b/ariths_gen/pdk.py @@ -18,3 +18,4 @@ from .one_bit_circuits import ( def set_pdk45_library(): one_bit_components.FullAdder.use_verilog_instance = "FAX1 {unit} (.A({wirea}), .B({wireb}), .C({wirec}), .YS({wireys}), .YC({wireyc}))" one_bit_components.HalfAdder.use_verilog_instance = "HAX1 {unit} (.A({wirea}), .B({wireb}), .YS({wireys}), .YC({wireyc}))" + one_bit_components.TwoOneMultiplexer.use_verilog_instance = "MUX2X1 {unit} (.A({wirea}), .B({wireb}), .S({wires}), .Y({wirey}))" From 7cf34d04f30185f18fa64b356b732894dcf5c252 Mon Sep 17 00:00:00 2001 From: honzastor Date: Wed, 22 Mar 2023 18:14:55 +0100 Subject: [PATCH 02/35] Bugfix in conditional statement. --- ariths_gen/core/arithmetic_circuits/general_circuit.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ariths_gen/core/arithmetic_circuits/general_circuit.py b/ariths_gen/core/arithmetic_circuits/general_circuit.py index 4e12819..42af3f6 100644 --- a/ariths_gen/core/arithmetic_circuits/general_circuit.py +++ b/ariths_gen/core/arithmetic_circuits/general_circuit.py @@ -97,12 +97,12 @@ class GeneralCircuit(): gates = [] for c in self.components: if isinstance(c, TwoInputLogicGate): - if c.disable_generation is False and (verilog_output is False or (hasattr(self, "use_verilog_instance") and self.use_verilog_instance is False)) or hasattr(self, "use_verilog_instance") is False: + if c.disable_generation is False and (verilog_output is False or ((hasattr(self, "use_verilog_instance") and self.use_verilog_instance is False) or hasattr(self, "use_verilog_instance") is False)): gates.append(c) else: # Check whether it is necessary to use gates for the Verilog component # description (ArithsGen internally defined comp) or not (technology specific instance) - if verilog_output is False or (hasattr(c, "use_verilog_instance") and c.use_verilog_instance is False) or hasattr(c, "use_verilog_instance") is False: + if verilog_output is False or ((hasattr(c, "use_verilog_instance") and c.use_verilog_instance is False) or hasattr(c, "use_verilog_instance") is False): gates.extend((c.get_circuit_gates(verilog_output))) return gates From 363e402e16246ca9e73e8cb1a7c99bb7d03c7437 Mon Sep 17 00:00:00 2001 From: Vojta Mrazek Date: Thu, 23 Mar 2023 08:00:37 +0100 Subject: [PATCH 03/35] workflow: docs --- .github/workflows/generate.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/generate.yml b/.github/workflows/generate.yml index ca6c8c3..fd8347c 100644 --- a/.github/workflows/generate.yml +++ b/.github/workflows/generate.yml @@ -128,7 +128,7 @@ jobs: name: documentation path: html - name: Deploy πŸš€ - uses: JamesIves/github-pages-deploy-action@4.4.1 + uses: JamesIves/github-pages-deploy-action@4 with: branch: gh-pages # The branch the action should deploy to. folder: html/ariths_gen # The folder the action should deploy. From 49bbc86a0fbb2539178bebc9c8b0590e037e4c00 Mon Sep 17 00:00:00 2001 From: Vojta Mrazek Date: Thu, 23 Mar 2023 13:39:32 +0100 Subject: [PATCH 04/35] accepts a wire as a bus --- ariths_gen/wire_components/wires.py | 5 +++++ tests/test_all.py | 17 +++++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/ariths_gen/wire_components/wires.py b/ariths_gen/wire_components/wires.py index 019a4fa..8a68d1e 100644 --- a/ariths_gen/wire_components/wires.py +++ b/ariths_gen/wire_components/wires.py @@ -255,6 +255,11 @@ class Wire(): else: return f"" + """ define read-only parameter N""" + @property + def N(self): + return 1 + # Wires with constant values # class ConstantWireValue0(Wire): diff --git a/tests/test_all.py b/tests/test_all.py index 75a9f7a..c8cd777 100644 --- a/tests/test_all.py +++ b/tests/test_all.py @@ -341,3 +341,20 @@ def test_direct(): expected = np.array([[0, 3, 0, 3], [2, 3, 2, 3], [0, 3, 0, 3], [2, 3, 2, 3]]) np.testing.assert_equal(r, expected) print(r) + +def test_wire_as_bus(): + """ accept a wire as a bus """ + class test_circuit(GeneralCircuit): + def __init__(self, a: Wire, b: Wire, c: Bus, prefix="test_circuit", **kwargs): + super().__init__(prefix=prefix, name="test_circuit", inputs=[a, b, c], out_N=1, **kwargs) + g = self.add_component(AndGate(a, b, prefix="g2")) + g2 = self.add_component(AndGate(g.out, c[0], prefix="g2")) + g3 = self.add_component(AndGate(g2.out, c[1], prefix="g2")) + self.out[0] = g3.out + + circ = test_circuit(Wire("a"), Wire("b"), Bus("c", 2), "c1") + r = circ(np.array([0, 1]), + np.array([0, 1]).reshape(-1, 1), + np.arange(4).reshape(-1, 1, 1)) + assert r.sum() == 1 + assert r[-1, -1, -1] == 1 \ No newline at end of file From 44e0a920d181fe33d9920ddf5858d3a84a99c3dc Mon Sep 17 00:00:00 2001 From: Vojta Mrazek Date: Fri, 24 Mar 2023 12:11:42 +0100 Subject: [PATCH 05/35] MUX support of constant values --- .../one_bit_components/three_input_one_bit_components.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/ariths_gen/one_bit_circuits/one_bit_components/three_input_one_bit_components.py b/ariths_gen/one_bit_circuits/one_bit_components/three_input_one_bit_components.py index 069d38b..7df5de4 100644 --- a/ariths_gen/one_bit_circuits/one_bit_components/three_input_one_bit_components.py +++ b/ariths_gen/one_bit_circuits/one_bit_components/three_input_one_bit_components.py @@ -311,13 +311,14 @@ class TwoOneMultiplexer(ThreeInputOneBitCircuit): if not self.use_verilog_instance: return super().get_init_v_flat() + # TODO - replace by one verilog_instance_format! neg_out_w_name = f"neg_{self.out.get_wire(0).name}" return f" wire {neg_out_w_name};\n " + self.use_verilog_instance.format( **{ "unit": self.prefix, - "wirea": self.a.name, - "wireb": self.b.name, - "wires": self.c.name, + "wirea": f"1'b{self.a.value}" if self.a.is_const() else self.a.name, + "wireb": f"1'b{self.b.value}" if self.b.is_const() else self.b.name, + "wires": f"1'b{self.c.value}" if self.c.is_const() else self.c.name, "wirey": neg_out_w_name, }) + ";\n" + f" assign {self.out.get_wire(0).name} = ~{neg_out_w_name};\n" From a4741db19107fd8ef48f27ec2f9f88b3cbe99ae2 Mon Sep 17 00:00:00 2001 From: Vojta Mrazek Date: Tue, 28 Mar 2023 11:16:55 +0200 Subject: [PATCH 06/35] connection checks (asserts) --- .../three_input_one_bit_components.py | 23 +++++++++++-------- ariths_gen/wire_components/buses.py | 4 +--- 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/ariths_gen/one_bit_circuits/one_bit_components/three_input_one_bit_components.py b/ariths_gen/one_bit_circuits/one_bit_components/three_input_one_bit_components.py index 7df5de4..2207164 100644 --- a/ariths_gen/one_bit_circuits/one_bit_components/three_input_one_bit_components.py +++ b/ariths_gen/one_bit_circuits/one_bit_components/three_input_one_bit_components.py @@ -311,16 +311,19 @@ class TwoOneMultiplexer(ThreeInputOneBitCircuit): if not self.use_verilog_instance: return super().get_init_v_flat() - # TODO - replace by one verilog_instance_format! - neg_out_w_name = f"neg_{self.out.get_wire(0).name}" - return f" wire {neg_out_w_name};\n " + self.use_verilog_instance.format( - **{ - "unit": self.prefix, - "wirea": f"1'b{self.a.value}" if self.a.is_const() else self.a.name, - "wireb": f"1'b{self.b.value}" if self.b.is_const() else self.b.name, - "wires": f"1'b{self.c.value}" if self.c.is_const() else self.c.name, - "wirey": neg_out_w_name, - }) + ";\n" + f" assign {self.out.get_wire(0).name} = ~{neg_out_w_name};\n" + if self.out[0].is_const(): + return "" + else: + # TODO - replace by one verilog_instance_format! + neg_out_w_name = f"neg_{self.out.get_wire(0).name}" + return f" wire {neg_out_w_name};\n " + self.use_verilog_instance.format( + **{ + "unit": self.prefix, + "wirea": self.a.get_wire_value_v_hier(), # former version: f"1'b{self.a.value}" if self.a.is_const() else self.a.name, + "wireb": self.b.get_wire_value_v_hier(), #f"1'b{self.b.value}" if self.b.is_const() else self.b.name, + "wires": self.c.get_wire_value_v_hier(), #f"1'b{self.c.value}" if self.c.is_const() else self.c.name, + "wirey": neg_out_w_name, + }) + ";\n" + f" assign {self.out.get_wire(0).name} = ~{neg_out_w_name};\n" def get_self_init_v_hier(self): """ support of custom PDK """ diff --git a/ariths_gen/wire_components/buses.py b/ariths_gen/wire_components/buses.py index ed532bf..f7c91f5 100644 --- a/ariths_gen/wire_components/buses.py +++ b/ariths_gen/wire_components/buses.py @@ -75,11 +75,9 @@ class Bus(): Returns: Wire: Returning wire from the bus. """ + assert wire_index < self.N, f"Wire index {wire_index} is out of bounds of the bus {self.prefix} with size {self.N}" return self.bus[wire_index] - def __getitem__(self, i): - return self.bus[i] - def __getitem__(self, i): return self.get_wire(i) From a44b0638a15085d6541bc9eb0093997371e38cd7 Mon Sep 17 00:00:00 2001 From: Vojta Mrazek Date: Tue, 28 Mar 2023 13:55:58 +0200 Subject: [PATCH 07/35] Implementation of QuAd approximate adder --- .../approximate_adders/__init__.py | 1 + .../approximate_adders/quad.py | 192 ++++++++++++++++++ generate_quad_lib.py | 79 +++++++ tests/test_ax.py | 26 +++ 4 files changed, 298 insertions(+) create mode 100644 ariths_gen/multi_bit_circuits/approximate_adders/__init__.py create mode 100644 ariths_gen/multi_bit_circuits/approximate_adders/quad.py create mode 100644 generate_quad_lib.py create mode 100644 tests/test_ax.py diff --git a/ariths_gen/multi_bit_circuits/approximate_adders/__init__.py b/ariths_gen/multi_bit_circuits/approximate_adders/__init__.py new file mode 100644 index 0000000..3c027b5 --- /dev/null +++ b/ariths_gen/multi_bit_circuits/approximate_adders/__init__.py @@ -0,0 +1 @@ +from .quad import QuAdder \ No newline at end of file diff --git a/ariths_gen/multi_bit_circuits/approximate_adders/quad.py b/ariths_gen/multi_bit_circuits/approximate_adders/quad.py new file mode 100644 index 0000000..473ee09 --- /dev/null +++ b/ariths_gen/multi_bit_circuits/approximate_adders/quad.py @@ -0,0 +1,192 @@ +""" +Implementation of QuAdder + +For more information, see: +M. A. Hanif, R. Hafiz, O. Hasan and M. Shafique, "QuAd: Design and analysis of Quality-area optimal Low-Latency approximate Adders," 2017 54th ACM/EDAC/IEEE Design Automation Conference (DAC), Austin, TX, USA, 2017, pp. 1-6, doi: 10.1145/3061639.3062306. + +""" + +from ...wire_components import ( + Wire, + ConstantWireValue0, + ConstantWireValue1, + Bus +) +from ariths_gen.core.arithmetic_circuits import ( + ArithmeticCircuit, + MultiplierCircuit +) +from ariths_gen.one_bit_circuits.one_bit_components import ( + HalfAdder, + FullAdder +) +from ariths_gen.one_bit_circuits.logic_gates import ( + AndGate, + NandGate, + OrGate, + NorGate, + XorGate, + XnorGate, + NotGate +) +from ariths_gen.multi_bit_circuits.adders.ripple_carry_adder import UnsignedRippleCarryAdder +import warnings + + +class QuAdder(ArithmeticCircuit): + """ + Implementation of QuAd + + https://ieeexplore.ieee.org/document/8060326 + + The implementation is inspired by Matlab code from the authors of the paper: + ```matlab + temp_count=1; + for iij=1:length(R_vect) + fprintf(fileID,['wire [' num2str(R_vect(iij)+P_vect(iij)) ':0] temp' num2str(temp_count) ';\n']); + temp_count=temp_count + 1; + end + + temp_count=1; + for iiij=1:length(R_vect) + if (sum(R_vect(1:iiij))+P_vect(1)-1) == (sum(R_vect(1:iiij))+P_vect(1)-R_vect(iiij)-P_vect(iiij)) + fprintf(fileID,['aassign temp' num2str(temp_count) '[' num2str(R_vect(iiij)+P_vect(iiij)) ':0] = in1[' num2str(sum(R_vect(1:iiij))+P_vect(1)-1) '] + in2[' num2str(sum(R_vect(1:iiij))+P_vect(1)-1) '];\n']); + else + disp(R_vect(1:iiij)) + fprintf(fileID,['bassign temp' num2str(temp_count) '[' num2str(R_vect(iiij)+P_vect(iiij)) ':0] = in1[' num2str(sum(R_vect(1:iiij))+P_vect(1)-1) ':' num2str(sum(R_vect(1:iiij))+P_vect(1)-R_vect(iiij)-P_vect(iiij)) '] + in2[' num2str(sum(R_vect(1:iiij))+P_vect(1)-1) ':' num2str(sum(R_vect(1:iiij))+P_vect(1)-R_vect(iiij)-P_vect(iiij)) '];\n']); + end + temp_count=temp_count+1; + end + + statement='};\n'; + temp_count=1; + for iiij=1:length(R_vect) + if iiij ~= length(R_vect) + if (R_vect(iiij)==1) + statement = [', temp' num2str(temp_count) '[' num2str(R_vect(iiij)+P_vect(iiij)-1) '] ' statement]; + else + statement = [', temp' num2str(temp_count) '[' num2str(R_vect(iiij)+P_vect(iiij)-1) ':' num2str(P_vect(iiij)) '] ' statement]; + end + else + statement = ['assign res[' num2str(N) ':0] =' '{ temp' num2str(temp_count) '[' num2str(R_vect(iiij)+P_vect(iiij)) ':' num2str(P_vect(iiij)) '] ' statement]; + end + temp_count=temp_count+1; + end + ``` + + + """ + + def log(self, *args): + if self.use_log: + print(*args) + + def __init__(self, a, b, R, P, prefix, name="quad", adder_type=None, use_log=False, **kwargs): + """ + :param a: Bus first input + :param b: Bus second input + :param R: list of integers, defines the resultant bits of all the sub-adders (the first index specifies the resultant bits of sub-adder 1 and so on) + :param P: list of integers, defines the prediction bits of all the sub-adders (again the first index specifies the prediction bits of sub-adder 1 and so on) + """ + + if not adder_type: + adder_type = UnsignedRippleCarryAdder + + # Assumptions checks + assert len(R) == len(P), "R and P must have the same length" + print([P[i] < P[i-1] + R[i-1] for i in range(1, len(P))]) + assert all([P[i] < P[i-1] + R[i-1] for i in range(1, len(P))] + ), "Pi must be lower than Pi-1 + Ri-1" + assert sum(R) == a.N, "Sum of R must be equal to number of bits" + + self.use_log = use_log + + self.N = max(a.N, b.N) + super().__init__(a=a, b=b, prefix=prefix, name=name, out_N=self.N+1, **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) + + #warnings.warn("QuAdder is not tested yet") + + # Connect all outputs to zero + for i in range(self.N+1): + self.out[i] = ConstantWireValue0() + + # Declaration of temporary wires (just for debug purposes) + temp_count = 0 + for iiij in range(0, len(R)): + self.log('wire [' + str(R[iiij]+P[iiij]) + + ':0] temp' + str(temp_count) + ';') + temp_count = temp_count + 1 + + def bus_subconnect(out_bus, in_bus, out_indexes, in_indexes): + out_indexes = list(out_indexes) + in_indexes = list(in_indexes) + assert len(out_indexes) == len(in_indexes) + + for i, j in zip(out_indexes, in_indexes): + if j >= in_bus.N: + out_bus[i] = ConstantWireValue0() # unsigned extension + else: + out_bus.connect(i, in_bus.get_wire(j)) # [i] = in_bus[j] + + # Connection of adders + temp_count = 0 + temp_bus = [] + for iiij in range(0, len(R)): + # Former verilog output + self.log("assign temp{}[{}:0] = in1[{}:{}] + in2[{}:{}];".format( + temp_count, + R[iiij]+P[iiij], + sum(R[0:iiij + 1]) + P[0]-1, + sum(R[0:iiij + 1]) + P[0]-R[iiij]-P[iiij], + sum(R[0:iiij + 1]) + P[0]-1, + sum(R[0:iiij + 1]) + P[0]-R[iiij]-P[iiij] + )) + + a1 = Bus(f"{prefix}_temp_{temp_count}_a", R[iiij]+P[iiij]) + b1 = Bus(f"{prefix}_temp_{temp_count}_b", R[iiij]+P[iiij]) + + bus_subconnect(b1, self.b, + range(R[iiij]+P[iiij]), + range(sum(R[0:iiij + 1])+P[0]-R[iiij]-P[iiij], sum(R[0:iiij + 1])+P[0])) + + bus_subconnect(a1, self.a, + range(R[iiij]+P[iiij]), + range(sum(R[0:iiij + 1])+P[0]-R[iiij]-P[iiij], sum(R[0:iiij + 1])+P[0])) + + temp_bus.append(self.add_component( + adder_type(a1, b1, prefix=f"{prefix}_add_{temp_count}") + + )) + temp_count = temp_count+1 + + # Final connection + temp_count = 0 + statement = "}" + wire_id = 0 + for iiij in range(0, len(R)): + if iiij != len(R) - 1: + if R[iiij] == 1: + statement = ', temp{}[{}]'.format( + temp_count, R[iiij]+P[iiij] - 1) + statement + else: + statement = ', temp{}[{}:{}]'.format( + temp_count, R[iiij]+P[iiij] - 1, P[iiij]) + statement + + else: + statement = 'assign res[' + str(self.N) + ':0] =' + '{ temp' + str( + temp_count) + '[' + str(R[iiij]+P[iiij]) + ':' + str(P[iiij]) + '] ' + statement + + self.log(statement) + for i in range(P[iiij], R[iiij]+P[iiij]): + self.log(temp_count, i, wire_id, temp_bus[temp_count].out[i]) + self.out[wire_id] = temp_bus[temp_count].out[i] + wire_id += 1 + + temp_count = temp_count+1 + + # Last carry (MSB) + self.out[wire_id] = temp_bus[temp_count - 1].out[R[iiij]+P[iiij]] diff --git a/generate_quad_lib.py b/generate_quad_lib.py new file mode 100644 index 0000000..1395ab0 --- /dev/null +++ b/generate_quad_lib.py @@ -0,0 +1,79 @@ +""" +This script generate the library of all possible QuAdders with N bits. +Note that the adders are not Pareto-optimal. +""" + +from ariths_gen.core.arithmetic_circuits.arithmetic_circuit import ArithmeticCircuit +from ariths_gen.core.arithmetic_circuits import GeneralCircuit +from ariths_gen.wire_components import Bus, Wire +from ariths_gen.multi_bit_circuits.adders import UnsignedRippleCarryAdder +from ariths_gen.multi_bit_circuits.approximate_adders import QuAdder +from ariths_gen.multi_bit_circuits.multipliers import UnsignedArrayMultiplier, UnsignedDaddaMultiplier +import os, sys + +import numpy as np +import itertools + +if __name__ == "__main__": + + directory = f"lib_quad/lib_quad{N}" + os.makedirs(directory, exist_ok=True) + + # generate the C code + cfile = open(f"{directory}/lib_quad_{N}.c", "w") + hfile = open(f"{directory}/lib_quad_{N}.h", "w") + hfile.write("#include \n") + + data = {} + + # verilog code is zipped + import zipfile + vfile = zipfile.ZipFile(file=f"{directory}/lib_quad_{N}.zip", mode="w", compression=zipfile.ZIP_DEFLATED) + cnt = 0 + N = 8 + + # up to 3 stages + for n in [1, 2, 3]: + Rall = list(itertools.product(range(1, N + 1), repeat=n)) + for R in Rall: + # skip invalid R + if sum(R) != N: + continue + + for P in itertools.product(range(0, N + 1), repeat=n): + # test the condition from the paper + if not all([P[i] < P[i-1] + R[i-1] for i in range(1, len(P))]): + continue + print(cnt, R, P) # print the current configuration + + prefix = f"quad_{N}" + name = "r_{}_p_{}".format("_".join([str(r) for r in R]), "_".join([str(p) for p in P])) + + try: + c = QuAdder(Bus("a", N), Bus("b", N), R = R, P=P, name=name, prefix=prefix, use_log=False) + c.get_c_code_flat(file_object=cfile) + vf = vfile.open(f"{prefix}_{name}.v", "w") + # convert byte file vf to text file + import io + vt = io.TextIOWrapper(vf, encoding="utf-8") + c.get_v_code_flat(file_object=vt) + vt.close() + + cfile.write("\n\n") + hfile.write(f"uint64_t {prefix}_{name}(uint64_t a, uint64_t b);") + + data[f"{name}_{prefix}"] = { + "bw": N, + "cfun": f"{prefix}_{name}", + "verilog": f"{prefix}_{name}.v", + "verilog_entity": f"{prefix}_{name}", + "quad_r" : R, + "quad_p" : P, + } + cnt += 1 + except IOError as e: + print(R, P, e) + +# store the metadata +import json, gzip +json.dump(data, gzip.open(f"{directory}/lib_quad_{N}.json.gz", "wt"), indent=4) \ No newline at end of file diff --git a/tests/test_ax.py b/tests/test_ax.py new file mode 100644 index 0000000..b78b8fe --- /dev/null +++ b/tests/test_ax.py @@ -0,0 +1,26 @@ +""" +Testing the QuAdder +""" +from ariths_gen.core.arithmetic_circuits.arithmetic_circuit import ArithmeticCircuit +from ariths_gen.core.arithmetic_circuits import GeneralCircuit +from ariths_gen.wire_components import Bus, Wire +from ariths_gen.multi_bit_circuits.adders import UnsignedRippleCarryAdder +from ariths_gen.multi_bit_circuits.approximate_adders import QuAdder +from ariths_gen.multi_bit_circuits.multipliers import UnsignedArrayMultiplier, UnsignedDaddaMultiplier +import os, sys + +import numpy as np +import itertools + +def test_quadder(): + c = QuAdder(Bus("a", 8), Bus("b", 8), R = [4, 2, 2], P=[0, 2, 2], prefix="quad") + c.get_v_code_hier(file_object=sys.stdout) + + x = np.arange(0, 256).reshape(-1, 1) + y = np.arange(0, 256).reshape(1, -1) + + r = c(x, y) + r2 = x + y + + assert np.abs(r - r2).max() == 64 + np.testing.assert_equal(np.abs(r - r2).mean(), 7.5) \ No newline at end of file From f853a467030bf29c631b7e9c25bb4ef76bc8693b Mon Sep 17 00:00:00 2001 From: Vojta Mrazek Date: Thu, 13 Apr 2023 12:09:07 +0200 Subject: [PATCH 08/35] CGP circuit checks --- ariths_gen/core/cgp_circuit.py | 11 +++++++++-- ariths_gen/wire_components/wires.py | 2 +- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/ariths_gen/core/cgp_circuit.py b/ariths_gen/core/cgp_circuit.py index 075155d..ae4e3df 100644 --- a/ariths_gen/core/cgp_circuit.py +++ b/ariths_gen/core/cgp_circuit.py @@ -60,8 +60,12 @@ class UnsignedCGPCircuit(GeneralCircuit): i, in_a, in_b, fn = map(int, re.match( r"\(?\[(\d+)\](\d+),(\d+),(\d+)\)?", definition).groups()) - assert in_a < i - assert in_b < i + if in_a > i or in_b > i: + raise ValueError(f"Backward connection in CGP gene \"{definition}\", maxid = {i}") + + if in_a == i or in_b == i: + raise ValueError(f"Loop connection in CGP gene: \"{definition}\", maxid = {i}") + comp_set = dict(prefix=f"{self.prefix}_core_{i:03d}", parent_component=self) a, b = self._get_wire(in_a), self._get_wire(in_b) @@ -91,6 +95,9 @@ class UnsignedCGPCircuit(GeneralCircuit): # Output connection for i, o in enumerate(map(int, cgp_outputs.split(","))): + if o >= c_in + c_rows * c_cols + 2: + raise ValueError( + f"Output {i} is connected to wire {o} which is not in the range of CGP wires ({c_in + c_rows * c_cols + 2})") w = self._get_wire(o) self.out.connect(i, w) diff --git a/ariths_gen/wire_components/wires.py b/ariths_gen/wire_components/wires.py index 8a68d1e..c34240b 100644 --- a/ariths_gen/wire_components/wires.py +++ b/ariths_gen/wire_components/wires.py @@ -99,7 +99,7 @@ class Wire(): if self.is_const(): return f"({self.c_const}) << {offset};\n" else: - return f"(({self.name} >> 0) & 0x01ull) << {offset};\n" + return f"(({self.prefix} >> {self.index}) & 0x01ull) << {offset};\n" def return_wire_value_c_hier(self, offset: int = 0): """Retrieves desired bit value from wire represented in C code variable and bitwise shifts it to desired position for storing it within a bus for hierarchical generation. From 7e1112cf8156344bc9d5e1434a01f3c618b7b4eb Mon Sep 17 00:00:00 2001 From: honzastor Date: Wed, 6 Mar 2024 00:42:12 +0100 Subject: [PATCH 09/35] Added individual input bus attributes to CGP Circuit objects to allow for the generation of output CGP representation. --- ariths_gen/core/cgp_circuit.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/ariths_gen/core/cgp_circuit.py b/ariths_gen/core/cgp_circuit.py index ae4e3df..cbb5fbc 100644 --- a/ariths_gen/core/cgp_circuit.py +++ b/ariths_gen/core/cgp_circuit.py @@ -43,6 +43,13 @@ class UnsignedCGPCircuit(GeneralCircuit): inputs = [Bus(N=bw, prefix=f"input_{chr(i)}") for i, bw in enumerate(input_widths, start=0x61)] + # Assign each Bus object in self.inputs to a named attribute of self + for bus in inputs: + # Here, bus.prefix is 'input_a', 'input_b', etc. + # We strip 'input_' and use the remaining part (e.g., 'a', 'b') to create the attribute name + attr_name = bus.prefix.replace('input_', '') + setattr(self, attr_name, bus) + # Adding values to the list self.vals = {} j = 2 # Start from two, 0=False, 1=True From 2e1694ccd5ff0155dfec0fa512bd5b2d822e1a36 Mon Sep 17 00:00:00 2001 From: Vojta Mrazek Date: Fri, 22 Mar 2024 14:19:23 +0100 Subject: [PATCH 10/35] popcount and compare --- .../arithmetic_circuits/general_circuit.py | 18 +++- .../multi_bit_circuits/adders/__init__.py | 2 +- .../multi_bit_circuits/others/__init__.py | 12 +++ .../multi_bit_circuits/others/bit_reduce.py | 89 +++++++++++++++++++ .../multi_bit_circuits/others/compare.py | 83 +++++++++++++++++ .../multi_bit_circuits/others/popcount.py | 89 +++++++++++++++++++ ariths_gen/wire_components/buses.py | 9 ++ tests/test_compare.py | 50 +++++++++++ tests/test_popcnt.py | 50 +++++++++++ tests/test_reduce.py | 78 ++++++++++++++++ 10 files changed, 477 insertions(+), 3 deletions(-) create mode 100644 ariths_gen/multi_bit_circuits/others/__init__.py create mode 100644 ariths_gen/multi_bit_circuits/others/bit_reduce.py create mode 100644 ariths_gen/multi_bit_circuits/others/compare.py create mode 100644 ariths_gen/multi_bit_circuits/others/popcount.py create mode 100644 tests/test_compare.py create mode 100644 tests/test_popcnt.py create mode 100644 tests/test_reduce.py diff --git a/ariths_gen/core/arithmetic_circuits/general_circuit.py b/ariths_gen/core/arithmetic_circuits/general_circuit.py index 42af3f6..c4fb720 100644 --- a/ariths_gen/core/arithmetic_circuits/general_circuit.py +++ b/ariths_gen/core/arithmetic_circuits/general_circuit.py @@ -1,3 +1,4 @@ +from typing import Dict from ariths_gen.core.logic_gate_circuits.logic_gate_circuit import OneInputLogicGate, TwoInputLogicGate from ariths_gen.wire_components import ( @@ -52,12 +53,22 @@ class GeneralCircuit(): # super().__init__(prefix, name, out_N, inner_component, inputs=[a, b], signed=signed, **kwargs) + + def get_circuit_def(self) -> Dict[str, Wire]: + """ returns IDs and wires of the inputs and output""" + #.{circuit_block.a.prefix}({self.a.prefix}), .{circuit_block.b.prefix}({self.b.prefix}), .{circuit_block.out.prefix}({self.out.prefix}));\n" + r = {chr(97 + i): self.inputs[i] for i in range(len(self.inputs))} + r['out'] = self.get_global_prefix() + "_out" + return r + def add_component(self, component): """Adds a component into list of circuit's inner subcomponents. Args: component: Subcomponent to be added into list of components composing described circuit. """ + prefixes = [c.prefix for c in self.components] + #assert component.prefix not in prefixes, f"Component with prefix {component.prefix} already exists in the circuit." self.components.append(component) return component @@ -550,6 +561,8 @@ class GeneralCircuit(): Returns: str: Hierarchical Verilog code of subcomponent arithmetic circuit's wires declaration. """ + return "".join(w.get_wire_declaration_v() for w in self.inputs + [self.out]) + "\n" + return f" wire [{self.a.N-1}:0] {self.a.prefix};\n" + \ f" wire [{self.b.N-1}:0] {self.b.prefix};\n" + \ f" wire [{self.out.N-1}:0] {self.out.prefix};\n" @@ -576,8 +589,9 @@ class GeneralCircuit(): circuit_type = self.__class__(a=Bus("a"), b=Bus("b")).prefix + str(self.N) circuit_block = self.__class__(a=Bus(N=self.N, prefix="a"), b=Bus( N=self.N, prefix="b"), name=circuit_type) - return self.a.return_bus_wires_values_v_hier() + self.b.return_bus_wires_values_v_hier() + \ - f" {circuit_type} {circuit_type}_{self.out.prefix}(.{circuit_block.a.prefix}({self.a.prefix}), .{circuit_block.b.prefix}({self.b.prefix}), .{circuit_block.out.prefix}({self.out.prefix}));\n" + return "".join([c.return_bus_wires_values_v_hier() for c in self.inputs]) + \ + f" {circuit_type} {circuit_type}_{self.out.prefix}(" + ",".join([f".{a.prefix}({b.prefix})" for a, b in zip(circuit_block.inputs, self.inputs)]) + f", .{circuit_block.out.prefix}({self.out.prefix}));\n" + #.{circuit_block.a.prefix}({self.a.prefix}), .{circuit_block.b.prefix}({self.b.prefix}), .{circuit_block.out.prefix}({self.out.prefix}));\n" def get_function_out_v_hier(self): """Generates hierarchical Verilog code assignment of corresponding arithmetic circuit's output bus wires. diff --git a/ariths_gen/multi_bit_circuits/adders/__init__.py b/ariths_gen/multi_bit_circuits/adders/__init__.py index f0ba494..0ba1f34 100644 --- a/ariths_gen/multi_bit_circuits/adders/__init__.py +++ b/ariths_gen/multi_bit_circuits/adders/__init__.py @@ -67,4 +67,4 @@ from ariths_gen.multi_bit_circuits.adders.conditional_sum_adder import ( from ariths_gen.multi_bit_circuits.adders.carry_increment_adder import ( UnsignedCarryIncrementAdder, SignedCarryIncrementAdder -) +) \ No newline at end of file diff --git a/ariths_gen/multi_bit_circuits/others/__init__.py b/ariths_gen/multi_bit_circuits/others/__init__.py new file mode 100644 index 0000000..44ed65b --- /dev/null +++ b/ariths_gen/multi_bit_circuits/others/__init__.py @@ -0,0 +1,12 @@ + +from ariths_gen.multi_bit_circuits.others.popcount import ( + UnsignedPopCount +) + +from ariths_gen.multi_bit_circuits.others.bit_reduce import ( + BitReduce, AndReduce, OrReduce +) + +from ariths_gen.multi_bit_circuits.others.compare import ( + UnsignedCompareLT +) \ No newline at end of file diff --git a/ariths_gen/multi_bit_circuits/others/bit_reduce.py b/ariths_gen/multi_bit_circuits/others/bit_reduce.py new file mode 100644 index 0000000..45da4d7 --- /dev/null +++ b/ariths_gen/multi_bit_circuits/others/bit_reduce.py @@ -0,0 +1,89 @@ +""" + +""" + +from ariths_gen.wire_components import ( + Wire, + ConstantWireValue0, + ConstantWireValue1, + Bus, + wires +) +from ariths_gen.core.arithmetic_circuits import ( + ArithmeticCircuit, + GeneralCircuit, + MultiplierCircuit +) +from ariths_gen.core.logic_gate_circuits import ( + MultipleInputLogicGate +) +from ariths_gen.one_bit_circuits.one_bit_components import ( + HalfAdder, + FullAdder, + FullAdderP, + TwoOneMultiplexer +) +from ariths_gen.one_bit_circuits.logic_gates import ( + AndGate, + NandGate, + OrGate, + NorGate, + XorGate, + XnorGate, + NotGate +) + +from ariths_gen.core.logic_gate_circuits import TwoInputLogicGate, TwoInputInvertedLogicGate, OneInputLogicGate +from ariths_gen.multi_bit_circuits.adders import UnsignedRippleCarryAdder + +from math import log2, ceil + +class BitReduce(GeneralCircuit): + """Class representing tree reducer circuit. Doent work for NAND gate! + """ + + def __init__(self, a: Bus, gate : TwoInputLogicGate, prefix : str = "", name : str = "bitreduce", **kwargs): + self.N = a.N + self.a = a + + outc = 1 + super().__init__(name=name, prefix=prefix, inputs = [self.a], out_N=outc) + + # tree reduction + def create_tree(a: Bus, depth: int, branch="A"): + + #print(a) + if a.N == 1: + return a[0] + else: + half = a.N // 2 + b_in = Bus(N=half, prefix=f"b_inn{depth}A") + c_in = Bus(N=a.N - half, prefix=f"b_inn{depth}B") + #print(a, half, a.N) + + + for i, j in enumerate(range(half)): + b_in[i] = a[j] + + for i, j in enumerate(range(half, a.N)): + c_in[i] = a[j] + + b = create_tree(b_in, depth=depth + 1, branch = "A") + c = create_tree(c_in, depth= depth + 1, branch = "B") + d = gate(a=b, b=c, prefix = f"{self.prefix}_red_{branch}_{depth}") + self.add_component(d) + return d.out + + sumwire = create_tree(self.a, 0, "X") + #print(sumbus) + self.out[0] = sumwire + + +class OrReduce(BitReduce): + def __init__(self, a: Bus, prefix : str = "", name : str = "orreduce", **kwargs): + super().__init__(a=a, gate=OrGate, prefix=prefix, name=name, **kwargs) + + +class AndReduce(BitReduce): + def __init__(self, a: Bus, prefix : str = "", name : str = "orreduce", **kwargs): + super().__init__(a=a, gate=AndGate, prefix=prefix, name=name, **kwargs) \ No newline at end of file diff --git a/ariths_gen/multi_bit_circuits/others/compare.py b/ariths_gen/multi_bit_circuits/others/compare.py new file mode 100644 index 0000000..6c62970 --- /dev/null +++ b/ariths_gen/multi_bit_circuits/others/compare.py @@ -0,0 +1,83 @@ +""" + +""" + +from ariths_gen.wire_components import ( + Wire, + ConstantWireValue0, + ConstantWireValue1, + Bus, + wires +) +from ariths_gen.core.arithmetic_circuits import ( + ArithmeticCircuit, + GeneralCircuit, + MultiplierCircuit +) + +from ariths_gen.core.logic_gate_circuits import ( + MultipleInputLogicGate +) +from ariths_gen.one_bit_circuits.one_bit_components import ( + HalfAdder, + FullAdder, + FullAdderP, + TwoOneMultiplexer +) +from ariths_gen.one_bit_circuits.logic_gates import ( + AndGate, + NandGate, + OrGate, + NorGate, + XorGate, + XnorGate, + NotGate +) + +from ariths_gen.multi_bit_circuits.others import OrReduce + + +from math import log2, ceil + +class UnsignedCompareLT(GeneralCircuit): + """Class representing unsigned compare + + + Returns true if a < b + + """ + + def __init__(self, a: Bus, b: Bus, prefix : str = "", name : str = "cmp_lt", **kwargs): + self.a = a + self.b = b + self.N = max(a.N, b.N) + + #print("outc", outc) + super().__init__(name=name, prefix=prefix, + inputs = [self.a, self.b], out_N=1) + + + self.a.bus_extend(self.N, prefix=a.prefix) + self.b.bus_extend(self.N, prefix=b.prefix) + + + # create wires + psum = ConstantWireValue1() + + res = Bus(N = self.N, prefix=self.prefix + "res") + + + for i in reversed(range(self.N)): + + i1 = self.add_component(NotGate(self.a[i], f"{self.prefix}_i1_{i}")).out + i2 = self.b[i] + + and1 = self.add_component(AndGate(i1, i2, f"{self.prefix}_and1_{i}")).out + res[i] = self.add_component(AndGate(and1, psum, f"{self.prefix}_and2_{i}")).out + + pi = self.add_component(XnorGate(self.a[i], self.b[i], f"{self.prefix}_pi_{i}")).out + psum = self.add_component(AndGate(pi, psum, f"{self.prefix}_psum_{i}")).out + + + self.out = self.add_component(OrReduce(res, prefix=f"{self.prefix}_orred")).out + #self.out.connect_bus(sumbus ) \ No newline at end of file diff --git a/ariths_gen/multi_bit_circuits/others/popcount.py b/ariths_gen/multi_bit_circuits/others/popcount.py new file mode 100644 index 0000000..cdca327 --- /dev/null +++ b/ariths_gen/multi_bit_circuits/others/popcount.py @@ -0,0 +1,89 @@ +""" + +""" + +from ariths_gen.wire_components import ( + Wire, + ConstantWireValue0, + ConstantWireValue1, + Bus, + wires +) +from ariths_gen.core.arithmetic_circuits import ( + ArithmeticCircuit, + GeneralCircuit, + MultiplierCircuit +) +from ariths_gen.core.logic_gate_circuits import ( + MultipleInputLogicGate +) +from ariths_gen.one_bit_circuits.one_bit_components import ( + HalfAdder, + FullAdder, + FullAdderP, + TwoOneMultiplexer +) +from ariths_gen.one_bit_circuits.logic_gates import ( + AndGate, + NandGate, + OrGate, + NorGate, + XorGate, + XnorGate, + NotGate +) + +from ariths_gen.multi_bit_circuits.adders import UnsignedRippleCarryAdder + +from math import log2, ceil + +class UnsignedPopCount(GeneralCircuit): + """Class representing unsigned popcount circuit. + + Popcount circuit is a circuit that counts the number of 1s in a binary number. + + """ + + def __init__(self, a: Bus, adder : ArithmeticCircuit|None = None, prefix : str = "", name : str = "popcnt", **kwargs): + self.N = a.N + self.a = a + + outc = ceil(log2(self.N + 1)) + #print("outc", outc) + super().__init__(name=name, prefix=prefix, inputs = [self.a], out_N=outc) + + + self.a.bus_extend(2**(outc - 1), prefix=a.prefix) + #print(self.a) + self.adder = adder + if not self.adder: + self.adder = UnsignedRippleCarryAdder + + # tree reduction + def create_tree(a: Bus, depth: int, branch="A"): + + #print(a) + if a.N == 1: + return a + else: + half = a.N // 2 + b_in = Bus(N=half, prefix=f"b_inn{depth}A") + c_in = Bus(N=a.N - half, prefix=f"b_inn{depth}B") + #print(a, half, a.N) + + + for i, j in enumerate(range(half)): + b_in[i] = a[j] + + for i, j in enumerate(range(half, a.N)): + c_in[i] = a[j] + + b = create_tree(b_in, depth=depth + 1, branch = "A") + c = create_tree(c_in, depth= depth + 1, branch = "B") + d = self.adder(a=b, b=c, prefix = f"{self.prefix}_add{branch}_{depth}") + self.add_component(d) + return d.out + + sumbus = create_tree(self.a,0, "X") + #print(sumbus) + self.out.connect_bus(sumbus ) \ No newline at end of file diff --git a/ariths_gen/wire_components/buses.py b/ariths_gen/wire_components/buses.py index f7c91f5..930d834 100644 --- a/ariths_gen/wire_components/buses.py +++ b/ariths_gen/wire_components/buses.py @@ -242,6 +242,15 @@ class Bus(): [unique_out_wires.append(w.prefix) if w.prefix not in unique_out_wires else None for w in self.bus] return "".join([f", .{circuit_block.out.get_wire(self.bus.index(o)).prefix}({unique_out_wires.pop(unique_out_wires.index(o.prefix))})" if o.prefix in unique_out_wires else f", .{circuit_block.out.get_wire(self.bus.index(o)).prefix}()" for o in self.bus]) + def get_wire_declaration_v(self): + """Declare the wire in Verilog code representation. + + Returns: + str: Verilog code for declaration of individual bus wires. + """ + return f" wire [{self.N-1}:0] {self.prefix};\n" + + """ BLIF CODE GENERATION """ def get_wire_declaration_blif(self, array: bool = True): """Declare each wire from the bus independently in Blif code representation. diff --git a/tests/test_compare.py b/tests/test_compare.py new file mode 100644 index 0000000..02e1312 --- /dev/null +++ b/tests/test_compare.py @@ -0,0 +1,50 @@ +from ariths_gen.wire_components import ( + Wire, + ConstantWireValue0, + ConstantWireValue1, + Bus +) + +from ariths_gen.core.arithmetic_circuits import GeneralCircuit + +from ariths_gen.multi_bit_circuits.others import ( + UnsignedCompareLT +) + +import numpy as np +import math +from io import StringIO + + +def test_compare(): + """ Test unsigned comparator """ + N = 8 + + a = Bus(N=N, prefix="a") + b = Bus(N=N, prefix="b") + av = np.arange(2**N).reshape(1, -1) + bv = np.arange(2**N).reshape(-1, 1) + + + cmp = UnsignedCompareLT(a=a, b=b) + o = StringIO() + cmp.get_v_code_hier(open("tmp.verilog", "w")) + print(o.getvalue()) + +# av = 0 + # bv = 5 + + + v = cmp(av, bv) + print("ret = ", v) + + expected = np.array(av < bv).astype(int) + + print("expected = ", expected) + + #expected = np.sum(r, axis=1) + + np.testing.assert_array_equal(v, expected) + + + \ No newline at end of file diff --git a/tests/test_popcnt.py b/tests/test_popcnt.py new file mode 100644 index 0000000..8d66628 --- /dev/null +++ b/tests/test_popcnt.py @@ -0,0 +1,50 @@ +from ariths_gen.wire_components import ( + Wire, + ConstantWireValue0, + ConstantWireValue1, + Bus +) + +from ariths_gen.core.arithmetic_circuits import GeneralCircuit + +from ariths_gen.multi_bit_circuits.others import ( + UnsignedPopCount +) + +import numpy as np +import math +from io import StringIO + + +def test_popcount(): + """ Test unsigned adders """ + N = 7 + + for N in [3, 7, 8, 9, 16]: + a = Bus(N=N, prefix="a") + av = np.arange(2**N) + + + popcnt = UnsignedPopCount(a=a) + #o = StringIO() + #popcnt.get_v_code_hier(o) + #print(o.getvalue()) + + + print(popcnt(av)) + + + # conv to binary + r = [] + a_s = av.copy() + for i in range(N): + r.append(a_s % 2) + a_s = a_s // 2 + r = np.dstack(r).reshape(-1, N) + print("r = ", r) + expected = np.sum(r, axis=1) + + np.testing.assert_array_equal(popcnt(av), expected) + + + \ No newline at end of file diff --git a/tests/test_reduce.py b/tests/test_reduce.py new file mode 100644 index 0000000..9ec4b7e --- /dev/null +++ b/tests/test_reduce.py @@ -0,0 +1,78 @@ +from ariths_gen.wire_components import ( + Wire, + ConstantWireValue0, + ConstantWireValue1, + Bus +) + +from ariths_gen.core.arithmetic_circuits import GeneralCircuit + +from ariths_gen.multi_bit_circuits.others import ( + BitReduce, AndReduce, OrReduce +) + +import numpy as np +import math +from io import StringIO + + +def test_orreduce(): + """ Test unsigned adders """ + N = 7 + + for N in [3, 7, 8, 9, 16]: + a = Bus(N=N, prefix="a") + av = np.arange(2**N) + + + reduce = OrReduce(a=a) + o = StringIO() + reduce.get_v_code_hier(o) + print(o.getvalue()) + + + #print(reduce(av)) + + # conv to binary + r = [] + a_s = av.copy() + for i in range(N): + r.append(a_s % 2) + a_s = a_s // 2 + r = np.dstack(r).reshape(-1, N) + print("r = ", r) + expected = np.bitwise_or.reduce(r, axis=1) + + np.testing.assert_array_equal(reduce(av), expected) + + +def test_andreduce(): + """ Test unsigned adders """ + N = 7 + + for N in [3, 7, 8, 9, 16]: + a = Bus(N=N, prefix="a") + av = np.arange(2**N) + + + reduce = AndReduce(a=a) + o = StringIO() + reduce.get_v_code_hier(o) + print(o.getvalue()) + + + #print(reduce(av)) + + # conv to binary + r = [] + a_s = av.copy() + for i in range(N): + r.append(a_s % 2) + a_s = a_s // 2 + r = np.dstack(r).reshape(-1, N) + print("r = ", r) + expected = np.bitwise_and.reduce(r, axis=1) + + np.testing.assert_array_equal(reduce(av), expected) + + \ No newline at end of file From d013a40145193c2d2fe2b658e3ddbf2202607bd0 Mon Sep 17 00:00:00 2001 From: honzastor Date: Wed, 27 Mar 2024 23:00:13 +0100 Subject: [PATCH 11/35] Added unsigned recursive multiplier and made some bugfixes. --- .testall.sh | 8 +- README.md | 5 + .../arithmetic_circuits/arithmetic_circuit.py | 20 +- .../arithmetic_circuits/general_circuit.py | 40 +- .../approximate_multipliers/__init__.py | 9 + .../recursive_multiplier.py | 404 ++++++++++++++++++ ariths_gen/wire_components/wires.py | 2 +- generate_quad_lib.py | 2 +- generate_test.py | 10 + tests/test_all.py | 48 ++- tests/test_ax.py | 22 +- tests/test_cgp.py | 27 +- tests/test_circuits.sh | 2 + tests/test_circuits_cgp.sh | 1 + tests/test_circuits_verilog.sh | 1 + tests/test_compare.py | 18 +- tests/test_popcnt.py | 18 +- tests/test_reduce.py | 24 +- 18 files changed, 593 insertions(+), 68 deletions(-) create mode 100644 ariths_gen/multi_bit_circuits/approximate_multipliers/recursive_multiplier.py diff --git a/.testall.sh b/.testall.sh index e74b507..9ca3e6b 100644 --- a/.testall.sh +++ b/.testall.sh @@ -5,4 +5,10 @@ cd tests bash test_mac.sh bash test_circuits.sh bash test_circuits_verilog.sh -bash test_circuits_cgp.sh \ No newline at end of file +bash test_circuits_cgp.sh +python test_all.py +python test_ax.py +python test_cgp.py +python test_compare.py +python test_popcnt.py +python test_reduce.py \ No newline at end of file diff --git a/README.md b/README.md index a454f66..8947d28 100644 --- a/README.md +++ b/README.md @@ -141,6 +141,11 @@ print("Mean average error", np.abs(r - (va * vb)).mean()) The `yosys_equiv_check.sh` script enables to formally check the equivalence of generated Verilog and BLIF representations of the same circuit. It uses the Yosys Open SYnthesis Suite tool by Claire Xenia Wolf. For further information, please visit: https://yosyshq.readthedocs.io/projects/yosys/en/latest/index.html. +## Install Yosys +```bash +sudo apt-get install yosys +``` + ## Execute permission ```bash chmod +x yosys_equiv_check.sh diff --git a/ariths_gen/core/arithmetic_circuits/arithmetic_circuit.py b/ariths_gen/core/arithmetic_circuits/arithmetic_circuit.py index 2be0cf8..608550f 100644 --- a/ariths_gen/core/arithmetic_circuits/arithmetic_circuit.py +++ b/ariths_gen/core/arithmetic_circuits/arithmetic_circuit.py @@ -18,25 +18,7 @@ class ArithmeticCircuit(GeneralCircuit): """ def __init__(self, a, b, prefix: str, name: str, out_N: int, inner_component: bool = False, one_bit_circuit: bool = False, signed: bool = False, **kwargs): - super().__init__(prefix, name, out_N, inner_component, inputs=[a, b], signed=signed, **kwargs) - if one_bit_circuit is False: - if prefix == "": - self.prefix = name - else: - self.prefix = prefix + "_" + name - - self.inner_component = inner_component - if self.inner_component is True: - self.a = Bus(prefix=f"{self.prefix}_{a.prefix}", wires_list=a.bus) - self.b = Bus(prefix=f"{self.prefix}_{b.prefix}", wires_list=b.bus) - - if a.is_output_bus(): - self.a.connect_bus(connecting_bus=a) - if b.is_output_bus(): - self.b.connect_bus(connecting_bus=b) - else: - self.a = Bus(prefix=f"{a.prefix}", wires_list=a.bus) - self.b = Bus(prefix=f"{b.prefix}", wires_list=b.bus) + super().__init__(prefix, name, out_N, inner_component, inputs=[a, b], signed=signed, one_bit_circuit=one_bit_circuit, **kwargs) """ C CODE GENERATION """ def get_prototype_c(self): diff --git a/ariths_gen/core/arithmetic_circuits/general_circuit.py b/ariths_gen/core/arithmetic_circuits/general_circuit.py index c4fb720..94b7e89 100644 --- a/ariths_gen/core/arithmetic_circuits/general_circuit.py +++ b/ariths_gen/core/arithmetic_circuits/general_circuit.py @@ -18,13 +18,31 @@ class GeneralCircuit(): 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 = [], 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, outname=None, **kwargs): if prefix == "": self.prefix = name else: self.prefix = prefix + "_" + name self.inner_component = inner_component - self.inputs = inputs + + if one_bit_circuit is False: + # Dynamic input bus assignment + self.inputs = [] + input_names = "abcdefghijklmnopqrstuvwxyz" # This should be enough.. + assert len(input_names) >= len(inputs) + for i, input_bus in enumerate(inputs): + attr_name = input_names[i] + full_prefix = f"{self.prefix}_{input_bus.prefix}" if self.inner_component else f"{input_bus.prefix}" + bus = Bus(prefix=full_prefix, wires_list=input_bus.bus) + setattr(self, attr_name, bus) + self.inputs.append(bus) + + # If the input bus is an output bus, connect it + if input_bus.is_output_bus(): + getattr(self, attr_name).connect_bus(connecting_bus=input_bus) + else: + self.inputs = inputs + if not outname: outname = self.prefix+"_out" self.out = Bus(outname, out_N, out_bus=True, signed=signed) @@ -67,7 +85,7 @@ class GeneralCircuit(): Args: component: Subcomponent to be added into list of components composing described circuit. """ - prefixes = [c.prefix for c in self.components] + prefixes = [c.prefix for c in self.components] # TODO ? #assert component.prefix not in prefixes, f"Component with prefix {component.prefix} already exists in the circuit." self.components.append(component) return component @@ -388,7 +406,7 @@ class GeneralCircuit(): """ # Obtain proper circuit name with its bit width circuit_prefix = self.__class__( - a=Bus("a"), b=Bus("b")).prefix + str(self.N) + a=Bus("a", self.N), b=Bus("b", self.N)).prefix + str(self.N) circuit_block = self.__class__(a=Bus(N=self.N, prefix="a"), b=Bus( N=self.N, prefix="b"), name=circuit_prefix, **self._parent_kwargs) return f"{circuit_block.get_circuit_c()}\n\n" @@ -433,7 +451,7 @@ class GeneralCircuit(): str: Hierarchical C code of subcomponent's C function invocation and output assignment. """ # Getting name of circuit type for proper C code generation without affecting actual generated composition - circuit_type = self.__class__(a=Bus("a"), b=Bus("b")).prefix + str(self.N) + circuit_type = self.__class__(a=Bus("a", self.N), b=Bus("b", self.N)).prefix + str(self.N) return self.a.return_bus_wires_values_c_hier() + self.b.return_bus_wires_values_c_hier() + \ f" {self.out.prefix} = {circuit_type}({self.a.prefix}, {self.b.prefix});\n" @@ -539,7 +557,7 @@ class GeneralCircuit(): """ # Obtain proper circuit name with its bit width circuit_prefix = self.__class__( - a=Bus("a"), b=Bus("b")).prefix + str(self.N) + a=Bus("a", self.N), b=Bus("b", self.N)).prefix + str(self.N) circuit_block = self.__class__(a=Bus(N=self.N, prefix="a"), b=Bus( N=self.N, prefix="b"), name=circuit_prefix, **self._parent_kwargs) return f"{circuit_block.get_circuit_v()}\n\n" @@ -563,6 +581,7 @@ class GeneralCircuit(): """ return "".join(w.get_wire_declaration_v() for w in self.inputs + [self.out]) + "\n" + # TODO del.. return f" wire [{self.a.N-1}:0] {self.a.prefix};\n" + \ f" wire [{self.b.N-1}:0] {self.b.prefix};\n" + \ f" wire [{self.out.N-1}:0] {self.out.prefix};\n" @@ -586,7 +605,7 @@ class GeneralCircuit(): str: Hierarchical Verilog code of subcomponent's module invocation and output assignment. """ # Getting name of circuit type and insitu copying out bus for proper Verilog code generation without affecting actual generated composition - circuit_type = self.__class__(a=Bus("a"), b=Bus("b")).prefix + str(self.N) + circuit_type = self.__class__(a=Bus("a", self.N), b=Bus("b", self.N)).prefix + str(self.N) circuit_block = self.__class__(a=Bus(N=self.N, prefix="a"), b=Bus( N=self.N, prefix="b"), name=circuit_type) return "".join([c.return_bus_wires_values_v_hier() for c in self.inputs]) + \ @@ -692,14 +711,13 @@ class GeneralCircuit(): str: Hierarchical Blif code of subcomponent's model invocation and output assignment. """ # Getting name of circuit type for proper Blif code generation without affecting actual generated composition - circuit_type = self.__class__(a=Bus("a"), b=Bus("b")).prefix + str(self.N) + circuit_type = self.__class__(a=Bus("a", self.N), b=Bus("b", self.N)).prefix + str(self.N) return f"{self.a.get_wire_assign_blif(output=True)}" + \ f"{self.b.get_wire_assign_blif(output=True)}" + \ f".subckt {circuit_type}" + \ "".join([f" a[{self.a.bus.index(w)}]={self.a.prefix}[{self.a.bus.index(w)}]" for w in self.a.bus]) + \ "".join([f" b[{self.b.bus.index(w)}]={self.b.prefix}[{self.b.bus.index(w)}]" for w in self.b.bus]) + \ - "".join( - [f" {circuit_type}_out[{self.out.bus.index(o)}]={o.name}" for o in self.out.bus]) + "\n" + "".join([f" {circuit_type}_out[{self.out.bus.index(o)}]={o.name}" for o in self.out.bus]) + "\n" def get_circuit_blif(self): """Generates hierarchical Blif code subcomponent's function block. @@ -734,7 +752,7 @@ class GeneralCircuit(): """ # Obtain proper circuit name with its bit width circuit_prefix = self.__class__( - a=Bus("a"), b=Bus("b")).prefix + str(self.N) + a=Bus("a", self.N), b=Bus("b", self.N)).prefix + str(self.N) circuit_block = self.__class__(a=Bus(N=self.N, prefix="a"), b=Bus( N=self.N, prefix="b"), name=circuit_prefix, **self._parent_kwargs) return f"{circuit_block.get_circuit_blif()}" diff --git a/ariths_gen/multi_bit_circuits/approximate_multipliers/__init__.py b/ariths_gen/multi_bit_circuits/approximate_multipliers/__init__.py index 1e5da67..0b40600 100644 --- a/ariths_gen/multi_bit_circuits/approximate_multipliers/__init__.py +++ b/ariths_gen/multi_bit_circuits/approximate_multipliers/__init__.py @@ -13,3 +13,12 @@ from ariths_gen.multi_bit_circuits.approximate_multipliers.broken_carry_save_mul from ariths_gen.multi_bit_circuits.approximate_multipliers.truncated_carry_save_multiplier import ( UnsignedTruncatedCarrySaveMultiplier ) + +from ariths_gen.multi_bit_circuits.approximate_multipliers.recursive_multiplier import ( + UnsignedAccurateTwoBitMultiplier, + UnsignedApproximateTwoBitMultiplierM1, + UnsignedApproximateTwoBitMultiplierM2, + UnsignedApproximateTwoBitMultiplierM3, + UnsignedApproximateTwoBitMultiplierM4, + UnsignedRecursiveMultiplier +) diff --git a/ariths_gen/multi_bit_circuits/approximate_multipliers/recursive_multiplier.py b/ariths_gen/multi_bit_circuits/approximate_multipliers/recursive_multiplier.py new file mode 100644 index 0000000..6a8ad38 --- /dev/null +++ b/ariths_gen/multi_bit_circuits/approximate_multipliers/recursive_multiplier.py @@ -0,0 +1,404 @@ +from ariths_gen.wire_components import ( + ConstantWireValue0, + Bus +) +from ariths_gen.core.arithmetic_circuits import ( + MultiplierCircuit +) +from ariths_gen.one_bit_circuits.logic_gates import ( + AndGate, + OrGate, + XorGate, + NotGate +) + +from ariths_gen.multi_bit_circuits.adders import ( + UnsignedCarryLookaheadAdder +) + +import math + + +class UnsignedAccurateTwoBitMultiplier(MultiplierCircuit): + """Class representing unsigned two-bit accurate multiplier. + + Description of the __init__ method. + + Args: + a (Bus): First input bus. + b (Bus): Second input bus. + prefix (str, optional): Prefix name of unsigned two-bit accurate multiplier. Defaults to "". + name (str, optional): Name of unsigned two-bit accurate multiplier. Defaults to "u_2bit_accm". + """ + def __init__(self, a: Bus, b: Bus, prefix: str = "", name: str = "u_2bit_accm", **kwargs): + self.N = max(a.N, b.N) + assert self.N == 2 + super().__init__(a=a, b=b, prefix=prefix, name=name, out_N=self.N*2, **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) + + and_obj1 = AndGate(self.a.get_wire(0), self.b.get_wire(0), prefix=self.prefix+"_and0") + and_obj2 = AndGate(self.a.get_wire(0), self.b.get_wire(1), prefix=self.prefix+"_and1") + and_obj3 = AndGate(self.a.get_wire(1), self.b.get_wire(0), prefix=self.prefix+"_and2") + and_obj4 = AndGate(self.a.get_wire(1), self.b.get_wire(1), prefix=self.prefix+"_and3") + + xor_obj1 = XorGate(and_obj2.out, and_obj3.out, prefix=self.prefix+"_xor0") + and_obj5 = AndGate(and_obj2.out, and_obj3.out, prefix=self.prefix+"_and4") + xor_obj2 = XorGate(and_obj5.out, and_obj4.out, prefix=self.prefix+"_xor1") + and_obj6 = AndGate(and_obj5.out, and_obj4.out, prefix=self.prefix+"_and5") + [self.add_component(obj) for obj in [and_obj1, and_obj2, and_obj3, and_obj4, xor_obj1, and_obj5, xor_obj2, and_obj6]] + + self.out.connect(0, and_obj1.out) + self.out.connect(1, xor_obj1.out) + self.out.connect(2, xor_obj2.out) + self.out.connect(3, and_obj6.out) + + +class SignedAccurateTwoBitMultiplier(MultiplierCircuit): + """Class representing signed two-bit accurate multiplier. + + Description of the __init__ method. + + Args: + a (Bus): First input bus. + b (Bus): Second input bus. + prefix (str, optional): Prefix name of signed two-bit accurate multiplier. Defaults to "". + name (str, optional): Name of signed two-bit accurate multiplier. Defaults to "s_2bit_accm". + """ + def __init__(self, a: Bus, b: Bus, prefix: str = "", name: str = "s_2bit_accm", **kwargs): + self.N = max(a.N, b.N) + raise NotImplementedError("SignedAccurateTwoBitMultiplier is not implemented yet.") + + +class UnsignedApproximateTwoBitMultiplierM1(MultiplierCircuit): + """Class representing unsigned two-bit approximate multiplier variant M1. + + M1 ax variant defined here: https://ieeexplore.ieee.org/document/8727537 + + Description of the __init__ method. + + Args: + a (Bus): First input bus. + b (Bus): Second input bus. + prefix (str, optional): Prefix name of unsigned two-bit approximate multiplier m1. Defaults to "". + name (str, optional): Name of unsigned two-bit approximate multiplier m1. Defaults to "u_2bit_axm1". + """ + def __init__(self, a: Bus, b: Bus, prefix: str = "", name: str = "u_2bit_axm1", **kwargs): + self.N = max(a.N, b.N) + assert self.N == 2 + super().__init__(a=a, b=b, prefix=prefix, name=name, out_N=self.N*2, **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) + + and_obj1 = AndGate(self.a.get_wire(0), self.b.get_wire(0), prefix=self.prefix+"_and0") + and_obj2 = AndGate(self.a.get_wire(0), self.b.get_wire(1), prefix=self.prefix+"_and1") + and_obj3 = AndGate(self.a.get_wire(1), self.b.get_wire(0), prefix=self.prefix+"_and2") + and_obj4 = AndGate(self.a.get_wire(1), self.b.get_wire(1), prefix=self.prefix+"_and3") + + or_obj1 = OrGate(and_obj2.out, and_obj3.out, prefix=self.prefix+"_or0") + [self.add_component(obj) for obj in [and_obj1, and_obj2, and_obj3, and_obj4, or_obj1]] + + self.out.connect(0, and_obj1.out) + self.out.connect(1, or_obj1.out) + self.out.connect(2, and_obj4.out) + self.out.connect(3, ConstantWireValue0()) + + +class SignedApproximateTwoBitMultiplierM1(MultiplierCircuit): + """Class representing signed two-bit approximate multiplier variant M1. + + Description of the __init__ method. + + Args: + a (Bus): First input bus. + b (Bus): Second input bus. + prefix (str, optional): Prefix name of signed two-bit approximate multiplier m1. Defaults to "". + name (str, optional): Name of signed two-bit approximate multiplier m1. Defaults to "s_2bit_axm1". + """ + def __init__(self, a: Bus, b: Bus, prefix: str = "", name: str = "s_2bit_axm1", **kwargs): + raise NotImplementedError("SignedApproximateTwoBitMultiplierM1 is not implemented yet.") + + +class UnsignedApproximateTwoBitMultiplierM2(MultiplierCircuit): + """Class representing unsigned two-bit approximate multiplier variant M2. + + M2 ax variant defined here: https://ieeexplore.ieee.org/document/8727537 + + Description of the __init__ method. + + Args: + a (Bus): First input bus. + b (Bus): Second input bus. + prefix (str, optional): Prefix name of unsigned two-bit approximate multiplier m1. Defaults to "". + name (str, optional): Name of unsigned two-bit approximate multiplier m1. Defaults to "u_2bit_axm1". + """ + def __init__(self, a: Bus, b: Bus, prefix: str = "", name: str = "u_2bit_axm1", **kwargs): + self.N = max(a.N, b.N) + assert self.N == 2 + super().__init__(a=a, b=b, prefix=prefix, name=name, out_N=self.N*2, **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) + + and_obj1 = AndGate(self.a.get_wire(0), self.b.get_wire(1), prefix=self.prefix+"_and0") + and_obj2 = AndGate(self.a.get_wire(1), self.b.get_wire(0), prefix=self.prefix+"_and1") + and_obj3 = AndGate(self.a.get_wire(1), self.b.get_wire(1), prefix=self.prefix+"_and2") + + and_obj4 = AndGate(and_obj1.out, and_obj2.out, prefix=self.prefix+"_and3") + xor_obj1 = XorGate(and_obj1.out, and_obj2.out, prefix=self.prefix+"_xor0") + + xor_obj2 = XorGate(and_obj4.out, and_obj3.out, prefix=self.prefix+"_xor1") + [self.add_component(obj) for obj in [and_obj1, and_obj2, and_obj3, and_obj4, xor_obj1, xor_obj2]] + + self.out.connect(0, and_obj4.out) + self.out.connect(1, xor_obj1.out) + self.out.connect(2, xor_obj2.out) + self.out.connect(3, and_obj4.out) + + +class SignedApproximateTwoBitMultiplierM2(MultiplierCircuit): + """Class representing signed two-bit approximate multiplier variant M2. + + Description of the __init__ method. + + Args: + a (Bus): First input bus. + b (Bus): Second input bus. + prefix (str, optional): Prefix name of signed two-bit approximate multiplier m2. Defaults to "". + name (str, optional): Name of signed two-bit approximate multiplier m2. Defaults to "s_2bit_axm2". + """ + def __init__(self, a: Bus, b: Bus, prefix: str = "", name: str = "s_2bit_axm2", **kwargs): + raise NotImplementedError("SignedApproximateTwoBitMultiplierM2 is not implemented yet.") + + +class UnsignedApproximateTwoBitMultiplierM3(MultiplierCircuit): + """Class representing unsigned two-bit approximate multiplier variant M3. + + M3 ax variant defined here: https://ieeexplore.ieee.org/document/8727537 + + Description of the __init__ method. + + Args: + a (Bus): First input bus. + b (Bus): Second input bus. + prefix (str, optional): Prefix name of unsigned two-bit approximate multiplier m3. Defaults to "". + name (str, optional): Name of unsigned two-bit approximate multiplier m3. Defaults to "u_2bit_axm3". + """ + def __init__(self, a: Bus, b: Bus, prefix: str = "", name: str = "u_2bit_axm3", **kwargs): + self.N = max(a.N, b.N) + assert self.N == 2 + super().__init__(a=a, b=b, prefix=prefix, name=name, out_N=self.N*2, **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) + + and_obj1 = AndGate(self.a.get_wire(0), self.b.get_wire(0), prefix=self.prefix+"_and0") + and_obj2 = AndGate(self.a.get_wire(0), self.b.get_wire(1), prefix=self.prefix+"_and1") + and_obj3 = AndGate(self.a.get_wire(1), self.b.get_wire(0), prefix=self.prefix+"_and2") + and_obj4 = AndGate(self.a.get_wire(1), self.b.get_wire(1), prefix=self.prefix+"_and3") + + or_obj1 = OrGate(and_obj2.out, and_obj3.out, prefix=self.prefix+"_xor0") + not_obj1 = NotGate(and_obj1.out, prefix=self.prefix+"_not0") + + and_obj5 = AndGate(not_obj1.out, and_obj4.out, prefix=self.prefix+"_and4") + and_obj6 = AndGate(and_obj1.out, and_obj4.out, prefix=self.prefix+"_and5") + [self.add_component(obj) for obj in [and_obj1, and_obj2, and_obj3, and_obj4, or_obj1, not_obj1, and_obj5, and_obj6]] + + self.out.connect(0, and_obj1.out) + self.out.connect(1, or_obj1.out) + self.out.connect(2, and_obj5.out) + self.out.connect(3, and_obj6.out) + + +class SignedApproximateTwoBitMultiplierM3(MultiplierCircuit): + """Class representing signed two-bit approximate multiplier variant M3. + + Description of the __init__ method. + + Args: + a (Bus): First input bus. + b (Bus): Second input bus. + prefix (str, optional): Prefix name of signed two-bit approximate multiplier m3. Defaults to "". + name (str, optional): Name of signed two-bit approximate multiplier m3. Defaults to "s_2bit_axm3". + """ + def __init__(self, a: Bus, b: Bus, prefix: str = "", name: str = "s_2bit_axm3", **kwargs): + raise NotImplementedError("SignedApproximateTwoBitMultiplierM3 is not implemented yet.") + + +class UnsignedApproximateTwoBitMultiplierM4(MultiplierCircuit): + """Class representing unsigned two-bit approximate multiplier variant M4. + + M4 ax variant defined here: https://ieeexplore.ieee.org/document/8727537 + + Description of the __init__ method. + + Args: + a (Bus): First input bus. + b (Bus): Second input bus. + prefix (str, optional): Prefix name of unsigned two-bit approximate multiplier m4. Defaults to "". + name (str, optional): Name of unsigned two-bit approximate multiplier m4. Defaults to "u_2bit_axm4". + """ + def __init__(self, a: Bus, b: Bus, prefix: str = "", name: str = "u_2bit_axm4", **kwargs): + self.N = max(a.N, b.N) + assert self.N == 2 + super().__init__(a=a, b=b, prefix=prefix, name=name, out_N=self.N*2, **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) + + and_obj1 = AndGate(self.a.get_wire(0), self.b.get_wire(0), prefix=self.prefix+"_and0") + and_obj2 = AndGate(self.a.get_wire(0), self.b.get_wire(1), prefix=self.prefix+"_and1") + and_obj3 = AndGate(self.a.get_wire(1), self.b.get_wire(0), prefix=self.prefix+"_and2") + and_obj4 = AndGate(self.a.get_wire(1), self.b.get_wire(1), prefix=self.prefix+"_and3") + + xor_obj1 = XorGate(and_obj2.out, and_obj3.out, prefix=self.prefix+"_xor0") + [self.add_component(obj) for obj in [and_obj1, and_obj2, and_obj3, and_obj4, xor_obj1]] + + self.out.connect(0, and_obj1.out) + self.out.connect(1, xor_obj1.out) + self.out.connect(2, and_obj4.out) + self.out.connect(3, ConstantWireValue0()) + + +class SignedApproximateTwoBitMultiplierM4(MultiplierCircuit): + """Class representing signed two-bit approximate multiplier variant M4. + + Description of the __init__ method. + + Args: + a (Bus): First input bus. + b (Bus): Second input bus. + prefix (str, optional): Prefix name of signed two-bit approximate multiplier m4. Defaults to "". + name (str, optional): Name of signed two-bit approximate multiplier m4. Defaults to "s_2bit_axm4". + """ + def __init__(self, a: Bus, b: Bus, prefix: str = "", name: str = "s_2bit_axm4", **kwargs): + raise NotImplementedError("SignedApproximateTwoBitMultiplierM4 is not implemented yet.") + + +class UnsignedRecursiveMultiplier(MultiplierCircuit): + """Class representing unsigned recursive multiplier. + + TODO + Description of the __init__ method. + + Args: + a (Bus): First input bus. + b (Bus): Second input bus. + prefix (str, optional): Prefix name of unsigned recursive multiplier. Defaults to "". + name (str, optional): Name of unsigned recursive multiplier. Defaults to "u_rm". + submultipliers (list[MultiplierCircuit], optional): List of submultipliers. + Defaults (if None) to the required number of UnsignedAccurateTwoBitMultiplier instances. + 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 = "", name: str = "u_rm", submultipliers: list[MultiplierCircuit] = None, unsigned_adder_class_name: str = UnsignedCarryLookaheadAdder, **kwargs): + self.N = max(a.N, b.N) + assert self.N > 1 and self.N & (self.N-1) == 0 # assure that N is a power of two greater than 1 (So allowed N is 2, 4, 8, ..) + super().__init__(a=a, b=b, prefix=prefix, name=name, out_N=self.N*2, **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) + + if submultipliers is None: # By default, we assume composition from accurate two bit submultipliers + if self.N == 2: + submultipliers = [UnsignedAccurateTwoBitMultiplier] + else: + submultipliers = [UnsignedAccurateTwoBitMultiplier for _ in range((self.N//2)**2)] + + assert (self.N > 2 and len(submultipliers) == (self.N//2)**2) or (self.N == 2 and len(submultipliers) == 1) + + if self.N == 2: # Base case for just one two-bit multiplier + # TODO add suffix in ariths_gen rework + mult = submultipliers[0](Bus(prefix=self.prefix + "_a", wires_list=self.a.bus), Bus(prefix=self.prefix + "_b", wires_list=self.b.bus), prefix=self.prefix + "_" + str(self.get_instance_num(cls=submultipliers[0])), **kwargs) + self.add_component(mult) + self.out.connect_bus(mult.out) + else: + # Levels of construction of the recursive multiplier + # recursive_levels = int(math.log2(self.N)-1) # Number of recursive levels based on the power ith power of 2 (e.g. for N=8, we have 2 recursive levels) + block_level = 1 + partial_products = [] + + for m in range(len(submultipliers)): # Iterate over all 2-bit submultipliers (equals range(0, 4**recursive_levels)) + a_bus_offset = 0 + b_bus_offset = 0 + curr_level = block_level + curr_id = m + + # Determine the wires offsets from MSB (for input bus `a` and `b`) for the current 2-bit submultiplier + # There is a pattern, for example for N=8, we have 16 two-bit submultipliers with offsets: + # Mult ID: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 + # MSB a_offsets: 0 0 2 2 0 0 2 2 4 4 6 6 4 4 6 6 + # MSB b_offsets: 0 2 0 2 4 6 4 6 0 2 0 2 4 6 4 6 + while curr_level != 0: + # A offset + if curr_id // ((4**curr_level)//2) != 0: + a_bus_offset += 2**curr_level + + # B offset + if (curr_id // ((4**curr_level)//4)) % 2 != 0: + b_bus_offset += 2**curr_level + + curr_level -= 1 + curr_id -= (4**curr_level)*((curr_id // (4**curr_level))) + + # Create 2-bit submultiplier with the corresponding input bits + # TODO add suffix in ariths_gen rework + submult_a_bus = Bus(prefix=f"mult{m}_a", wires_list=self.a.bus[::-1][0+a_bus_offset:0+a_bus_offset+2][::-1], N=2) + submult_b_bus = Bus(prefix=f"mult{m}_b", wires_list=self.b.bus[::-1][0+b_bus_offset:0+b_bus_offset+2][::-1], N=2) + submult = submultipliers[m](submult_a_bus, submult_b_bus, prefix=self.prefix + "_" + str(self.get_instance_num(cls=submultipliers[m]))) + self.add_component(submult) + + # Create wire vectors holding partial products for final summation + pp = Bus(prefix=f"pp_{m}", N=self.out.N, wires_list=[ConstantWireValue0() for _ in range(self.out.N)]) + #[pp.connect_bus(submult.out, offset=(self.out.N-4)-(a_bus_offset+b_bus_offset))] + [pp.connect((self.out.N-1)-(a_bus_offset+b_bus_offset)-i, submult.out[3-i], inserted_wire_desired_index=3-i) for i in range(4)] + partial_products.append(pp) + + # Distinction of levels of blocks to properly instantiate and connect 2-bit submultipliers + # For example, for N=8, we have 4 two-bit submultipliers in the first level, but then we have 4 times the + # same structure (4 two-bit mults) as the base component for the second level, similarly for N=16, but + # with additional third layer (consisting of 16 two-bit submultipliers) + if (m+1) % (4**block_level) == 0: + block_level += 1 # Increase the block level + + # Create tree of partial product adders + while len(partial_products) != 1: + N = len(partial_products)//2 + # Creation of composite unsigned multi bit adders from set of partial product vectors and addition of generated blocks outputs for next iteration + for bus_index in range(0, N): + # TODO arithsgen_rework: update check for bus declaration and assignment (if true do declare/assign again - here we would not create new bus, just assign the existing one); it should create cleaner outcode with unncecessary new bus declarations + adder_name = unsigned_adder_class_name(a=a, b=b).prefix + str(self.get_instance_num(cls=unsigned_adder_class_name)) + # TODO rework the buses + bus_a = Bus(prefix=f"{adder_name}_a", wires_list=partial_products[bus_index].bus, out_bus=True) if partial_products[bus_index].out_bus else partial_products[bus_index] + bus_b = Bus(prefix=f"{adder_name}_b", wires_list=partial_products[bus_index+N].bus, out_bus=True) if partial_products[bus_index+N].out_bus else partial_products[bus_index+N] + adder = unsigned_adder_class_name(a=bus_a, b=bus_b, prefix=self.prefix, name=adder_name, inner_component=True, **kwargs) + self.add_component(adder) + partial_products.append(adder.out) + + # Update partial products list for next iteration until it contains only one output vector + partial_products = partial_products[2*N:] + # Connect the final output of the recursive multiplier + self.out.connect_bus(partial_products[0]) + + +class SignedRecursiveMultiplier(MultiplierCircuit): + """Class representing signed recursive multiplier. + + TODO + Description of the __init__ method. + + Args: + a (Bus): First input bus. + b (Bus): Second input bus. + prefix (str, optional): Prefix name of signed recursive multiplier. Defaults to "". + name (str, optional): Name of signed recursive multiplier. Defaults to "s_rm". + """ + def __init__(self, a: Bus, b: Bus, prefix: str = "", name: str = "s_rm", **kwargs): + raise NotImplementedError("SignedRecursiveMultiplier is not implemented yet.") diff --git a/ariths_gen/wire_components/wires.py b/ariths_gen/wire_components/wires.py index c34240b..8a68d1e 100644 --- a/ariths_gen/wire_components/wires.py +++ b/ariths_gen/wire_components/wires.py @@ -99,7 +99,7 @@ class Wire(): if self.is_const(): return f"({self.c_const}) << {offset};\n" else: - return f"(({self.prefix} >> {self.index}) & 0x01ull) << {offset};\n" + return f"(({self.name} >> 0) & 0x01ull) << {offset};\n" def return_wire_value_c_hier(self, offset: int = 0): """Retrieves desired bit value from wire represented in C code variable and bitwise shifts it to desired position for storing it within a bus for hierarchical generation. diff --git a/generate_quad_lib.py b/generate_quad_lib.py index 1395ab0..8d7c62a 100644 --- a/generate_quad_lib.py +++ b/generate_quad_lib.py @@ -16,6 +16,7 @@ import itertools if __name__ == "__main__": + N = 8 directory = f"lib_quad/lib_quad{N}" os.makedirs(directory, exist_ok=True) @@ -30,7 +31,6 @@ if __name__ == "__main__": import zipfile vfile = zipfile.ZipFile(file=f"{directory}/lib_quad_{N}.zip", mode="w", compression=zipfile.ZIP_DEFLATED) cnt = 0 - N = 8 # up to 3 stages for n in [1, 2, 3]: diff --git a/generate_test.py b/generate_test.py index b8b5a0f..6d1ebd9 100644 --- a/generate_test.py +++ b/generate_test.py @@ -65,6 +65,11 @@ from ariths_gen.multi_bit_circuits.multipliers import ( SignedCarrySaveMultiplier ) +from ariths_gen.multi_bit_circuits.approximate_multipliers import ( + UnsignedRecursiveMultiplier, + UnsignedAccurateTwoBitMultiplier +) + from ariths_gen.multi_bit_circuits.dividers import ( ArrayDivider ) @@ -226,6 +231,11 @@ if __name__ == "__main__": name = f"s_arrmul{N}" circuit = SignedArrayMultiplier(a, b, name=name) export_circuit(circuit, name) + + # Accurate recursive multiplier + name = f"u_recmul{N}" + circuit = UnsignedRecursiveMultiplier(a, b, name=name) + export_circuit(circuit, name) # Csamul (Braun multiplier) – the ppa adders are also configurable as above if desirable name = f"u_csamul_cla{N}" diff --git a/tests/test_all.py b/tests/test_all.py index c8cd777..33af3c1 100644 --- a/tests/test_all.py +++ b/tests/test_all.py @@ -1,3 +1,11 @@ +import os +import sys +# Add the parent directory to the system path +DIR_PATH = os.path.dirname(os.path.abspath(__file__)) +sys.path.insert(0, os.path.join(DIR_PATH, '..')) +import numpy as np +import math + from ariths_gen.wire_components import ( Wire, ConstantWireValue0, @@ -51,7 +59,9 @@ from ariths_gen.multi_bit_circuits.approximate_multipliers import ( UnsignedTruncatedArrayMultiplier, UnsignedTruncatedCarrySaveMultiplier, UnsignedBrokenArrayMultiplier, - UnsignedBrokenCarrySaveMultiplier + UnsignedBrokenCarrySaveMultiplier, + UnsignedRecursiveMultiplier, + UnsignedAccurateTwoBitMultiplier ) from ariths_gen.one_bit_circuits.logic_gates import ( @@ -63,8 +73,6 @@ from ariths_gen.one_bit_circuits.logic_gates import ( XnorGate, NotGate ) -import numpy as np -import math def test_unsigned_approxmul(values=False): @@ -120,6 +128,19 @@ def test_unsigned_mul(): assert mul(0, 0) == 0 r = mul(av, bv) np.testing.assert_array_equal(expected, r) + + # Accurate variant of recursive multiplier + for c in [UnsignedRecursiveMultiplier]: + N_rec = 8 + a_rec = Bus(N=N_rec, prefix="a") + b_rec = Bus(N=N_rec, prefix="b") + av_rec = np.arange(2**N_rec) + bv_rec = av_rec.reshape(-1, 1) + expected_rec = av_rec * bv_rec + mul = c(a_rec, b_rec, submultipliers=[UnsignedAccurateTwoBitMultiplier for _ in range((N_rec//2)**2)]) + assert mul(0, 0) == 0 + r = mul(av_rec, bv_rec) + np.testing.assert_array_equal(expected_rec, r) # Configurable PPA for c in [UnsignedDaddaMultiplier, UnsignedCarrySaveMultiplier, UnsignedWallaceMultiplier]: @@ -342,6 +363,7 @@ def test_direct(): np.testing.assert_equal(r, expected) print(r) + def test_wire_as_bus(): """ accept a wire as a bus """ class test_circuit(GeneralCircuit): @@ -353,8 +375,20 @@ def test_wire_as_bus(): self.out[0] = g3.out circ = test_circuit(Wire("a"), Wire("b"), Bus("c", 2), "c1") - r = circ(np.array([0, 1]), - np.array([0, 1]).reshape(-1, 1), - np.arange(4).reshape(-1, 1, 1)) + r = circ(np.array([0, 1]), + np.array([0, 1]).reshape(-1, 1), + np.arange(4).reshape(-1, 1, 1)) assert r.sum() == 1 - assert r[-1, -1, -1] == 1 \ No newline at end of file + assert r[-1, -1, -1] == 1 + + +if __name__ == "__main__": + test_unsigned_approxmul() + test_unsigned_mul() + test_signed_mul() + test_unsigned_add() + test_signed_add() + test_mac() + test_direct() + test_wire_as_bus() + print("Python tests were successful!") diff --git a/tests/test_ax.py b/tests/test_ax.py index b78b8fe..a833fe7 100644 --- a/tests/test_ax.py +++ b/tests/test_ax.py @@ -1,21 +1,26 @@ """ Testing the QuAdder """ +import os +import sys +# Add the parent directory to the system path +DIR_PATH = os.path.dirname(os.path.abspath(__file__)) +sys.path.insert(0, os.path.join(DIR_PATH, '..')) +import numpy as np +import itertools + from ariths_gen.core.arithmetic_circuits.arithmetic_circuit import ArithmeticCircuit from ariths_gen.core.arithmetic_circuits import GeneralCircuit from ariths_gen.wire_components import Bus, Wire from ariths_gen.multi_bit_circuits.adders import UnsignedRippleCarryAdder from ariths_gen.multi_bit_circuits.approximate_adders import QuAdder from ariths_gen.multi_bit_circuits.multipliers import UnsignedArrayMultiplier, UnsignedDaddaMultiplier -import os, sys -import numpy as np -import itertools def test_quadder(): - c = QuAdder(Bus("a", 8), Bus("b", 8), R = [4, 2, 2], P=[0, 2, 2], prefix="quad") + c = QuAdder(Bus("a", 8), Bus("b", 8), R=[4, 2, 2], P=[0, 2, 2], prefix="quad") c.get_v_code_hier(file_object=sys.stdout) - + x = np.arange(0, 256).reshape(-1, 1) y = np.arange(0, 256).reshape(1, -1) @@ -23,4 +28,9 @@ def test_quadder(): r2 = x + y assert np.abs(r - r2).max() == 64 - np.testing.assert_equal(np.abs(r - r2).mean(), 7.5) \ No newline at end of file + np.testing.assert_equal(np.abs(r - r2).mean(), 7.5) + + +if __name__ == "__main__": + test_quadder() + print("Quadder Python tests were successful!") diff --git a/tests/test_cgp.py b/tests/test_cgp.py index 26f66e4..dd1cb3a 100644 --- a/tests/test_cgp.py +++ b/tests/test_cgp.py @@ -1,3 +1,13 @@ +import os +import sys +# Add the parent directory to the system path +DIR_PATH = os.path.dirname(os.path.abspath(__file__)) +sys.path.insert(0, os.path.join(DIR_PATH, '..')) + +import numpy as np +import math +from io import StringIO + from ariths_gen.wire_components import ( Wire, ConstantWireValue0, @@ -57,10 +67,6 @@ from ariths_gen.multi_bit_circuits.approximate_multipliers import ( from ariths_gen.core.cgp_circuit import UnsignedCGPCircuit, SignedCGPCircuit -import numpy as np -import math -from io import StringIO - def test_cgp_unsigned_add(): """ Test unsigned adders """ @@ -199,7 +205,7 @@ def test_cgp_signed_add(): np.testing.assert_array_equal(expected, r) -def test_unsigned_mul(): +def test_cgp_unsigned_mul(): """ Test unsigned multipliers """ N = 7 a = Bus(N=N, prefix="a") @@ -311,7 +317,7 @@ def test_unsigned_mul(): np.testing.assert_array_equal(expected, r) -def test_signed_mul(): +def test_cgp_signed_mul(): """ Test signed multipliers """ N = 7 a = Bus(N=N, prefix="a") @@ -429,3 +435,12 @@ def test_cgp_variant1(): c = UnsignedCGPCircuit(cgp, [8, 8], name="cgp_circuit") assert c(0, 0) == 8 # TypeError: 'int' object is not subscriptable + + +if __name__ == "__main__": + test_cgp_unsigned_add() + test_cgp_signed_add() + test_cgp_unsigned_mul() + test_cgp_signed_mul() + test_cgp_variant1() + print("CGP Python tests were successful!") diff --git a/tests/test_circuits.sh b/tests/test_circuits.sh index e6a5af7..0dda0d4 100755 --- a/tests/test_circuits.sh +++ b/tests/test_circuits.sh @@ -109,6 +109,7 @@ test_circuit "multiplier_signed" "s_dadda_lfa8" test_circuit "multiplier_unsigned" "u_arrmul8" +test_circuit "multiplier_unsigned" "u_recmul8" test_circuit "multiplier_unsigned" "u_csamul_cla8" test_circuit "multiplier_unsigned" "u_csamul_rca8" test_circuit "multiplier_unsigned" "u_csamul_pg_rca8" @@ -253,6 +254,7 @@ fi # exporting u_ka8 # exporting u_lfa8 # exporting u_arrmul8 +# exporting u_recmul8 # exporting u_csamul_cla8" # exporting u_csamul_rca8" # exporting u_csamul_pg_rca8" diff --git a/tests/test_circuits_cgp.sh b/tests/test_circuits_cgp.sh index f291e75..005b4b4 100755 --- a/tests/test_circuits_cgp.sh +++ b/tests/test_circuits_cgp.sh @@ -113,6 +113,7 @@ test_circuit "multiplier_signed" "s_dadda_lfa8" test_circuit "multiplier_unsigned" "u_arrmul8" +test_circuit "multiplier_unsigned" "u_recmul8" test_circuit "multiplier_unsigned" "u_csamul_cla8" test_circuit "multiplier_unsigned" "u_csamul_rca8" test_circuit "multiplier_unsigned" "u_csamul_pg_rca8" diff --git a/tests/test_circuits_verilog.sh b/tests/test_circuits_verilog.sh index 948e8ec..37b56e2 100644 --- a/tests/test_circuits_verilog.sh +++ b/tests/test_circuits_verilog.sh @@ -122,6 +122,7 @@ test_circuit "multiplier_signed" "s_dadda_lfa8" test_circuit "multiplier_unsigned" "u_arrmul8" +test_circuit "multiplier_unsigned" "u_recmul8" test_circuit "multiplier_unsigned" "u_csamul_cla8" test_circuit "multiplier_unsigned" "u_csamul_rca8" test_circuit "multiplier_unsigned" "u_csamul_pg_rca8" diff --git a/tests/test_compare.py b/tests/test_compare.py index 02e1312..9375887 100644 --- a/tests/test_compare.py +++ b/tests/test_compare.py @@ -1,3 +1,13 @@ +import os +import sys +# Add the parent directory to the system path +DIR_PATH = os.path.dirname(os.path.abspath(__file__)) +sys.path.insert(0, os.path.join(DIR_PATH, '..')) + +import numpy as np +import math +from io import StringIO + from ariths_gen.wire_components import ( Wire, ConstantWireValue0, @@ -11,10 +21,6 @@ from ariths_gen.multi_bit_circuits.others import ( UnsignedCompareLT ) -import numpy as np -import math -from io import StringIO - def test_compare(): """ Test unsigned comparator """ @@ -47,4 +53,6 @@ def test_compare(): np.testing.assert_array_equal(v, expected) - \ No newline at end of file +if __name__ == "__main__": + test_compare() + print("Python compare tests were successful!") \ No newline at end of file diff --git a/tests/test_popcnt.py b/tests/test_popcnt.py index 8d66628..85bf55c 100644 --- a/tests/test_popcnt.py +++ b/tests/test_popcnt.py @@ -1,3 +1,13 @@ +import os +import sys +# Add the parent directory to the system path +DIR_PATH = os.path.dirname(os.path.abspath(__file__)) +sys.path.insert(0, os.path.join(DIR_PATH, '..')) + +import numpy as np +import math +from io import StringIO + from ariths_gen.wire_components import ( Wire, ConstantWireValue0, @@ -11,10 +21,6 @@ from ariths_gen.multi_bit_circuits.others import ( UnsignedPopCount ) -import numpy as np -import math -from io import StringIO - def test_popcount(): """ Test unsigned adders """ @@ -47,4 +53,6 @@ def test_popcount(): np.testing.assert_array_equal(popcnt(av), expected) - \ No newline at end of file +if __name__ == "__main__": + test_popcount() + print("Python popcnt tests were successful!") diff --git a/tests/test_reduce.py b/tests/test_reduce.py index 9ec4b7e..b93e7c0 100644 --- a/tests/test_reduce.py +++ b/tests/test_reduce.py @@ -1,3 +1,13 @@ +import os +import sys +# Add the parent directory to the system path +DIR_PATH = os.path.dirname(os.path.abspath(__file__)) +sys.path.insert(0, os.path.join(DIR_PATH, '..')) + +import numpy as np +import math +from io import StringIO + from ariths_gen.wire_components import ( Wire, ConstantWireValue0, @@ -8,13 +18,11 @@ from ariths_gen.wire_components import ( from ariths_gen.core.arithmetic_circuits import GeneralCircuit from ariths_gen.multi_bit_circuits.others import ( - BitReduce, AndReduce, OrReduce + BitReduce, + AndReduce, + OrReduce ) -import numpy as np -import math -from io import StringIO - def test_orreduce(): """ Test unsigned adders """ @@ -75,4 +83,8 @@ def test_andreduce(): np.testing.assert_array_equal(reduce(av), expected) - \ No newline at end of file + +if __name__ == "__main__": + test_andreduce() + test_orreduce() + print("Python reduce tests were successful!") From 73101eb055f1248d794a89c9a27d265a43e4c013 Mon Sep 17 00:00:00 2001 From: honzastor Date: Wed, 27 Mar 2024 23:10:06 +0100 Subject: [PATCH 12/35] Type hint bugfix for pytest. --- .../approximate_multipliers/recursive_multiplier.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ariths_gen/multi_bit_circuits/approximate_multipliers/recursive_multiplier.py b/ariths_gen/multi_bit_circuits/approximate_multipliers/recursive_multiplier.py index 6a8ad38..a985e91 100644 --- a/ariths_gen/multi_bit_circuits/approximate_multipliers/recursive_multiplier.py +++ b/ariths_gen/multi_bit_circuits/approximate_multipliers/recursive_multiplier.py @@ -293,11 +293,11 @@ class UnsignedRecursiveMultiplier(MultiplierCircuit): b (Bus): Second input bus. prefix (str, optional): Prefix name of unsigned recursive multiplier. Defaults to "". name (str, optional): Name of unsigned recursive multiplier. Defaults to "u_rm". - submultipliers (list[MultiplierCircuit], optional): List of submultipliers. + submultipliers (list, optional): List of composite two bit submultiplier classes for instantiation. If None are provided, accurate submultipliers are assumed. Defaults to None. Defaults (if None) to the required number of UnsignedAccurateTwoBitMultiplier instances. 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 = "", name: str = "u_rm", submultipliers: list[MultiplierCircuit] = None, unsigned_adder_class_name: str = UnsignedCarryLookaheadAdder, **kwargs): + def __init__(self, a: Bus, b: Bus, prefix: str = "", name: str = "u_rm", submultipliers: list = None, unsigned_adder_class_name: str = UnsignedCarryLookaheadAdder, **kwargs): self.N = max(a.N, b.N) assert self.N > 1 and self.N & (self.N-1) == 0 # assure that N is a power of two greater than 1 (So allowed N is 2, 4, 8, ..) super().__init__(a=a, b=b, prefix=prefix, name=name, out_N=self.N*2, **kwargs) From 21a6437eb8b7deff184e72b5d3b1c4d52815e11a Mon Sep 17 00:00:00 2001 From: honzastor Date: Wed, 27 Mar 2024 23:19:40 +0100 Subject: [PATCH 13/35] Additional type hint fix. --- ariths_gen/multi_bit_circuits/others/popcount.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ariths_gen/multi_bit_circuits/others/popcount.py b/ariths_gen/multi_bit_circuits/others/popcount.py index cdca327..00e5d79 100644 --- a/ariths_gen/multi_bit_circuits/others/popcount.py +++ b/ariths_gen/multi_bit_circuits/others/popcount.py @@ -1,6 +1,7 @@ """ """ +from typing import Union from ariths_gen.wire_components import ( Wire, @@ -44,7 +45,7 @@ class UnsignedPopCount(GeneralCircuit): """ - def __init__(self, a: Bus, adder : ArithmeticCircuit|None = None, prefix : str = "", name : str = "popcnt", **kwargs): + def __init__(self, a: Bus, adder : Union[ArithmeticCircuit, None] = None, prefix : str = "", name : str = "popcnt", **kwargs): self.N = a.N self.a = a From cd3441ff000c4fa7cb9e79f2c235a2500575d52b Mon Sep 17 00:00:00 2001 From: honzastor Date: Wed, 27 Mar 2024 23:44:34 +0100 Subject: [PATCH 14/35] Removed error tests from overall testing. --- tests/test_all.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/test_all.py b/tests/test_all.py index 33af3c1..cb4f85a 100644 --- a/tests/test_all.py +++ b/tests/test_all.py @@ -389,6 +389,4 @@ if __name__ == "__main__": test_unsigned_add() test_signed_add() test_mac() - test_direct() - test_wire_as_bus() print("Python tests were successful!") From da733cf44eac519636989a8e6598ab1418267126 Mon Sep 17 00:00:00 2001 From: honzastor Date: Thu, 28 Mar 2024 00:06:53 +0100 Subject: [PATCH 15/35] Added instantiation of wires and buses from inputs. Hopefully fixed now. --- .../arithmetic_circuits/general_circuit.py | 22 ++++++++++++------- tests/test_all.py | 4 +++- 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/ariths_gen/core/arithmetic_circuits/general_circuit.py b/ariths_gen/core/arithmetic_circuits/general_circuit.py index 94b7e89..20b380d 100644 --- a/ariths_gen/core/arithmetic_circuits/general_circuit.py +++ b/ariths_gen/core/arithmetic_circuits/general_circuit.py @@ -30,16 +30,22 @@ class GeneralCircuit(): self.inputs = [] input_names = "abcdefghijklmnopqrstuvwxyz" # This should be enough.. assert len(input_names) >= len(inputs) - for i, input_bus in enumerate(inputs): + for i, input in enumerate(inputs): attr_name = input_names[i] - full_prefix = f"{self.prefix}_{input_bus.prefix}" if self.inner_component else f"{input_bus.prefix}" - bus = Bus(prefix=full_prefix, wires_list=input_bus.bus) - setattr(self, attr_name, bus) - self.inputs.append(bus) + full_prefix = f"{self.prefix}_{input.prefix}" if self.inner_component else f"{input.prefix}" + if isinstance(input, Bus): + bus = Bus(prefix=full_prefix, wires_list=input.bus) + setattr(self, attr_name, bus) + self.inputs.append(bus) + + # If the input bus is an output bus, connect it + if input.is_output_bus(): + getattr(self, attr_name).connect_bus(connecting_bus=input) + else: + wire = Wire(name=input.name, prefix=full_prefix) + setattr(self, attr_name, wire) + self.inputs.append(wire) - # If the input bus is an output bus, connect it - if input_bus.is_output_bus(): - getattr(self, attr_name).connect_bus(connecting_bus=input_bus) else: self.inputs = inputs diff --git a/tests/test_all.py b/tests/test_all.py index cb4f85a..d194669 100644 --- a/tests/test_all.py +++ b/tests/test_all.py @@ -337,7 +337,7 @@ def test_mac(): def test_direct(): class err_circuit(GeneralCircuit): - def __init__(self, prefix: str = "", name: str = "adder", inner_component: bool = True, a: Bus = Bus(), b: Bus = Bus()): + def __init__(self, a: Bus = Bus(), b: Bus = Bus(), prefix: str = "", name: str = "adder", inner_component: bool = False): super().__init__(prefix=prefix, name=name, out_N=(a.N + 1), inner_component=inner_component, inputs=[a, b]) self.N = 1 self.prefix = prefix @@ -389,4 +389,6 @@ if __name__ == "__main__": test_unsigned_add() test_signed_add() test_mac() + test_direct() + test_wire_as_bus() print("Python tests were successful!") From 2cf7b921ea03f91726cbc6b0cfc8f7b8fa1a037b Mon Sep 17 00:00:00 2001 From: Vojta Mrazek Date: Fri, 5 Apr 2024 08:46:02 +0200 Subject: [PATCH 16/35] Popcount implementation --- .../arithmetic_circuits/general_circuit.py | 30 +- ariths_gen/core/cgp_circuit.py | 8 +- .../logic_gate_circuits/logic_gate_circuit.py | 8 +- .../adders/carry_lookahead_adder.py | 6 +- .../multi_bit_circuits/others/__init__.py | 6 +- .../multi_bit_circuits/others/bit_reduce.py | 4 +- .../multi_bit_circuits/others/compare.py | 139 +++++++- .../multi_bit_circuits/others/popcount.py | 4 +- .../others/popcount_compare.py | 81 +++++ .../logic_gates/logic_gates.py | 24 +- ariths_gen/wire_components/wires.py | 3 + tests/test_all.py | 4 +- tests/test_compare.py | 328 +++++++++++++++++- tests/test_popcnt.py | 35 ++ tests/test_popcount_compare.py | 169 +++++++++ 15 files changed, 788 insertions(+), 61 deletions(-) create mode 100644 ariths_gen/multi_bit_circuits/others/popcount_compare.py create mode 100644 tests/test_popcount_compare.py diff --git a/ariths_gen/core/arithmetic_circuits/general_circuit.py b/ariths_gen/core/arithmetic_circuits/general_circuit.py index c4fb720..6cd6c79 100644 --- a/ariths_gen/core/arithmetic_circuits/general_circuit.py +++ b/ariths_gen/core/arithmetic_circuits/general_circuit.py @@ -68,7 +68,7 @@ class GeneralCircuit(): component: Subcomponent to be added into list of components composing described circuit. """ prefixes = [c.prefix for c in self.components] - #assert component.prefix not in prefixes, f"Component with prefix {component.prefix} already exists in the circuit." + assert component.prefix not in prefixes, f"Component with prefix {component.prefix} already exists in the circuit." self.components.append(component) return component @@ -212,28 +212,16 @@ class GeneralCircuit(): """ self.circuit_wires = [] circuit_wires_names = [] - if isinstance(self.a, Bus): - [self.circuit_wires.append( - (w, f"{w.name}", self.save_wire_id(wire=w))) for w in self.a.bus] - [self.circuit_wires.append( - (w, f"{w.name}", self.save_wire_id(wire=w))) for w in self.b.bus] - [circuit_wires_names.append(w.name) for w in self.a.bus] - [circuit_wires_names.append(w.name) for w in self.b.bus] - if hasattr(self, 'c'): + + for input in self.inputs: + if isinstance(input, Bus): [self.circuit_wires.append( - (w, f"{w.name}", self.save_wire_id(wire=w))) for w in self.c.bus] - [circuit_wires_names.append(w.name) for w in self.c.bus] - else: - self.circuit_wires.append( - (self.a, f"{self.a.name}", self.save_wire_id(wire=self.a))) - self.circuit_wires.append( - (self.b, f"{self.b.name}", self.save_wire_id(wire=self.b))) - circuit_wires_names.append(self.a.name) - circuit_wires_names.append(self.b.name) - if hasattr(self, 'c'): + (w, f"{w.name}", self.save_wire_id(wire=w))) for w in input.bus] + [circuit_wires_names.append(w.name) for w in input.bus] + else: self.circuit_wires.append( - (self.c, f"{self.c.name}", self.save_wire_id(wire=self.c))) - circuit_wires_names.append(self.c.name) + (input, f"{input.name}", self.save_wire_id(wire=input))) + circuit_wires_names.append(input.name) for gate in self.circuit_gates: if gate.a.name not in circuit_wires_names: diff --git a/ariths_gen/core/cgp_circuit.py b/ariths_gen/core/cgp_circuit.py index cbb5fbc..be1a6cb 100644 --- a/ariths_gen/core/cgp_circuit.py +++ b/ariths_gen/core/cgp_circuit.py @@ -123,7 +123,13 @@ class UnsignedCGPCircuit(GeneralCircuit): return ConstantWireValue0() if i == 1: return ConstantWireValue1() - return self.vals[i] + try: + return self.vals[i] + except KeyError: + + raise KeyError(f"Key {i} not found in " + ", ".join( + [f"{i}: {v}" for i, v in self.vals.items()] + )) class SignedCGPCircuit(UnsignedCGPCircuit): diff --git a/ariths_gen/core/logic_gate_circuits/logic_gate_circuit.py b/ariths_gen/core/logic_gate_circuits/logic_gate_circuit.py index eac7a40..c24c2c2 100644 --- a/ariths_gen/core/logic_gate_circuits/logic_gate_circuit.py +++ b/ariths_gen/core/logic_gate_circuits/logic_gate_circuit.py @@ -33,14 +33,16 @@ class MultipleInputLogicGate(): prefix (str, optional): Prefix used to name inner composite logic gates. Defaults to "". """ def __init__(self, a: Bus, two_input_gate_cls, parent_component: object, prefix: str = ""): + i = 0 while a.N != 1: N = math.floor(a.N/2) out_wires = [] # Creation of composite two input logic gates from bus `a`'s bit pairs and addition of generated blocks outputs for next iteration for bus_index in range(0, N): - gate = two_input_gate_cls(a=a.get_wire(bus_index), b=a.get_wire(bus_index+N), prefix=prefix+str(parent_component.get_instance_num(cls=two_input_gate_cls, count_disabled_gates=False)), parent_component=parent_component) + gate = two_input_gate_cls(a=a.get_wire(bus_index), b=a.get_wire(bus_index+N), prefix=prefix+ "_" + str(i) + "_" + str(parent_component.get_instance_num(cls=two_input_gate_cls, count_disabled_gates=False)), parent_component=parent_component) parent_component.add_component(gate) out_wires.append(gate.out) + i += 1 # In case bus `a` has odd number of wires if a.N % 2 != 0: @@ -110,6 +112,8 @@ class TwoInputLogicGate(): Returns: str: C code description of logic gate's Boolean function (with bitwise shifted inputs). """ + if self.out.is_const(): + return self.out.get_wire_value_c_flat() return f"{self.a.get_wire_value_c_flat()} {self.operator} {self.b.get_wire_value_c_flat()}" def get_declaration_c_flat(self): @@ -206,6 +210,8 @@ class TwoInputLogicGate(): # No gate logic is generated if one of the inputs is a wire with constant value. # I.e. either the constant or the second input wire is propagated to the output for the corresponding logic gate's logic function. if self.disable_generation: + #return f" {self.out.prefix} = {self.get_function_c()} # DD {self.prefix} \n" + return "" else: return f" {self.out.prefix} = {self.get_function_c()}\n" diff --git a/ariths_gen/multi_bit_circuits/adders/carry_lookahead_adder.py b/ariths_gen/multi_bit_circuits/adders/carry_lookahead_adder.py index 4665b50..439286c 100644 --- a/ariths_gen/multi_bit_circuits/adders/carry_lookahead_adder.py +++ b/ariths_gen/multi_bit_circuits/adders/carry_lookahead_adder.py @@ -109,14 +109,14 @@ class UnsignedCarryLookaheadAdder(ArithmeticCircuit): # 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) + multi_bit_and_gate = MultipleInputLogicGate(a=pg_wires, two_input_gate_cls=AndGate, prefix=self.prefix+f"_and{block_n}_{i}_{g_index}", 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) + multi_bit_or_gate = MultipleInputLogicGate(a=composite_or_wires, two_input_gate_cls=OrGate, prefix=self.prefix+f"_orred{block_n}_{i}_{g_index}_", 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) + obj_cout_or = OrGate(pg_block.get_generate_wire(), multi_bit_or_gate.out, prefix=self.prefix+f"_or{block_n}_{i}_{g_index}_"+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 cla block/sum XOR cin = obj_cout_or.out diff --git a/ariths_gen/multi_bit_circuits/others/__init__.py b/ariths_gen/multi_bit_circuits/others/__init__.py index 44ed65b..de090f3 100644 --- a/ariths_gen/multi_bit_circuits/others/__init__.py +++ b/ariths_gen/multi_bit_circuits/others/__init__.py @@ -8,5 +8,7 @@ from ariths_gen.multi_bit_circuits.others.bit_reduce import ( ) from ariths_gen.multi_bit_circuits.others.compare import ( - UnsignedCompareLT -) \ No newline at end of file + UnsignedCompareLT, UnsignedCompareLTE, UnsignedCompareGT, UnsignedCompareGTE +) + +from ariths_gen.multi_bit_circuits.others.popcount_compare import PopCountCompare \ No newline at end of file diff --git a/ariths_gen/multi_bit_circuits/others/bit_reduce.py b/ariths_gen/multi_bit_circuits/others/bit_reduce.py index 45da4d7..bd0cd8b 100644 --- a/ariths_gen/multi_bit_circuits/others/bit_reduce.py +++ b/ariths_gen/multi_bit_circuits/others/bit_reduce.py @@ -68,8 +68,8 @@ class BitReduce(GeneralCircuit): for i, j in enumerate(range(half, a.N)): c_in[i] = a[j] - b = create_tree(b_in, depth=depth + 1, branch = "A") - c = create_tree(c_in, depth= depth + 1, branch = "B") + b = create_tree(b_in, depth=depth + 1, branch = branch + "A") + c = create_tree(c_in, depth= depth + 1, branch = branch + "B") d = gate(a=b, b=c, prefix = f"{self.prefix}_red_{branch}_{depth}") self.add_component(d) return d.out diff --git a/ariths_gen/multi_bit_circuits/others/compare.py b/ariths_gen/multi_bit_circuits/others/compare.py index 6c62970..6e92ce1 100644 --- a/ariths_gen/multi_bit_circuits/others/compare.py +++ b/ariths_gen/multi_bit_circuits/others/compare.py @@ -44,7 +44,6 @@ class UnsignedCompareLT(GeneralCircuit): Returns true if a < b - """ def __init__(self, a: Bus, b: Bus, prefix : str = "", name : str = "cmp_lt", **kwargs): @@ -52,32 +51,144 @@ class UnsignedCompareLT(GeneralCircuit): self.b = b self.N = max(a.N, b.N) - #print("outc", outc) super().__init__(name=name, prefix=prefix, inputs = [self.a, self.b], out_N=1) - - self.a.bus_extend(self.N, prefix=a.prefix) - self.b.bus_extend(self.N, prefix=b.prefix) - - # create wires psum = ConstantWireValue1() res = Bus(N = self.N, prefix=self.prefix + "res") - for i in reversed(range(self.N)): - - i1 = self.add_component(NotGate(self.a[i], f"{self.prefix}_i1_{i}")).out - i2 = self.b[i] + iA = self.a[i] if i < self.a.N else ConstantWireValue0() + iB = self.b[i] if i < self.b.N else ConstantWireValue0() + + i1 = self.add_component(NotGate(iA, f"{self.prefix}_i1_{i}")).out + i2 = iB and1 = self.add_component(AndGate(i1, i2, f"{self.prefix}_and1_{i}")).out res[i] = self.add_component(AndGate(and1, psum, f"{self.prefix}_and2_{i}")).out - pi = self.add_component(XnorGate(self.a[i], self.b[i], f"{self.prefix}_pi_{i}")).out + pi = self.add_component(XnorGate(iA, iB, f"{self.prefix}_pi_{i}", parent_component=self)).out psum = self.add_component(AndGate(pi, psum, f"{self.prefix}_psum_{i}")).out - self.out = self.add_component(OrReduce(res, prefix=f"{self.prefix}_orred")).out - #self.out.connect_bus(sumbus ) \ No newline at end of file + red = self.add_component(OrReduce(res, prefix=f"{self.prefix}_orred", inner_component=True)) + self.out.connect_bus(red.out) + + +class UnsignedCompareLTE(GeneralCircuit): + """Class representing unsigned compare + + Returns true if a <= b + """ + + def __init__(self, a: Bus, b: Bus, prefix : str = "", name : str = "cmp_lt", **kwargs): + self.a = a + self.b = b + self.N = max(a.N, b.N) + + super().__init__(name=name, prefix=prefix, + inputs = [self.a, self.b], out_N=1) + + # create wires + psum = ConstantWireValue1() + + res = Bus(N = self.N + 1, prefix=self.prefix + "res") + + for i in reversed(range(self.N)): + iA = self.a[i] if i < self.a.N else ConstantWireValue0() + iB = self.b[i] if i < self.b.N else ConstantWireValue0() + + i1 = self.add_component(NotGate(iA, f"{self.prefix}_i1_{i}")).out + i2 = iB + + and1 = self.add_component(AndGate(i1, i2, f"{self.prefix}_and1_{i}")).out + res[i] = self.add_component(AndGate(and1, psum, f"{self.prefix}_and2_{i}")).out + + pi = self.add_component(XnorGate(iA, iB, f"{self.prefix}_pi_{i}", parent_component=self)).out + psum = self.add_component(AndGate(pi, psum, f"{self.prefix}_psum_{i}")).out + + res[self.N] = psum # or all equal (xor) + + red = self.add_component(OrReduce(res, prefix=f"{self.prefix}_orred", inner_component=True)) + + self.out.connect_bus(red.out) + + + +class UnsignedCompareGT(GeneralCircuit): + """Class representing unsigned compare + + + Returns true if a < b + """ + + def __init__(self, a: Bus, b: Bus, prefix : str = "", name : str = "cmp_gt", **kwargs): + self.a = a + self.b = b + self.N = max(a.N, b.N) + + super().__init__(name=name, prefix=prefix, + inputs = [self.a, self.b], out_N=1) + + # create wires + psum = ConstantWireValue1() + + res = Bus(N = self.N, prefix=self.prefix + "res") + + for i in reversed(range(self.N)): + iA = self.a[i] if i < self.a.N else ConstantWireValue0() + iB = self.b[i] if i < self.b.N else ConstantWireValue0() + + i1 = iA + i2 = self.add_component(NotGate(iB, f"{self.prefix}_i2_{i}")).out + + and1 = self.add_component(AndGate(i1, i2, f"{self.prefix}_and1_{i}")).out + res[i] = self.add_component(AndGate(and1, psum, f"{self.prefix}_and2_{i}")).out + + pi = self.add_component(XnorGate(iA, iB, f"{self.prefix}_pi_{i}", parent_component=self)).out + psum = self.add_component(AndGate(pi, psum, f"{self.prefix}_psum_{i}")).out + + + red = self.add_component(OrReduce(res, prefix=f"{self.prefix}_orred", inner_component=True)) + self.out.connect_bus(red.out) + + +class UnsignedCompareGTE(GeneralCircuit): + """Class representing unsigned compare + + Returns true if a <= b + """ + + def __init__(self, a: Bus, b: Bus, prefix : str = "", name : str = "cmp_gte", **kwargs): + self.a = a + self.b = b + self.N = max(a.N, b.N) + + super().__init__(name=name, prefix=prefix, + inputs = [self.a, self.b], out_N=1) + + # create wires + psum = ConstantWireValue1() + + res = Bus(N = self.N + 1, prefix=self.prefix + "res") + + for i in reversed(range(self.N)): + iA = self.a[i] if i < self.a.N else ConstantWireValue0() + iB = self.b[i] if i < self.b.N else ConstantWireValue0() + + i1 = iA + i2 = self.add_component(NotGate(iB, f"{self.prefix}_i1_{i}", parent_component=self)).out + + and1 = self.add_component(AndGate(i1, i2, f"{self.prefix}_and1_{i}", parent_component=self)).out + res[i] = self.add_component(AndGate(and1, psum, f"{self.prefix}_and2_{i}", parent_component=self)).out + + pi = self.add_component(XnorGate(iA, iB, f"{self.prefix}_pi_{i}", parent_component=self)).out + psum = self.add_component(AndGate(pi, psum, f"{self.prefix}_psum_{i}", parent_component=self)).out + + res[self.N] = psum # or all equal (xor) + + red = self.add_component(OrReduce(res, prefix=f"{self.prefix}_orred", inner_component=True)) + + self.out.connect_bus(red.out) \ No newline at end of file diff --git a/ariths_gen/multi_bit_circuits/others/popcount.py b/ariths_gen/multi_bit_circuits/others/popcount.py index cdca327..459365f 100644 --- a/ariths_gen/multi_bit_circuits/others/popcount.py +++ b/ariths_gen/multi_bit_circuits/others/popcount.py @@ -78,8 +78,8 @@ class UnsignedPopCount(GeneralCircuit): for i, j in enumerate(range(half, a.N)): c_in[i] = a[j] - b = create_tree(b_in, depth=depth + 1, branch = "A") - c = create_tree(c_in, depth= depth + 1, branch = "B") + b = create_tree(b_in, depth=depth + 1, branch = branch + "A") + c = create_tree(c_in, depth= depth + 1, branch = branch + "B") d = self.adder(a=b, b=c, prefix = f"{self.prefix}_add{branch}_{depth}") self.add_component(d) return d.out diff --git a/ariths_gen/multi_bit_circuits/others/popcount_compare.py b/ariths_gen/multi_bit_circuits/others/popcount_compare.py new file mode 100644 index 0000000..1f2095c --- /dev/null +++ b/ariths_gen/multi_bit_circuits/others/popcount_compare.py @@ -0,0 +1,81 @@ +""" + +""" + +from ariths_gen.multi_bit_circuits.others import UnsignedPopCount +from ariths_gen.multi_bit_circuits.others.compare import UnsignedCompareGTE +from ariths_gen.wire_components import ( + Wire, + ConstantWireValue0, + ConstantWireValue1, + Bus, + wires +) +from ariths_gen.core.arithmetic_circuits import ( + ArithmeticCircuit, + GeneralCircuit, + MultiplierCircuit +) + +from ariths_gen.core.logic_gate_circuits import ( + MultipleInputLogicGate +) +from ariths_gen.one_bit_circuits.one_bit_components import ( + HalfAdder, + FullAdder, + FullAdderP, + TwoOneMultiplexer +) +from ariths_gen.one_bit_circuits.logic_gates import ( + AndGate, + NandGate, + OrGate, + NorGate, + XorGate, + XnorGate, + NotGate +) + +from ariths_gen.multi_bit_circuits.others import OrReduce + + +from math import log2, ceil + +class PopCountCompare(GeneralCircuit): + """Class representing a circiut + if number of ones in a is larger or equal than number of ones in b + + β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ + β–Ό β–Ό β–Ό β–Ό β–Ό β–Ό β–Ό β–Ό β–Ό + β”Œβ”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β” + β”‚ popcntβ”‚ β”‚ popcnt β”‚ + β””β”€β”€β”¬β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”˜ + β”‚ β”‚ + └───────┐ β”Œβ”€β”€β”€β”€β”€β”€β”˜ + β”Œβ”€β”€β–Όβ”€β”€β”€β–Όβ”€β”€β” + β”‚ <= β”‚ + β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”˜ + β”‚ + β–Ό + """ + + def __init__(self, a: Bus, b: Bus, prefix : str = "", name : str = "cmp_lt", **kwargs): + self.a = a + self.b = b + + super().__init__(name=name, prefix=prefix, + inputs = [self.a, self.b], out_N=1) + + p1 = self.add_component(UnsignedPopCount(self.a, + prefix=f"{prefix}_popcount1", + inner_component=True)).out + p2 = self.add_component(UnsignedPopCount(self.b, + prefix=f"{prefix}_popcount2", + inner_component=True)).out + + N = max(p1.N, p2.N) + p1.bus_extend(N) + p2.bus_extend(N) + + red = self.add_component(UnsignedCompareGTE(p1, p2, prefix=f"{prefix}_cmp", inner_component = True)) + self.out.connect_bus(red.out) diff --git a/ariths_gen/one_bit_circuits/logic_gates/logic_gates.py b/ariths_gen/one_bit_circuits/logic_gates/logic_gates.py index f0c74db..822b850 100644 --- a/ariths_gen/one_bit_circuits/logic_gates/logic_gates.py +++ b/ariths_gen/one_bit_circuits/logic_gates/logic_gates.py @@ -92,7 +92,8 @@ class NandGate(TwoInputInvertedLogicGate): # If constant input is present, logic gate is not generated and corresponding # input value is propagated to the output to connect to other components if a.is_const() and a.value == 1: - output = NotGate(a=b, prefix=prefix, outid=outid, parent_component=parent_component) + assert self.parent_component, "Parent component for gate {self} is not defined" + output = NotGate(a=b, prefix=prefix + "_not", outid=outid, parent_component=parent_component) self.parent_component.add_component(output) if parent_component is not None else None self.out = output.out self.disable_generation = True @@ -100,7 +101,8 @@ class NandGate(TwoInputInvertedLogicGate): self.out = ConstantWireValue1() self.disable_generation = True elif b.is_const() and b.value == 1: - output = NotGate(a=a, prefix=prefix, outid=outid, parent_component=parent_component) + assert self.parent_component, "Parent component for gate {self} is not defined" + output = NotGate(a=a, prefix=prefix + "_not", outid=outid, parent_component=parent_component) self.parent_component.add_component(output) if parent_component is not None else None self.out = output.out self.disable_generation = True @@ -217,7 +219,8 @@ class NorGate(TwoInputInvertedLogicGate): self.out = ConstantWireValue0() self.disable_generation = True elif a.is_const() and a.value == 0: - output = NotGate(a=b, prefix=prefix, outid=outid, parent_component=parent_component) + assert self.parent_component, "Parent component for gate {self} is not defined" + output = NotGate(a=b, prefix=prefix + "_not", outid=outid, parent_component=parent_component) self.parent_component.add_component(output) if parent_component is not None else None self.out = output.out self.disable_generation = True @@ -225,7 +228,8 @@ class NorGate(TwoInputInvertedLogicGate): self.out = ConstantWireValue0() self.disable_generation = True elif b.is_const() and b.value == 0: - output = NotGate(a=a, prefix=prefix, outid=outid, parent_component=parent_component) + assert self.parent_component, "Parent component for gate {self} is not defined" + output = NotGate(a=a, prefix=prefix + "_not", outid=outid, parent_component=parent_component) self.parent_component.add_component(output) if parent_component is not None else None self.out = output.out self.disable_generation = True @@ -277,7 +281,8 @@ class XorGate(TwoInputLogicGate): # If constant input is present, logic gate is not generated and corresponding # input value is propagated to the output to connect to other components if a.is_const() and a.value == 1: - output = NotGate(a=b, prefix=prefix, outid=outid, parent_component=parent_component) + assert self.parent_component, "Parent component for gate {self} is not defined" + output = NotGate(a=b, prefix=prefix + "_not", outid=outid, parent_component=parent_component) self.parent_component.add_component(output) if parent_component is not None else None self.out = output.out self.disable_generation = True @@ -285,7 +290,8 @@ class XorGate(TwoInputLogicGate): self.out = b self.disable_generation = True elif b.is_const() and b.value == 1: - output = NotGate(a=a, prefix=prefix, outid=outid, parent_component=parent_component) + assert self.parent_component, "Parent component for gate {self} is not defined" + output = NotGate(a=a, prefix=prefix + "_not", outid=outid, parent_component=parent_component) self.parent_component.add_component(output) if parent_component is not None else None self.out = output.out self.disable_generation = True @@ -343,7 +349,8 @@ class XnorGate(TwoInputInvertedLogicGate): self.out = b self.disable_generation = True elif a.is_const() and a.value == 0: - output = NotGate(a=b, prefix=prefix, outid=outid, parent_component=parent_component) + assert self.parent_component, "Parent component for gate {self} is not defined" + output = NotGate(a=b, prefix=prefix + "_not", outid=outid, parent_component=parent_component) self.parent_component.add_component(output) if parent_component is not None else None self.out = output.out self.disable_generation = True @@ -351,7 +358,8 @@ class XnorGate(TwoInputInvertedLogicGate): self.out = a self.disable_generation = True elif b.is_const() and b.value == 0: - output = NotGate(a=a, prefix=prefix, outid=outid, parent_component=parent_component) + assert self.parent_component, "Parent component for gate {self} is not defined" + output = NotGate(a=a, prefix=prefix + "_not", outid=outid, parent_component=parent_component) self.parent_component.add_component(output) if parent_component is not None else None self.out = output.out self.disable_generation = True diff --git a/ariths_gen/wire_components/wires.py b/ariths_gen/wire_components/wires.py index c34240b..63d8267 100644 --- a/ariths_gen/wire_components/wires.py +++ b/ariths_gen/wire_components/wires.py @@ -17,6 +17,9 @@ class Wire(): self.prefix = name if prefix == "" else prefix self.parent_bus = parent_bus + def __str__(self): + return f"wire{self.name}{self.value}{self.index}" + @staticmethod def is_const(): """Information whether wire carries constant value. diff --git a/tests/test_all.py b/tests/test_all.py index c8cd777..48cf85d 100644 --- a/tests/test_all.py +++ b/tests/test_all.py @@ -348,8 +348,8 @@ def test_wire_as_bus(): def __init__(self, a: Wire, b: Wire, c: Bus, prefix="test_circuit", **kwargs): super().__init__(prefix=prefix, name="test_circuit", inputs=[a, b, c], out_N=1, **kwargs) g = self.add_component(AndGate(a, b, prefix="g2")) - g2 = self.add_component(AndGate(g.out, c[0], prefix="g2")) - g3 = self.add_component(AndGate(g2.out, c[1], prefix="g2")) + g2 = self.add_component(AndGate(g.out, c[0], prefix="g3")) + g3 = self.add_component(AndGate(g2.out, c[1], prefix="g4")) self.out[0] = g3.out circ = test_circuit(Wire("a"), Wire("b"), Bus("c", 2), "c1") diff --git a/tests/test_compare.py b/tests/test_compare.py index 02e1312..4420d9c 100644 --- a/tests/test_compare.py +++ b/tests/test_compare.py @@ -8,15 +8,18 @@ from ariths_gen.wire_components import ( from ariths_gen.core.arithmetic_circuits import GeneralCircuit from ariths_gen.multi_bit_circuits.others import ( - UnsignedCompareLT + UnsignedCompareLT, UnsignedCompareLTE, UnsignedCompareGT, UnsignedCompareGTE ) +from ariths_gen.core.cgp_circuit import UnsignedCGPCircuit, SignedCGPCircuit + + import numpy as np import math from io import StringIO -def test_compare(): +def test_compare_lt_same(): """ Test unsigned comparator """ N = 8 @@ -27,9 +30,39 @@ def test_compare(): cmp = UnsignedCompareLT(a=a, b=b) - o = StringIO() cmp.get_v_code_hier(open("tmp.verilog", "w")) - print(o.getvalue()) + + v = cmp(av, bv) + print("ret = ", v) + + expected = np.array(av < bv).astype(int) + + print("expected = ", expected) + + #expected = np.sum(r, axis=1) + + np.testing.assert_array_equal(v, expected) + + + + +def test_compare_lt_small(): + """ Test unsigned comparator """ + N = 8 + + a = Bus(N=N, prefix="a") + b = Bus(N=N//2, prefix="b") + av = np.arange(2**N).reshape(1, -1) + bv = np.arange(2**(N //2)).reshape(-1, 1) + + + cmp = UnsignedCompareLT(a=a, b=b) + #o = StringIO() + cmp.get_python_code_flat(open("tmp.py", "w")) + cmp.get_c_code_flat(open("tmp.c", "w")) + cmp.get_cgp_code_flat(open("tmp.cgp", "w")) + #cmp.get_v_code_hier(open("tmp.verilog", "w")) + #print(o.getvalue()) # av = 0 # bv = 5 @@ -47,4 +80,289 @@ def test_compare(): np.testing.assert_array_equal(v, expected) - \ No newline at end of file + +def test_compare_lte_same(): + """ Test unsigned comparator """ + N = 8 + + a = Bus(N=N, prefix="a") + b = Bus(N=N, prefix="b") + av = np.arange(2**N).reshape(1, -1) + bv = np.arange(2**N).reshape(-1, 1) + + + cmp = UnsignedCompareLTE(a=a, b=b) + cmp.get_v_code_hier(open("tmp.verilog", "w")) + + v = cmp(av, bv) + print("ret = ", v) + + expected = np.array(av <= bv).astype(int) + + print("expected = ", expected) + + #expected = np.sum(r, axis=1) + + np.testing.assert_array_equal(v, expected) + + + + +def test_compare_lte_small(): + """ Test unsigned comparator """ + N = 8 + + a = Bus(N=N, prefix="a") + b = Bus(N=N//2, prefix="b") + av = np.arange(2**N).reshape(1, -1) + bv = np.arange(2**(N //2)).reshape(-1, 1) + + + cmp = UnsignedCompareLTE(a=a, b=b) + #o = StringIO() + cmp.get_python_code_flat(open("tmp.py", "w")) + cmp.get_c_code_flat(open("tmp.c", "w")) + cmp.get_cgp_code_flat(open("tmp.cgp", "w")) + #cmp.get_v_code_hier(open("tmp.verilog", "w")) + #print(o.getvalue()) + +# av = 0 + # bv = 5 + + + v = cmp(av, bv) + print("ret = ", v) + + expected = np.array(av <= bv).astype(int) + + print("expected = ", expected) + + #expected = np.sum(r, axis=1) + + np.testing.assert_array_equal(v, expected) + + +def test_compare_lte_small2(): + """ Test unsigned comparator """ + N = 8 + + a = Bus(N=N//2, prefix="a") + b = Bus(N=N, prefix="b") + av = np.arange(2**(N // 2)).reshape(1, -1) + bv = np.arange(2**(N)).reshape(-1, 1) + + + cmp = UnsignedCompareLTE(a=a, b=b) + #o = StringIO() + cmp.get_python_code_flat(open("tmp.py", "w")) + cmp.get_c_code_flat(open("tmp.c", "w")) + cmp.get_cgp_code_flat(open("tmp.cgp", "w")) + #cmp.get_v_code_hier(open("tmp.verilog", "w")) + #print(o.getvalue()) + +# av = 0 + # bv = 5 + + + v = cmp(av, bv) + print("ret = ", v) + + expected = np.array(av <= bv).astype(int) + + print("expected = ", expected) + + #expected = np.sum(r, axis=1) + + np.testing.assert_array_equal(v, expected) + + +def test_compare_gt_same(): + """ Test unsigned comparator """ + N = 8 + + a = Bus(N=N, prefix="a") + b = Bus(N=N, prefix="b") + av = np.arange(2**N).reshape(1, -1) + bv = np.arange(2**N).reshape(-1, 1) + + + cmp = UnsignedCompareGT(a=a, b=b) + cmp.get_v_code_hier(open("tmp.verilog", "w")) + + v = cmp(av, bv) + print("ret = ", v) + + expected = np.array(av > bv).astype(int) + + print("expected = ", expected) + + #expected = np.sum(r, axis=1) + + np.testing.assert_array_equal(v, expected) + + + + +def test_compare_gt_small(): + """ Test unsigned comparator """ + N = 8 + + a = Bus(N=N, prefix="a") + b = Bus(N=N//2, prefix="b") + av = np.arange(2**N).reshape(1, -1) + bv = np.arange(2**(N //2)).reshape(-1, 1) + + + cmp = UnsignedCompareGT(a=a, b=b) + #o = StringIO() + cmp.get_python_code_flat(open("tmp.py", "w")) + cmp.get_c_code_flat(open("tmp.c", "w")) + cmp.get_cgp_code_flat(open("tmp.cgp", "w")) + #cmp.get_v_code_hier(open("tmp.verilog", "w")) + #print(o.getvalue()) + +# av = 0 + # bv = 5 + + + v = cmp(av, bv) + print("ret = ", v) + + expected = np.array(av > bv).astype(int) + + print("expected = ", expected) + + #expected = np.sum(r, axis=1) + + np.testing.assert_array_equal(v, expected) + + + +def test_compare_gte_same(): + """ Test unsigned comparator """ + N = 8 + + a = Bus(N=N, prefix="a") + b = Bus(N=N, prefix="b") + av = np.arange(2**N).reshape(1, -1) + bv = np.arange(2**N).reshape(-1, 1) + + + cmp = UnsignedCompareGTE(a=a, b=b) + cmp.get_v_code_hier(open("tmp.verilog", "w")) + + v = cmp(av, bv) + print("ret = ", v) + + expected = np.array(av >= bv).astype(int) + + print("expected = ", expected) + + #expected = np.sum(r, axis=1) + + np.testing.assert_array_equal(v, expected) + + + + +def test_compare_gte_small(): + """ Test unsigned comparator """ + N = 8 + + a = Bus(N=N, prefix="a") + b = Bus(N=N//2, prefix="b") + av = np.arange(2**N).reshape(1, -1) + bv = np.arange(2**(N //2)).reshape(-1, 1) + + + cmp = UnsignedCompareGTE(a=a, b=b) + #o = StringIO() + cmp.get_python_code_flat(open("tmp.py", "w")) + cmp.get_c_code_flat(open("tmp.c", "w")) + cmp.get_cgp_code_flat(open("tmp.cgp", "w")) + #cmp.get_v_code_hier(open("tmp.verilog", "w")) + #print(o.getvalue()) + +# av = 0 + # bv = 5 + + + v = cmp(av, bv) + print("ret = ", v) + + expected = np.array(av >= bv).astype(int) + + print("expected = ", expected) + + #expected = np.sum(r, axis=1) + + np.testing.assert_array_equal(v, expected) + + + +def test_compare_gte_cgp_same(): + """ Test unsigned comparator """ + N = 8 + + a = Bus(N=N, prefix="a") + b = Bus(N=N, prefix="b") + av = np.arange(2**N).reshape(1, -1) + bv = np.arange(2**N).reshape(-1, 1) + + + cmp = UnsignedCompareGTE(a=a, b=b) + o = StringIO() + cmp.get_v_code_hier(open("tmp.verilog", "w")) + cmp.get_cgp_code_flat(o) + + cgp = UnsignedCGPCircuit(o.getvalue(), [N, N]) + + v = cgp(av, bv) + print("ret = ", v) + + expected = np.array(av >= bv).astype(int) + + print("expected = ", expected) + + #expected = np.sum(r, axis=1) + + np.testing.assert_array_equal(v, expected) + + + + +def test_compare_gte_cgp_small(): + """ Test unsigned comparator """ + N = 8 + + a = Bus(N=N, prefix="a") + b = Bus(N=N//2, prefix="b") + av = np.arange(2**N).reshape(1, -1) + bv = np.arange(2**(N //2)).reshape(-1, 1) + + + cmp = UnsignedCompareGTE(a=a, b=b) + o = StringIO() + cmp.get_v_code_flat(open("tmp.verilog", "w")) + cmp.get_cgp_code_flat(o) + cmp.get_cgp_code_flat(open("tmp.cgp", "w")) + + cgp = UnsignedCGPCircuit(o.getvalue(), [N, N // 2]) + + v = cgp(av, bv) + #cmp.get_v_code_hier(open("tmp.verilog", "w")) + #print(o.getvalue()) + +# av = 0 + # bv = 5 + + + print("ret = ", v) + + expected = np.array(av >= bv).astype(int) + + print("expected = ", expected) + + #expected = np.sum(r, axis=1) + + np.testing.assert_array_equal(v, expected) \ No newline at end of file diff --git a/tests/test_popcnt.py b/tests/test_popcnt.py index 8d66628..936084e 100644 --- a/tests/test_popcnt.py +++ b/tests/test_popcnt.py @@ -1,3 +1,4 @@ +from ariths_gen.core.cgp_circuit import UnsignedCGPCircuit from ariths_gen.wire_components import ( Wire, ConstantWireValue0, @@ -47,4 +48,38 @@ def test_popcount(): np.testing.assert_array_equal(popcnt(av), expected) + + + +def test_popcount_cgp(): + """ Test unsigned adders """ + N = 7 + + for N in [3, 7, 8, 9, 16]: + a = Bus(N=N, prefix="a") + av = np.arange(2**N) + + + popcnt = UnsignedPopCount(a=a) + o = StringIO() + popcnt.get_cgp_code_flat(o) + cgp = UnsignedCGPCircuit(o.getvalue(), [N]) + v = cgp(av) + + + print(popcnt(av)) + + + # conv to binary + r = [] + a_s = av.copy() + for i in range(N): + r.append(a_s % 2) + a_s = a_s // 2 + r = np.dstack(r).reshape(-1, N) + print("r = ", r) + expected = np.sum(r, axis=1) + + np.testing.assert_array_equal(v, expected) + \ No newline at end of file diff --git a/tests/test_popcount_compare.py b/tests/test_popcount_compare.py new file mode 100644 index 0000000..3f02ede --- /dev/null +++ b/tests/test_popcount_compare.py @@ -0,0 +1,169 @@ +from ariths_gen.wire_components import ( + Wire, + ConstantWireValue0, + ConstantWireValue1, + Bus +) + +from ariths_gen.core.arithmetic_circuits import GeneralCircuit + +from ariths_gen.multi_bit_circuits.others import ( + PopCountCompare +) + +from ariths_gen.core.cgp_circuit import UnsignedCGPCircuit, SignedCGPCircuit + + +import numpy as np +import math +from io import StringIO + +from itertools import product + +def test_popcountcompare_same(): + N = 10 + + a = Bus(N=N, prefix="a") + b = Bus(N=N, prefix="b") + + test_cases = list(product(list(range(2**N)), repeat=2)) + all = np.array(test_cases) + + print(all) + av = all[:, 0] + bv = all[:, 1] + def popcnt(x): + mask = x & (2**np.arange(N)).reshape(-1, 1) + return np.sum(mask > 0, axis=0) + cnta = (popcnt(av)) + cntb = (popcnt(bv)) + print(cnta) + print(cntb) + + + + cmp = PopCountCompare(a=a, b=b) + cmp.get_v_code_hier(open("tmp.verilog", "w")) + + v = cmp(av, bv) + print("ret = ", v) + + + expected = np.array(cnta >= cntb).astype(int) + + print("expected = ", expected) + + #expected = np.sum(r, axis=1) + + np.testing.assert_array_equal(v, expected) + + +def test_popcountcompare_small(): + N = 10 + + a = Bus(N=N, prefix="a") + b = Bus(N=N // 2, prefix="b") + + test_cases = list(product(range(2**N), range(2**(N//2)))) + all = np.array(test_cases) + + print(all) + av = all[:, 0] + bv = all[:, 1] + def popcnt(x): + mask = x & (2**np.arange(N)).reshape(-1, 1) + return np.sum(mask > 0, axis=0) + cnta = (popcnt(av)) + cntb = (popcnt(bv)) + + + cmp = PopCountCompare(a=a, b=b) + cmp.get_v_code_hier(open("tmp.verilog", "w")) + + v = cmp(av, bv) + print("ret = ", v) + + + expected = np.array(cnta >= cntb).astype(int) + + print("expected = ", expected) + + #expected = np.sum(r, axis=1) + + np.testing.assert_array_equal(v, expected) + + +def test_popcountcompare_small2(): + N = 10 + + a = Bus(N=N // 2, prefix="a") + b = Bus(N=N, prefix="b") + + test_cases = list(product( range(2**(N//2)), range(2**N))) + all = np.array(test_cases) + + print(all) + av = all[:, 0] + bv = all[:, 1] + def popcnt(x): + mask = x & (2**np.arange(N)).reshape(-1, 1) + return np.sum(mask > 0, axis=0) + cnta = (popcnt(av)) + cntb = (popcnt(bv)) + + + cmp = PopCountCompare(a=a, b=b) + cmp.get_v_code_hier(open("tmp.verilog", "w")) + + v = cmp(av, bv) + print("ret = ", v) + + + expected = np.array(cnta >= cntb).astype(int) + + print("expected = ", expected) + + #expected = np.sum(r, axis=1) + + np.testing.assert_array_equal(v, expected) + + + +def test_popcountcompare_small2_cgp(): + N = 10 + + a = Bus(N=N // 2, prefix="a") + b = Bus(N=N, prefix="b") + + test_cases = list(product( range(2**(N//2)), range(2**N))) + all = np.array(test_cases) + + print(all) + av = all[:, 0] + bv = all[:, 1] + def popcnt(x): + mask = x & (2**np.arange(N)).reshape(-1, 1) + return np.sum(mask > 0, axis=0) + cnta = (popcnt(av)) + cntb = (popcnt(bv)) + + + cmp = PopCountCompare(a=a, b=b) + cmp.get_v_code_hier(open("tmp.verilog", "w")) + cmp.get_cgp_code_flat(open("tmp.cgp", "w")) + + o = StringIO() + cmp.get_cgp_code_flat(o) + cgp = UnsignedCGPCircuit(o.getvalue(), [N//2, N]) + + v = cgp(av, bv) + print("ret = ", v) + + + expected = np.array(cnta >= cntb).astype(int) + + print("expected = ", expected) + + #expected = np.sum(r, axis=1) + + np.testing.assert_array_equal(v, expected) From 128b1309a1cedc857049a4d063e82c1ca4ecd10c Mon Sep 17 00:00:00 2001 From: Vojta Mrazek Date: Fri, 5 Apr 2024 09:24:03 +0200 Subject: [PATCH 17/35] popcount fixes --- ariths_gen/multi_bit_circuits/others/popcount.py | 4 ++-- tests/test_popcnt.py | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/ariths_gen/multi_bit_circuits/others/popcount.py b/ariths_gen/multi_bit_circuits/others/popcount.py index 04fcda3..4721bce 100644 --- a/ariths_gen/multi_bit_circuits/others/popcount.py +++ b/ariths_gen/multi_bit_circuits/others/popcount.py @@ -1,7 +1,7 @@ """ """ -from typing import Union +from typing import Union, Optional from ariths_gen.wire_components import ( Wire, @@ -45,7 +45,7 @@ class UnsignedPopCount(GeneralCircuit): """ - def __init__(self, a: Bus, adder : Union[ArithmeticCircuit, None] = None, prefix : str = "", name : str = "popcnt", **kwargs): + def __init__(self, a: Bus, adder : Optional[ArithmeticCircuit] = None, prefix : str = "", name : str = "popcnt", **kwargs): self.N = a.N self.a = a diff --git a/tests/test_popcnt.py b/tests/test_popcnt.py index 52de63f..a877254 100644 --- a/tests/test_popcnt.py +++ b/tests/test_popcnt.py @@ -1,3 +1,4 @@ +from io import StringIO from ariths_gen.core.cgp_circuit import UnsignedCGPCircuit from ariths_gen.wire_components import ( Wire, @@ -11,6 +12,7 @@ from ariths_gen.core.arithmetic_circuits import GeneralCircuit from ariths_gen.multi_bit_circuits.others import ( UnsignedPopCount ) +import numpy as np def test_popcount(): From 77724ad115800497e1934ef9c9494dae6b6de0e7 Mon Sep 17 00:00:00 2001 From: Vojta Mrazek Date: Fri, 5 Apr 2024 11:21:42 +0200 Subject: [PATCH 18/35] workflow update --- .github/workflows/generate.yml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/generate.yml b/.github/workflows/generate.yml index fd8347c..0b9cde1 100644 --- a/.github/workflows/generate.yml +++ b/.github/workflows/generate.yml @@ -17,7 +17,7 @@ jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up Python 3.x uses: actions/setup-python@v4 with: @@ -44,11 +44,11 @@ jobs: runs-on: ubuntu-latest needs: build steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Install iverilog run: sudo apt install iverilog - name: Set up Python 3.x - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: # Semantic version range syntax or exact version of a Python version python-version: '3.x' @@ -90,9 +90,9 @@ jobs: python-version: [ '3.7', '3.8', '3.9', '3.10', '3.11' ] name: Python ${{ matrix.python-version }} test steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup python - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} architecture: x64 @@ -107,9 +107,9 @@ jobs: needs: test if: github.ref == 'refs/heads/main' steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up Python 3.x - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: # Semantic version range syntax or exact version of a Python version python-version: '3.x' @@ -123,7 +123,7 @@ jobs: - name: Generate documentation run: pdoc --html ariths_gen - name: Upload results - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: documentation path: html From 84a41ad93c5b62e9bf240b52211645ef34e18930 Mon Sep 17 00:00:00 2001 From: Vojta Mrazek Date: Fri, 5 Apr 2024 11:25:37 +0200 Subject: [PATCH 19/35] test unique #21 --- tests/test_all.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/tests/test_all.py b/tests/test_all.py index bcd9342..5a22005 100644 --- a/tests/test_all.py +++ b/tests/test_all.py @@ -381,6 +381,25 @@ def test_wire_as_bus(): assert r.sum() == 1 assert r[-1, -1, -1] == 1 +def test_unique(): + from ariths_gen.wire_components import Wire, Bus + import pytest + import sys + from ariths_gen.one_bit_circuits.logic_gates import AndGate + from ariths_gen.one_bit_circuits.one_bit_components import TwoOneMultiplexer + from ariths_gen.core.arithmetic_circuits import GeneralCircuit + + class test_circuit(GeneralCircuit): + def __init__(self, a: Bus, prefix="test_circuit", **kwargs): + super().__init__(prefix=prefix, name="test_circuit", inputs=[a], out_N=1, **kwargs) + g = self.add_component(AndGate(a[0], a[1], prefix="g2")) + g2 = self.add_component(AndGate(g.out, a[2], prefix="g2")) + g3 = self.add_component(AndGate(g2.out, g.out, prefix="g2")) + self.out[0] = g3.out + + with pytest.raises(AssertionError): + circ = test_circuit(Bus("a", 3), "c1") + circ.get_v_code_flat(file_object=sys.stdout) if __name__ == "__main__": test_unsigned_approxmul() From 0180735dd5de522843378b5b0cc9aca4dc8f522d Mon Sep 17 00:00:00 2001 From: Vojta Mrazek Date: Fri, 5 Apr 2024 11:27:41 +0200 Subject: [PATCH 20/35] workflow to node.js 20 --- .github/workflows/generate.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/generate.yml b/.github/workflows/generate.yml index 0b9cde1..e1b6530 100644 --- a/.github/workflows/generate.yml +++ b/.github/workflows/generate.yml @@ -19,7 +19,7 @@ jobs: steps: - uses: actions/checkout@v4 - name: Set up Python 3.x - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: # Semantic version range syntax or exact version of a Python version python-version: '3.x' From 4e331f05255ecf43fd16ceaa07a11fb6cfe0f55e Mon Sep 17 00:00:00 2001 From: Vojta Mrazek Date: Mon, 8 Apr 2024 13:48:25 +0200 Subject: [PATCH 21/35] popcount with variable sizes --- ariths_gen/multi_bit_circuits/others/popcount_compare.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ariths_gen/multi_bit_circuits/others/popcount_compare.py b/ariths_gen/multi_bit_circuits/others/popcount_compare.py index 1f2095c..3f26875 100644 --- a/ariths_gen/multi_bit_circuits/others/popcount_compare.py +++ b/ariths_gen/multi_bit_circuits/others/popcount_compare.py @@ -73,9 +73,9 @@ class PopCountCompare(GeneralCircuit): prefix=f"{prefix}_popcount2", inner_component=True)).out - N = max(p1.N, p2.N) - p1.bus_extend(N) - p2.bus_extend(N) + #N = max(p1.N, p2.N) + #p1.bus_extend(N) + #p2.bus_extend(N) red = self.add_component(UnsignedCompareGTE(p1, p2, prefix=f"{prefix}_cmp", inner_component = True)) self.out.connect_bus(red.out) From 739d5fafcef4c9c8ab9eb9b351fc6a61589b0485 Mon Sep 17 00:00:00 2001 From: honzastor Date: Mon, 8 Apr 2024 21:37:34 +0200 Subject: [PATCH 22/35] Added documentation to Recursive multiplier and hopefully fixed some issues with popcount output generation. --- .../arithmetic_circuits/general_circuit.py | 31 +++-- .../recursive_multiplier.py | 112 +++++++++++++++++- .../multi_bit_circuits/others/__init__.py | 13 +- .../multi_bit_circuits/others/bit_reduce.py | 6 +- .../multi_bit_circuits/others/compare.py | 8 +- .../multi_bit_circuits/others/popcount.py | 6 +- .../others/popcount_compare.py | 2 +- tests/test_popcnt.py | 6 + tests/test_popcount_compare.py | 6 + 9 files changed, 167 insertions(+), 23 deletions(-) diff --git a/ariths_gen/core/arithmetic_circuits/general_circuit.py b/ariths_gen/core/arithmetic_circuits/general_circuit.py index c6571a1..bdf9df8 100644 --- a/ariths_gen/core/arithmetic_circuits/general_circuit.py +++ b/ariths_gen/core/arithmetic_circuits/general_circuit.py @@ -54,8 +54,9 @@ class GeneralCircuit(): self.out = Bus(outname, out_N, out_bus=True, signed=signed) self.components = [] - self.circuit_wires = [] + self._prefixes = [] # TODO rename to fullname and add distinct attr for prefix, name, suffix self.circuit_gates = [] + self.circuit_wires = [] self.signed = signed self.c_data_type = "int64_t" if self.signed is True else "uint64_t" self.pyc = None # Python compiled function @@ -87,12 +88,26 @@ class GeneralCircuit(): def add_component(self, component): """Adds a component into list of circuit's inner subcomponents. + + Additionally it adds all the gates of the component to the circuit's list of gates and all + sbcomponents prefixes to check for naming duplicates which could cause issues in the circuit generation. Args: component: Subcomponent to be added into list of components composing described circuit. """ - prefixes = [c.prefix for c in self.components] - assert component.prefix not in prefixes, f"Component with prefix {component.prefix} already exists in the circuit." + # TODO will be redone in ArithsGen rework + # We should probably check also wire names for especially hierarchical generation + if isinstance(component, TwoInputLogicGate): + if component.disable_generation is False: + self.circuit_gates.append(component) + else: + self.circuit_gates.extend(component.get_circuit_gates()) + for prefix in component._prefixes: + assert prefix not in self._prefixes, f"Component with prefix {prefix} already exists in the circuit." + self._prefixes.extend(component._prefixes) + + assert component.prefix not in self._prefixes, f"Component with prefix {component.prefix} already exists in the circuit." + self._prefixes.append(component.prefix) self.components.append(component) return component @@ -120,7 +135,7 @@ class GeneralCircuit(): return sum(isinstance(c, cls) for c in self.components if isinstance(c, cls) and c.disable_generation is False) else: return sum(isinstance(c, cls) for c in self.components) - + def get_circuit_gates(self, verilog_output: bool = False): """Gets a list of all the logic gates in circuit that should be generated. @@ -227,14 +242,13 @@ class GeneralCircuit(): else: return len(self.circuit_wires)+2 - def get_cgp_wires(self): + def get_circuit_wires(self): """Gets a list of all wires in circuit along with their index position for cgp chromosome generation and stores them inside `self.circuit_wires` list. Constant wire with value 0 has constant index of 0. Constant wire with value 1 has constant index of 1. Other wires indexes start counting from 2 and up. """ - self.circuit_wires = [] circuit_wires_names = [] for input in self.inputs: @@ -262,6 +276,7 @@ class GeneralCircuit(): self.circuit_wires.append( (gate.out, gate.out.name, self.save_wire_id(wire=gate.out))) circuit_wires_names.append(gate.out.name) + def get_circuit_wire_index(self, wire: Wire): """Searches for circuit's wire unique index position within the circuit. Used for cgp chromosome generation. @@ -773,7 +788,7 @@ class GeneralCircuit(): Returns: str: CGP chromosome parameters of described arithmetic circuit. """ - self.circuit_gates = self.get_circuit_gates() + # self.circuit_gates = self.get_circuit_gates() TODO delete return f"{{{sum(input_bus.N for input_bus in self.inputs)},{self.out.N},1,{len(self.circuit_gates)},2,1,0}}" def get_triplets_cgp(self): @@ -789,7 +804,7 @@ class GeneralCircuit(): Returns: str: List of triplets each describing logic function of corresponding two input logic gate and as a whole describe the arithmetic circuit. """ - self.get_cgp_wires() + self.get_circuit_wires() return "".join([g.get_triplet_cgp(a_id=self.get_circuit_wire_index(g.a), out_id=self.get_circuit_wire_index(g.out)) if isinstance(g, OneInputLogicGate) else g.get_triplet_cgp(a_id=self.get_circuit_wire_index(g.a), b_id=self.get_circuit_wire_index(g.b), out_id=self.get_circuit_wire_index(g.out)) for g in self.circuit_gates]) diff --git a/ariths_gen/multi_bit_circuits/approximate_multipliers/recursive_multiplier.py b/ariths_gen/multi_bit_circuits/approximate_multipliers/recursive_multiplier.py index a985e91..b5f67dc 100644 --- a/ariths_gen/multi_bit_circuits/approximate_multipliers/recursive_multiplier.py +++ b/ariths_gen/multi_bit_circuits/approximate_multipliers/recursive_multiplier.py @@ -22,6 +22,28 @@ import math class UnsignedAccurateTwoBitMultiplier(MultiplierCircuit): """Class representing unsigned two-bit accurate multiplier. + ``` + A1B1 A1B0 A0B1 A0B0 + β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ + β”Œβ–Όβ”€β–Όβ” β”Œβ–Όβ”€β–Όβ” β”Œβ–Όβ”€β–Όβ” β”Œβ–Όβ”€β–Όβ” + β”‚ANDβ”‚ β”‚ANDβ”‚ β”‚ANDβ”‚ β”‚ANDβ”‚ + β””β”€β”¬β”€β”˜ β””β”€β”¬β”€β”˜ β””β”€β”¬β”€β”˜ β””β”€β”¬β”€β”˜ + β”‚ └──┬──┐└┬─┐ β”‚ + β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ + β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ + β”‚ β”‚ β”Œβ–Όβ”€β–Όβ”β”‚ β”‚ + β”‚ β”‚ β”‚ANDβ”‚β”‚ β”‚ + β”‚ β”‚ β””β”€β”¬β”€β”˜β”‚ β”‚ + └─────┐ β”Œβ”€β”Όβ”€β”€β”€β”˜β”Œβ”€β”˜ β”‚ + β”‚ β”‚ └──┐ β”‚ β”‚ + β”Œβ—„β”€β–Ίβ” β”Œβ–Όβ”€β–Όβ” β”Œβ–Όβ”€β–Όβ” β”‚ + β”‚ANDβ”‚ β”‚XORβ”‚ β”‚XORβ”‚ β”‚ + β””β”€β”¬β”€β”˜ β””β”€β”¬β”€β”˜ β””β”€β”¬β”€β”˜ β”‚ + β”‚ β”‚ β”‚ β”‚ + β–Ό β–Ό β–Ό β–Ό + O3 O2 O1 O0 + ``` + Description of the __init__ method. Args: @@ -76,6 +98,22 @@ class UnsignedApproximateTwoBitMultiplierM1(MultiplierCircuit): """Class representing unsigned two-bit approximate multiplier variant M1. M1 ax variant defined here: https://ieeexplore.ieee.org/document/8727537 + + ``` + A1B1 A1B0 A0B1 A0B0 + β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ + β”Œβ–Όβ”€β–Όβ” β”Œβ–Όβ”€β–Όβ” β”Œβ–Όβ”€β–Όβ” β”Œβ–Όβ”€β–Όβ” + β”‚ANDβ”‚ β”‚ANDβ”‚ β”‚ANDβ”‚ β”‚ANDβ”‚ + β””β”€β”¬β”€β”˜ β””β”€β”¬β”€β”˜ β””β”€β”¬β”€β”˜ β””β”€β”¬β”€β”˜ + β”‚ β”‚ └┐ β”‚ + β”‚ └─────┐ β”‚ β”‚ + └──────┐ β”Œβ–Όβ”€β–Όβ” β”‚ + β”‚ β”‚ ORβ”‚ β”‚ + β”‚ β””β”€β”¬β”€β”˜ β”‚ + β”‚ β”‚ β”‚ + β–Ό β–Ό β–Ό β–Ό + O3=0 O2 O1 O0 + ``` Description of the __init__ method. @@ -127,6 +165,29 @@ class UnsignedApproximateTwoBitMultiplierM2(MultiplierCircuit): """Class representing unsigned two-bit approximate multiplier variant M2. M2 ax variant defined here: https://ieeexplore.ieee.org/document/8727537 + + ``` + A1B1 A1B0 A0B1 + β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ + β”Œβ–Όβ”€β–Όβ” β”Œβ–Όβ”€β–Όβ” β”Œβ–Όβ”€β–Όβ” + β”‚ANDβ”‚ β”‚ANDβ”‚ β”‚ANDβ”‚ + β””β”€β”¬β”€β”˜ β””β”€β”¬β”€β”˜ β””β”€β”¬β”€β”˜ + β”‚ β”‚β”Œβ”€β”€β”€β”€β”€β”΄β” + β”‚ β”Œβ”΄β”Όβ”€β”€β”€β”€β” β”‚ + β”‚ β”‚ β”‚ β”‚ β”‚ + β”‚ β”Œβ–Όβ”€β–Όβ” β”Œβ–Όβ”€β–Όβ” + β”‚ β”‚XORβ”‚ β”‚ANDβ”‚ + β”‚ β””β”€β”¬β”€β”˜ β””β”€β”¬β”€β”˜ + └─────┐ β”Œβ”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€ + β”‚ β”‚ β”‚ β”‚ + β”Œβ–Όβ”€β–Όβ” β”‚ β”‚ + β”‚XORβ”‚ β”‚ β”‚ + β””β”€β”¬β”€β”˜ β”‚ β”‚ + β”Œβ”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€ + β”‚ β”‚ β”‚ β”‚ + β–Ό β–Ό β–Ό β–Ό + O3 O2 O1 O0 + ``` Description of the __init__ method. @@ -180,6 +241,33 @@ class UnsignedApproximateTwoBitMultiplierM3(MultiplierCircuit): """Class representing unsigned two-bit approximate multiplier variant M3. M3 ax variant defined here: https://ieeexplore.ieee.org/document/8727537 + + ``` + A1B1 A1B0 A0B1 A0B0 + β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ + β”Œβ–Όβ”€β–Όβ” β”Œβ–Όβ”€β–Όβ” β”Œβ–Όβ”€β–Όβ” β”Œβ–Όβ”€β–Όβ” + β”‚ANDβ”‚ β”‚ANDβ”‚ β”‚ANDβ”‚ β”‚ANDβ”‚ + β””β”€β”¬β”€β”˜ β””β”€β”¬β”€β”˜ β””β”€β”¬β”€β”˜ β””β”€β”¬β”€β”˜ + β”‚ β”‚ └┐ β”‚ + β”‚ β”‚ β”‚ β”‚ + β”‚ └─────┐ β”‚ β”‚ + β”‚ β”Œβ–Όβ”€β–Όβ” β”‚ + β”‚ β”‚ ORβ”‚ β”‚ + β”‚ β””β”€β”¬β”€β”˜ β”‚ + β”‚β”Œβ”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€ + β”‚β”‚ β”‚ β”‚ β”‚ + β”‚β”‚ β”Œβ”€β–Όβ”€β” β”‚ β”‚ + β”‚β”‚ β”‚NOTβ”‚ β”‚ β”‚ + β”‚β”‚ β””β”€β”¬β”€β”˜ β”‚ β”‚ + β”Œβ”΄β”Όβ”€β”€β”€β”€β”β””β” β”‚ β”‚ + β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ + β”Œβ–Όβ”€β–Όβ” β”Œβ–Όβ”€β–Όβ” β”‚ β”‚ + β”‚ANDβ”‚ β”‚ANDβ”‚ β”‚ β”‚ + β””β”€β”¬β”€β”˜ β””β”€β”¬β”€β”˜ β”‚ β”‚ + β”‚ β”‚ β”‚ β”‚ + β–Ό β–Ό β–Ό β–Ό + O3 O2 O1 O0 + ``` Description of the __init__ method. @@ -236,6 +324,22 @@ class UnsignedApproximateTwoBitMultiplierM4(MultiplierCircuit): M4 ax variant defined here: https://ieeexplore.ieee.org/document/8727537 + ``` + A1B1 A1B0 A0B1 A0B0 + β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ + β”Œβ–Όβ”€β–Όβ” β”Œβ–Όβ”€β–Όβ” β”Œβ–Όβ”€β–Όβ” β”Œβ–Όβ”€β–Όβ” + β”‚ANDβ”‚ β”‚ANDβ”‚ β”‚ANDβ”‚ β”‚ANDβ”‚ + β””β”€β”¬β”€β”˜ β””β”€β”¬β”€β”˜ β””β”€β”¬β”€β”˜ β””β”€β”¬β”€β”˜ + β”‚ β”‚ └┐ β”‚ + β”‚ └─────┐ β”‚ β”‚ + └──────┐ β”Œβ–Όβ”€β–Όβ” β”‚ + β”‚ β”‚XORβ”‚ β”‚ + β”‚ β””β”€β”¬β”€β”˜ β”‚ + β”‚ β”‚ β”‚ + β–Ό β–Ό β–Ό β–Ό + O3=0 O2 O1 O0 + ``` + Description of the __init__ method. Args: @@ -285,7 +389,13 @@ class SignedApproximateTwoBitMultiplierM4(MultiplierCircuit): class UnsignedRecursiveMultiplier(MultiplierCircuit): """Class representing unsigned recursive multiplier. - TODO + Input bit-vector length N can be any power of two greater than 1 (e.g. 2, 4, 8, ...). + + The internal structure of the recursive multiplier is composed of subsequent two-bit submultipliers provided in the input `submultipliers` list. + The `submultipliers` list should contain the classes of the two-bit submultipliers that will be used for instantiation. If None are provided, accurate two-bit submultipliers are assumed. + + The number of submultipliers required is equal to (N/2)Β² for N > 2. For N = 2, only one two-bit submultiplier is required. + Description of the __init__ method. Args: diff --git a/ariths_gen/multi_bit_circuits/others/__init__.py b/ariths_gen/multi_bit_circuits/others/__init__.py index de090f3..ecbb4e8 100644 --- a/ariths_gen/multi_bit_circuits/others/__init__.py +++ b/ariths_gen/multi_bit_circuits/others/__init__.py @@ -4,11 +4,18 @@ from ariths_gen.multi_bit_circuits.others.popcount import ( ) from ariths_gen.multi_bit_circuits.others.bit_reduce import ( - BitReduce, AndReduce, OrReduce + BitReduce, + AndReduce, + OrReduce ) from ariths_gen.multi_bit_circuits.others.compare import ( - UnsignedCompareLT, UnsignedCompareLTE, UnsignedCompareGT, UnsignedCompareGTE + UnsignedCompareLT, + UnsignedCompareLTE, + UnsignedCompareGT, + UnsignedCompareGTE ) -from ariths_gen.multi_bit_circuits.others.popcount_compare import PopCountCompare \ No newline at end of file +from ariths_gen.multi_bit_circuits.others.popcount_compare import ( + PopCountCompare +) diff --git a/ariths_gen/multi_bit_circuits/others/bit_reduce.py b/ariths_gen/multi_bit_circuits/others/bit_reduce.py index bd0cd8b..9752c67 100644 --- a/ariths_gen/multi_bit_circuits/others/bit_reduce.py +++ b/ariths_gen/multi_bit_circuits/others/bit_reduce.py @@ -80,10 +80,10 @@ class BitReduce(GeneralCircuit): class OrReduce(BitReduce): - def __init__(self, a: Bus, prefix : str = "", name : str = "orreduce", **kwargs): + def __init__(self, a: Bus, prefix: str = "", name: str = "orreduce", **kwargs): super().__init__(a=a, gate=OrGate, prefix=prefix, name=name, **kwargs) class AndReduce(BitReduce): - def __init__(self, a: Bus, prefix : str = "", name : str = "orreduce", **kwargs): - super().__init__(a=a, gate=AndGate, prefix=prefix, name=name, **kwargs) \ No newline at end of file + def __init__(self, a: Bus, prefix: str = "", name: str = "andreduce", **kwargs): + super().__init__(a=a, gate=AndGate, prefix=prefix, name=name, **kwargs) diff --git a/ariths_gen/multi_bit_circuits/others/compare.py b/ariths_gen/multi_bit_circuits/others/compare.py index 6e92ce1..ab89fd1 100644 --- a/ariths_gen/multi_bit_circuits/others/compare.py +++ b/ariths_gen/multi_bit_circuits/others/compare.py @@ -83,7 +83,7 @@ class UnsignedCompareLTE(GeneralCircuit): Returns true if a <= b """ - def __init__(self, a: Bus, b: Bus, prefix : str = "", name : str = "cmp_lt", **kwargs): + def __init__(self, a: Bus, b: Bus, prefix : str = "", name : str = "cmp_lte", **kwargs): self.a = a self.b = b self.N = max(a.N, b.N) @@ -177,7 +177,7 @@ class UnsignedCompareGTE(GeneralCircuit): for i in reversed(range(self.N)): iA = self.a[i] if i < self.a.N else ConstantWireValue0() iB = self.b[i] if i < self.b.N else ConstantWireValue0() - + i1 = iA i2 = self.add_component(NotGate(iB, f"{self.prefix}_i1_{i}", parent_component=self)).out @@ -188,7 +188,7 @@ class UnsignedCompareGTE(GeneralCircuit): psum = self.add_component(AndGate(pi, psum, f"{self.prefix}_psum_{i}", parent_component=self)).out res[self.N] = psum # or all equal (xor) - + red = self.add_component(OrReduce(res, prefix=f"{self.prefix}_orred", inner_component=True)) - self.out.connect_bus(red.out) \ No newline at end of file + self.out.connect_bus(red.out) diff --git a/ariths_gen/multi_bit_circuits/others/popcount.py b/ariths_gen/multi_bit_circuits/others/popcount.py index 4721bce..16fd141 100644 --- a/ariths_gen/multi_bit_circuits/others/popcount.py +++ b/ariths_gen/multi_bit_circuits/others/popcount.py @@ -68,8 +68,8 @@ class UnsignedPopCount(GeneralCircuit): return a else: half = a.N // 2 - b_in = Bus(N=half, prefix=f"b_inn{depth}A") - c_in = Bus(N=a.N - half, prefix=f"b_inn{depth}B") + b_in = Bus(N=half, prefix=f"b_inn_{branch}_{depth}A") + c_in = Bus(N=a.N - half, prefix=f"b_inn_{branch}_{depth}B") #print(a, half, a.N) @@ -87,4 +87,4 @@ class UnsignedPopCount(GeneralCircuit): sumbus = create_tree(self.a,0, "X") #print(sumbus) - self.out.connect_bus(sumbus ) \ No newline at end of file + self.out.connect_bus(sumbus) diff --git a/ariths_gen/multi_bit_circuits/others/popcount_compare.py b/ariths_gen/multi_bit_circuits/others/popcount_compare.py index 3f26875..6b8570d 100644 --- a/ariths_gen/multi_bit_circuits/others/popcount_compare.py +++ b/ariths_gen/multi_bit_circuits/others/popcount_compare.py @@ -59,7 +59,7 @@ class PopCountCompare(GeneralCircuit): β–Ό """ - def __init__(self, a: Bus, b: Bus, prefix : str = "", name : str = "cmp_lt", **kwargs): + def __init__(self, a: Bus, b: Bus, prefix : str = "", name : str = "popcnt_cmp", **kwargs): self.a = a self.b = b diff --git a/tests/test_popcnt.py b/tests/test_popcnt.py index a877254..6676d8d 100644 --- a/tests/test_popcnt.py +++ b/tests/test_popcnt.py @@ -1,3 +1,9 @@ +import os +import sys +# Add the parent directory to the system path +DIR_PATH = os.path.dirname(os.path.abspath(__file__)) +sys.path.insert(0, os.path.join(DIR_PATH, '..')) + from io import StringIO from ariths_gen.core.cgp_circuit import UnsignedCGPCircuit from ariths_gen.wire_components import ( diff --git a/tests/test_popcount_compare.py b/tests/test_popcount_compare.py index 3f02ede..9f30875 100644 --- a/tests/test_popcount_compare.py +++ b/tests/test_popcount_compare.py @@ -1,3 +1,9 @@ +import os +import sys +# Add the parent directory to the system path +DIR_PATH = os.path.dirname(os.path.abspath(__file__)) +sys.path.insert(0, os.path.join(DIR_PATH, '..')) + from ariths_gen.wire_components import ( Wire, ConstantWireValue0, From 97e79b93da68f22518d43ad99531859f13285254 Mon Sep 17 00:00:00 2001 From: honzastor Date: Sat, 13 Apr 2024 17:04:03 +0200 Subject: [PATCH 23/35] Big code cleanup and some fixes. Hierarchical generation for popcount seems problematic. It seems bus connections are the issue. --- .../core/arithmetic_circuits/__init__.py | 7 +- .../arithmetic_circuits/arithmetic_circuit.py | 247 ------------------ .../arithmetic_circuits/general_circuit.py | 186 +++++++------ .../arithmetic_circuits/multiplier_circuit.py | 38 +-- ariths_gen/core/cgp_circuit.py | 9 - .../logic_gate_circuits/logic_gate_circuit.py | 2 +- .../four_input_one_bit_circuit.py | 13 +- .../three_input_one_bit_circuit.py | 12 +- .../two_input_one_bit_circuit.py | 9 +- .../multi_bit_circuits/adders/__init__.py | 2 +- .../adders/brent_kung_adder.py | 29 +- .../adders/carry_increment_adder.py | 23 +- .../adders/carry_lookahead_adder.py | 20 +- .../adders/carry_save_adder.py | 31 +-- .../adders/carry_select_adder.py | 23 +- .../adders/carry_skip_adder.py | 21 +- .../adders/conditional_sum_adder.py | 25 +- .../adders/han_carlson_adder.py | 29 +- .../adders/knowles_adder.py | 29 +- .../adders/kogge_stone_adder.py | 29 +- .../adders/ladner_fischer_adder.py | 29 +- .../adders/pg_ripple_carry_adder.py | 19 +- .../adders/ripple_carry_adder.py | 20 +- .../adders/sklansky_adder.py | 29 +- .../approximate_adders/__init__.py | 2 +- .../approximate_adders/quad.py | 29 +- .../broken_array_multiplier.py | 17 +- .../broken_carry_save_multiplier.py | 14 +- .../recursive_multiplier.py | 34 ++- .../truncated_array_multiplier.py | 13 +- .../truncated_carry_save_multiplier.py | 11 +- .../dividers/array_divider.py | 17 +- .../multipliers/array_multiplier.py | 11 +- .../multipliers/carry_save_multiplier.py | 12 +- .../multipliers/dadda_multiplier.py | 18 +- .../multipliers/wallace_multiplier.py | 18 +- .../multi_bit_circuits/others/bit_reduce.py | 44 +--- .../multi_bit_circuits/others/compare.py | 57 +--- .../multi_bit_circuits/others/popcount.py | 54 +--- .../others/popcount_compare.py | 59 +---- ariths_gen/wire_components/wires.py | 2 +- generate_mac.py | 3 +- tests/test_ax.py | 7 +- 43 files changed, 307 insertions(+), 996 deletions(-) delete mode 100644 ariths_gen/core/arithmetic_circuits/arithmetic_circuit.py diff --git a/ariths_gen/core/arithmetic_circuits/__init__.py b/ariths_gen/core/arithmetic_circuits/__init__.py index 1119134..c30a209 100644 --- a/ariths_gen/core/arithmetic_circuits/__init__.py +++ b/ariths_gen/core/arithmetic_circuits/__init__.py @@ -1,10 +1,7 @@ -from .arithmetic_circuit import ( - ArithmeticCircuit, - ThreeInputArithmeticCircuit +from .general_circuit import ( + GeneralCircuit ) -from .general_circuit import (GeneralCircuit) - from .multiplier_circuit import ( MultiplierCircuit ) diff --git a/ariths_gen/core/arithmetic_circuits/arithmetic_circuit.py b/ariths_gen/core/arithmetic_circuits/arithmetic_circuit.py deleted file mode 100644 index 608550f..0000000 --- a/ariths_gen/core/arithmetic_circuits/arithmetic_circuit.py +++ /dev/null @@ -1,247 +0,0 @@ -from ariths_gen.core.logic_gate_circuits.logic_gate_circuit import OneInputLogicGate, TwoInputLogicGate - -from ariths_gen.wire_components import ( - Wire, - ConstantWireValue0, - ConstantWireValue1, - Bus -) - -from ariths_gen.core.arithmetic_circuits.general_circuit import GeneralCircuit - - -class ArithmeticCircuit(GeneralCircuit): - """Class represents a general arithmetic circuit and ensures its generation to various representations. - - The __init__ method fills some mandatory attributes concerning arithmetic circuit - that are later used for generation into various representations. - """ - - def __init__(self, a, b, prefix: str, name: str, out_N: int, inner_component: bool = False, one_bit_circuit: bool = False, signed: bool = False, **kwargs): - super().__init__(prefix, name, out_N, inner_component, inputs=[a, b], signed=signed, one_bit_circuit=one_bit_circuit, **kwargs) - - """ C CODE GENERATION """ - def get_prototype_c(self): - """Generates C code function header to describe corresponding arithmetic circuit's interface in C code. - - Returns: - str: Function's name and parameters in C code. - """ - return f"{self.c_data_type} {self.prefix}({self.c_data_type} {self.a.prefix}, {self.c_data_type} {self.b.prefix})" + "{" + "\n" - - """ VERILOG CODE GENERATION """ - def get_prototype_v(self): - """Generates Verilog code module header to describe corresponding arithmetic circuit's interface in Verilog code. - - Returns: - str: Module's name and parameters in Verilog code. - """ - return f"module {self.prefix}(input [{self.N-1}:0] {self.a.prefix}, input [{self.N-1}:0] {self.b.prefix}, output [{self.out.N-1}:0] {self.out.prefix});\n" - - """ BLIF CODE GENERATION """ - def get_declaration_blif(self): - """Generates flat Blif code declaration of input/output circuit wires. - - Returns: - str: Flat Blif code containing declaration of circuit's wires. - """ - if self.N == 1: - return f".inputs {self.a.prefix} {self.b.prefix}\n" + \ - f".outputs{self.out.get_wire_declaration_blif()}\n" + \ - f".names vdd\n1\n" + \ - f".names gnd\n0\n" - else: - return f".inputs{self.a.get_wire_declaration_blif()}{self.b.get_wire_declaration_blif()}\n" + \ - f".outputs{self.out.get_wire_declaration_blif()}\n" + \ - f".names vdd\n1\n" + \ - f".names gnd\n0\n" - - -class ThreeInputArithmeticCircuit(GeneralCircuit): - """Class represents a general three input arithmetic circuit and ensures its generation to various representations. - - The __init__ method fills some mandatory attributes concerning arithmetic circuit - that are later used for generation into various representations. - """ - - def __init__(self, a, b, c, prefix: str, name: str, out_N: int, inner_component: bool = False, one_bit_circuit: bool = False, signed: bool = False, **kwargs): - super().__init__(prefix, name, out_N, inner_component, inputs=[a, b, c], signed=signed, **kwargs) - if one_bit_circuit is False: - if prefix == "": - self.prefix = name - else: - self.prefix = prefix + "_" + name - - self.inner_component = inner_component - if self.inner_component is True: - self.a = Bus(prefix=f"{self.prefix}_{a.prefix}", wires_list=a.bus) - self.b = Bus(prefix=f"{self.prefix}_{b.prefix}", wires_list=b.bus) - self.c = Bus(prefix=f"{self.prefix}_{c.prefix}", wires_list=c.bus) - - if a.is_output_bus(): - self.a.connect_bus(connecting_bus=a) - if b.is_output_bus(): - self.b.connect_bus(connecting_bus=b) - if c.is_output_bus(): - self.c.connect_bus(connecting_bus=c) - else: - self.a = Bus(prefix=f"{a.prefix}", wires_list=a.bus) - self.b = Bus(prefix=f"{b.prefix}", wires_list=b.bus) - self.c = Bus(prefix=f"{c.prefix}", wires_list=c.bus) - - """ C CODE GENERATION """ - def get_prototype_c(self): - """Generates C code function header to describe corresponding arithmetic circuit's interface in C code. - - Returns: - str: Function's name and parameters in C code. - """ - return f"{self.c_data_type} {self.prefix}({self.c_data_type} {self.a.prefix}, {self.c_data_type} {self.b.prefix}, {self.c_data_type} {self.c.prefix})" + "{" + "\n" - - def get_function_block_c(self): - """Generates hierarchical C code representation of corresponding multi-bit arithmetic circuit used as function block in hierarchical circuit description. - - Returns: - str: Hierarchical C code of multi-bit arithmetic circuit's function block description. - """ - # Obtain proper circuit name with its bit width - circuit_prefix = self.__class__( - a=Bus("a"), b=Bus("b"), c=Bus("c")).prefix + str(self.N) - circuit_block = self.__class__(a=Bus(N=self.N, prefix="a"), b=Bus( - N=self.N, prefix="b"), c=Bus( - N=self.N, prefix="c"), name=circuit_prefix, **self._parent_kwargs) - return f"{circuit_block.get_circuit_c()}\n\n" - - def get_declaration_c_hier(self): - """Generates hierarchical C code declaration of corresponding subcomponent input/output wires inside the upper component. - - Generates wires used to connect input/output values to/from invocation of the corresponding function block into inner wires present - inside the upper component from which function block has been invoked. - - Returns: - str: Hierarchical C code of subcomponent arithmetic circuit's wires declaration. - """ - return f" {self.c_data_type} {self.a.prefix} = 0;\n" + \ - f" {self.c_data_type} {self.b.prefix} = 0;\n" + \ - f" {self.c_data_type} {self.c.prefix} = 0;\n" + \ - f" {self.c_data_type} {self.out.prefix} = 0;\n" - - def get_out_invocation_c(self): - """Generates hierarchical C code invocation of corresponding arithmetic circuit's generated function block. - - Assigns input values from other subcomponents into multi-bit input buses used as inputs for function block invocation. - Assigns output values from invocation of the corresponding function block into inner wires present inside - the upper component from which function block has been invoked. - - Returns: - str: Hierarchical C code of subcomponent's C function invocation and output assignment. - """ - # Getting name of circuit type for proper C code generation without affecting actual generated composition - circuit_type = self.__class__(a=Bus("a"), b=Bus("b"), c=Bus("c")).prefix + str(self.N) - return self.a.return_bus_wires_values_c_hier() + self.b.return_bus_wires_values_c_hier() + self.c.return_bus_wires_values_c_hier() + \ - f" {self.out.prefix} = {circuit_type}({self.a.prefix}, {self.b.prefix}, {self.c.prefix});\n" - - """ VERILOG CODE GENERATION """ - def get_prototype_v(self): - """Generates Verilog code module header to describe corresponding arithmetic circuit's interface in Verilog code. - - Returns: - str: Module's name and parameters in Verilog code. - """ - return f"module {self.prefix}(input [{self.N-1}:0] {self.a.prefix}, input [{self.N-1}:0] {self.b.prefix}, input [{self.N-1}:0] {self.c.prefix}, output [{self.out.N-1}:0] {self.out.prefix});\n" - - def get_function_block_v(self): - """Generates hierarchical Verilog code representation of corresponding multi-bit arithmetic circuit used as function block in hierarchical circuit description. - - Returns: - str: Hierarchical Verilog code of multi-bit arithmetic circuit's function block description. - """ - # Obtain proper circuit name with its bit width - circuit_prefix = self.__class__( - a=Bus("a"), b=Bus("b"), c=Bus("c")).prefix + str(self.N) - circuit_block = self.__class__(a=Bus(N=self.N, prefix="a"), b=Bus( - N=self.N, prefix="b"), c=Bus(N=self.N, prefix="c"), name=circuit_prefix, **self._parent_kwargs) - return f"{circuit_block.get_circuit_v()}\n\n" - - def get_declaration_v_hier(self): - """Generates hierarchical Verilog code declaration of corresponding subcomponent input/output wires inside the upper component. - - Generates wires used to connect input/output values to/from invocation of the corresponding function block into inner wires present - inside the upper component from which function block has been invoked. - - Returns: - str: Hierarchical Verilog code of subcomponent arithmetic circuit's wires declaration. - """ - return f" wire [{self.a.N-1}:0] {self.a.prefix};\n" + \ - f" wire [{self.b.N-1}:0] {self.b.prefix};\n" + \ - f" wire [{self.c.N-1}:0] {self.c.prefix};\n" + \ - f" wire [{self.out.N-1}:0] {self.out.prefix};\n" - - def get_out_invocation_v(self): - """Generates hierarchical Verilog code invocation of corresponding arithmetic circuit's generated function block. - - Assigns input values from other subcomponents into multi-bit input buses used as inputs for function block invocation. - Assigns output values from invocation of the corresponding function block into inner wires present inside - the upper component from which function block has been invoked. - - Returns: - str: Hierarchical Verilog code of subcomponent's module invocation and output assignment. - """ - # Getting name of circuit type and insitu copying out bus for proper Verilog code generation without affecting actual generated composition - circuit_type = self.__class__(a=Bus("a"), b=Bus("b"), c=Bus("c")).prefix + str(self.N) - circuit_block = self.__class__(a=Bus(N=self.N, prefix="a"), b=Bus( - N=self.N, prefix="b"), c=Bus( - N=self.N, prefix="c"), name=circuit_type) - return self.a.return_bus_wires_values_v_hier() + self.b.return_bus_wires_values_v_hier() + self.c.return_bus_wires_values_v_hier() + \ - f" {circuit_type} {circuit_type}_{self.out.prefix}(.{circuit_block.a.prefix}({self.a.prefix}), .{circuit_block.b.prefix}({self.b.prefix}), .{circuit_block.c.prefix}({self.c.prefix}), .{circuit_block.out.prefix}({self.out.prefix}));\n" - - """ BLIF CODE GENERATION """ - def get_declaration_blif(self): - """Generates flat Blif code declaration of input/output circuit wires. - - Returns: - str: Flat Blif code containing declaration of circuit's wires. - """ - if self.N == 1: - return f".inputs {self.a.prefix} {self.b.prefix} {self.c.prefix}\n" + \ - f".outputs{self.out.get_wire_declaration_blif()}\n" + \ - f".names vdd\n1\n" + \ - f".names gnd\n0\n" - else: - return f".inputs{self.a.get_wire_declaration_blif()}{self.b.get_wire_declaration_blif()}{self.c.get_wire_declaration_blif()}\n" + \ - f".outputs{self.out.get_wire_declaration_blif()}\n" + \ - f".names vdd\n1\n" + \ - f".names gnd\n0\n" - - def get_invocation_blif_hier(self): - """Generates hierarchical Blif code invocation of corresponding arithmetic circuit's generated function block. - - Used for multi-bit subcomponent's modul invocation. - - Returns: - str: Hierarchical Blif code of subcomponent's model invocation and output assignment. - """ - # Getting name of circuit type for proper Blif code generation without affecting actual generated composition - circuit_type = self.__class__(a=Bus("a"), b=Bus("b"), c=Bus("c")).prefix + str(self.N) - return f"{self.a.get_wire_assign_blif(output=True)}" + \ - f"{self.b.get_wire_assign_blif(output=True)}" + \ - f"{self.c.get_wire_assign_blif(output=True)}" + \ - f".subckt {circuit_type}" + \ - "".join([f" a[{self.a.bus.index(w)}]={self.a.prefix}[{self.a.bus.index(w)}]" for w in self.a.bus]) + \ - "".join([f" b[{self.b.bus.index(w)}]={self.b.prefix}[{self.b.bus.index(w)}]" for w in self.b.bus]) + \ - "".join([f" c[{self.c.bus.index(w)}]={self.c.prefix}[{self.c.bus.index(w)}]" for w in self.c.bus]) + \ - "".join( - [f" {circuit_type}_out[{self.out.bus.index(o)}]={o.name}" for o in self.out.bus]) + "\n" - - def get_function_block_blif(self): - """Generates hierarchical Blif code representation of corresponding multi-bit arithmetic circuit used as function block in hierarchical circuit description. - - Returns: - str: Hierarchical Blif code of multi-bit arithmetic circuit's function block description. - """ - # Obtain proper circuit name with its bit width - circuit_prefix = self.__class__( - a=Bus("a"), b=Bus("b"), c=Bus("c")).prefix + str(self.N) - circuit_block = self.__class__(a=Bus(N=self.N, prefix="a"), b=Bus( - N=self.N, prefix="b"), c=Bus(N=self.N, prefix="c"), name=circuit_prefix, **self._parent_kwargs) - return f"{circuit_block.get_circuit_blif()}" diff --git a/ariths_gen/core/arithmetic_circuits/general_circuit.py b/ariths_gen/core/arithmetic_circuits/general_circuit.py index bdf9df8..9feec93 100644 --- a/ariths_gen/core/arithmetic_circuits/general_circuit.py +++ b/ariths_gen/core/arithmetic_circuits/general_circuit.py @@ -1,13 +1,14 @@ -from typing import Dict -from ariths_gen.core.logic_gate_circuits.logic_gate_circuit import OneInputLogicGate, TwoInputLogicGate - +from ariths_gen.core.logic_gate_circuits.logic_gate_circuit import ( + OneInputLogicGate, + TwoInputLogicGate +) from ariths_gen.wire_components import ( Wire, - ConstantWireValue0, - ConstantWireValue1, Bus ) - +from typing import Dict +import inspect +import copy from io import StringIO @@ -17,7 +18,6 @@ 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): if prefix == "": self.prefix = name @@ -25,29 +25,19 @@ class GeneralCircuit(): self.prefix = prefix + "_" + name self.inner_component = inner_component - if one_bit_circuit is False: - # Dynamic input bus assignment - self.inputs = [] - input_names = "abcdefghijklmnopqrstuvwxyz" # This should be enough.. - assert len(input_names) >= len(inputs) - for i, input in enumerate(inputs): - attr_name = input_names[i] - full_prefix = f"{self.prefix}_{input.prefix}" if self.inner_component else f"{input.prefix}" - if isinstance(input, Bus): - bus = Bus(prefix=full_prefix, wires_list=input.bus) - setattr(self, attr_name, bus) - self.inputs.append(bus) - - # If the input bus is an output bus, connect it - if input.is_output_bus(): - getattr(self, attr_name).connect_bus(connecting_bus=input) - else: - wire = Wire(name=input.name, prefix=full_prefix) - setattr(self, attr_name, wire) - self.inputs.append(wire) - - else: - self.inputs = inputs + # Dynamic input bus assignment + self.inputs = [] + for i, input in enumerate(inputs): + attr_name = chr(97+i) + full_prefix = f"{self.prefix}_{input.prefix}" if self.inner_component else f"{input.prefix}" + if isinstance(input, Bus) or isinstance(input, Wire): + circuit_input = copy.deepcopy(input) + circuit_input.prefix = full_prefix + setattr(self, attr_name, circuit_input) + self.inputs.append(circuit_input) + # If the input bus is an output bus, connect it + if isinstance(input, Bus) and input.is_output_bus(): + getattr(self, attr_name).connect_bus(connecting_bus=input) if not outname: outname = self.prefix+"_out" @@ -75,17 +65,50 @@ class GeneralCircuit(): def __str__(self): return f"<{type(self).__name__} prefix={self.prefix} " + (", ".join([f"input={i}" for i in self.inputs])) + ">" - # super().__init__(prefix, name, out_N, inner_component, inputs=[a, b], signed=signed, **kwargs) + def get_hier_subcomponent_def(self, parent_kwargs: dict = {}): + """ Creates and returns a new instance of the current circuit block used for definition of a subcomponent in a hierarchical circuit. + + Args: + parent_kwargs (dict): Dictionary containing all the configuration settings of the parent circuit block. + + Returns: + GeneralCircuit: A new instance of the current circuit block with proper prefix and input wires. + """ + # Obtain proper circuit name with its input bit widths + init_signature = inspect.signature(self.__class__.__init__) + init_params = list(init_signature.parameters.keys()) + default_circuit_name = init_signature.parameters['name'].default + circuit_type = default_circuit_name + "x".join(str(getattr(self, chr(97+i)).N) for i, _ in enumerate(self.inputs)) + # Initialize and fill args for the new instance based on the current instance + init_args = {} + + for param in init_params[1:]: # Skip 'self' + attr = getattr(self, param, None) # Get the attribute from the current instance + + if attr is not None: # If attribute does not exist, it will use default value from the signature + if isinstance(attr, Bus): # If the input is a Bus, create a copy of the Bus object with same length, but proper prefix + init_args[param] = Bus(N=attr.N, prefix=param) + elif isinstance(attr, Wire): # If the input is a Wire, create a copy of the Wire object with proper prefix + init_args[param] = Wire(name=param) + else: # Copy other types of attributes + init_args[param] = copy.deepcopy(attr) + + init_args['name'] = circuit_type + init_args['prefix'] = "" + + circuit_block = self.__class__(**init_args, **parent_kwargs) + return circuit_block def get_circuit_def(self) -> Dict[str, Wire]: """ returns IDs and wires of the inputs and output""" - #.{circuit_block.a.prefix}({self.a.prefix}), .{circuit_block.b.prefix}({self.b.prefix}), .{circuit_block.out.prefix}({self.out.prefix}));\n" + # TODO delete? (probably replaced by get_hier_subcomponent_def) + #.{circuit_block.a.prefix}({self.a.prefix}), .{circuit_block.b.prefix}({self.b.prefix}), .{circuit_block.out.prefix}({self.out.prefix}));\n" r = {chr(97 + i): self.inputs[i] for i in range(len(self.inputs))} r['out'] = self.get_global_prefix() + "_out" return r - + def add_component(self, component): """Adds a component into list of circuit's inner subcomponents. @@ -95,7 +118,7 @@ class GeneralCircuit(): Args: component: Subcomponent to be added into list of components composing described circuit. """ - # TODO will be redone in ArithsGen rework + # TODO should be refactored in ArithsGen rework # We should probably check also wire names for especially hierarchical generation if isinstance(component, TwoInputLogicGate): if component.disable_generation is False: @@ -166,7 +189,7 @@ class GeneralCircuit(): for c in self.components: if isinstance(c, TwoInputLogicGate): continue - elif isinstance(getattr(c, 'a'), Wire): + elif all(isinstance(i, Wire) for i in self.inputs): one_bit_comps.append(c) else: one_bit_comps.extend(c.get_one_bit_components()) @@ -183,14 +206,15 @@ class GeneralCircuit(): for c in self.components: if isinstance(c, TwoInputLogicGate): continue - elif isinstance(getattr(c, 'a'), Wire): + elif all(isinstance(i, Wire) for i in self.inputs): continue else: multi_bit_comps.append(c) + multi_bit_comps.extend(c.get_multi_bit_components()) return multi_bit_comps @staticmethod - def get_unique_types(components: list, multi_bit: bool = False): + def get_unique_types(components: list, name="", multi_bit: bool = False): """Retrieves just the unique representatives of class types present inside the provided components list. Args: @@ -201,7 +225,7 @@ class GeneralCircuit(): list: List of unique composite class types. """ if multi_bit is True: - return list({(type(c), c.N): c for c in components}.values()) + return list({(type(c), tuple(i.N for i in c.inputs)): c for c in components[::-1]}.values()) else: return list({type(c): c for c in components}.values()) @@ -218,7 +242,7 @@ class GeneralCircuit(): gate_comps = self.get_unique_types(components=self.get_circuit_gates(verilog_output)) one_bit_comps = self.get_unique_types( components=self.get_one_bit_components()) - multi_bit_comps = self.get_unique_types( + multi_bit_comps = self.get_unique_types(name=self.prefix, components=self.get_multi_bit_components(), multi_bit=True) all_components = gate_comps + one_bit_comps + multi_bit_comps @@ -328,8 +352,6 @@ class GeneralCircuit(): file_object (TextIOWrapper): Destination file object where circuit's representation will be written to. """ file_object.write(self.get_prototype_python()) - # file_object.write(self.out.get_declaration_python()) - # file_object.write(self.get_declaration_python_flat()+"\n") file_object.write(self.get_init_python_flat()+"\n") file_object.write(self.get_function_out_python_flat()) file_object.write(self.out.return_bus_wires_sign_extend_python_flat()) @@ -403,8 +425,6 @@ class GeneralCircuit(): """ # Retrieve all unique component types composing this circuit and add them kwargs from the parent circuit to allow propagatation of config settings for subcomponents self.component_types = self.get_component_types() - for c in self.component_types: - c._parent_kwargs = self.kwargs return "".join([c.get_function_block_c() for c in self.component_types]) def get_function_block_c(self): @@ -413,12 +433,7 @@ class GeneralCircuit(): Returns: str: Hierarchical C code of multi-bit arithmetic circuit's function block description. """ - # Obtain proper circuit name with its bit width - circuit_prefix = self.__class__( - a=Bus("a", self.N), b=Bus("b", self.N)).prefix + str(self.N) - circuit_block = self.__class__(a=Bus(N=self.N, prefix="a"), b=Bus( - N=self.N, prefix="b"), name=circuit_prefix, **self._parent_kwargs) - return f"{circuit_block.get_circuit_c()}\n\n" + return f"{self.get_hier_subcomponent_def(parent_kwargs=self.kwargs).get_circuit_c()}\n\n" def get_declarations_c_hier(self): """Generates hierarchical C code declaration of input/output circuit wires. @@ -437,9 +452,8 @@ class GeneralCircuit(): Returns: str: Hierarchical C code of subcomponent arithmetic circuit's wires declaration. """ - return f" {self.c_data_type} {self.a.prefix} = 0;\n" + \ - f" {self.c_data_type} {self.b.prefix} = 0;\n" + \ - f" {self.c_data_type} {self.out.prefix} = 0;\n" + return ";\n".join([f" {self.c_data_type} {i.prefix} = 0" for i in self.inputs]) + ";\n" + \ + f" {self.c_data_type} {self.out.prefix} = 0;\n" def get_init_c_hier(self): """Generates hierarchical C code initialization and assignment of corresponding arithmetic circuit's input/output wires. @@ -460,9 +474,12 @@ class GeneralCircuit(): str: Hierarchical C code of subcomponent's C function invocation and output assignment. """ # Getting name of circuit type for proper C code generation without affecting actual generated composition - circuit_type = self.__class__(a=Bus("a", self.N), b=Bus("b", self.N)).prefix + str(self.N) - return self.a.return_bus_wires_values_c_hier() + self.b.return_bus_wires_values_c_hier() + \ - f" {self.out.prefix} = {circuit_type}({self.a.prefix}, {self.b.prefix});\n" + init_signature = inspect.signature(self.__class__.__init__) + default_circuit_name = init_signature.parameters['name'].default + circuit_type = default_circuit_name + "x".join(str(getattr(self, chr(97+i)).N) for i, _ in enumerate(self.inputs)) + # TODO .. now only works for input buses + return "".join(w.return_bus_wires_values_c_hier() for w in self.inputs) + \ + f" {self.out.prefix} = {circuit_type}({', '.join(w.prefix if isinstance(w, Bus) else w.get_wire_value_c_hier() for w in self.inputs)});\n" def get_function_out_c_hier(self): """Generates hierarchical C code assignment of corresponding arithmetic circuit's output bus wires. @@ -499,14 +516,13 @@ class GeneralCircuit(): """ VERILOG CODE GENERATION """ # FLAT VERILOG # - def get_prototype_v(self): """Generates Verilog code module header to describe corresponding arithmetic circuit's interface in Verilog code. Returns: str: Module's name and parameters in Verilog code. """ - return f"module {self.prefix}(" + ",".join(f"input [{x.N-1}:0] {x.prefix}" for x in self.inputs) + f", output [{self.out.N-1}:0] {self.out.prefix});\n" + return f"module {self.prefix}(" + ", ".join(f"input [{x.N-1}:0] {x.prefix}" for x in self.inputs) + f", output [{self.out.N-1}:0] {self.out.prefix});\n" def get_declaration_v_flat(self): """Generates flat Verilog code declaration of input/output circuit wires. @@ -554,8 +570,6 @@ class GeneralCircuit(): """ # Retrieve all unique component types composing this circuit and add them kwargs from the parent circuit to allow propagatation of config settings for subcomponents self.component_types = self.get_component_types(verilog_output=True) - for c in self.component_types: - c._parent_kwargs = self.kwargs return "".join([c.get_function_block_v() for c in self.component_types]) def get_function_block_v(self): @@ -565,11 +579,7 @@ class GeneralCircuit(): str: Hierarchical Verilog code of multi-bit arithmetic circuit's function block description. """ # Obtain proper circuit name with its bit width - circuit_prefix = self.__class__( - a=Bus("a", self.N), b=Bus("b", self.N)).prefix + str(self.N) - circuit_block = self.__class__(a=Bus(N=self.N, prefix="a"), b=Bus( - N=self.N, prefix="b"), name=circuit_prefix, **self._parent_kwargs) - return f"{circuit_block.get_circuit_v()}\n\n" + return f"{self.get_hier_subcomponent_def(parent_kwargs=self.kwargs).get_circuit_v()}\n\n" def get_declarations_v_hier(self): """Generates hierarchical Verilog code declaration of input/output circuit wires. @@ -590,10 +600,7 @@ class GeneralCircuit(): """ return "".join(w.get_wire_declaration_v() for w in self.inputs + [self.out]) + "\n" - # TODO del.. - return f" wire [{self.a.N-1}:0] {self.a.prefix};\n" + \ - f" wire [{self.b.N-1}:0] {self.b.prefix};\n" + \ - f" wire [{self.out.N-1}:0] {self.out.prefix};\n" + #return "".join(b.get_wire_declaration_v() for b in self.inputs + [self.out] if not all((w.is_const()) or (w.parent_bus is not None and w.prefix == b.prefix) for w in b.bus)) + "\n" def get_init_v_hier(self): """Generates hierarchical Verilog code initialization and assignment of corresponding arithmetic circuit's input/output wires. @@ -614,12 +621,13 @@ class GeneralCircuit(): str: Hierarchical Verilog code of subcomponent's module invocation and output assignment. """ # Getting name of circuit type and insitu copying out bus for proper Verilog code generation without affecting actual generated composition - circuit_type = self.__class__(a=Bus("a", self.N), b=Bus("b", self.N)).prefix + str(self.N) - circuit_block = self.__class__(a=Bus(N=self.N, prefix="a"), b=Bus( - N=self.N, prefix="b"), name=circuit_type) + init_signature = inspect.signature(self.__class__.__init__) + default_circuit_name = init_signature.parameters['name'].default + circuit_type = default_circuit_name + "x".join(str(getattr(self, chr(97+i)).N) for i, _ in enumerate(self.inputs)) + circuit_block = self.get_hier_subcomponent_def(parent_kwargs=self.kwargs) + # TODO .. now only works for input buses return "".join([c.return_bus_wires_values_v_hier() for c in self.inputs]) + \ - f" {circuit_type} {circuit_type}_{self.out.prefix}(" + ",".join([f".{a.prefix}({b.prefix})" for a, b in zip(circuit_block.inputs, self.inputs)]) + f", .{circuit_block.out.prefix}({self.out.prefix}));\n" - #.{circuit_block.a.prefix}({self.a.prefix}), .{circuit_block.b.prefix}({self.b.prefix}), .{circuit_block.out.prefix}({self.out.prefix}));\n" + f" {circuit_type} {circuit_type}_{self.out.prefix}(" + ",".join([f".{a.prefix}({b.prefix})" for a, b in zip(circuit_block.inputs, self.inputs)]) + f", .{circuit_block.out.prefix}({self.out.prefix}));\n" def get_function_out_v_hier(self): """Generates hierarchical Verilog code assignment of corresponding arithmetic circuit's output bus wires. @@ -668,10 +676,16 @@ class GeneralCircuit(): Returns: str: Flat Blif code containing declaration of circuit's wires. """ - return f".inputs{''.join([w.get_wire_declaration_blif() for w in self.inputs])}\n" + \ - f".outputs{self.out.get_wire_declaration_blif()}\n" + \ - f".names vdd\n1\n" + \ - f".names gnd\n0\n" + if self.N == 1: + return f".inputs {' '.join([w.prefix for w in self.inputs])}\n" + \ + f".outputs{self.out.get_wire_declaration_blif()}\n" + \ + f".names vdd\n1\n" + \ + f".names gnd\n0\n" + else: + return f".inputs{''.join([w.get_wire_declaration_blif() for w in self.inputs])}\n" + \ + f".outputs{self.out.get_wire_declaration_blif()}\n" + \ + f".names vdd\n1\n" + \ + f".names gnd\n0\n" def get_function_blif_flat(self): """Generates flat Blif code with invocation of subcomponents logic gates functions via their corresponding truth tables. @@ -720,7 +734,15 @@ class GeneralCircuit(): str: Hierarchical Blif code of subcomponent's model invocation and output assignment. """ # Getting name of circuit type for proper Blif code generation without affecting actual generated composition - circuit_type = self.__class__(a=Bus("a", self.N), b=Bus("b", self.N)).prefix + str(self.N) + init_signature = inspect.signature(self.__class__.__init__) + default_circuit_name = init_signature.parameters['name'].default + circuit_type = default_circuit_name + "x".join(str(getattr(self, chr(97+i)).N) for i, _ in enumerate(self.inputs)) + return "".join([w.get_wire_assign_blif(output=True) for w in self.inputs]) + \ + f".subckt {circuit_type}" + \ + "".join([f" {chr(97+i)}[{b.bus.index(w)}]={b.prefix}[{b.bus.index(w)}]" for i, b in enumerate(self.inputs) for w in b.bus]) + \ + "".join([f" {circuit_type}_out[{self.out.bus.index(o)}]={o.name}" for o in self.out.bus]) + "\n" + + # TODO delete return f"{self.a.get_wire_assign_blif(output=True)}" + \ f"{self.b.get_wire_assign_blif(output=True)}" + \ f".subckt {circuit_type}" + \ @@ -749,8 +771,6 @@ class GeneralCircuit(): # Retrieve all unique component types composing this circuit and add them kwargs from the parent circuit to allow propagatation of config settings for subcomponents # (iterating backwards as opposed to other representations so the top modul is always above its subcomponents) self.component_types = self.get_component_types() - for c in self.component_types: - c._parent_kwargs = self.kwargs return "\n".join([c.get_function_block_blif() for c in self.component_types[::-1]]) def get_function_block_blif(self): @@ -760,11 +780,7 @@ class GeneralCircuit(): str: Hierarchical Blif code of multi-bit arithmetic circuit's function block description. """ # Obtain proper circuit name with its bit width - circuit_prefix = self.__class__( - a=Bus("a", self.N), b=Bus("b", self.N)).prefix + str(self.N) - circuit_block = self.__class__(a=Bus(N=self.N, prefix="a"), b=Bus( - N=self.N, prefix="b"), name=circuit_prefix, **self._parent_kwargs) - return f"{circuit_block.get_circuit_blif()}" + return f"{self.get_hier_subcomponent_def(parent_kwargs=self.kwargs).get_circuit_blif()}" # Generating hierarchical BLIF code representation of circuit def get_blif_code_hier(self, file_object): diff --git a/ariths_gen/core/arithmetic_circuits/multiplier_circuit.py b/ariths_gen/core/arithmetic_circuits/multiplier_circuit.py index dfc0854..f939029 100644 --- a/ariths_gen/core/arithmetic_circuits/multiplier_circuit.py +++ b/ariths_gen/core/arithmetic_circuits/multiplier_circuit.py @@ -1,36 +1,24 @@ -from .arithmetic_circuit import ( - ArithmeticCircuit +from .general_circuit import ( + GeneralCircuit ) - -from ariths_gen.one_bit_circuits.logic_gates import ( - AndGate, - NandGate, - OrGate, - NorGate, - XorGate, - XnorGate, - NotGate -) - from ariths_gen.wire_components import ( - Wire, - ConstantWireValue0, - ConstantWireValue1, Bus ) - +from ariths_gen.one_bit_circuits.logic_gates import ( + AndGate, + NandGate +) import math -class MultiplierCircuit(ArithmeticCircuit): - """Class represents a general multiplier circuit derived from `ArithmeticCircuit` class. +class MultiplierCircuit(GeneralCircuit): + """Class represents a general multiplier circuit derived from `GeneralCircuit` class. - The __init__ method calls parent class __init__ method which fills some mandatory attributes concerning arithmetic circuit + The __init__ method calls parent class __init__ method which fills some mandatory attributes concerning general circuit that are later used for generation into various representations. """ - - def __init__(self, a, b, prefix: str, name: str, out_N: int, **kwargs): - super().__init__(a=a, b=b, prefix=prefix, name=name, out_N=out_N, **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, outname: str = "", **kwargs): + super().__init__(prefix=prefix, name=name, out_N=out_N, inner_component=inner_component, inputs=inputs, one_bit_circuit=one_bit_circuit, signed=signed, outname=outname, **kwargs) # Array/approximate multipliers def get_previous_partial_product(self, a_index: int, b_index: int, mult_type=""): @@ -266,7 +254,7 @@ class MultiplierCircuit(ArithmeticCircuit): else: return self.columns[column][bit+1] - def update_column_wires(self, curr_column: int, adder: ArithmeticCircuit, next_column: int = 0): + def update_column_wires(self, curr_column: int, adder: GeneralCircuit, next_column: int = 0): """Provides bit height reduction of the chosen column. Inserts chosen column's top bits into an `adder` circuit to reduce its bit height. @@ -274,7 +262,7 @@ class MultiplierCircuit(ArithmeticCircuit): Args: curr_column (int): Current pp column index. - adder (ArithmeticCircuit): Two/three input one bit adder. + adder (GeneralCircuit): Two/three input one bit adder. next_column (int, optional): Subsequent pp column index. Defaults to 0. """ if hasattr(adder, "c"): diff --git a/ariths_gen/core/cgp_circuit.py b/ariths_gen/core/cgp_circuit.py index be1a6cb..a453a7a 100644 --- a/ariths_gen/core/cgp_circuit.py +++ b/ariths_gen/core/cgp_circuit.py @@ -1,5 +1,4 @@ from ariths_gen.wire_components import ( - Wire, ConstantWireValue0, ConstantWireValue1, Bus @@ -7,14 +6,6 @@ from ariths_gen.wire_components import ( from ariths_gen.core.arithmetic_circuits import ( GeneralCircuit ) - -from ariths_gen.core.logic_gate_circuits import ( - MultipleInputLogicGate -) -from ariths_gen.one_bit_circuits.one_bit_components import ( - HalfAdder, - FullAdder -) from ariths_gen.one_bit_circuits.logic_gates import ( AndGate, NandGate, diff --git a/ariths_gen/core/logic_gate_circuits/logic_gate_circuit.py b/ariths_gen/core/logic_gate_circuits/logic_gate_circuit.py index c24c2c2..38f91d8 100644 --- a/ariths_gen/core/logic_gate_circuits/logic_gate_circuit.py +++ b/ariths_gen/core/logic_gate_circuits/logic_gate_circuit.py @@ -1,4 +1,4 @@ -from ariths_gen.wire_components.wires import Wire, ConstantWireValue0, ConstantWireValue1 +from ariths_gen.wire_components.wires import Wire from ariths_gen.wire_components.buses import Bus import math diff --git a/ariths_gen/core/one_bit_circuits/four_input_one_bit_circuit.py b/ariths_gen/core/one_bit_circuits/four_input_one_bit_circuit.py index 4c8a262..b9e8eee 100644 --- a/ariths_gen/core/one_bit_circuits/four_input_one_bit_circuit.py +++ b/ariths_gen/core/one_bit_circuits/four_input_one_bit_circuit.py @@ -1,11 +1,13 @@ from .two_input_one_bit_circuit import ( TwoInputOneBitCircuit ) - +from ariths_gen.core.arithmetic_circuits import ( + GeneralCircuit +) from ariths_gen.wire_components.wires import Wire -class FourInputOneBitCircuit(TwoInputOneBitCircuit): +class FourInputOneBitCircuit(TwoInputOneBitCircuit, GeneralCircuit): """Class represents a general four input one bit circuit and implements their generation to various representations. It is derived from `TwoInputOneBitCircuit` class. Description of the __init__ method. @@ -18,13 +20,8 @@ class FourInputOneBitCircuit(TwoInputOneBitCircuit): prefix (str, optional): Prefix name of circuit. Defaults to "four_input_one_bit_circuit". """ def __init__(self, a: Wire = Wire(name="a"), b: Wire = Wire(name="b"), c: Wire = Wire(name="c"), d: Wire = Wire(name="d"), prefix: str = "four_input_one_bit_circuit"): - super().__init__() + GeneralCircuit.__init__(self, inputs=[a, b, c, d], prefix=prefix, name="", out_N=1) self.c_data_type = "uint8_t" - self.prefix = prefix - self.a = a - self.b = b - self.c = c - self.d = d """ C CODE GENERATION """ # FLAT C # diff --git a/ariths_gen/core/one_bit_circuits/three_input_one_bit_circuit.py b/ariths_gen/core/one_bit_circuits/three_input_one_bit_circuit.py index a552d59..5586f22 100644 --- a/ariths_gen/core/one_bit_circuits/three_input_one_bit_circuit.py +++ b/ariths_gen/core/one_bit_circuits/three_input_one_bit_circuit.py @@ -1,11 +1,13 @@ from .two_input_one_bit_circuit import ( TwoInputOneBitCircuit ) - +from ariths_gen.core.arithmetic_circuits import ( + GeneralCircuit +) from ariths_gen.wire_components.wires import Wire -class ThreeInputOneBitCircuit(TwoInputOneBitCircuit): +class ThreeInputOneBitCircuit(TwoInputOneBitCircuit, GeneralCircuit): """Class represents a general three input one bit circuit and implements their generation to various representations. It is derived from `TwoInputOneBitCircuit` class. Description of the __init__ method. @@ -17,12 +19,8 @@ class ThreeInputOneBitCircuit(TwoInputOneBitCircuit): prefix (str, optional): Prefix name of circuit. Defaults to "three_input_one_bit_circuit". """ def __init__(self, a: Wire = Wire(name="a"), b: Wire = Wire(name="b"), c: Wire = Wire(name="cin"), prefix: str = "three_input_one_bit_circuit"): - super().__init__() + GeneralCircuit.__init__(self, inputs=[a, b, c], prefix=prefix, name="", out_N=1) self.c_data_type = "uint8_t" - self.prefix = prefix - self.a = a - self.b = b - self.c = c """ C CODE GENERATION """ # FLAT C # diff --git a/ariths_gen/core/one_bit_circuits/two_input_one_bit_circuit.py b/ariths_gen/core/one_bit_circuits/two_input_one_bit_circuit.py index 1f81423..1d442a0 100644 --- a/ariths_gen/core/one_bit_circuits/two_input_one_bit_circuit.py +++ b/ariths_gen/core/one_bit_circuits/two_input_one_bit_circuit.py @@ -1,11 +1,11 @@ from ariths_gen.core.arithmetic_circuits import ( - ArithmeticCircuit + GeneralCircuit ) from ariths_gen.wire_components.wires import Wire -class TwoInputOneBitCircuit(ArithmeticCircuit): +class TwoInputOneBitCircuit(GeneralCircuit): """Class represents a general two input one bit circuit and implements their generation to various representations. It is derived from `ArithmeticCircuit` class. Description of the __init__ method. @@ -16,11 +16,8 @@ class TwoInputOneBitCircuit(ArithmeticCircuit): prefix (str, optional): Prefix name of circuit. Defaults to "two_input_one_bit_circuit". """ def __init__(self, a: Wire = Wire(name="a"), b: Wire = Wire(name="b"), prefix: str = "two_input_one_bit_circuit"): - super().__init__(a=a, b=b, prefix=prefix, name="", out_N=1, one_bit_circuit=True) + super().__init__(inputs=[a, b], prefix=prefix, name="", out_N=1, one_bit_circuit=True) self.c_data_type = "uint8_t" - self.prefix = prefix - self.a = a - self.b = b """ C CODE GENERATION """ # FLAT C # diff --git a/ariths_gen/multi_bit_circuits/adders/__init__.py b/ariths_gen/multi_bit_circuits/adders/__init__.py index 0ba1f34..f0ba494 100644 --- a/ariths_gen/multi_bit_circuits/adders/__init__.py +++ b/ariths_gen/multi_bit_circuits/adders/__init__.py @@ -67,4 +67,4 @@ from ariths_gen.multi_bit_circuits.adders.conditional_sum_adder import ( from ariths_gen.multi_bit_circuits.adders.carry_increment_adder import ( UnsignedCarryIncrementAdder, SignedCarryIncrementAdder -) \ No newline at end of file +) diff --git a/ariths_gen/multi_bit_circuits/adders/brent_kung_adder.py b/ariths_gen/multi_bit_circuits/adders/brent_kung_adder.py index d8bc322..5415a8e 100644 --- a/ariths_gen/multi_bit_circuits/adders/brent_kung_adder.py +++ b/ariths_gen/multi_bit_circuits/adders/brent_kung_adder.py @@ -1,41 +1,22 @@ from ariths_gen.wire_components import ( - Wire, ConstantWireValue0, - ConstantWireValue1, Bus ) from ariths_gen.core.arithmetic_circuits import ( - ArithmeticCircuit, - MultiplierCircuit + GeneralCircuit ) from ariths_gen.one_bit_circuits.one_bit_components import ( - HalfAdder, - FullAdder, PGSumLogic, GreyCell, BlackCell ) from ariths_gen.one_bit_circuits.logic_gates import ( - AndGate, - NandGate, - OrGate, - NorGate, - XorGate, - XnorGate, - NotGate -) -from ariths_gen.multi_bit_circuits.adders import ( - UnsignedCarryLookaheadAdder, - UnsignedPGRippleCarryAdder, - UnsignedRippleCarryAdder, - SignedCarryLookaheadAdder, - SignedPGRippleCarryAdder, - SignedRippleCarryAdder + XorGate ) import math -class UnsignedBrentKungAdder(ArithmeticCircuit): +class UnsignedBrentKungAdder(GeneralCircuit): """Class representing unsigned Brent-Kung adder (using valency-2 logic gates). The Brent-Kung adder belongs to a type of tree (parallel-prefix) adders. @@ -87,7 +68,7 @@ class UnsignedBrentKungAdder(ArithmeticCircuit): """ def __init__(self, a: Bus, b: Bus, prefix: str = "", name: str = "u_bka", **kwargs): self.N = max(a.N, b.N) - super().__init__(a=a, b=b, prefix=prefix, name=name, out_N=self.N+1, **kwargs) + super().__init__(inputs=[a, b], prefix=prefix, name=name, out_N=self.N+1, **kwargs) # Bus sign extension in case buses have different lengths self.a.bus_extend(N=self.N, prefix=a.prefix) @@ -155,7 +136,7 @@ class UnsignedBrentKungAdder(ArithmeticCircuit): self.generate_sig[i_wire+1].append(self.get_previous_component().get_generate_wire()) -class SignedBrentKungAdder(UnsignedBrentKungAdder, ArithmeticCircuit): +class SignedBrentKungAdder(UnsignedBrentKungAdder, GeneralCircuit): """Class representing signed Brent-Kung adder (using valency-2 logic gates). The Brent-Kung adder belongs to a type of tree (parallel-prefix) adders. diff --git a/ariths_gen/multi_bit_circuits/adders/carry_increment_adder.py b/ariths_gen/multi_bit_circuits/adders/carry_increment_adder.py index f633508..aa1ad9a 100644 --- a/ariths_gen/multi_bit_circuits/adders/carry_increment_adder.py +++ b/ariths_gen/multi_bit_circuits/adders/carry_increment_adder.py @@ -1,13 +1,9 @@ from ariths_gen.wire_components import ( - Wire, ConstantWireValue0, - ConstantWireValue1, - Bus, - wires + Bus ) from ariths_gen.core.arithmetic_circuits import ( - ArithmeticCircuit, - MultiplierCircuit + GeneralCircuit ) from ariths_gen.core.logic_gate_circuits import ( MultipleInputLogicGate @@ -15,21 +11,16 @@ from ariths_gen.core.logic_gate_circuits import ( from ariths_gen.one_bit_circuits.one_bit_components import ( HalfAdder, FullAdder, - FullAdderP, - TwoOneMultiplexer + FullAdderP ) from ariths_gen.one_bit_circuits.logic_gates import ( AndGate, - NandGate, OrGate, - NorGate, - XorGate, - XnorGate, - NotGate + XorGate ) -class UnsignedCarryIncrementAdder(ArithmeticCircuit): +class UnsignedCarryIncrementAdder(GeneralCircuit): """Class representing unsigned carry increment adder. Carry increment adder represents a modified carry select adder that achieves about the same critical delay @@ -71,7 +62,7 @@ class UnsignedCarryIncrementAdder(ArithmeticCircuit): """ def __init__(self, a: Bus, b: Bus, increment_block_size: int = 4, prefix: str = "", name: str = "u_cia", **kwargs): self.N = max(a.N, b.N) - super().__init__(a=a, b=b, prefix=prefix, name=name, out_N=self.N+1, **kwargs) + super().__init__(inputs=[a, b], prefix=prefix, name=name, out_N=self.N+1, **kwargs) # Bus sign extension in case buses have different lengths self.a.bus_extend(N=self.N, prefix=a.prefix) @@ -115,7 +106,7 @@ class UnsignedCarryIncrementAdder(ArithmeticCircuit): self.out.connect(self.N, cin) -class SignedCarryIncrementAdder(UnsignedCarryIncrementAdder, ArithmeticCircuit): +class SignedCarryIncrementAdder(UnsignedCarryIncrementAdder, GeneralCircuit): """Class representing signed carry increment adder. Carry increment adder represents a modified carry select adder that achieves about the same critical delay diff --git a/ariths_gen/multi_bit_circuits/adders/carry_lookahead_adder.py b/ariths_gen/multi_bit_circuits/adders/carry_lookahead_adder.py index 439286c..e4e82bc 100644 --- a/ariths_gen/multi_bit_circuits/adders/carry_lookahead_adder.py +++ b/ariths_gen/multi_bit_circuits/adders/carry_lookahead_adder.py @@ -1,34 +1,24 @@ from ariths_gen.wire_components import ( - Wire, ConstantWireValue0, - ConstantWireValue1, Bus ) from ariths_gen.core.arithmetic_circuits import ( - ArithmeticCircuit, - MultiplierCircuit + GeneralCircuit ) from ariths_gen.core.logic_gate_circuits import ( MultipleInputLogicGate ) from ariths_gen.one_bit_circuits.one_bit_components import ( - HalfAdder, - FullAdder, - FullAdderPG, PGLogicBlock ) from ariths_gen.one_bit_circuits.logic_gates import ( AndGate, - NandGate, OrGate, - NorGate, - XorGate, - XnorGate, - NotGate + XorGate ) -class UnsignedCarryLookaheadAdder(ArithmeticCircuit): +class UnsignedCarryLookaheadAdder(GeneralCircuit): """Class representing unsigned carry-lookahead adder. Unsigned carry-lookahead adder represents faster adder circuit which is composed @@ -67,7 +57,7 @@ class UnsignedCarryLookaheadAdder(ArithmeticCircuit): """ def __init__(self, a: Bus, b: Bus, cla_block_size: int = 4, prefix: str = "", name: str = "u_cla", **kwargs): self.N = max(a.N, b.N) - super().__init__(a=a, b=b, prefix=prefix, name=name, out_N=self.N+1, **kwargs) + super().__init__(inputs=[a, b], prefix=prefix, name=name, out_N=self.N+1, **kwargs) # Bus sign extension in case buses have different lengths self.a.bus_extend(N=self.N, prefix=a.prefix) @@ -128,7 +118,7 @@ class UnsignedCarryLookaheadAdder(ArithmeticCircuit): self.out.connect(self.N, cin) -class SignedCarryLookaheadAdder(UnsignedCarryLookaheadAdder, ArithmeticCircuit): +class SignedCarryLookaheadAdder(UnsignedCarryLookaheadAdder, GeneralCircuit): """Class representing signed carry-lookahead adder. Signed carry-lookahead adder represents faster adder circuit which is composed diff --git a/ariths_gen/multi_bit_circuits/adders/carry_save_adder.py b/ariths_gen/multi_bit_circuits/adders/carry_save_adder.py index a770862..bc0be5b 100644 --- a/ariths_gen/multi_bit_circuits/adders/carry_save_adder.py +++ b/ariths_gen/multi_bit_circuits/adders/carry_save_adder.py @@ -1,38 +1,20 @@ from ariths_gen.wire_components import ( - Wire, ConstantWireValue0, ConstantWireValue1, Bus ) from ariths_gen.core.arithmetic_circuits import ( - ArithmeticCircuit, - ThreeInputArithmeticCircuit, - MultiplierCircuit + GeneralCircuit ) from ariths_gen.one_bit_circuits.one_bit_components import ( - HalfAdder, FullAdder ) -from ariths_gen.one_bit_circuits.logic_gates import ( - AndGate, - NandGate, - OrGate, - NorGate, - XorGate, - XnorGate, - NotGate -) from ariths_gen.multi_bit_circuits.adders import ( - UnsignedCarryLookaheadAdder, - UnsignedPGRippleCarryAdder, - UnsignedRippleCarryAdder, - SignedCarryLookaheadAdder, - SignedPGRippleCarryAdder, - SignedRippleCarryAdder + UnsignedCarryLookaheadAdder ) -class CarrySaveAdderComponent(ThreeInputArithmeticCircuit): +class CarrySaveAdderComponent(GeneralCircuit): """Class representing carry save adder component. The carry save adder component is especially useful when constructing tree multiplier architectures to reduce the propagation delay as opposed to traditional implementation of tree multipliers with half/full adders. @@ -70,7 +52,8 @@ class CarrySaveAdderComponent(ThreeInputArithmeticCircuit): """ def __init__(self, a: Bus, b: Bus, c: Bus, prefix: str = "", name: str = "csa_component", signed: bool = False, **kwargs): self.N = max(a.N, b.N, c.N) - super().__init__(a=a, b=b, c=c, prefix=prefix, name=name, out_N=(2*self.N)+2, signed=signed, **kwargs) + super().__init__(inputs=[a, b, c], prefix=prefix, name=name, out_N=(2*self.N)+2, signed=signed, **kwargs) + self.out.signed = False # CSA component has always unsigned output bus_extension_wire = ConstantWireValue1() if self.signed is True else ConstantWireValue0() self.a.bus_extend(N=self.N, prefix=a.prefix, desired_extension_wire=bus_extension_wire) @@ -94,7 +77,7 @@ class CarrySaveAdderComponent(ThreeInputArithmeticCircuit): [self.out.connect(o, self.carry_bits.get_wire(o-int(self.out.N/2))) for o in range(int(self.out.N/2), self.out.N)] -class UnsignedCarrySaveAdder(ThreeInputArithmeticCircuit): +class UnsignedCarrySaveAdder(GeneralCircuit): """Class representing unsigned carry save adder. Unsigned carry save adder represents 3 input N-bit unsigned adder which is composed of @@ -132,7 +115,7 @@ class UnsignedCarrySaveAdder(ThreeInputArithmeticCircuit): """ def __init__(self, a: Bus, b: Bus, c: Bus, prefix: str = "", name: str = "u_csa", unsigned_adder_class_name: str = UnsignedCarryLookaheadAdder, **kwargs): self.N = max(a.N, b.N, c.N) - super().__init__(a=a, b=b, c=c, prefix=prefix, name=name, out_N=self.N+2, **kwargs) + super().__init__(inputs=[a, b, c], prefix=prefix, name=name, out_N=self.N+2, **kwargs) # Bus sign extension in case buses have different lengths self.a.bus_extend(N=self.N, prefix=a.prefix) diff --git a/ariths_gen/multi_bit_circuits/adders/carry_select_adder.py b/ariths_gen/multi_bit_circuits/adders/carry_select_adder.py index 36c7fa4..6d97ff1 100644 --- a/ariths_gen/multi_bit_circuits/adders/carry_select_adder.py +++ b/ariths_gen/multi_bit_circuits/adders/carry_select_adder.py @@ -1,34 +1,23 @@ from ariths_gen.wire_components import ( - Wire, ConstantWireValue0, ConstantWireValue1, - Bus, - wires + Bus ) from ariths_gen.core.arithmetic_circuits import ( - ArithmeticCircuit, - MultiplierCircuit -) -from ariths_gen.core.logic_gate_circuits import ( - MultipleInputLogicGate + GeneralCircuit ) from ariths_gen.one_bit_circuits.one_bit_components import ( - HalfAdder, FullAdder, TwoOneMultiplexer ) from ariths_gen.one_bit_circuits.logic_gates import ( AndGate, - NandGate, OrGate, - NorGate, - XorGate, - XnorGate, - NotGate + XorGate ) -class UnsignedCarrySelectAdder(ArithmeticCircuit): +class UnsignedCarrySelectAdder(GeneralCircuit): """Class representing unsigned carry select adder. Carry select adder's logic is divided into a number of carry select blocks. @@ -75,7 +64,7 @@ class UnsignedCarrySelectAdder(ArithmeticCircuit): """ def __init__(self, a: Bus, b: Bus, select_block_size: int = 4, prefix: str = "", name: str = "u_csla", **kwargs): self.N = max(a.N, b.N) - super().__init__(a=a, b=b, prefix=prefix, name=name, out_N=self.N+1, **kwargs) + super().__init__(inputs=[a, b], prefix=prefix, name=name, out_N=self.N+1, **kwargs) # Bus sign extension in case buses have different lengths self.a.bus_extend(N=self.N, prefix=a.prefix) @@ -125,7 +114,7 @@ class UnsignedCarrySelectAdder(ArithmeticCircuit): self.out.connect(self.N, cin) -class SignedCarrySelectAdder(UnsignedCarrySelectAdder, ArithmeticCircuit): +class SignedCarrySelectAdder(UnsignedCarrySelectAdder, GeneralCircuit): """Class representing signed carry select adder. Carry select adder's logic is divided into a number of carry select blocks. diff --git a/ariths_gen/multi_bit_circuits/adders/carry_skip_adder.py b/ariths_gen/multi_bit_circuits/adders/carry_skip_adder.py index c4ea318..938d890 100644 --- a/ariths_gen/multi_bit_circuits/adders/carry_skip_adder.py +++ b/ariths_gen/multi_bit_circuits/adders/carry_skip_adder.py @@ -1,13 +1,9 @@ from ariths_gen.wire_components import ( - Wire, ConstantWireValue0, - ConstantWireValue1, - Bus, - wires + Bus ) from ariths_gen.core.arithmetic_circuits import ( - ArithmeticCircuit, - MultiplierCircuit + GeneralCircuit ) from ariths_gen.core.logic_gate_circuits import ( MultipleInputLogicGate @@ -19,16 +15,11 @@ from ariths_gen.one_bit_circuits.one_bit_components import ( ) from ariths_gen.one_bit_circuits.logic_gates import ( AndGate, - NandGate, - OrGate, - NorGate, - XorGate, - XnorGate, - NotGate + XorGate ) -class UnsignedCarrySkipAdder(ArithmeticCircuit): +class UnsignedCarrySkipAdder(GeneralCircuit): """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 @@ -72,7 +63,7 @@ class UnsignedCarrySkipAdder(ArithmeticCircuit): """ def __init__(self, a: Bus, b: Bus, bypass_block_size: int = 4, prefix: str = "", name: str = "u_cska", **kwargs): self.N = max(a.N, b.N) - super().__init__(a=a, b=b, prefix=prefix, name=name, out_N=self.N+1, **kwargs) + super().__init__(inputs=[a, b], prefix=prefix, name=name, out_N=self.N+1, **kwargs) # Bus sign extension in case buses have different lengths self.a.bus_extend(N=self.N, prefix=a.prefix) @@ -120,7 +111,7 @@ class UnsignedCarrySkipAdder(ArithmeticCircuit): self.out.connect(self.N, cin) -class SignedCarrySkipAdder(UnsignedCarrySkipAdder, ArithmeticCircuit): +class SignedCarrySkipAdder(UnsignedCarrySkipAdder, GeneralCircuit): """Class representing signed carry skip (bypass) adder composed of smaller carry bypass blocks of chosen size to reduce propagation delay. Signed carry skip (bypass) adder represents faster adder circuit which is composed diff --git a/ariths_gen/multi_bit_circuits/adders/conditional_sum_adder.py b/ariths_gen/multi_bit_circuits/adders/conditional_sum_adder.py index c66661d..5463294 100644 --- a/ariths_gen/multi_bit_circuits/adders/conditional_sum_adder.py +++ b/ariths_gen/multi_bit_circuits/adders/conditional_sum_adder.py @@ -1,35 +1,22 @@ from ariths_gen.wire_components import ( - Wire, ConstantWireValue0, ConstantWireValue1, - Bus, - wires + Bus ) from ariths_gen.core.arithmetic_circuits import ( - ArithmeticCircuit, - MultiplierCircuit -) -from ariths_gen.core.logic_gate_circuits import ( - MultipleInputLogicGate + GeneralCircuit ) from ariths_gen.one_bit_circuits.one_bit_components import ( - HalfAdder, FullAdder, TwoOneMultiplexer ) from ariths_gen.one_bit_circuits.logic_gates import ( - AndGate, - NandGate, - OrGate, - NorGate, - XorGate, - XnorGate, - NotGate + XorGate ) import math -class UnsignedConditionalSumAdder(ArithmeticCircuit): +class UnsignedConditionalSumAdder(GeneralCircuit): """Class representing unsigned conditional sum adder. Conditional sum adder performs carry-select addition starting with @@ -108,7 +95,7 @@ class UnsignedConditionalSumAdder(ArithmeticCircuit): """ def __init__(self, a: Bus, b: Bus, prefix: str = "", name: str = "u_cosa", **kwargs): self.N = max(a.N, b.N) - super().__init__(a=a, b=b, prefix=prefix, name=name, out_N=self.N+1, **kwargs) + super().__init__(inputs=[a, b], prefix=prefix, name=name, out_N=self.N+1, **kwargs) # Bus sign extension in case buses have different lengths self.a.bus_extend(N=self.N, prefix=a.prefix) @@ -191,7 +178,7 @@ class UnsignedConditionalSumAdder(ArithmeticCircuit): self.out.connect(self.N, self.carry_sig[i_wire+1][0][-1]) -class SignedConditionalSumAdder(UnsignedConditionalSumAdder, ArithmeticCircuit): +class SignedConditionalSumAdder(UnsignedConditionalSumAdder, GeneralCircuit): """Class representing signed conditional sum adder. Conditional sum adder performs carry-select addition starting with diff --git a/ariths_gen/multi_bit_circuits/adders/han_carlson_adder.py b/ariths_gen/multi_bit_circuits/adders/han_carlson_adder.py index c6b4b8e..6321418 100644 --- a/ariths_gen/multi_bit_circuits/adders/han_carlson_adder.py +++ b/ariths_gen/multi_bit_circuits/adders/han_carlson_adder.py @@ -1,41 +1,22 @@ from ariths_gen.wire_components import ( - Wire, ConstantWireValue0, - ConstantWireValue1, Bus ) from ariths_gen.core.arithmetic_circuits import ( - ArithmeticCircuit, - MultiplierCircuit + GeneralCircuit ) from ariths_gen.one_bit_circuits.one_bit_components import ( - HalfAdder, - FullAdder, PGSumLogic, GreyCell, BlackCell ) from ariths_gen.one_bit_circuits.logic_gates import ( - AndGate, - NandGate, - OrGate, - NorGate, - XorGate, - XnorGate, - NotGate -) -from ariths_gen.multi_bit_circuits.adders import ( - UnsignedCarryLookaheadAdder, - UnsignedPGRippleCarryAdder, - UnsignedRippleCarryAdder, - SignedCarryLookaheadAdder, - SignedPGRippleCarryAdder, - SignedRippleCarryAdder + XorGate ) import math -class UnsignedHanCarlsonAdder(ArithmeticCircuit): +class UnsignedHanCarlsonAdder(GeneralCircuit): """Class representing unsigned Han-Carlson adder (using valency-2 logic gates). The Han-Carlson adder belongs to a type of tree (parallel-prefix) adders. @@ -88,7 +69,7 @@ class UnsignedHanCarlsonAdder(ArithmeticCircuit): """ def __init__(self, a: Bus, b: Bus, prefix: str = "", name: str = "u_hca", config_choice: int = 1, **kwargs): self.N = max(a.N, b.N) - super().__init__(a=a, b=b, prefix=prefix, name=name, out_N=self.N+1, **kwargs) + super().__init__(inputs=[a, b], prefix=prefix, name=name, out_N=self.N+1, **kwargs) # Bus sign extension in case buses have different lengths self.a.bus_extend(N=self.N, prefix=a.prefix) @@ -184,7 +165,7 @@ class UnsignedHanCarlsonAdder(ArithmeticCircuit): self.generate_sig[i_wire+1].append(self.get_previous_component().get_generate_wire()) -class SignedHanCarlsonAdder(UnsignedHanCarlsonAdder, ArithmeticCircuit): +class SignedHanCarlsonAdder(UnsignedHanCarlsonAdder, GeneralCircuit): """Class representing signed Han-Carlson adder (using valency-2 logic gates). The Han-Carlson adder belongs to a type of tree (parallel-prefix) adders. diff --git a/ariths_gen/multi_bit_circuits/adders/knowles_adder.py b/ariths_gen/multi_bit_circuits/adders/knowles_adder.py index 4f724cf..bea9e87 100644 --- a/ariths_gen/multi_bit_circuits/adders/knowles_adder.py +++ b/ariths_gen/multi_bit_circuits/adders/knowles_adder.py @@ -1,41 +1,22 @@ from ariths_gen.wire_components import ( - Wire, ConstantWireValue0, - ConstantWireValue1, Bus ) from ariths_gen.core.arithmetic_circuits import ( - ArithmeticCircuit, - MultiplierCircuit + GeneralCircuit ) from ariths_gen.one_bit_circuits.one_bit_components import ( - HalfAdder, - FullAdder, PGSumLogic, GreyCell, BlackCell ) from ariths_gen.one_bit_circuits.logic_gates import ( - AndGate, - NandGate, - OrGate, - NorGate, - XorGate, - XnorGate, - NotGate -) -from ariths_gen.multi_bit_circuits.adders import ( - UnsignedCarryLookaheadAdder, - UnsignedPGRippleCarryAdder, - UnsignedRippleCarryAdder, - SignedCarryLookaheadAdder, - SignedPGRippleCarryAdder, - SignedRippleCarryAdder + XorGate ) import math -class UnsignedKnowlesAdder(ArithmeticCircuit): +class UnsignedKnowlesAdder(GeneralCircuit): """Class representing unsigned Knowles adder (using valency-2 logic gates). The Knowles adder belongs to a type of tree (parallel-prefix) adders. @@ -88,7 +69,7 @@ class UnsignedKnowlesAdder(ArithmeticCircuit): """ def __init__(self, a: Bus, b: Bus, prefix: str = "", name: str = "u_ka", config_choice: int = 1, **kwargs): self.N = max(a.N, b.N) - super().__init__(a=a, b=b, prefix=prefix, name=name, out_N=self.N+1, **kwargs) + super().__init__(inputs=[a, b], prefix=prefix, name=name, out_N=self.N+1, **kwargs) # Bus sign extension in case buses have different lengths self.a.bus_extend(N=self.N, prefix=a.prefix) @@ -143,7 +124,7 @@ class UnsignedKnowlesAdder(ArithmeticCircuit): self.generate_sig[i_wire+1].append(self.get_previous_component().get_generate_wire()) -class SignedKnowlesAdder(UnsignedKnowlesAdder, ArithmeticCircuit): +class SignedKnowlesAdder(UnsignedKnowlesAdder, GeneralCircuit): """Class representing signed Knowles adder (using valency-2 logic gates). The Knowles adder belongs to a type of tree (parallel-prefix) adders. diff --git a/ariths_gen/multi_bit_circuits/adders/kogge_stone_adder.py b/ariths_gen/multi_bit_circuits/adders/kogge_stone_adder.py index e3a263f..3aa7d7c 100644 --- a/ariths_gen/multi_bit_circuits/adders/kogge_stone_adder.py +++ b/ariths_gen/multi_bit_circuits/adders/kogge_stone_adder.py @@ -1,41 +1,22 @@ from ariths_gen.wire_components import ( - Wire, ConstantWireValue0, - ConstantWireValue1, Bus ) from ariths_gen.core.arithmetic_circuits import ( - ArithmeticCircuit, - MultiplierCircuit + GeneralCircuit ) from ariths_gen.one_bit_circuits.one_bit_components import ( - HalfAdder, - FullAdder, PGSumLogic, GreyCell, BlackCell ) from ariths_gen.one_bit_circuits.logic_gates import ( - AndGate, - NandGate, - OrGate, - NorGate, - XorGate, - XnorGate, - NotGate -) -from ariths_gen.multi_bit_circuits.adders import ( - UnsignedCarryLookaheadAdder, - UnsignedPGRippleCarryAdder, - UnsignedRippleCarryAdder, - SignedCarryLookaheadAdder, - SignedPGRippleCarryAdder, - SignedRippleCarryAdder + XorGate ) import math -class UnsignedKoggeStoneAdder(ArithmeticCircuit): +class UnsignedKoggeStoneAdder(GeneralCircuit): """Class representing unsigned Kogge-Stone adder (using valency-2 logic gates). The Kogge-Stone adder belongs to a type of tree (parallel-prefix) adders. @@ -86,7 +67,7 @@ class UnsignedKoggeStoneAdder(ArithmeticCircuit): """ def __init__(self, a: Bus, b: Bus, prefix: str = "", name: str = "u_ksa", **kwargs): self.N = max(a.N, b.N) - super().__init__(a=a, b=b, prefix=prefix, name=name, out_N=self.N+1, **kwargs) + super().__init__(inputs=[a, b], prefix=prefix, name=name, out_N=self.N+1, **kwargs) # Bus sign extension in case buses have different lengths self.a.bus_extend(N=self.N, prefix=a.prefix) @@ -129,7 +110,7 @@ class UnsignedKoggeStoneAdder(ArithmeticCircuit): self.generate_sig[i_wire+1].append(self.get_previous_component().get_generate_wire()) -class SignedKoggeStoneAdder(UnsignedKoggeStoneAdder, ArithmeticCircuit): +class SignedKoggeStoneAdder(UnsignedKoggeStoneAdder, GeneralCircuit): """Class representing signed Kogge-Stone adder (using valency-2 logic gates). The Kogge-Stone adder belongs to a type of tree (parallel-prefix) adders. diff --git a/ariths_gen/multi_bit_circuits/adders/ladner_fischer_adder.py b/ariths_gen/multi_bit_circuits/adders/ladner_fischer_adder.py index 68919dd..a0d170e 100644 --- a/ariths_gen/multi_bit_circuits/adders/ladner_fischer_adder.py +++ b/ariths_gen/multi_bit_circuits/adders/ladner_fischer_adder.py @@ -1,41 +1,22 @@ from ariths_gen.wire_components import ( - Wire, ConstantWireValue0, - ConstantWireValue1, Bus ) from ariths_gen.core.arithmetic_circuits import ( - ArithmeticCircuit, - MultiplierCircuit + GeneralCircuit ) from ariths_gen.one_bit_circuits.one_bit_components import ( - HalfAdder, - FullAdder, PGSumLogic, GreyCell, BlackCell ) from ariths_gen.one_bit_circuits.logic_gates import ( - AndGate, - NandGate, - OrGate, - NorGate, - XorGate, - XnorGate, - NotGate -) -from ariths_gen.multi_bit_circuits.adders import ( - UnsignedCarryLookaheadAdder, - UnsignedPGRippleCarryAdder, - UnsignedRippleCarryAdder, - SignedCarryLookaheadAdder, - SignedPGRippleCarryAdder, - SignedRippleCarryAdder + XorGate ) import math -class UnsignedLadnerFischerAdder(ArithmeticCircuit): +class UnsignedLadnerFischerAdder(GeneralCircuit): """Class representing unsigned Ladner-Fischer adder (using valency-2 logic gates). The Ladner-Fischer adder belongs to a type of tree (parallel-prefix) adders. @@ -88,7 +69,7 @@ class UnsignedLadnerFischerAdder(ArithmeticCircuit): """ def __init__(self, a: Bus, b: Bus, prefix: str = "", name: str = "u_lfa", config_choice: int = 1, **kwargs): self.N = max(a.N, b.N) - super().__init__(a=a, b=b, prefix=prefix, name=name, out_N=self.N+1, **kwargs) + super().__init__(inputs=[a, b], prefix=prefix, name=name, out_N=self.N+1, **kwargs) # Bus sign extension in case buses have different lengths self.a.bus_extend(N=self.N, prefix=a.prefix) @@ -188,7 +169,7 @@ class UnsignedLadnerFischerAdder(ArithmeticCircuit): self.generate_sig[i_wire+1].append(self.get_previous_component().get_generate_wire()) -class SignedLadnerFischerAdder(UnsignedLadnerFischerAdder, ArithmeticCircuit): +class SignedLadnerFischerAdder(UnsignedLadnerFischerAdder, GeneralCircuit): """Class representing signed Ladner-Fischer adder (using valency-2 logic gates). The Ladner-Fischer adder belongs to a type of tree (parallel-prefix) adders. diff --git a/ariths_gen/multi_bit_circuits/adders/pg_ripple_carry_adder.py b/ariths_gen/multi_bit_circuits/adders/pg_ripple_carry_adder.py index 2a41bdf..f100019 100644 --- a/ariths_gen/multi_bit_circuits/adders/pg_ripple_carry_adder.py +++ b/ariths_gen/multi_bit_circuits/adders/pg_ripple_carry_adder.py @@ -1,30 +1,21 @@ from ariths_gen.wire_components import ( - Wire, ConstantWireValue0, - ConstantWireValue1, Bus ) from ariths_gen.core.arithmetic_circuits import ( - ArithmeticCircuit, - MultiplierCircuit + GeneralCircuit ) from ariths_gen.one_bit_circuits.one_bit_components import ( - HalfAdder, - FullAdder, PGSumLogic ) from ariths_gen.one_bit_circuits.logic_gates import ( AndGate, - NandGate, OrGate, - NorGate, - XorGate, - XnorGate, - NotGate + XorGate ) -class UnsignedPGRippleCarryAdder(ArithmeticCircuit): +class UnsignedPGRippleCarryAdder(GeneralCircuit): """Class representing unsigned ripple carry adder with propagate/generate logic. Unsigned ripple carry adder with PG logic represents slightly different rca implementation @@ -69,7 +60,7 @@ class UnsignedPGRippleCarryAdder(ArithmeticCircuit): """ def __init__(self, a: Bus, b: Bus, prefix: str = "", name: str = "u_pg_rca", **kwargs): self.N = max(a.N, b.N) - super().__init__(a=a, b=b, prefix=prefix, name=name, out_N=self.N+1, **kwargs) + super().__init__(inputs=[a, b], prefix=prefix, name=name, out_N=self.N+1, **kwargs) # Bus sign extension in case buses have different lengths self.a.bus_extend(N=self.N, prefix=a.prefix) @@ -96,7 +87,7 @@ class UnsignedPGRippleCarryAdder(ArithmeticCircuit): self.out.connect(self.N, obj_or.out) -class SignedPGRippleCarryAdder(UnsignedPGRippleCarryAdder, ArithmeticCircuit): +class SignedPGRippleCarryAdder(UnsignedPGRippleCarryAdder, GeneralCircuit): """Class representing signed ripple carry adder with propagate/generate logic. Signed ripple carry adder with PG logic represents slightly different rca implementation diff --git a/ariths_gen/multi_bit_circuits/adders/ripple_carry_adder.py b/ariths_gen/multi_bit_circuits/adders/ripple_carry_adder.py index 252762b..6daf541 100644 --- a/ariths_gen/multi_bit_circuits/adders/ripple_carry_adder.py +++ b/ariths_gen/multi_bit_circuits/adders/ripple_carry_adder.py @@ -1,29 +1,19 @@ from ariths_gen.wire_components import ( - Wire, - ConstantWireValue0, - ConstantWireValue1, Bus ) from ariths_gen.core.arithmetic_circuits import ( - ArithmeticCircuit, - MultiplierCircuit + GeneralCircuit ) from ariths_gen.one_bit_circuits.one_bit_components import ( HalfAdder, FullAdder ) from ariths_gen.one_bit_circuits.logic_gates import ( - AndGate, - NandGate, - OrGate, - NorGate, - XorGate, - XnorGate, - NotGate + XorGate ) -class UnsignedRippleCarryAdder(ArithmeticCircuit): +class UnsignedRippleCarryAdder(GeneralCircuit): """Class representing unsigned ripple carry adder. Unsigned ripple carry adder represents N-bit unsigned adder which is composed of @@ -53,7 +43,7 @@ class UnsignedRippleCarryAdder(ArithmeticCircuit): """ def __init__(self, a: Bus, b: Bus, prefix: str = "", name: str = "u_rca", **kwargs): self.N = max(a.N, b.N) - super().__init__(a=a, b=b, prefix=prefix, name=name, out_N=self.N+1, **kwargs) + super().__init__(inputs=[a, b], prefix=prefix, name=name, out_N=self.N+1, **kwargs) # Bus sign extension in case buses have different lengths self.a.bus_extend(N=self.N, prefix=a.prefix) @@ -74,7 +64,7 @@ class UnsignedRippleCarryAdder(ArithmeticCircuit): self.out.connect(self.N, obj_adder.get_carry_wire()) -class SignedRippleCarryAdder(UnsignedRippleCarryAdder, ArithmeticCircuit): +class SignedRippleCarryAdder(UnsignedRippleCarryAdder, GeneralCircuit): """Class representing signed ripple carry adder. Signed ripple carry adder represents N-bit signed adder which is composed of diff --git a/ariths_gen/multi_bit_circuits/adders/sklansky_adder.py b/ariths_gen/multi_bit_circuits/adders/sklansky_adder.py index 84d08ff..1785627 100644 --- a/ariths_gen/multi_bit_circuits/adders/sklansky_adder.py +++ b/ariths_gen/multi_bit_circuits/adders/sklansky_adder.py @@ -1,40 +1,21 @@ from ariths_gen.wire_components import ( - Wire, ConstantWireValue0, - ConstantWireValue1, Bus ) from ariths_gen.core.arithmetic_circuits import ( - ArithmeticCircuit, - MultiplierCircuit + GeneralCircuit ) from ariths_gen.one_bit_circuits.one_bit_components import ( - HalfAdder, - FullAdder, PGSumLogic, GreyCell, BlackCell ) from ariths_gen.one_bit_circuits.logic_gates import ( - AndGate, - NandGate, - OrGate, - NorGate, - XorGate, - XnorGate, - NotGate -) -from ariths_gen.multi_bit_circuits.adders import ( - UnsignedCarryLookaheadAdder, - UnsignedPGRippleCarryAdder, - UnsignedRippleCarryAdder, - SignedCarryLookaheadAdder, - SignedPGRippleCarryAdder, - SignedRippleCarryAdder + XorGate ) -class UnsignedSklanskyAdder(ArithmeticCircuit): +class UnsignedSklanskyAdder(GeneralCircuit): """Class representing unsigned Sklansky (or divide-and-conquer) adder (using valency-2 logic gates). The Sklansky adder belongs to a type of tree (parallel-prefix) adders. @@ -85,7 +66,7 @@ class UnsignedSklanskyAdder(ArithmeticCircuit): """ def __init__(self, a: Bus, b: Bus, prefix: str = "", name: str = "u_sa", **kwargs): self.N = max(a.N, b.N) - super().__init__(a=a, b=b, prefix=prefix, name=name, out_N=self.N+1, **kwargs) + super().__init__(inputs=[a, b], prefix=prefix, name=name, out_N=self.N+1, **kwargs) # Bus sign extension in case buses have different lengths self.a.bus_extend(N=self.N, prefix=a.prefix) @@ -132,7 +113,7 @@ class UnsignedSklanskyAdder(ArithmeticCircuit): prev_stage_int_value += 2**stage -class SignedSklanskyAdder(UnsignedSklanskyAdder, ArithmeticCircuit): +class SignedSklanskyAdder(UnsignedSklanskyAdder, GeneralCircuit): """Class representing signed Sklansky (or divide-and-conquer) adder (using valency-2 logic gates). The Sklansky adder belongs to a type of tree (parallel-prefix) adders. diff --git a/ariths_gen/multi_bit_circuits/approximate_adders/__init__.py b/ariths_gen/multi_bit_circuits/approximate_adders/__init__.py index 3c027b5..7b10c5c 100644 --- a/ariths_gen/multi_bit_circuits/approximate_adders/__init__.py +++ b/ariths_gen/multi_bit_circuits/approximate_adders/__init__.py @@ -1 +1 @@ -from .quad import QuAdder \ No newline at end of file +from .quad import QuAdder diff --git a/ariths_gen/multi_bit_circuits/approximate_adders/quad.py b/ariths_gen/multi_bit_circuits/approximate_adders/quad.py index 473ee09..8f8b8de 100644 --- a/ariths_gen/multi_bit_circuits/approximate_adders/quad.py +++ b/ariths_gen/multi_bit_circuits/approximate_adders/quad.py @@ -7,33 +7,20 @@ M. A. Hanif, R. Hafiz, O. Hasan and M. Shafique, "QuAd: Design and analysis of Q """ from ...wire_components import ( - Wire, ConstantWireValue0, - ConstantWireValue1, Bus ) from ariths_gen.core.arithmetic_circuits import ( - ArithmeticCircuit, - MultiplierCircuit + GeneralCircuit ) -from ariths_gen.one_bit_circuits.one_bit_components import ( - HalfAdder, - FullAdder + +from ariths_gen.multi_bit_circuits.adders.ripple_carry_adder import ( + UnsignedRippleCarryAdder ) -from ariths_gen.one_bit_circuits.logic_gates import ( - AndGate, - NandGate, - OrGate, - NorGate, - XorGate, - XnorGate, - NotGate -) -from ariths_gen.multi_bit_circuits.adders.ripple_carry_adder import UnsignedRippleCarryAdder import warnings -class QuAdder(ArithmeticCircuit): +class QuAdder(GeneralCircuit): """ Implementation of QuAd @@ -102,13 +89,13 @@ class QuAdder(ArithmeticCircuit): self.use_log = use_log self.N = max(a.N, b.N) - super().__init__(a=a, b=b, prefix=prefix, name=name, out_N=self.N+1, **kwargs) + super().__init__(inputs=[a, b], prefix=prefix, name=name, out_N=self.N+1, **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) - #warnings.warn("QuAdder is not tested yet") + # warnings.warn("QuAdder is not tested yet") # Connect all outputs to zero for i in range(self.N+1): @@ -128,7 +115,7 @@ class QuAdder(ArithmeticCircuit): for i, j in zip(out_indexes, in_indexes): if j >= in_bus.N: - out_bus[i] = ConstantWireValue0() # unsigned extension + out_bus[i] = ConstantWireValue0() # unsigned extension else: out_bus.connect(i, in_bus.get_wire(j)) # [i] = in_bus[j] diff --git a/ariths_gen/multi_bit_circuits/approximate_multipliers/broken_array_multiplier.py b/ariths_gen/multi_bit_circuits/approximate_multipliers/broken_array_multiplier.py index fb2177d..c3ecc9d 100644 --- a/ariths_gen/multi_bit_circuits/approximate_multipliers/broken_array_multiplier.py +++ b/ariths_gen/multi_bit_circuits/approximate_multipliers/broken_array_multiplier.py @@ -1,11 +1,8 @@ from ariths_gen.wire_components import ( - Wire, ConstantWireValue0, - ConstantWireValue1, Bus ) from ariths_gen.core.arithmetic_circuits import ( - ArithmeticCircuit, MultiplierCircuit ) from ariths_gen.one_bit_circuits.one_bit_components import ( @@ -13,17 +10,7 @@ from ariths_gen.one_bit_circuits.one_bit_components import ( FullAdder ) from ariths_gen.one_bit_circuits.logic_gates import ( - AndGate, - NandGate, - OrGate, - NorGate, - XorGate, - XnorGate, - NotGate -) -from ariths_gen.multi_bit_circuits.multipliers import ( - UnsignedArrayMultiplier, - SignedArrayMultiplier + AndGate ) @@ -107,7 +94,7 @@ class UnsignedBrokenArrayMultiplier(MultiplierCircuit): # Vertical cut should be greater or equal to horizontal cut assert vertical_cut >= horizontal_cut - super().__init__(a=a, b=b, prefix=prefix, name=name, out_N=self.N*2, **kwargs) + super().__init__(inputs=[a, b], prefix=prefix, name=name, out_N=self.N*2, **kwargs) # Bus sign extension in case buses have different lengths self.a.bus_extend(N=self.N, prefix=a.prefix) diff --git a/ariths_gen/multi_bit_circuits/approximate_multipliers/broken_carry_save_multiplier.py b/ariths_gen/multi_bit_circuits/approximate_multipliers/broken_carry_save_multiplier.py index 141500b..910bc9c 100644 --- a/ariths_gen/multi_bit_circuits/approximate_multipliers/broken_carry_save_multiplier.py +++ b/ariths_gen/multi_bit_circuits/approximate_multipliers/broken_carry_save_multiplier.py @@ -1,11 +1,8 @@ from ariths_gen.wire_components import ( - Wire, ConstantWireValue0, - ConstantWireValue1, Bus ) from ariths_gen.core.arithmetic_circuits import ( - ArithmeticCircuit, MultiplierCircuit, ) from ariths_gen.core.logic_gate_circuits import ( @@ -16,13 +13,7 @@ from ariths_gen.one_bit_circuits.one_bit_components import ( FullAdder ) from ariths_gen.one_bit_circuits.logic_gates import ( - AndGate, - NandGate, - OrGate, - NorGate, - XorGate, - XnorGate, - NotGate + AndGate ) from ariths_gen.multi_bit_circuits.adders import ( UnsignedCarryLookaheadAdder @@ -114,7 +105,7 @@ class UnsignedBrokenCarrySaveMultiplier(MultiplierCircuit): # Vertical cut should be greater or equal to horizontal cut assert vertical_cut >= horizontal_cut - super().__init__(a=a, b=b, prefix=prefix, name=name, out_N=self.N*2, **kwargs) + super().__init__(inputs=[a, b], prefix=prefix, name=name, out_N=self.N*2, **kwargs) # Bus sign extension in case buses have different lengths self.a.bus_extend(N=self.N, prefix=a.prefix) @@ -213,7 +204,6 @@ class UnsignedBrokenCarrySaveMultiplier(MultiplierCircuit): adder_name = unsigned_adder_class_name(a=a, b=b).prefix + str(final_cpa_N) adder_a = Bus(prefix=f"a", wires_list=previous_sums) adder_b = Bus(prefix=f"b", wires_list=previous_carries) - final_adder = unsigned_adder_class_name(a=adder_a, b=adder_b, prefix=self.prefix, name=adder_name, inner_component=True) self.add_component(final_adder) [self.out.connect(o, final_adder.out.get_wire(o-max(self.N, self.vertical_cut)), inserted_wire_desired_index=o-max(self.N, self.vertical_cut)) for o in range(max(self.N, self.vertical_cut), self.out.N)] diff --git a/ariths_gen/multi_bit_circuits/approximate_multipliers/recursive_multiplier.py b/ariths_gen/multi_bit_circuits/approximate_multipliers/recursive_multiplier.py index b5f67dc..9795bdc 100644 --- a/ariths_gen/multi_bit_circuits/approximate_multipliers/recursive_multiplier.py +++ b/ariths_gen/multi_bit_circuits/approximate_multipliers/recursive_multiplier.py @@ -11,11 +11,9 @@ from ariths_gen.one_bit_circuits.logic_gates import ( XorGate, NotGate ) - from ariths_gen.multi_bit_circuits.adders import ( UnsignedCarryLookaheadAdder ) - import math @@ -55,7 +53,7 @@ class UnsignedAccurateTwoBitMultiplier(MultiplierCircuit): def __init__(self, a: Bus, b: Bus, prefix: str = "", name: str = "u_2bit_accm", **kwargs): self.N = max(a.N, b.N) assert self.N == 2 - super().__init__(a=a, b=b, prefix=prefix, name=name, out_N=self.N*2, **kwargs) + super().__init__(inputs=[a, b], prefix=prefix, name=name, out_N=self.N*2, **kwargs) # Bus sign extension in case buses have different lengths self.a.bus_extend(N=self.N, prefix=a.prefix) @@ -98,14 +96,14 @@ class UnsignedApproximateTwoBitMultiplierM1(MultiplierCircuit): """Class representing unsigned two-bit approximate multiplier variant M1. M1 ax variant defined here: https://ieeexplore.ieee.org/document/8727537 - + ``` A1B1 A1B0 A0B1 A0B0 β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”Œβ–Όβ”€β–Όβ” β”Œβ–Όβ”€β–Όβ” β”Œβ–Όβ”€β–Όβ” β”Œβ–Όβ”€β–Όβ” β”‚ANDβ”‚ β”‚ANDβ”‚ β”‚ANDβ”‚ β”‚ANDβ”‚ β””β”€β”¬β”€β”˜ β””β”€β”¬β”€β”˜ β””β”€β”¬β”€β”˜ β””β”€β”¬β”€β”˜ - β”‚ β”‚ └┐ β”‚ + β”‚ β”‚ └┐ β”‚ β”‚ └─────┐ β”‚ β”‚ └──────┐ β”Œβ–Όβ”€β–Όβ” β”‚ β”‚ β”‚ ORβ”‚ β”‚ @@ -126,7 +124,7 @@ class UnsignedApproximateTwoBitMultiplierM1(MultiplierCircuit): def __init__(self, a: Bus, b: Bus, prefix: str = "", name: str = "u_2bit_axm1", **kwargs): self.N = max(a.N, b.N) assert self.N == 2 - super().__init__(a=a, b=b, prefix=prefix, name=name, out_N=self.N*2, **kwargs) + super().__init__(inputs=[a, b], prefix=prefix, name=name, out_N=self.N*2, **kwargs) # Bus sign extension in case buses have different lengths self.a.bus_extend(N=self.N, prefix=a.prefix) @@ -165,7 +163,7 @@ class UnsignedApproximateTwoBitMultiplierM2(MultiplierCircuit): """Class representing unsigned two-bit approximate multiplier variant M2. M2 ax variant defined here: https://ieeexplore.ieee.org/document/8727537 - + ``` A1B1 A1B0 A0B1 β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ @@ -200,7 +198,7 @@ class UnsignedApproximateTwoBitMultiplierM2(MultiplierCircuit): def __init__(self, a: Bus, b: Bus, prefix: str = "", name: str = "u_2bit_axm1", **kwargs): self.N = max(a.N, b.N) assert self.N == 2 - super().__init__(a=a, b=b, prefix=prefix, name=name, out_N=self.N*2, **kwargs) + super().__init__(inputs=[a, b], prefix=prefix, name=name, out_N=self.N*2, **kwargs) # Bus sign extension in case buses have different lengths self.a.bus_extend(N=self.N, prefix=a.prefix) @@ -241,7 +239,7 @@ class UnsignedApproximateTwoBitMultiplierM3(MultiplierCircuit): """Class representing unsigned two-bit approximate multiplier variant M3. M3 ax variant defined here: https://ieeexplore.ieee.org/document/8727537 - + ``` A1B1 A1B0 A0B1 A0B0 β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ @@ -280,7 +278,7 @@ class UnsignedApproximateTwoBitMultiplierM3(MultiplierCircuit): def __init__(self, a: Bus, b: Bus, prefix: str = "", name: str = "u_2bit_axm3", **kwargs): self.N = max(a.N, b.N) assert self.N == 2 - super().__init__(a=a, b=b, prefix=prefix, name=name, out_N=self.N*2, **kwargs) + super().__init__(inputs=[a, b], prefix=prefix, name=name, out_N=self.N*2, **kwargs) # Bus sign extension in case buses have different lengths self.a.bus_extend(N=self.N, prefix=a.prefix) @@ -330,7 +328,7 @@ class UnsignedApproximateTwoBitMultiplierM4(MultiplierCircuit): β”Œβ–Όβ”€β–Όβ” β”Œβ–Όβ”€β–Όβ” β”Œβ–Όβ”€β–Όβ” β”Œβ–Όβ”€β–Όβ” β”‚ANDβ”‚ β”‚ANDβ”‚ β”‚ANDβ”‚ β”‚ANDβ”‚ β””β”€β”¬β”€β”˜ β””β”€β”¬β”€β”˜ β””β”€β”¬β”€β”˜ β””β”€β”¬β”€β”˜ - β”‚ β”‚ └┐ β”‚ + β”‚ β”‚ └┐ β”‚ β”‚ └─────┐ β”‚ β”‚ └──────┐ β”Œβ–Όβ”€β–Όβ” β”‚ β”‚ β”‚XORβ”‚ β”‚ @@ -351,7 +349,7 @@ class UnsignedApproximateTwoBitMultiplierM4(MultiplierCircuit): def __init__(self, a: Bus, b: Bus, prefix: str = "", name: str = "u_2bit_axm4", **kwargs): self.N = max(a.N, b.N) assert self.N == 2 - super().__init__(a=a, b=b, prefix=prefix, name=name, out_N=self.N*2, **kwargs) + super().__init__(inputs=[a, b], prefix=prefix, name=name, out_N=self.N*2, **kwargs) # Bus sign extension in case buses have different lengths self.a.bus_extend(N=self.N, prefix=a.prefix) @@ -390,12 +388,12 @@ class UnsignedRecursiveMultiplier(MultiplierCircuit): """Class representing unsigned recursive multiplier. Input bit-vector length N can be any power of two greater than 1 (e.g. 2, 4, 8, ...). - + The internal structure of the recursive multiplier is composed of subsequent two-bit submultipliers provided in the input `submultipliers` list. The `submultipliers` list should contain the classes of the two-bit submultipliers that will be used for instantiation. If None are provided, accurate two-bit submultipliers are assumed. - + The number of submultipliers required is equal to (N/2)Β² for N > 2. For N = 2, only one two-bit submultiplier is required. - + Description of the __init__ method. Args: @@ -410,7 +408,7 @@ class UnsignedRecursiveMultiplier(MultiplierCircuit): def __init__(self, a: Bus, b: Bus, prefix: str = "", name: str = "u_rm", submultipliers: list = None, unsigned_adder_class_name: str = UnsignedCarryLookaheadAdder, **kwargs): self.N = max(a.N, b.N) assert self.N > 1 and self.N & (self.N-1) == 0 # assure that N is a power of two greater than 1 (So allowed N is 2, 4, 8, ..) - super().__init__(a=a, b=b, prefix=prefix, name=name, out_N=self.N*2, **kwargs) + super().__init__(inputs=[a, b], prefix=prefix, name=name, out_N=self.N*2, **kwargs) # Bus sign extension in case buses have different lengths self.a.bus_extend(N=self.N, prefix=a.prefix) @@ -424,7 +422,7 @@ class UnsignedRecursiveMultiplier(MultiplierCircuit): assert (self.N > 2 and len(submultipliers) == (self.N//2)**2) or (self.N == 2 and len(submultipliers) == 1) - if self.N == 2: # Base case for just one two-bit multiplier + if self.N == 2: # Base case for just one two-bit multiplier # TODO add suffix in ariths_gen rework mult = submultipliers[0](Bus(prefix=self.prefix + "_a", wires_list=self.a.bus), Bus(prefix=self.prefix + "_b", wires_list=self.b.bus), prefix=self.prefix + "_" + str(self.get_instance_num(cls=submultipliers[0])), **kwargs) self.add_component(mult) @@ -467,7 +465,7 @@ class UnsignedRecursiveMultiplier(MultiplierCircuit): # Create wire vectors holding partial products for final summation pp = Bus(prefix=f"pp_{m}", N=self.out.N, wires_list=[ConstantWireValue0() for _ in range(self.out.N)]) - #[pp.connect_bus(submult.out, offset=(self.out.N-4)-(a_bus_offset+b_bus_offset))] + # [pp.connect_bus(submult.out, offset=(self.out.N-4)-(a_bus_offset+b_bus_offset))] [pp.connect((self.out.N-1)-(a_bus_offset+b_bus_offset)-i, submult.out[3-i], inserted_wire_desired_index=3-i) for i in range(4)] partial_products.append(pp) diff --git a/ariths_gen/multi_bit_circuits/approximate_multipliers/truncated_array_multiplier.py b/ariths_gen/multi_bit_circuits/approximate_multipliers/truncated_array_multiplier.py index dcaa949..5a6bf8a 100644 --- a/ariths_gen/multi_bit_circuits/approximate_multipliers/truncated_array_multiplier.py +++ b/ariths_gen/multi_bit_circuits/approximate_multipliers/truncated_array_multiplier.py @@ -1,11 +1,8 @@ from ariths_gen.wire_components import ( - Wire, ConstantWireValue0, - ConstantWireValue1, Bus ) from ariths_gen.core.arithmetic_circuits import ( - ArithmeticCircuit, MultiplierCircuit ) from ariths_gen.one_bit_circuits.one_bit_components import ( @@ -13,13 +10,7 @@ from ariths_gen.one_bit_circuits.one_bit_components import ( FullAdder ) from ariths_gen.one_bit_circuits.logic_gates import ( - AndGate, - NandGate, - OrGate, - NorGate, - XorGate, - XnorGate, - NotGate + AndGate ) @@ -87,7 +78,7 @@ class UnsignedTruncatedArrayMultiplier(MultiplierCircuit): # Cut level should be: 0 <= truncation_cut < N assert truncation_cut < self.N - super().__init__(a=a, b=b, prefix=prefix, name=name, out_N=self.N*2, **kwargs) + super().__init__(inputs=[a, b], prefix=prefix, name=name, out_N=self.N*2, **kwargs) # Bus sign extension in case buses have different lengths self.a.bus_extend(N=self.N, prefix=a.prefix) diff --git a/ariths_gen/multi_bit_circuits/approximate_multipliers/truncated_carry_save_multiplier.py b/ariths_gen/multi_bit_circuits/approximate_multipliers/truncated_carry_save_multiplier.py index 0a455cf..a20f9b4 100644 --- a/ariths_gen/multi_bit_circuits/approximate_multipliers/truncated_carry_save_multiplier.py +++ b/ariths_gen/multi_bit_circuits/approximate_multipliers/truncated_carry_save_multiplier.py @@ -1,11 +1,8 @@ from ariths_gen.wire_components import ( - Wire, ConstantWireValue0, - ConstantWireValue1, Bus ) from ariths_gen.core.arithmetic_circuits import ( - ArithmeticCircuit, MultiplierCircuit ) from ariths_gen.one_bit_circuits.one_bit_components import ( @@ -14,12 +11,6 @@ from ariths_gen.one_bit_circuits.one_bit_components import ( ) from ariths_gen.one_bit_circuits.logic_gates import ( AndGate, - NandGate, - OrGate, - NorGate, - XorGate, - XnorGate, - NotGate ) from ariths_gen.multi_bit_circuits.adders import ( UnsignedCarryLookaheadAdder @@ -101,7 +92,7 @@ class UnsignedTruncatedCarrySaveMultiplier(MultiplierCircuit): # Cut level should be: 0 <= truncation_cut < N assert truncation_cut < self.N - super().__init__(a=a, b=b, prefix=prefix, name=name, out_N=self.N*2, **kwargs) + super().__init__(inputs=[a, b], prefix=prefix, name=name, out_N=self.N*2, **kwargs) # Bus sign extension in case buses have different lengths self.a.bus_extend(N=self.N, prefix=a.prefix) diff --git a/ariths_gen/multi_bit_circuits/dividers/array_divider.py b/ariths_gen/multi_bit_circuits/dividers/array_divider.py index 946266e..32dbce7 100644 --- a/ariths_gen/multi_bit_circuits/dividers/array_divider.py +++ b/ariths_gen/multi_bit_circuits/dividers/array_divider.py @@ -1,31 +1,20 @@ from ariths_gen.wire_components import ( - Wire, ConstantWireValue0, - ConstantWireValue1, Bus ) from ariths_gen.core.arithmetic_circuits import ( - ArithmeticCircuit, - MultiplierCircuit + GeneralCircuit ) from ariths_gen.one_bit_circuits.one_bit_components import ( - HalfAdder, - FullAdder, TwoOneMultiplexer, FullSubtractor ) from ariths_gen.one_bit_circuits.logic_gates import ( - AndGate, - NandGate, - OrGate, - NorGate, - XorGate, - XnorGate, NotGate ) -class ArrayDivider(ArithmeticCircuit): +class ArrayDivider(GeneralCircuit): """Class representing array divider. Array divider performs division between two N bit numbers and stores their @@ -96,7 +85,7 @@ class ArrayDivider(ArithmeticCircuit): """ def __init__(self, a: Bus, b: Bus, prefix: str = "", name: str = "arrdiv", **kwargs): self.N = max(a.N, b.N) - super().__init__(a=a, b=b, prefix=prefix, name=name, out_N=self.N, **kwargs) + super().__init__(inputs=[a, b], prefix=prefix, name=name, out_N=self.N, **kwargs) # Bus sign extension in case buses have different lengths self.a.bus_extend(N=self.N, prefix=a.prefix) diff --git a/ariths_gen/multi_bit_circuits/multipliers/array_multiplier.py b/ariths_gen/multi_bit_circuits/multipliers/array_multiplier.py index 38c4330..3979f17 100644 --- a/ariths_gen/multi_bit_circuits/multipliers/array_multiplier.py +++ b/ariths_gen/multi_bit_circuits/multipliers/array_multiplier.py @@ -1,11 +1,9 @@ from ariths_gen.wire_components import ( - Wire, ConstantWireValue0, ConstantWireValue1, Bus ) from ariths_gen.core.arithmetic_circuits import ( - ArithmeticCircuit, MultiplierCircuit ) from ariths_gen.one_bit_circuits.one_bit_components import ( @@ -15,11 +13,8 @@ from ariths_gen.one_bit_circuits.one_bit_components import ( from ariths_gen.one_bit_circuits.logic_gates import ( AndGate, NandGate, - OrGate, NorGate, - XorGate, - XnorGate, - NotGate + XorGate ) @@ -83,7 +78,7 @@ class UnsignedArrayMultiplier(MultiplierCircuit): """ def __init__(self, a: Bus, b: Bus, prefix: str = "", name: str = "u_arrmul", **kwargs): self.N = max(a.N, b.N) - super().__init__(a=a, b=b, prefix=prefix, name=name, out_N=self.N*2, **kwargs) + super().__init__(inputs=[a, b], prefix=prefix, name=name, out_N=self.N*2, **kwargs) # Bus sign extension in case buses have different lengths self.a.bus_extend(N=self.N, prefix=a.prefix) @@ -190,7 +185,7 @@ class SignedArrayMultiplier(MultiplierCircuit): """ def __init__(self, a: Bus, b: Bus, prefix: str = "", name: str = "s_arrmul", **kwargs): self.N = max(a.N, b.N) - super().__init__(a=a, b=b, prefix=prefix, name=name, out_N=self.N*2, signed=True, **kwargs) + super().__init__(inputs=[a, b], prefix=prefix, name=name, out_N=self.N*2, signed=True, **kwargs) # Bus sign extension in case buses have different lengths self.a.bus_extend(N=self.N, prefix=a.prefix) diff --git a/ariths_gen/multi_bit_circuits/multipliers/carry_save_multiplier.py b/ariths_gen/multi_bit_circuits/multipliers/carry_save_multiplier.py index e7d2602..8390860 100644 --- a/ariths_gen/multi_bit_circuits/multipliers/carry_save_multiplier.py +++ b/ariths_gen/multi_bit_circuits/multipliers/carry_save_multiplier.py @@ -1,11 +1,9 @@ from ariths_gen.wire_components import ( - Wire, ConstantWireValue0, ConstantWireValue1, Bus ) from ariths_gen.core.arithmetic_circuits import ( - ArithmeticCircuit, MultiplierCircuit ) from ariths_gen.one_bit_circuits.one_bit_components import ( @@ -15,11 +13,7 @@ from ariths_gen.one_bit_circuits.one_bit_components import ( from ariths_gen.one_bit_circuits.logic_gates import ( AndGate, NandGate, - OrGate, - NorGate, - XorGate, - XnorGate, - NotGate + NorGate ) from ariths_gen.multi_bit_circuits.adders import ( UnsignedCarryLookaheadAdder @@ -96,7 +90,7 @@ class UnsignedCarrySaveMultiplier(MultiplierCircuit): """ def __init__(self, a: Bus, b: Bus, prefix: str = "", name: str = "u_csamul", unsigned_adder_class_name: str = UnsignedCarryLookaheadAdder, **kwargs): self.N = max(a.N, b.N) - super().__init__(a=a, b=b, prefix=prefix, name=name, out_N=self.N*2, **kwargs) + super().__init__(inputs=[a, b], prefix=prefix, name=name, out_N=self.N*2, **kwargs) # Bus sign extension in case buses have different lengths self.a.bus_extend(N=self.N, prefix=a.prefix) @@ -219,7 +213,7 @@ class SignedCarrySaveMultiplier(MultiplierCircuit): """ def __init__(self, a: Bus, b: Bus, prefix: str = "", name: str = "s_csamul", unsigned_adder_class_name: str = UnsignedCarryLookaheadAdder, **kwargs): self.N = max(a.N, b.N) - super().__init__(a=a, b=b, prefix=prefix, name=name, out_N=self.N*2, signed=True, **kwargs) + super().__init__(inputs=[a, b], prefix=prefix, name=name, out_N=self.N*2, signed=True, **kwargs) # Bus sign extension in case buses have different lengths self.a.bus_extend(N=self.N, prefix=a.prefix) diff --git a/ariths_gen/multi_bit_circuits/multipliers/dadda_multiplier.py b/ariths_gen/multi_bit_circuits/multipliers/dadda_multiplier.py index 2da77c8..c6f9971 100644 --- a/ariths_gen/multi_bit_circuits/multipliers/dadda_multiplier.py +++ b/ariths_gen/multi_bit_circuits/multipliers/dadda_multiplier.py @@ -1,12 +1,12 @@ -from ariths_gen.multi_bit_circuits.adders.carry_lookahead_adder import UnsignedCarryLookaheadAdder +from ariths_gen.multi_bit_circuits.adders.carry_lookahead_adder import ( + UnsignedCarryLookaheadAdder +) from ariths_gen.wire_components import ( - Wire, ConstantWireValue0, ConstantWireValue1, Bus ) from ariths_gen.core.arithmetic_circuits import ( - ArithmeticCircuit, MultiplierCircuit ) from ariths_gen.one_bit_circuits.one_bit_components import ( @@ -14,13 +14,7 @@ from ariths_gen.one_bit_circuits.one_bit_components import ( FullAdder ) from ariths_gen.one_bit_circuits.logic_gates import ( - AndGate, - NandGate, - OrGate, - NorGate, - XorGate, - XnorGate, - NotGate + XorGate ) @@ -50,7 +44,7 @@ class UnsignedDaddaMultiplier(MultiplierCircuit): """ def __init__(self, a: Bus, b: Bus, prefix: str = "", name: str = "u_dadda_cla", unsigned_adder_class_name: str = UnsignedCarryLookaheadAdder, **kwargs): self.N = max(a.N, b.N) - super().__init__(a=a, b=b, prefix=prefix, name=name, out_N=self.N*2, **kwargs) + super().__init__(inputs=[a, b], prefix=prefix, name=name, out_N=self.N*2, **kwargs) # Bus sign extension in case buses have different lengths self.a.bus_extend(N=self.N, prefix=a.prefix) @@ -154,7 +148,7 @@ class SignedDaddaMultiplier(MultiplierCircuit): """ def __init__(self, a: Bus, b: Bus, prefix: str = "", name: str = "s_dadda_cla", unsigned_adder_class_name: str = UnsignedCarryLookaheadAdder, **kwargs): self.N = max(a.N, b.N) - super().__init__(a=a, b=b, prefix=prefix, name=name, out_N=self.N*2, signed=True, **kwargs) + super().__init__(inputs=[a, b], prefix=prefix, name=name, out_N=self.N*2, signed=True, **kwargs) # Bus sign extension in case buses have different lengths self.a.bus_extend(N=self.N, prefix=a.prefix) diff --git a/ariths_gen/multi_bit_circuits/multipliers/wallace_multiplier.py b/ariths_gen/multi_bit_circuits/multipliers/wallace_multiplier.py index 24ba4ad..6cf165a 100644 --- a/ariths_gen/multi_bit_circuits/multipliers/wallace_multiplier.py +++ b/ariths_gen/multi_bit_circuits/multipliers/wallace_multiplier.py @@ -1,12 +1,12 @@ -from ariths_gen.multi_bit_circuits.adders.carry_lookahead_adder import UnsignedCarryLookaheadAdder +from ariths_gen.multi_bit_circuits.adders.carry_lookahead_adder import ( + UnsignedCarryLookaheadAdder +) from ariths_gen.wire_components import ( - Wire, ConstantWireValue0, ConstantWireValue1, Bus ) from ariths_gen.core.arithmetic_circuits import ( - ArithmeticCircuit, MultiplierCircuit ) from ariths_gen.one_bit_circuits.one_bit_components import ( @@ -14,13 +14,7 @@ from ariths_gen.one_bit_circuits.one_bit_components import ( FullAdder ) from ariths_gen.one_bit_circuits.logic_gates import ( - AndGate, - NandGate, - OrGate, - NorGate, - XorGate, - XnorGate, - NotGate + XorGate ) from ariths_gen.multi_bit_circuits.adders import ( CarrySaveAdderComponent, @@ -86,7 +80,7 @@ class UnsignedWallaceMultiplier(MultiplierCircuit): """ def __init__(self, a: Bus, b: Bus, prefix: str = "", name: str = "u_wallace_cla", use_csa: bool = True, unsigned_adder_class_name: str = UnsignedCarryLookaheadAdder, **kwargs): self.N = max(a.N, b.N) - super().__init__(a=a, b=b, prefix=prefix, name=name, out_N=self.N*2, **kwargs) + super().__init__(inputs=[a, b], prefix=prefix, name=name, out_N=self.N*2, **kwargs) # Bus sign extension in case buses have different lengths self.a.bus_extend(N=self.N, prefix=a.prefix) @@ -249,7 +243,7 @@ class SignedWallaceMultiplier(MultiplierCircuit): """ def __init__(self, a: Bus, b: Bus, prefix: str = "", name: str = "s_wallace_cla", use_csa: bool = True, unsigned_adder_class_name: str = UnsignedCarryLookaheadAdder, **kwargs): self.N = max(a.N, b.N) - super().__init__(a=a, b=b, prefix=prefix, name=name, out_N=self.N*2, signed=True, **kwargs) + super().__init__(inputs=[a, b], prefix=prefix, name=name, out_N=self.N*2, signed=True, **kwargs) # Bus sign extension in case buses have different lengths self.a.bus_extend(N=self.N, prefix=a.prefix) diff --git a/ariths_gen/multi_bit_circuits/others/bit_reduce.py b/ariths_gen/multi_bit_circuits/others/bit_reduce.py index 9752c67..2cabd83 100644 --- a/ariths_gen/multi_bit_circuits/others/bit_reduce.py +++ b/ariths_gen/multi_bit_circuits/others/bit_reduce.py @@ -1,53 +1,25 @@ -""" - -""" - from ariths_gen.wire_components import ( - Wire, - ConstantWireValue0, - ConstantWireValue1, - Bus, - wires + Bus ) from ariths_gen.core.arithmetic_circuits import ( - ArithmeticCircuit, - GeneralCircuit, - MultiplierCircuit -) -from ariths_gen.core.logic_gate_circuits import ( - MultipleInputLogicGate -) -from ariths_gen.one_bit_circuits.one_bit_components import ( - HalfAdder, - FullAdder, - FullAdderP, - TwoOneMultiplexer + GeneralCircuit ) from ariths_gen.one_bit_circuits.logic_gates import ( AndGate, - NandGate, - OrGate, - NorGate, - XorGate, - XnorGate, - NotGate + OrGate +) +from ariths_gen.core.logic_gate_circuits import ( + TwoInputLogicGate ) -from ariths_gen.core.logic_gate_circuits import TwoInputLogicGate, TwoInputInvertedLogicGate, OneInputLogicGate -from ariths_gen.multi_bit_circuits.adders import UnsignedRippleCarryAdder - -from math import log2, ceil class BitReduce(GeneralCircuit): """Class representing tree reducer circuit. Doent work for NAND gate! """ - def __init__(self, a: Bus, gate : TwoInputLogicGate, prefix : str = "", name : str = "bitreduce", **kwargs): + def __init__(self, a: Bus, gate: TwoInputLogicGate, prefix: str = "", name: str = "bitreduce", **kwargs): self.N = a.N - self.a = a - - outc = 1 - super().__init__(name=name, prefix=prefix, inputs = [self.a], out_N=outc) + super().__init__(name=name, prefix=prefix, inputs=[a], out_N=1, **kwargs) # tree reduction def create_tree(a: Bus, depth: int, branch="A"): diff --git a/ariths_gen/multi_bit_circuits/others/compare.py b/ariths_gen/multi_bit_circuits/others/compare.py index ab89fd1..0c252ec 100644 --- a/ariths_gen/multi_bit_circuits/others/compare.py +++ b/ariths_gen/multi_bit_circuits/others/compare.py @@ -1,43 +1,20 @@ -""" - -""" - from ariths_gen.wire_components import ( - Wire, ConstantWireValue0, ConstantWireValue1, - Bus, - wires + Bus ) from ariths_gen.core.arithmetic_circuits import ( - ArithmeticCircuit, - GeneralCircuit, - MultiplierCircuit -) - -from ariths_gen.core.logic_gate_circuits import ( - MultipleInputLogicGate -) -from ariths_gen.one_bit_circuits.one_bit_components import ( - HalfAdder, - FullAdder, - FullAdderP, - TwoOneMultiplexer + GeneralCircuit ) from ariths_gen.one_bit_circuits.logic_gates import ( AndGate, - NandGate, - OrGate, - NorGate, - XorGate, XnorGate, NotGate ) +from ariths_gen.multi_bit_circuits.others import ( + OrReduce +) -from ariths_gen.multi_bit_circuits.others import OrReduce - - -from math import log2, ceil class UnsignedCompareLT(GeneralCircuit): """Class representing unsigned compare @@ -47,12 +24,8 @@ class UnsignedCompareLT(GeneralCircuit): """ def __init__(self, a: Bus, b: Bus, prefix : str = "", name : str = "cmp_lt", **kwargs): - self.a = a - self.b = b self.N = max(a.N, b.N) - - super().__init__(name=name, prefix=prefix, - inputs = [self.a, self.b], out_N=1) + super().__init__(name=name, prefix=prefix, inputs=[a, b], out_N=1) # create wires psum = ConstantWireValue1() @@ -84,12 +57,8 @@ class UnsignedCompareLTE(GeneralCircuit): """ def __init__(self, a: Bus, b: Bus, prefix : str = "", name : str = "cmp_lte", **kwargs): - self.a = a - self.b = b self.N = max(a.N, b.N) - - super().__init__(name=name, prefix=prefix, - inputs = [self.a, self.b], out_N=1) + super().__init__(name=name, prefix=prefix, inputs=[a, b], out_N=1) # create wires psum = ConstantWireValue1() @@ -125,12 +94,8 @@ class UnsignedCompareGT(GeneralCircuit): """ def __init__(self, a: Bus, b: Bus, prefix : str = "", name : str = "cmp_gt", **kwargs): - self.a = a - self.b = b self.N = max(a.N, b.N) - - super().__init__(name=name, prefix=prefix, - inputs = [self.a, self.b], out_N=1) + super().__init__(name=name, prefix=prefix, inputs=[a, b], out_N=1) # create wires psum = ConstantWireValue1() @@ -162,12 +127,8 @@ class UnsignedCompareGTE(GeneralCircuit): """ def __init__(self, a: Bus, b: Bus, prefix : str = "", name : str = "cmp_gte", **kwargs): - self.a = a - self.b = b self.N = max(a.N, b.N) - - super().__init__(name=name, prefix=prefix, - inputs = [self.a, self.b], out_N=1) + super().__init__(name=name, prefix=prefix, inputs=[a, b], out_N=1) # create wires psum = ConstantWireValue1() diff --git a/ariths_gen/multi_bit_circuits/others/popcount.py b/ariths_gen/multi_bit_circuits/others/popcount.py index 16fd141..000cae1 100644 --- a/ariths_gen/multi_bit_circuits/others/popcount.py +++ b/ariths_gen/multi_bit_circuits/others/popcount.py @@ -1,43 +1,17 @@ -""" - -""" -from typing import Union, Optional - from ariths_gen.wire_components import ( - Wire, - ConstantWireValue0, - ConstantWireValue1, - Bus, - wires + Bus ) from ariths_gen.core.arithmetic_circuits import ( - ArithmeticCircuit, GeneralCircuit, - MultiplierCircuit + GeneralCircuit ) -from ariths_gen.core.logic_gate_circuits import ( - MultipleInputLogicGate +from ariths_gen.multi_bit_circuits.adders import ( + UnsignedRippleCarryAdder ) -from ariths_gen.one_bit_circuits.one_bit_components import ( - HalfAdder, - FullAdder, - FullAdderP, - TwoOneMultiplexer -) -from ariths_gen.one_bit_circuits.logic_gates import ( - AndGate, - NandGate, - OrGate, - NorGate, - XorGate, - XnorGate, - NotGate -) - -from ariths_gen.multi_bit_circuits.adders import UnsignedRippleCarryAdder - +from typing import Optional from math import log2, ceil + class UnsignedPopCount(GeneralCircuit): """Class representing unsigned popcount circuit. @@ -45,14 +19,11 @@ class UnsignedPopCount(GeneralCircuit): """ - def __init__(self, a: Bus, adder : Optional[ArithmeticCircuit] = None, prefix : str = "", name : str = "popcnt", **kwargs): + def __init__(self, a: Bus, adder : Optional[GeneralCircuit] = None, prefix : str = "", name : str = "popcnt", **kwargs): self.N = a.N - self.a = a - outc = ceil(log2(self.N + 1)) #print("outc", outc) - super().__init__(name=name, prefix=prefix, inputs = [self.a], out_N=outc) - + super().__init__(name=name, prefix=prefix, inputs=[a], out_N=outc) self.a.bus_extend(2**(outc - 1), prefix=a.prefix) #print(self.a) @@ -62,7 +33,6 @@ class UnsignedPopCount(GeneralCircuit): # tree reduction def create_tree(a: Bus, depth: int, branch="A"): - #print(a) if a.N == 1: return a @@ -70,17 +40,17 @@ class UnsignedPopCount(GeneralCircuit): half = a.N // 2 b_in = Bus(N=half, prefix=f"b_inn_{branch}_{depth}A") c_in = Bus(N=a.N - half, prefix=f"b_inn_{branch}_{depth}B") + #print(b_in.prefix) #print(a, half, a.N) - - for i, j in enumerate(range(half)): b_in[i] = a[j] - for i, j in enumerate(range(half, a.N)): c_in[i] = a[j] b = create_tree(b_in, depth=depth + 1, branch = branch + "A") - c = create_tree(c_in, depth= depth + 1, branch = branch + "B") + c = create_tree(c_in, depth=depth + 1, branch = branch + "B") + + d = self.adder(a=b, b=c, prefix = f"{self.prefix}_add{branch}_{depth}") self.add_component(d) return d.out diff --git a/ariths_gen/multi_bit_circuits/others/popcount_compare.py b/ariths_gen/multi_bit_circuits/others/popcount_compare.py index 6b8570d..f9a6b20 100644 --- a/ariths_gen/multi_bit_circuits/others/popcount_compare.py +++ b/ariths_gen/multi_bit_circuits/others/popcount_compare.py @@ -1,45 +1,15 @@ -""" - -""" - -from ariths_gen.multi_bit_circuits.others import UnsignedPopCount -from ariths_gen.multi_bit_circuits.others.compare import UnsignedCompareGTE from ariths_gen.wire_components import ( - Wire, - ConstantWireValue0, - ConstantWireValue1, - Bus, - wires + Bus ) from ariths_gen.core.arithmetic_circuits import ( - ArithmeticCircuit, - GeneralCircuit, - MultiplierCircuit + GeneralCircuit ) -from ariths_gen.core.logic_gate_circuits import ( - MultipleInputLogicGate -) -from ariths_gen.one_bit_circuits.one_bit_components import ( - HalfAdder, - FullAdder, - FullAdderP, - TwoOneMultiplexer -) -from ariths_gen.one_bit_circuits.logic_gates import ( - AndGate, - NandGate, - OrGate, - NorGate, - XorGate, - XnorGate, - NotGate +from ariths_gen.multi_bit_circuits.others import ( + UnsignedPopCount, + UnsignedCompareGTE ) -from ariths_gen.multi_bit_circuits.others import OrReduce - - -from math import log2, ceil class PopCountCompare(GeneralCircuit): """Class representing a circiut @@ -60,22 +30,21 @@ class PopCountCompare(GeneralCircuit): """ def __init__(self, a: Bus, b: Bus, prefix : str = "", name : str = "popcnt_cmp", **kwargs): - self.a = a - self.b = b - - super().__init__(name=name, prefix=prefix, - inputs = [self.a, self.b], out_N=1) + super().__init__(name=name, prefix=prefix, inputs=[a, b], out_N=1, **kwargs) - p1 = self.add_component(UnsignedPopCount(self.a, + p1 = self.add_component(UnsignedPopCount(a=Bus(wires_list=self.a.bus, prefix=f"{prefix}_popcount1_a"), prefix=f"{prefix}_popcount1", inner_component=True)).out - p2 = self.add_component(UnsignedPopCount(self.b, + p2 = self.add_component(UnsignedPopCount(a=Bus(wires_list=self.b.bus, prefix=f"{prefix}_popcount2_a"), prefix=f"{prefix}_popcount2", inner_component=True)).out - + print(p1) #N = max(p1.N, p2.N) #p1.bus_extend(N) #p2.bus_extend(N) - - red = self.add_component(UnsignedCompareGTE(p1, p2, prefix=f"{prefix}_cmp", inner_component = True)) + cmp_gte_a = Bus(wires_list=p1.bus, prefix=f"{prefix}_cmp_gte_a") + cmp_gte_a.connect_bus(p1) + cmp_gte_b = Bus(wires_list=p2.bus, prefix=f"{prefix}_cmp_gte_b") + cmp_gte_b.connect_bus(p2) + red = self.add_component(UnsignedCompareGTE(cmp_gte_a, cmp_gte_b, prefix=f"{prefix}_cmp", inner_component = True)) self.out.connect_bus(red.out) diff --git a/ariths_gen/wire_components/wires.py b/ariths_gen/wire_components/wires.py index 6c8f993..de1a653 100644 --- a/ariths_gen/wire_components/wires.py +++ b/ariths_gen/wire_components/wires.py @@ -74,7 +74,7 @@ class Wire(): return f"({self.c_const})" # If wire is part of an input bus (where wire names are concatenated from bus prefix and their index position inside the bus in square brackets) # then the wire value is obtained from bitwise shifting the required wire from the parent bus ('parent_bus.prefix' is the same value as 'self.prefix') - elif self.is_buswire(): + elif self.is_buswire() and self.name == f"{self.prefix}[{self.index}]": return f"(({self.prefix} >> {self.index}) & 0x01)" else: return f"(({self.name} >> 0) & 0x01)" diff --git a/generate_mac.py b/generate_mac.py index 68afed3..8d68437 100644 --- a/generate_mac.py +++ b/generate_mac.py @@ -1,9 +1,8 @@ -from ariths_gen.core.arithmetic_circuits.arithmetic_circuit import ArithmeticCircuit from ariths_gen.core.arithmetic_circuits import GeneralCircuit from ariths_gen.wire_components import Bus, Wire from ariths_gen.multi_bit_circuits.adders import UnsignedRippleCarryAdder -from ariths_gen.multi_bit_circuits.multipliers import UnsignedArrayMultiplier, UnsignedDaddaMultiplier +from ariths_gen.multi_bit_circuits.multipliers import UnsignedArrayMultiplier import os diff --git a/tests/test_ax.py b/tests/test_ax.py index a833fe7..1042576 100644 --- a/tests/test_ax.py +++ b/tests/test_ax.py @@ -8,13 +8,8 @@ DIR_PATH = os.path.dirname(os.path.abspath(__file__)) sys.path.insert(0, os.path.join(DIR_PATH, '..')) import numpy as np import itertools - -from ariths_gen.core.arithmetic_circuits.arithmetic_circuit import ArithmeticCircuit -from ariths_gen.core.arithmetic_circuits import GeneralCircuit -from ariths_gen.wire_components import Bus, Wire -from ariths_gen.multi_bit_circuits.adders import UnsignedRippleCarryAdder +from ariths_gen.wire_components import Bus from ariths_gen.multi_bit_circuits.approximate_adders import QuAdder -from ariths_gen.multi_bit_circuits.multipliers import UnsignedArrayMultiplier, UnsignedDaddaMultiplier def test_quadder(): From 6003886eb7a1fbcb928303e8d2e5d2ca6317b912 Mon Sep 17 00:00:00 2001 From: honzastor Date: Sun, 14 Apr 2024 16:29:10 +0200 Subject: [PATCH 24/35] Fixed hierarchical Verilog generation of popcount compare. BLIF probably needs a similar treatment, TBD later --- .../arithmetic_circuits/general_circuit.py | 27 ++++++++----------- .../multi_bit_circuits/others/popcount.py | 9 +++---- .../others/popcount_compare.py | 7 +---- ariths_gen/wire_components/buses.py | 5 ++-- ariths_gen/wire_components/wires.py | 16 ++++++++--- generate_mac.py | 4 +-- tests/test_all.py | 4 +-- tests/test_popcount_compare.py | 6 +++++ 8 files changed, 40 insertions(+), 38 deletions(-) diff --git a/ariths_gen/core/arithmetic_circuits/general_circuit.py b/ariths_gen/core/arithmetic_circuits/general_circuit.py index 9feec93..017c725 100644 --- a/ariths_gen/core/arithmetic_circuits/general_circuit.py +++ b/ariths_gen/core/arithmetic_circuits/general_circuit.py @@ -31,7 +31,7 @@ class GeneralCircuit(): attr_name = chr(97+i) full_prefix = f"{self.prefix}_{input.prefix}" if self.inner_component else f"{input.prefix}" if isinstance(input, Bus) or isinstance(input, Wire): - circuit_input = copy.deepcopy(input) + circuit_input = input circuit_input.prefix = full_prefix setattr(self, attr_name, circuit_input) self.inputs.append(circuit_input) @@ -452,8 +452,8 @@ class GeneralCircuit(): Returns: str: Hierarchical C code of subcomponent arithmetic circuit's wires declaration. """ - return ";\n".join([f" {self.c_data_type} {i.prefix} = 0" for i in self.inputs]) + ";\n" + \ - f" {self.c_data_type} {self.out.prefix} = 0;\n" + return "".join([f" {self.c_data_type} {i.prefix} = 0;\n" for i in self.inputs if ((isinstance(i, Wire)) or (not all((w.is_const()) or (w.parent_bus is not None and w.prefix == i.prefix) for w in i.bus)))]) + \ + f" {self.c_data_type} {self.out.prefix} = 0;\n" def get_init_c_hier(self): """Generates hierarchical C code initialization and assignment of corresponding arithmetic circuit's input/output wires. @@ -598,9 +598,7 @@ class GeneralCircuit(): Returns: str: Hierarchical Verilog code of subcomponent arithmetic circuit's wires declaration. """ - return "".join(w.get_wire_declaration_v() for w in self.inputs + [self.out]) + "\n" - - #return "".join(b.get_wire_declaration_v() for b in self.inputs + [self.out] if not all((w.is_const()) or (w.parent_bus is not None and w.prefix == b.prefix) for w in b.bus)) + "\n" + return "".join(b.get_wire_declaration_v() for b in self.inputs + [self.out] if (b == self.out) or (not all((w.is_const()) or (w.parent_bus is not None and w.prefix == b.prefix) for w in b.bus))) def get_init_v_hier(self): """Generates hierarchical Verilog code initialization and assignment of corresponding arithmetic circuit's input/output wires. @@ -676,16 +674,10 @@ class GeneralCircuit(): Returns: str: Flat Blif code containing declaration of circuit's wires. """ - if self.N == 1: - return f".inputs {' '.join([w.prefix for w in self.inputs])}\n" + \ - f".outputs{self.out.get_wire_declaration_blif()}\n" + \ - f".names vdd\n1\n" + \ - f".names gnd\n0\n" - else: - return f".inputs{''.join([w.get_wire_declaration_blif() for w in self.inputs])}\n" + \ - f".outputs{self.out.get_wire_declaration_blif()}\n" + \ - f".names vdd\n1\n" + \ - f".names gnd\n0\n" + return f".inputs {''.join([w.get_wire_declaration_blif() for w in self.inputs])}\n" + \ + f".outputs{self.out.get_wire_declaration_blif()}\n" + \ + f".names vdd\n1\n" + \ + f".names gnd\n0\n" def get_function_blif_flat(self): """Generates flat Blif code with invocation of subcomponents logic gates functions via their corresponding truth tables. @@ -716,6 +708,9 @@ class GeneralCircuit(): file_object.write(self.get_function_out_blif()) file_object.write(f".end\n") + + + # HIERARCHICAL BLIF # def get_invocations_blif_hier(self): """Generates hierarchical Blif code with invocations of subcomponents function blocks. diff --git a/ariths_gen/multi_bit_circuits/others/popcount.py b/ariths_gen/multi_bit_circuits/others/popcount.py index 000cae1..f0dec78 100644 --- a/ariths_gen/multi_bit_circuits/others/popcount.py +++ b/ariths_gen/multi_bit_circuits/others/popcount.py @@ -43,18 +43,17 @@ class UnsignedPopCount(GeneralCircuit): #print(b_in.prefix) #print(a, half, a.N) for i, j in enumerate(range(half)): - b_in[i] = a[j] + b_in.connect(i, a.get_wire(j)) for i, j in enumerate(range(half, a.N)): - c_in[i] = a[j] + c_in.connect(i, a.get_wire(j)) b = create_tree(b_in, depth=depth + 1, branch = branch + "A") c = create_tree(c_in, depth=depth + 1, branch = branch + "B") - - + d = self.adder(a=b, b=c, prefix = f"{self.prefix}_add{branch}_{depth}") self.add_component(d) return d.out - + sumbus = create_tree(self.a,0, "X") #print(sumbus) self.out.connect_bus(sumbus) diff --git a/ariths_gen/multi_bit_circuits/others/popcount_compare.py b/ariths_gen/multi_bit_circuits/others/popcount_compare.py index f9a6b20..db4700e 100644 --- a/ariths_gen/multi_bit_circuits/others/popcount_compare.py +++ b/ariths_gen/multi_bit_circuits/others/popcount_compare.py @@ -38,13 +38,8 @@ class PopCountCompare(GeneralCircuit): p2 = self.add_component(UnsignedPopCount(a=Bus(wires_list=self.b.bus, prefix=f"{prefix}_popcount2_a"), prefix=f"{prefix}_popcount2", inner_component=True)).out - print(p1) #N = max(p1.N, p2.N) #p1.bus_extend(N) #p2.bus_extend(N) - cmp_gte_a = Bus(wires_list=p1.bus, prefix=f"{prefix}_cmp_gte_a") - cmp_gte_a.connect_bus(p1) - cmp_gte_b = Bus(wires_list=p2.bus, prefix=f"{prefix}_cmp_gte_b") - cmp_gte_b.connect_bus(p2) - red = self.add_component(UnsignedCompareGTE(cmp_gte_a, cmp_gte_b, prefix=f"{prefix}_cmp", inner_component = True)) + red = self.add_component(UnsignedCompareGTE(p1, p2, prefix=f"{prefix}_cmp", inner_component = True)) self.out.connect_bus(red.out) diff --git a/ariths_gen/wire_components/buses.py b/ariths_gen/wire_components/buses.py index 930d834..3189e0c 100644 --- a/ariths_gen/wire_components/buses.py +++ b/ariths_gen/wire_components/buses.py @@ -179,7 +179,7 @@ class Bus(): """ # Ensures correct binding between the bus wire index and the wire itself # It is used for the case when multiple of the same wire (e.g. `ContantWireValue0()`) are present in the bus (its id would otherwise be incorrect when using `self.bus.index(_)`) - mapped_positions = [(w_id, self.bus[w_id]) for w_id in range(self.N)] + mapped_positions = [(w_id, w) for w_id, w in enumerate(self.bus) if ((w.parent_bus is None) or (w.parent_bus is not None and w.prefix != self.prefix) or (w.is_const()))] return "".join([f" {self.prefix} |= {w[1].return_wire_value_c_hier(offset=w[0])}" for w in mapped_positions]) def return_bus_wires_sign_extend_c_flat(self): @@ -226,7 +226,7 @@ class Bus(): """ # Ensures correct binding between the bus wire index and the wire itself # It is used for the case when multiple of the same wire (e.g. `ContantWireValue0()`) are present in the bus (its id would otherwise be incorrect when using `self.bus.index(_)`) - mapped_positions = [(w_id, self.bus[w_id]) for w_id in range(self.N)] + mapped_positions = [(w_id, w) for w_id, w in enumerate(self.bus) if ((w.parent_bus is None) or (w.parent_bus is not None and w.prefix != self.prefix) or (w.is_const()))] return "".join([f" assign {self.prefix}[{w[0]}] = {w[1].return_wire_value_v_hier()}" for w in mapped_positions]) def get_unique_assign_out_wires_v(self, circuit_block: object): @@ -250,7 +250,6 @@ class Bus(): """ return f" wire [{self.N-1}:0] {self.prefix};\n" - """ BLIF CODE GENERATION """ def get_wire_declaration_blif(self, array: bool = True): """Declare each wire from the bus independently in Blif code representation. diff --git a/ariths_gen/wire_components/wires.py b/ariths_gen/wire_components/wires.py index de1a653..6df760d 100644 --- a/ariths_gen/wire_components/wires.py +++ b/ariths_gen/wire_components/wires.py @@ -189,7 +189,7 @@ class Wire(): """ BLIF CODE GENERATION """ def get_declaration_blif(self, prefix: str = "", offset: int = 0, array: bool = False): - """Wire declaration in Blif code. + """Declaration of wire which is part of a bus in Blif code. Declares basic wire name if wire is not part of a bus or declares wire by an offset of its position within the bus. @@ -207,6 +207,16 @@ class Wire(): else: return f"{self.name}" + def get_wire_declaration_blif(self): + """Declaration of a single wire in Blif code. + + Used for declaration of modul inputs. + + Returns: + str: Blif code for declaration of a wire. + """ + return f" {self.prefix}\n" + def get_assign_blif(self, prefix: str, output: bool = False): """Assignment of wire value to another desired wire in Blif code. @@ -245,10 +255,8 @@ class Wire(): """ if self.is_const(): return self.blif_const - elif self.parent_bus is not None and self.parent_bus.N > 1: - return self.name else: - return self.prefix + return self.name def __str__(self): if self.is_const(): diff --git a/generate_mac.py b/generate_mac.py index 8d68437..7c6ff20 100644 --- a/generate_mac.py +++ b/generate_mac.py @@ -12,8 +12,8 @@ class MAC(GeneralCircuit): assert a.N == b.N assert r.N == 2 * a.N - self.mul = self.add_component(UnsignedArrayMultiplier(a=a, b=b, prefix=self.prefix, name=f"u_arrmul{a.N}", inner_component=True)) - self.add = self.add_component(UnsignedRippleCarryAdder(a=r, b=self.mul.out, prefix=self.prefix, name=f"u_rca{r.N}", inner_component=True)) + self.mul = self.add_component(UnsignedArrayMultiplier(a=Bus(wires_list=a.bus, prefix=a.prefix), b=Bus(wires_list=b.bus, prefix=b.prefix), prefix=self.prefix, name=f"u_arrmul{a.N}", inner_component=True)) + self.add = self.add_component(UnsignedRippleCarryAdder(a=Bus(wires_list=r.bus, prefix=r.prefix), b=self.mul.out, prefix=self.prefix, name=f"u_rca{r.N}", inner_component=True)) self.out.connect_bus(connecting_bus=self.add.out) diff --git a/tests/test_all.py b/tests/test_all.py index 5a22005..9e0c93e 100644 --- a/tests/test_all.py +++ b/tests/test_all.py @@ -319,8 +319,8 @@ def test_mac(): assert a.N == b.N assert r.N == 2 * a.N - self.mul = self.add_component(UnsignedArrayMultiplier(a=a, b=b, prefix=self.prefix, name=f"u_arrmul{a.N}", inner_component=True)) - self.add = self.add_component(UnsignedRippleCarryAdder(a=r, b=self.mul.out, prefix=self.prefix, name=f"u_rca{r.N}", inner_component=True)) + self.mul = self.add_component(UnsignedArrayMultiplier(a=Bus(wires_list=a.bus, prefix=a.prefix), b=Bus(wires_list=b.bus, prefix=b.prefix), prefix=self.prefix, name=f"u_arrmul{a.N}", inner_component=True)) + self.add = self.add_component(UnsignedRippleCarryAdder(a=Bus(wires_list=r.bus, prefix=r.prefix), b=self.mul.out, prefix=self.prefix, name=f"u_rca{r.N}", inner_component=True)) self.out.connect_bus(connecting_bus=self.add.out) # usage diff --git a/tests/test_popcount_compare.py b/tests/test_popcount_compare.py index 9f30875..fde16f1 100644 --- a/tests/test_popcount_compare.py +++ b/tests/test_popcount_compare.py @@ -173,3 +173,9 @@ def test_popcountcompare_small2_cgp(): #expected = np.sum(r, axis=1) np.testing.assert_array_equal(v, expected) + +if __name__ == "__main__": + #test_popcountcompare_small() + #test_popcountcompare_same() + #test_popcountcompare_small2() + #test_popcountcompare_small2_cgp() \ No newline at end of file From f4b816fc09a6deac4dca3db4c26b0df2f4b02bfa Mon Sep 17 00:00:00 2001 From: honzastor Date: Sun, 14 Apr 2024 16:36:45 +0200 Subject: [PATCH 25/35] Fix for workflow tests --- tests/test_popcount_compare.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/tests/test_popcount_compare.py b/tests/test_popcount_compare.py index fde16f1..9f30875 100644 --- a/tests/test_popcount_compare.py +++ b/tests/test_popcount_compare.py @@ -173,9 +173,3 @@ def test_popcountcompare_small2_cgp(): #expected = np.sum(r, axis=1) np.testing.assert_array_equal(v, expected) - -if __name__ == "__main__": - #test_popcountcompare_small() - #test_popcountcompare_same() - #test_popcountcompare_small2() - #test_popcountcompare_small2_cgp() \ No newline at end of file From ce36ebf77b638b34c9aed856d2569f6e86247b0b Mon Sep 17 00:00:00 2001 From: honzastor Date: Wed, 17 Apr 2024 18:47:41 +0200 Subject: [PATCH 26/35] Fixed hierarchical BLIF generation for popcount_compare. --- .../arithmetic_circuits/general_circuit.py | 33 ++++++++----- .../logic_gate_circuits/logic_gate_circuit.py | 4 +- .../four_input_one_bit_circuit.py | 15 +++--- .../three_input_one_bit_circuit.py | 15 +++--- .../two_input_one_bit_circuit.py | 15 +++--- .../four_input_one_bit_components.py | 7 +-- .../three_input_one_bit_components.py | 49 +++++++++++-------- .../two_input_one_bit_components.py | 21 ++++---- ariths_gen/wire_components/buses.py | 13 ++--- ariths_gen/wire_components/wires.py | 4 +- 10 files changed, 101 insertions(+), 75 deletions(-) diff --git a/ariths_gen/core/arithmetic_circuits/general_circuit.py b/ariths_gen/core/arithmetic_circuits/general_circuit.py index 017c725..2061ee5 100644 --- a/ariths_gen/core/arithmetic_circuits/general_circuit.py +++ b/ariths_gen/core/arithmetic_circuits/general_circuit.py @@ -162,7 +162,7 @@ class GeneralCircuit(): def get_circuit_gates(self, verilog_output: bool = False): """Gets a list of all the logic gates in circuit that should be generated. - Args: + Args: verilog_output (bool): Specifies whether the call has been invoked by a verilog output generation method. Returns: list: List of composite logic gates. @@ -170,13 +170,13 @@ class GeneralCircuit(): gates = [] for c in self.components: if isinstance(c, TwoInputLogicGate): - if c.disable_generation is False and (verilog_output is False or ((hasattr(self, "use_verilog_instance") and self.use_verilog_instance is False) or hasattr(self, "use_verilog_instance") is False)): + if (c.disable_generation is False) and (verilog_output is False or getattr(c, "use_verilog_instance", False) is False): gates.append(c) else: # Check whether it is necessary to use gates for the Verilog component # description (ArithsGen internally defined comp) or not (technology specific instance) if verilog_output is False or ((hasattr(c, "use_verilog_instance") and c.use_verilog_instance is False) or hasattr(c, "use_verilog_instance") is False): - gates.extend((c.get_circuit_gates(verilog_output))) + gates.extend(c.get_circuit_gates(verilog_output)) return gates def get_one_bit_components(self): @@ -214,7 +214,7 @@ class GeneralCircuit(): return multi_bit_comps @staticmethod - def get_unique_types(components: list, name="", multi_bit: bool = False): + def get_unique_types(components: list, multi_bit: bool = False): """Retrieves just the unique representatives of class types present inside the provided components list. Args: @@ -242,8 +242,8 @@ class GeneralCircuit(): gate_comps = self.get_unique_types(components=self.get_circuit_gates(verilog_output)) one_bit_comps = self.get_unique_types( components=self.get_one_bit_components()) - multi_bit_comps = self.get_unique_types(name=self.prefix, - components=self.get_multi_bit_components(), multi_bit=True) + multi_bit_comps = self.get_unique_types(components=self.get_multi_bit_components(), + multi_bit=True) all_components = gate_comps + one_bit_comps + multi_bit_comps return all_components @@ -711,6 +711,11 @@ class GeneralCircuit(): + + + + + # HIERARCHICAL BLIF # def get_invocations_blif_hier(self): """Generates hierarchical Blif code with invocations of subcomponents function blocks. @@ -732,11 +737,17 @@ class GeneralCircuit(): init_signature = inspect.signature(self.__class__.__init__) default_circuit_name = init_signature.parameters['name'].default circuit_type = default_circuit_name + "x".join(str(getattr(self, chr(97+i)).N) for i, _ in enumerate(self.inputs)) - return "".join([w.get_wire_assign_blif(output=True) for w in self.inputs]) + \ - f".subckt {circuit_type}" + \ - "".join([f" {chr(97+i)}[{b.bus.index(w)}]={b.prefix}[{b.bus.index(w)}]" for i, b in enumerate(self.inputs) for w in b.bus]) + \ - "".join([f" {circuit_type}_out[{self.out.bus.index(o)}]={o.name}" for o in self.out.bus]) + "\n" - + if self.out.N > 1: + return "".join([w.get_wire_assign_blif(output=True) for w in self.inputs]) + \ + f".subckt {circuit_type}" + \ + "".join([f" {chr(97+i)}[{b.bus.index(w)}]={b.prefix}[{b.bus.index(w)}]" if b.N > 1 else f" {chr(97+i)}={b.prefix}" for i, b in enumerate(self.inputs) for w in b.bus]) + \ + "".join([f" {circuit_type}_out[{self.out.bus.index(o)}]={o.name}" for o in self.out.bus if not o.is_const()]) + "\n" + else: + return "".join([w.get_wire_assign_blif(output=True) for w in self.inputs]) + \ + f".subckt {circuit_type}" + \ + "".join([f" {chr(97+i)}[{b.bus.index(w)}]={b.prefix}[{b.bus.index(w)}]" if b.N > 1 else f" {chr(97+i)}={b.prefix}" for i, b in enumerate(self.inputs) for w in b.bus]) + \ + "".join([f" {circuit_type}_out={o.name}" for o in self.out.bus if not o.is_const()]) + "\n" + # TODO delete return f"{self.a.get_wire_assign_blif(output=True)}" + \ f"{self.b.get_wire_assign_blif(output=True)}" + \ diff --git a/ariths_gen/core/logic_gate_circuits/logic_gate_circuit.py b/ariths_gen/core/logic_gate_circuits/logic_gate_circuit.py index 38f91d8..76c122d 100644 --- a/ariths_gen/core/logic_gate_circuits/logic_gate_circuit.py +++ b/ariths_gen/core/logic_gate_circuits/logic_gate_circuit.py @@ -344,7 +344,7 @@ class TwoInputLogicGate(): Returns: str: Blif logic gate's wires declaration. """ - return f".inputs {self.a.get_declaration_blif()} {self.b.get_declaration_blif()}\n" + \ + return f".inputs {self.a.get_wire_declaration_blif()}{self.b.get_wire_declaration_blif()}\n" + \ f".outputs" + \ "".join([f" {self.out.name}\n" if self.disable_generation is False else f" {self.out.name}_out\n" for _ in range(1)]) + \ f".names vdd\n1\n" + \ @@ -687,7 +687,7 @@ class OneInputLogicGate(TwoInputLogicGate): Returns: str: Blif logic gate's wires declaration. """ - return f".inputs {self.a.get_declaration_blif()}\n" + \ + return f".inputs {self.a.get_wire_declaration_blif()}\n" + \ f".outputs" + \ "".join([f" {self.out.name}\n" if self.disable_generation is False else f" {self.out.name}_out\n" for _ in range(1)]) + \ f".names vdd\n1\n" + \ diff --git a/ariths_gen/core/one_bit_circuits/four_input_one_bit_circuit.py b/ariths_gen/core/one_bit_circuits/four_input_one_bit_circuit.py index b9e8eee..d04ca8b 100644 --- a/ariths_gen/core/one_bit_circuits/four_input_one_bit_circuit.py +++ b/ariths_gen/core/one_bit_circuits/four_input_one_bit_circuit.py @@ -17,10 +17,11 @@ class FourInputOneBitCircuit(TwoInputOneBitCircuit, GeneralCircuit): b (Wire): Second input wire. c (Wire): Third input wire. d (Wire): Fourth input wire. - prefix (str, optional): Prefix name of circuit. Defaults to "four_input_one_bit_circuit". + prefix (str, optional): Prefix name of circuit. Defaults to "". + name (str, optional): Name of circuit. Defaults to "four_input_one_bit_circuit". """ - def __init__(self, a: Wire = Wire(name="a"), b: Wire = Wire(name="b"), c: Wire = Wire(name="c"), d: Wire = Wire(name="d"), prefix: str = "four_input_one_bit_circuit"): - GeneralCircuit.__init__(self, inputs=[a, b, c, d], prefix=prefix, name="", out_N=1) + def __init__(self, a: Wire = Wire(name="a"), b: Wire = Wire(name="b"), c: Wire = Wire(name="c"), d: Wire = Wire(name="d"), prefix: str = "", name: str = "four_input_one_bit_circuit"): + GeneralCircuit.__init__(self, inputs=[a, b, c, d], prefix=prefix, name=name, out_N=1) self.c_data_type = "uint8_t" """ C CODE GENERATION """ @@ -36,7 +37,7 @@ class FourInputOneBitCircuit(TwoInputOneBitCircuit, GeneralCircuit): # HIERARCHICAL C # # Subcomponent generation (four inputs) - def get_out_invocation_c(self, *args, **kwargs): + def get_out_invocation_c(self): """Generates hierarchical C code invocation of corresponding four input one bit circuit's generated function block. Assigns output values from invocation of the corresponding function block into inner wires present inside the upper @@ -117,7 +118,7 @@ class FourInputOneBitCircuit(TwoInputOneBitCircuit, GeneralCircuit): f"" for c in self.components]) # Subcomponent generation - def get_out_invocation_v(self, *args, **kwargs): + def get_out_invocation_v(self): """Generates hierarchical Verilog code invocation of corresponding four input one bit circuit's generated function block. Assigns output values from invocation of the corresponding function block into inner wires present inside the upper @@ -143,7 +144,7 @@ class FourInputOneBitCircuit(TwoInputOneBitCircuit, GeneralCircuit): """ unique_out_wires = [] [unique_out_wires.append(o.name+"_outid"+str(self.out.bus.index(o))) if o.is_const() or o.name in [self.a.name, self.b.name, self.c.name, self.d.name] else unique_out_wires.append(o.name) for o in self.out.bus] - return f".inputs {self.a.get_declaration_blif()} {self.b.get_declaration_blif()} {self.c.get_declaration_blif()} {self.d.get_declaration_blif()}\n" + \ + return f".inputs {self.a.get_wire_declaration_blif()}{self.b.get_wire_declaration_blif()}{self.c.get_wire_declaration_blif()}{self.d.get_wire_declaration_blif()}\n" + \ f".outputs" + \ "".join([f" {o}" for o in unique_out_wires]) + "\n" + \ f".names vdd\n1\n" + \ @@ -187,7 +188,7 @@ class FourInputOneBitCircuit(TwoInputOneBitCircuit, GeneralCircuit): c.out.get_assign_blif(prefix=f"{unique_out_wires.pop(unique_out_wires.index(c.out.name+'_outid'+str(c.outid)))}", output=True) if f"{c.out.name+'_outid'+str(c.outid)}" in unique_out_wires else "" for c in self.components]) - def get_invocation_blif_hier(self, *args, **kwargs): + def get_invocation_blif_hier(self): """Generates hierarchical Blif code invocation of corresponding four input one bit circuit's generated function block. Returns: diff --git a/ariths_gen/core/one_bit_circuits/three_input_one_bit_circuit.py b/ariths_gen/core/one_bit_circuits/three_input_one_bit_circuit.py index 5586f22..f061b45 100644 --- a/ariths_gen/core/one_bit_circuits/three_input_one_bit_circuit.py +++ b/ariths_gen/core/one_bit_circuits/three_input_one_bit_circuit.py @@ -16,10 +16,11 @@ class ThreeInputOneBitCircuit(TwoInputOneBitCircuit, GeneralCircuit): a (Wire): First input wire. b (Wire): Second input wire. c (Wire): Third input wire. - prefix (str, optional): Prefix name of circuit. Defaults to "three_input_one_bit_circuit". + prefix (str, optional): Prefix name of circuit. Defaults to "". + name (str, optional): Name of circuit. Defaults to "three_input_one_bit_circuit". """ - def __init__(self, a: Wire = Wire(name="a"), b: Wire = Wire(name="b"), c: Wire = Wire(name="cin"), prefix: str = "three_input_one_bit_circuit"): - GeneralCircuit.__init__(self, inputs=[a, b, c], prefix=prefix, name="", out_N=1) + def __init__(self, a: Wire = Wire(name="a"), b: Wire = Wire(name="b"), c: Wire = Wire(name="cin"), prefix: str = "", name: str = "three_input_one_bit_circuit"): + GeneralCircuit.__init__(self, inputs=[a, b, c], prefix=prefix, name=name, out_N=1) self.c_data_type = "uint8_t" """ C CODE GENERATION """ @@ -35,7 +36,7 @@ class ThreeInputOneBitCircuit(TwoInputOneBitCircuit, GeneralCircuit): # HIERARCHICAL C # # Subcomponent generation (three inputs) - def get_out_invocation_c(self, *args, **kwargs): + def get_out_invocation_c(self): """Generates hierarchical C code invocation of corresponding three input one bit circuit's generated function block. Assigns output values from invocation of the corresponding function block into inner wires present inside the upper @@ -116,7 +117,7 @@ class ThreeInputOneBitCircuit(TwoInputOneBitCircuit, GeneralCircuit): f"" for c in self.components]) # Subcomponent generation - def get_out_invocation_v(self, *args, **kwargs): + def get_out_invocation_v(self): """Generates hierarchical Verilog code invocation of corresponding three input one bit circuit's generated function block. Assigns output values from invocation of the corresponding function block into inner wires present inside the upper @@ -142,7 +143,7 @@ class ThreeInputOneBitCircuit(TwoInputOneBitCircuit, GeneralCircuit): """ unique_out_wires = [] [unique_out_wires.append(o.name+"_outid"+str(self.out.bus.index(o))) if o.is_const() or o.name in [self.a.name, self.b.name, self.c.name] else unique_out_wires.append(o.name) for o in self.out.bus] - return f".inputs {self.a.get_declaration_blif()} {self.b.get_declaration_blif()} {self.c.get_declaration_blif()}\n" + \ + return f".inputs {self.a.get_wire_declaration_blif()}{self.b.get_wire_declaration_blif()}{self.c.get_wire_declaration_blif()}\n" + \ f".outputs" + \ "".join([f" {o}" for o in unique_out_wires]) + "\n" + \ f".names vdd\n1\n" + \ @@ -186,7 +187,7 @@ class ThreeInputOneBitCircuit(TwoInputOneBitCircuit, GeneralCircuit): c.out.get_assign_blif(prefix=f"{unique_out_wires.pop(unique_out_wires.index(c.out.name+'_outid'+str(c.outid)))}", output=True) if f"{c.out.name+'_outid'+str(c.outid)}" in unique_out_wires else "" for c in self.components]) - def get_invocation_blif_hier(self, *args, **kwargs): + def get_invocation_blif_hier(self): """Generates hierarchical Blif code invocation of corresponding three input one bit circuit's generated function block. Returns: diff --git a/ariths_gen/core/one_bit_circuits/two_input_one_bit_circuit.py b/ariths_gen/core/one_bit_circuits/two_input_one_bit_circuit.py index 1d442a0..9cd8533 100644 --- a/ariths_gen/core/one_bit_circuits/two_input_one_bit_circuit.py +++ b/ariths_gen/core/one_bit_circuits/two_input_one_bit_circuit.py @@ -13,10 +13,11 @@ class TwoInputOneBitCircuit(GeneralCircuit): Args: a (Wire): First input wire. b (Wire): Second input wire. - prefix (str, optional): Prefix name of circuit. Defaults to "two_input_one_bit_circuit". + prefix (str, optional): Prefix name of circuit. Defaults to "". + name (str, optional): Name of circuit. Defaults to "two_input_one_bit_circuit". """ - def __init__(self, a: Wire = Wire(name="a"), b: Wire = Wire(name="b"), prefix: str = "two_input_one_bit_circuit"): - super().__init__(inputs=[a, b], prefix=prefix, name="", out_N=1, one_bit_circuit=True) + def __init__(self, a: Wire = Wire(name="a"), b: Wire = Wire(name="b"), prefix: str = "", name: str = "two_input_one_bit_circuit"): + super().__init__(inputs=[a, b], prefix=prefix, name=name, out_N=1, one_bit_circuit=True) self.c_data_type = "uint8_t" """ C CODE GENERATION """ @@ -50,7 +51,7 @@ class TwoInputOneBitCircuit(GeneralCircuit): adder_block = self.__class__() return f"{adder_block.get_circuit_c()}\n\n" - def get_out_invocation_c(self, *args, **kwargs): + def get_out_invocation_c(self): """Generates hierarchical C code invocation of corresponding two input one bit circuit's generated function block. Assigns output values from invocation of the corresponding function block into inner wires present inside the upper @@ -165,7 +166,7 @@ class TwoInputOneBitCircuit(GeneralCircuit): adder_block = self.__class__() return f"{adder_block.get_circuit_v()}\n\n" - def get_out_invocation_v(self, *args, **kwargs): + def get_out_invocation_v(self): """Generates hierarchical Verilog code invocation of corresponding two input one bit circuit's generated function block. Assigns output values from invocation of the corresponding function block into inner wires present inside the upper @@ -250,7 +251,7 @@ class TwoInputOneBitCircuit(GeneralCircuit): """ unique_out_wires = [] [unique_out_wires.append(o.name+"_outid"+str(self.out.bus.index(o))) if o.is_const() or o.name in [self.a.name, self.b.name] else unique_out_wires.append(o.name) for o in self.out.bus] - return f".inputs {self.a.get_declaration_blif()} {self.b.get_declaration_blif()}\n" + \ + return f".inputs {self.a.get_wire_declaration_blif()}{self.b.get_wire_declaration_blif()}\n" + \ f".outputs" + \ "".join([f" {o}" for o in unique_out_wires]) + "\n" + \ f".names vdd\n1\n" + \ @@ -326,7 +327,7 @@ class TwoInputOneBitCircuit(GeneralCircuit): c.out.get_assign_blif(prefix=f"{unique_out_wires.pop(unique_out_wires.index(c.out.name+'_outid'+str(c.outid)))}", output=True) if f"{c.out.name+'_outid'+str(c.outid)}" in unique_out_wires else "" for c in self.components]) - def get_invocation_blif_hier(self, *args, **kwargs): + def get_invocation_blif_hier(self): """Generates hierarchical Blif code invocation of corresponding two input one bit circuit's generated function block. Returns: diff --git a/ariths_gen/one_bit_circuits/one_bit_components/four_input_one_bit_components.py b/ariths_gen/one_bit_circuits/one_bit_components/four_input_one_bit_components.py index 840aa9e..cc51287 100644 --- a/ariths_gen/one_bit_circuits/one_bit_components/four_input_one_bit_components.py +++ b/ariths_gen/one_bit_circuits/one_bit_components/four_input_one_bit_components.py @@ -24,10 +24,11 @@ class BlackCell(FourInputOneBitCircuit): b (Wire, optional): Second input wire, represents propagate signal from the current stage. Defaults to Wire(name="p1"). c (Wire, optional): Third input wire, represents generate signal from a preceding stage. Defaults to Wire(name="g0"). d (Wire, optional): Fourth input wire, represents propagate signal from a preceding stage. Defaults to Wire(name="p0"). - prefix (str, optional): Prefix name of grey cell. Defaults to "gc". + prefix (str, optional): Prefix name of grey cell. Defaults to "". + name (str, optional): Name of grey cell. Defaults to "gc". """ - def __init__(self, a: Wire = Wire(name="g1"), b: Wire = Wire(name="p1"), c: Wire = Wire(name="g0"), d: Wire = Wire(name="p0"), prefix: str = "bc"): - super().__init__(a, b, c, d, prefix) + def __init__(self, a: Wire = Wire(name="g1"), b: Wire = Wire(name="p1"), c: Wire = Wire(name="g0"), d: Wire = Wire(name="p0"), prefix: str = "", name: str = "bc"): + super().__init__(a, b, c, d, prefix=prefix, name=name) # 2 wires for component's bus output (generate, propagate) self.out = Bus(self.prefix+"_out", 2) diff --git a/ariths_gen/one_bit_circuits/one_bit_components/three_input_one_bit_components.py b/ariths_gen/one_bit_circuits/one_bit_components/three_input_one_bit_components.py index 2207164..a0fd6e4 100644 --- a/ariths_gen/one_bit_circuits/one_bit_components/three_input_one_bit_components.py +++ b/ariths_gen/one_bit_circuits/one_bit_components/three_input_one_bit_components.py @@ -21,12 +21,13 @@ class FullAdder(ThreeInputOneBitCircuit): 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". + prefix (str, optional): Prefix name of full adder. Defaults to "". + name (str, optional): Name of full adder. Defaults to "fa". """ use_verilog_instance = False - 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) + def __init__(self, a: Wire = Wire(name="a"), b: Wire = Wire(name="b"), c: Wire = Wire(name="cin"), prefix: str = "", name: str = "fa"): + super().__init__(a, b, c, prefix=prefix, name=name) # 2 wires for component's bus output (sum, cout) self.out = Bus(self.prefix+"_out", 2) @@ -129,10 +130,11 @@ class FullAdderP(FullAdder, ThreeInputOneBitCircuit): 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 p logic. Defaults to "fa_p". + prefix (str, optional): Prefix name of full adder with p logic. Defaults to "". + name (str, optional): Name of full adder with p logic. Defaults to "fa_p". """ - def __init__(self, a: Wire = Wire(name="a"), b: Wire = Wire(name="b"), c: Wire = Wire(name="cin"), prefix: str = "fa_p"): - super().__init__(a, b, c, prefix) + def __init__(self, a: Wire = Wire(name="a"), b: Wire = Wire(name="b"), c: Wire = Wire(name="cin"), prefix: str = "", name: str = "fa_p"): + super().__init__(a, b, c, prefix=prefix, name=name) # 3 wires for component's bus output (sum, cout, propagate) self.out.bus_extend(3) self.out.connect(2, self.get_previous_component(5).out) @@ -164,10 +166,11 @@ class FullAdderPG(FullAdder, ThreeInputOneBitCircuit): 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 "fa_pg". + prefix (str, optional): Prefix name of full adder with pg logic. Defaults to "". + name (str, optional): Name of full adder with pg logic. Defaults to "fa_pg". """ - def __init__(self, a: Wire = Wire(name="a"), b: Wire = Wire(name="b"), c: Wire = Wire(name="cin"), prefix: str = "fa_pg"): - super().__init__(a, b, c, prefix) + def __init__(self, a: Wire = Wire(name="a"), b: Wire = Wire(name="b"), c: Wire = Wire(name="cin"), prefix: str = "", name: str = "fa_pg"): + super().__init__(a, b, c, prefix=prefix, name=name) # 4 wires for component's bus output (sum, cout, propagate, generate) self.out.bus_extend(4) self.out.connect(2, self.get_previous_component(5).out) @@ -207,10 +210,11 @@ class PGSumLogic(ThreeInputOneBitCircuit): 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 pg sum logic. Defaults to "pg_sum". + prefix (str, optional): Prefix name of pg sum logic. Defaults to "". + name (str, optional): Name of pg sum logic. Defaults to "pg_sum". """ - def __init__(self, a: Wire = Wire(name="a"), b: Wire = Wire(name="b"), c: Wire = Wire(name="cin"), prefix: str = "pg_sum"): - super().__init__(a, b, c, prefix) + def __init__(self, a: Wire = Wire(name="a"), b: Wire = Wire(name="b"), c: Wire = Wire(name="cin"), prefix: str = "", name: str = "pg_sum"): + super().__init__(a, b, c, prefix=prefix, name=name) # 3 wires for component's bus output (sum, propagate, generate) self.out = Bus(self.prefix+"_out", 3) @@ -271,12 +275,13 @@ class TwoOneMultiplexer(ThreeInputOneBitCircuit): 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". + prefix (str, optional): Prefix name of two to one multiplexer. Defaults to "". + name (str, optional): Name of two to one multiplexer. Defaults to "mux2to1". """ use_verilog_instance = False - 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) + def __init__(self, a: Wire = Wire(name="d0"), b: Wire = Wire(name="d1"), c: Wire = Wire(name="sel"), prefix: str = "", name: str = "mux2to1"): + super().__init__(a, b, c, prefix=prefix, name=name) # Represents select signal (self.c naming for proper unified generation) self.c = c # 1 wire for component's output bus @@ -371,10 +376,11 @@ class FullSubtractor(ThreeInputOneBitCircuit): 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". + prefix (str, optional): Prefix name of full subtractor. Defaults to "". + name (str, optional): 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) + def __init__(self, a: Wire = Wire(name="a"), b: Wire = Wire(name="b"), c: Wire = Wire(name="bin"), prefix: str = "", name: str = "fs"): + super().__init__(a, b, c, prefix=prefix, name=name) # 2 wires for component's bus output (difference, bout) self.out = Bus(self.prefix+"_out", 2) @@ -438,10 +444,11 @@ class GreyCell(ThreeInputOneBitCircuit): a (Wire, optional): First input wire, represents generate signal from the current stage. Defaults to Wire(name="g1"). b (Wire, optional): Second input wire, represents propagate signal from the current stage. Defaults to Wire(name="p1"). c (Wire, optional): Third input wire, represents generate signal from a preceding stage. Defaults to Wire(name="g0"). - prefix (str, optional): Prefix name of grey cell. Defaults to "gc". + prefix (str, optional): Prefix name of grey cell. Defaults to "". + name (str, optional): Name of grey cell. Defaults to "gc". """ - def __init__(self, a: Wire = Wire(name="g1"), b: Wire = Wire(name="p1"), c: Wire = Wire(name="g0"), prefix: str = "gc"): - super().__init__(a, b, c, prefix) + def __init__(self, a: Wire = Wire(name="g1"), b: Wire = Wire(name="p1"), c: Wire = Wire(name="g0"), prefix: str = "", name: str = "gc"): + super().__init__(a, b, c, prefix=prefix, name=name) # 1 wire for component's bus output (output generate) self.out = Bus(self.prefix+"_out", 1) diff --git a/ariths_gen/one_bit_circuits/one_bit_components/two_input_one_bit_components.py b/ariths_gen/one_bit_circuits/one_bit_components/two_input_one_bit_components.py index d6bb318..079f3b6 100644 --- a/ariths_gen/one_bit_circuits/one_bit_components/two_input_one_bit_components.py +++ b/ariths_gen/one_bit_circuits/one_bit_components/two_input_one_bit_components.py @@ -19,12 +19,13 @@ class HalfAdder(TwoInputOneBitCircuit): 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". + prefix (str, optional): Prefix name of half adder. Defaults to "". + name (str, optional): Name of half adder. Defaults to "ha". """ use_verilog_instance = False - def __init__(self, a: Wire = Wire(name="a"), b: Wire = Wire(name="b"), prefix: str = "ha"): - super().__init__(a, b, prefix) + def __init__(self, a: Wire = Wire(name="a"), b: Wire = Wire(name="b"), prefix: str = "", name: str = "ha"): + super().__init__(a, b, prefix=prefix, name=name) # 2 wires for component's bus output (sum, cout) self.out = Bus(self.prefix+"_out", 2) @@ -114,10 +115,11 @@ class PGLogicBlock(TwoInputOneBitCircuit): 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". + prefix (str, optional): Prefix name of pg logic block. Defaults to "". + name (str, optional): 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) + def __init__(self, a: Wire = Wire(name="a"), b: Wire = Wire(name="b"), prefix: str = "", name: str = "pg_logic"): + super().__init__(a, b, prefix=prefix, name=name) # 3 wires for component's bus output (propagate, generate, sum) self.out = Bus(self.prefix+"_out", 3) @@ -174,10 +176,11 @@ class HalfSubtractor(TwoInputOneBitCircuit): 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". + prefix (str, optional): Prefix name of half subtractor adder. Defaults to "". + name (str, optional): 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) + def __init__(self, a: Wire = Wire(name="a"), b: Wire = Wire(name="b"), prefix: str = "", name: str = "hs"): + super().__init__(a, b, prefix=prefix, name=name) # 2 wires for component's bus output (difference, bout) self.out = Bus(self.prefix+"_out", 2) diff --git a/ariths_gen/wire_components/buses.py b/ariths_gen/wire_components/buses.py index 3189e0c..24afa4b 100644 --- a/ariths_gen/wire_components/buses.py +++ b/ariths_gen/wire_components/buses.py @@ -18,7 +18,7 @@ class Bus(): if wires_list is None: self.prefix = prefix # Adding wires into current bus's wires list (wire names are concatenated from bus prefix and their index position inside the bus in square brackets) - self.bus = [Wire(name=prefix+f"[{i}]", prefix=prefix, index=i, parent_bus=self) for i in range(N)] + self.bus = [Wire(name=prefix+f"[{i}]" if N != 1 else prefix, prefix=prefix, index=i, parent_bus=self) for i in range(N)] self.N = N else: self.prefix = prefix @@ -251,18 +251,16 @@ class Bus(): return f" wire [{self.N-1}:0] {self.prefix};\n" """ BLIF CODE GENERATION """ - def get_wire_declaration_blif(self, array: bool = True): + def get_wire_declaration_blif(self): """Declare each wire from the bus independently in Blif code representation. - Args: - array (bool, optional): Specifies whether to declare wires from bus by their offset e.g. out[0] or by their wire name e.g. out_0. Defaults to True. - Returns: str: Blif code for declaration of individual bus wires. """ # Ensures correct binding between the bus wire index and the wire itself # It is used for the case when multiple of the same wire (e.g. `ContantWireValue0()`) are present in the bus (its id would otherwise be incorrect when using `self.bus.index(_)`) mapped_positions = [(w_id, self.bus[w_id]) for w_id in range(self.N)] + array = True if self.N > 1 else False return "".join([f" {w[1].get_declaration_blif(prefix=self.prefix, offset=w[0], array=array)}" for w in mapped_positions]) def get_wire_assign_blif(self, output: bool = False): @@ -277,7 +275,10 @@ class Bus(): # Ensures correct binding between the bus wire index and the wire itself # It is used for the case when multiple of the same wire (e.g. `ContantWireValue0()`) are present in the bus (its id would otherwise be incorrect when using `self.bus.index(_)`) mapped_positions = [(w_id, self.bus[w_id]) for w_id in range(self.N)] - return "".join([w[1].get_assign_blif(prefix=self.prefix+f"[{w[0]}]", output=output) for w in mapped_positions]) + if self.N > 1: + return "".join([w[1].get_assign_blif(prefix=self.prefix+f"[{w[0]}]", output=output) for w in mapped_positions]) + else: + return "".join([w[1].get_assign_blif(prefix=self.prefix, output=output) for w in mapped_positions]) def get_unique_assign_out_wires_blif(self, function_block_out_bus: object): """Assigns unique output wires to their respective outputs of subcomponent's function block modul in hierarchical Blif subcomponent's invocation. diff --git a/ariths_gen/wire_components/wires.py b/ariths_gen/wire_components/wires.py index 6df760d..b5797a4 100644 --- a/ariths_gen/wire_components/wires.py +++ b/ariths_gen/wire_components/wires.py @@ -205,7 +205,7 @@ class Wire(): if array is True: return f"{prefix}[{offset}]" else: - return f"{self.name}" + return f"{prefix}" def get_wire_declaration_blif(self): """Declaration of a single wire in Blif code. @@ -215,7 +215,7 @@ class Wire(): Returns: str: Blif code for declaration of a wire. """ - return f" {self.prefix}\n" + return f"{self.prefix} " def get_assign_blif(self, prefix: str, output: bool = False): """Assignment of wire value to another desired wire in Blif code. From c480eeacf955e10b3fd38afbfc7b3b1694f4cbd2 Mon Sep 17 00:00:00 2001 From: Vojta Mrazek Date: Mon, 8 Jul 2024 11:29:25 +0200 Subject: [PATCH 27/35] popcount parent component --- ariths_gen/multi_bit_circuits/others/compare.py | 2 +- ariths_gen/multi_bit_circuits/others/popcount.py | 8 +++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/ariths_gen/multi_bit_circuits/others/compare.py b/ariths_gen/multi_bit_circuits/others/compare.py index 6e92ce1..a7e5a31 100644 --- a/ariths_gen/multi_bit_circuits/others/compare.py +++ b/ariths_gen/multi_bit_circuits/others/compare.py @@ -189,6 +189,6 @@ class UnsignedCompareGTE(GeneralCircuit): res[self.N] = psum # or all equal (xor) - red = self.add_component(OrReduce(res, prefix=f"{self.prefix}_orred", inner_component=True)) + red = self.add_component(OrReduce(res, prefix=f"{self.prefix}_orred", inner_component=True, parent_component=self)) self.out.connect_bus(red.out) \ No newline at end of file diff --git a/ariths_gen/multi_bit_circuits/others/popcount.py b/ariths_gen/multi_bit_circuits/others/popcount.py index 4721bce..af0bfb8 100644 --- a/ariths_gen/multi_bit_circuits/others/popcount.py +++ b/ariths_gen/multi_bit_circuits/others/popcount.py @@ -68,8 +68,8 @@ class UnsignedPopCount(GeneralCircuit): return a else: half = a.N // 2 - b_in = Bus(N=half, prefix=f"b_inn{depth}A") - c_in = Bus(N=a.N - half, prefix=f"b_inn{depth}B") + b_in = Bus(N=half, prefix=f"b_inn{branch}_{depth}A") + c_in = Bus(N=a.N - half, prefix=f"c_inn{branch}_{depth}B") #print(a, half, a.N) @@ -81,7 +81,9 @@ class UnsignedPopCount(GeneralCircuit): b = create_tree(b_in, depth=depth + 1, branch = branch + "A") c = create_tree(c_in, depth= depth + 1, branch = branch + "B") - d = self.adder(a=b, b=c, prefix = f"{self.prefix}_add{branch}_{depth}") + d = self.adder(a=b, b=c, + prefix = f"{self.prefix}_add{branch}_{depth}", inner_component=True, + parent_component=self) self.add_component(d) return d.out From bc0104de12d2c4ccaeec607dac19bd8d3e259662 Mon Sep 17 00:00:00 2001 From: Vojta Mrazek Date: Tue, 9 Jul 2024 09:22:11 +0200 Subject: [PATCH 28/35] ripple cary subtractor --- .../arithmetic_circuits/general_circuit.py | 8 +- .../subtractors/__init__.py | 3 + .../subtractors/ripple_carry_subtractor.py | 139 ++++++++++++++++++ tests/test_all.py | 35 +++++ 4 files changed, 183 insertions(+), 2 deletions(-) create mode 100644 ariths_gen/multi_bit_circuits/subtractors/__init__.py create mode 100644 ariths_gen/multi_bit_circuits/subtractors/ripple_carry_subtractor.py 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): From 4cd1189d4aee53a13acb2509c3434675741a2f5b Mon Sep 17 00:00:00 2001 From: Vojta Mrazek Date: Thu, 18 Jul 2024 13:15:56 +0200 Subject: [PATCH 29/35] CGP circuit accepts BUS inputs --- ariths_gen/core/cgp_circuit.py | 42 +++++++++++++++++++++++----------- 1 file changed, 29 insertions(+), 13 deletions(-) diff --git a/ariths_gen/core/cgp_circuit.py b/ariths_gen/core/cgp_circuit.py index a453a7a..8cd381a 100644 --- a/ariths_gen/core/cgp_circuit.py +++ b/ariths_gen/core/cgp_circuit.py @@ -21,25 +21,37 @@ import re class UnsignedCGPCircuit(GeneralCircuit): """Unsigned circuit variant that loads CGP code and is able to export it to C/verilog/Blif/CGP.""" - def __init__(self, code: str, input_widths: list, prefix: str = "", name: str = "cgp", **kwargs): + def __init__(self, code: str = "", input_widths: list = None, inputs: list = None, prefix: str = "", name: str = "cgp", **kwargs): cgp_prefix, cgp_core, cgp_outputs = re.match( r"{(.*)}(.*)\(([^()]+)\)", code).groups() c_in, c_out, c_rows, c_cols, c_ni, c_no, c_lback = map( int, cgp_prefix.split(",")) + + assert inputs is not None or input_widths is not None, "Either inputs or input_widths must be provided" - assert sum( - input_widths) == c_in, f"CGP input width {c_in} doesn't match input_widths {input_widths}" + if inputs: + assert input_widths is None, "Only one of inputs or input_widths must be provided" - inputs = [Bus(N=bw, prefix=f"input_{chr(i)}") - for i, bw in enumerate(input_widths, start=0x61)] + input_widths =[i.N for i in inputs] + assert sum(input_widths) == c_in, f"CGP input width {c_in} doesn't match inputs {inputs_widths}" - # Assign each Bus object in self.inputs to a named attribute of self - for bus in inputs: - # Here, bus.prefix is 'input_a', 'input_b', etc. - # We strip 'input_' and use the remaining part (e.g., 'a', 'b') to create the attribute name - attr_name = bus.prefix.replace('input_', '') - setattr(self, attr_name, bus) + + + else: + + assert sum( + input_widths) == c_in, f"CGP input width {c_in} doesn't match input_widths {input_widths}" + + inputs = [Bus(N=bw, prefix=f"input_{chr(i)}") + for i, bw in enumerate(input_widths, start=0x61)] + + # Assign each Bus object in self.inputs to a named attribute of self + for bus in inputs: + # Here, bus.prefix is 'input_a', 'input_b', etc. + # We strip 'input_' and use the remaining part (e.g., 'a', 'b') to create the attribute name + attr_name = bus.prefix.replace('input_', '') + setattr(self, attr_name, bus) # Adding values to the list self.vals = {} @@ -51,6 +63,10 @@ class UnsignedCGPCircuit(GeneralCircuit): j += 1 super().__init__(prefix=prefix, name=name, out_N=c_out, inputs=inputs, **kwargs) + + if not code: + return # only for getting the name + cgp_core = cgp_core.split(")(") i = 0 @@ -125,6 +141,6 @@ class UnsignedCGPCircuit(GeneralCircuit): class SignedCGPCircuit(UnsignedCGPCircuit): """Signed circuit variant that loads CGP code and is able to export it to C/verilog/Blif/CGP.""" - def __init__(self, code: str, input_widths: list, prefix: str = "", name: str = "cgp", **kwargs): - super().__init__(code=code, input_widths=input_widths, prefix=prefix, name=name, signed=True, **kwargs) + def __init__(self, code: str, input_widths: list = None, inputs: list=None, prefix: str = "", name: str = "cgp", **kwargs): + super().__init__(code=code, input_widths=input_widths, inputs=inputs, prefix=prefix, name=name, signed=True, **kwargs) self.c_data_type = "int64_t" From f34471bfe3e3a976216556247d901180eb75b0d0 Mon Sep 17 00:00:00 2001 From: Vojta Mrazek Date: Thu, 18 Jul 2024 13:16:15 +0200 Subject: [PATCH 30/35] signed version of python code --- .../core/arithmetic_circuits/general_circuit.py | 6 ++++-- ariths_gen/wire_components/buses.py | 16 ++++++++++++++-- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/ariths_gen/core/arithmetic_circuits/general_circuit.py b/ariths_gen/core/arithmetic_circuits/general_circuit.py index 76142a2..b8543e5 100644 --- a/ariths_gen/core/arithmetic_circuits/general_circuit.py +++ b/ariths_gen/core/arithmetic_circuits/general_circuit.py @@ -349,16 +349,18 @@ class GeneralCircuit(): return self.out.return_bus_wires_values_python_flat() # Generating flat Python code representation of circuit - def get_python_code_flat(self, file_object): + + def get_python_code_flat(self, file_object, retype=True): """Generates flat Python code representation of corresponding arithmetic circuit. Args: file_object (TextIOWrapper): Destination file object where circuit's representation will be written to. + retype (bool) specifies if signed output should return int64_t """ file_object.write(self.get_prototype_python()) file_object.write(self.get_init_python_flat()+"\n") file_object.write(self.get_function_out_python_flat()) - file_object.write(self.out.return_bus_wires_sign_extend_python_flat()) + file_object.write(self.out.return_bus_wires_sign_extend_python_flat(retype=True)) file_object.write(f" return {self.out.prefix}"+"\n") """ C CODE GENERATION """ diff --git a/ariths_gen/wire_components/buses.py b/ariths_gen/wire_components/buses.py index 24afa4b..1919ad6 100644 --- a/ariths_gen/wire_components/buses.py +++ b/ariths_gen/wire_components/buses.py @@ -136,7 +136,7 @@ class Bus(): mapped_positions = [(w_id, self.bus[w_id]) for w_id in range(self.N)] return "".join([f" {self.prefix} = 0\n"] + [f" {self.prefix} = ({self.prefix}) | {w[1].return_wire_value_python_flat(offset=w[0])}" for w in mapped_positions]) - def return_bus_wires_sign_extend_python_flat(self): + def return_bus_wires_sign_extend_python_flat(self, retype: bool = False): """Sign extends the bus's corresponding Python variable (object) to ensure proper flat Python code variable signedness. Returns: @@ -144,7 +144,19 @@ class Bus(): """ if self.signed is True: last_bus_wire = self.bus[-1] - return "".join([f" {self.prefix} = ({self.prefix}) | {last_bus_wire.return_wire_value_python_flat(offset=i)}" for i in range(len(self.bus), 64)]) + + assert self.N < 64, "Sign extension is not supported for bus with more than 64 bits" + if retype: + rewrite = f""" + if hasattr({self.prefix}, 'astype'): + {self.prefix} = {self.prefix}.astype("int64") + else: + from ctypes import c_int64 + {self.prefix} = c_int64({self.prefix}).value\n""" + else: + rewrite = "" + + return "".join([f" {self.prefix} = ({self.prefix}) | {last_bus_wire.return_wire_value_python_flat(offset=i)}" for i in range(len(self.bus), 64)]) + rewrite else: return "" From 04cd3e44d30b0fbac2008cbc3a7cbb2989fe2def Mon Sep 17 00:00:00 2001 From: Vojta Mrazek Date: Mon, 22 Jul 2024 15:09:50 +0200 Subject: [PATCH 31/35] bug in cgp indexes with constant wires, they were encouted --- ariths_gen/core/arithmetic_circuits/general_circuit.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ariths_gen/core/arithmetic_circuits/general_circuit.py b/ariths_gen/core/arithmetic_circuits/general_circuit.py index b8543e5..95d3f31 100644 --- a/ariths_gen/core/arithmetic_circuits/general_circuit.py +++ b/ariths_gen/core/arithmetic_circuits/general_circuit.py @@ -268,7 +268,9 @@ class GeneralCircuit(): if wire.is_const(): return wire.cgp_const else: - return len(self.circuit_wires)+2 + # [1] is reservation for a constant wire with value 1 + pos = max([1] + [x[2] for x in self.circuit_wires]) + return pos + 1 def get_circuit_wires(self): """Gets a list of all wires in circuit along with their index position for cgp chromosome generation and stores them inside `self.circuit_wires` list. From bc95444995adfa74c83b6479cbc977a54354337b Mon Sep 17 00:00:00 2001 From: Vojta Mrazek Date: Mon, 22 Jul 2024 15:10:21 +0200 Subject: [PATCH 32/35] reconnected wire was not identified as a bus --- ariths_gen/wire_components/wires.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/ariths_gen/wire_components/wires.py b/ariths_gen/wire_components/wires.py index b5797a4..a9b246d 100644 --- a/ariths_gen/wire_components/wires.py +++ b/ariths_gen/wire_components/wires.py @@ -1,3 +1,5 @@ +import re + class Wire(): """Class representing basic wire used to interconnect components. @@ -76,6 +78,12 @@ class Wire(): # then the wire value is obtained from bitwise shifting the required wire from the parent bus ('parent_bus.prefix' is the same value as 'self.prefix') elif self.is_buswire() and self.name == f"{self.prefix}[{self.index}]": return f"(({self.prefix} >> {self.index}) & 0x01)" + elif self.is_buswire(): + g = re.match(r"(.*)\[(\d+)\]", self.name) + if g: + return f"(({g.group(1)} >> {g.group(2)}) & 0x01)" + else: + return f"(({self.name} >> 0) & 0x01)" else: return f"(({self.name} >> 0) & 0x01)" From b87f8350fc0e2895dce8d114447b97c12b48f2a9 Mon Sep 17 00:00:00 2001 From: honzastor Date: Tue, 1 Oct 2024 18:42:11 +0200 Subject: [PATCH 33/35] Added ripple borrow subtractor circuit and updated automated testing. --- .../adders/ripple_carry_adder.py | 4 +- .../subtractors/__init__.py | 5 + .../subtractors/ripple_borrow_subtractor.py | 101 ++++++++++++++++++ .../subtractors/ripple_carry_subtractor.py | 87 +++++---------- generate_test.py | 24 +++++ tests/subtractor_signed.c | 18 ++++ tests/subtractor_unsigned.c | 18 ++++ tests/tb_adder_signed.v | 2 +- tests/tb_subtractor_signed.v | 33 ++++++ tests/tb_subtractor_unsigned.v | 33 ++++++ tests/test_all.py | 43 ++++---- tests/test_cgp.py | 64 +++++++++++ tests/test_circuits.sh | 8 ++ tests/test_circuits_cgp.sh | 4 + tests/test_circuits_verilog.sh | 8 ++ tests/test_popcnt.py | 37 ++++--- tests/test_popcount_compare.py | 78 +++++++++----- tests/test_reduce.py | 28 +++-- 18 files changed, 468 insertions(+), 127 deletions(-) create mode 100644 ariths_gen/multi_bit_circuits/subtractors/ripple_borrow_subtractor.py create mode 100644 tests/subtractor_signed.c create mode 100644 tests/subtractor_unsigned.c create mode 100644 tests/tb_subtractor_signed.v create mode 100644 tests/tb_subtractor_unsigned.v diff --git a/ariths_gen/multi_bit_circuits/adders/ripple_carry_adder.py b/ariths_gen/multi_bit_circuits/adders/ripple_carry_adder.py index 6daf541..a0f4daf 100644 --- a/ariths_gen/multi_bit_circuits/adders/ripple_carry_adder.py +++ b/ariths_gen/multi_bit_circuits/adders/ripple_carry_adder.py @@ -17,7 +17,7 @@ class UnsignedRippleCarryAdder(GeneralCircuit): """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. + N one bit adders, where the first is a half adder and rest are full adders. Its downside is its long propagation delay the bigger the circuit is. @@ -68,7 +68,7 @@ class SignedRippleCarryAdder(UnsignedRippleCarryAdder, GeneralCircuit): """Class representing signed ripple carry adder. 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. + N one bit adders, where the 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. diff --git a/ariths_gen/multi_bit_circuits/subtractors/__init__.py b/ariths_gen/multi_bit_circuits/subtractors/__init__.py index 9f9e86a..85a5f1e 100644 --- a/ariths_gen/multi_bit_circuits/subtractors/__init__.py +++ b/ariths_gen/multi_bit_circuits/subtractors/__init__.py @@ -1,3 +1,8 @@ from ariths_gen.multi_bit_circuits.subtractors.ripple_carry_subtractor import ( UnsignedRippleCarrySubtractor, SignedRippleCarrySubtractor ) + + +from ariths_gen.multi_bit_circuits.subtractors.ripple_borrow_subtractor import ( + UnsignedRippleBorrowSubtractor, SignedRippleBorrowSubtractor +) \ No newline at end of file diff --git a/ariths_gen/multi_bit_circuits/subtractors/ripple_borrow_subtractor.py b/ariths_gen/multi_bit_circuits/subtractors/ripple_borrow_subtractor.py new file mode 100644 index 0000000..cd6ffd0 --- /dev/null +++ b/ariths_gen/multi_bit_circuits/subtractors/ripple_borrow_subtractor.py @@ -0,0 +1,101 @@ +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 ( + HalfSubtractor, + FullSubtractor +) +from ariths_gen.one_bit_circuits.logic_gates import ( + XorGate +) + + +class UnsignedRippleBorrowSubtractor(GeneralCircuit): + """Class representing unsigned ripple borrow subtractor. + + Unsigned ripple borrow subtractor represents N-bit unsigned subtractor which is composed of + N one bit subtractors, where the first is a half subtractor and rest are full subtractor. + ``` + B3 A3 B2 A2 B1 A1 B0 A0 + β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ + β”Œβ”€β–Όβ”€β”€β–Όβ”€β” β”Œβ”€β–Όβ”€β”€β–Όβ”€β” β”Œβ”€β–Όβ”€β”€β–Όβ”€β” β”Œβ”€β–Όβ”€β”€β–Όβ”€β” + β”‚ β”‚ B3β”‚ β”‚ B2β”‚ β”‚ B1β”‚ β”‚ + β”Œβ”€β”€β”€ FS │◄─── FS │◄─── FS │◄─── HS β”‚ + β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ + β”‚ β””β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”˜ + β–Ό β–Ό β–Ό β–Ό β–Ό + Bout D3 D2 D1 D0 + ``` + + Description of the __init__ method. + + Args: + a (Bus): First input bus. + b (Bus): Second input bus. + prefix (str, optional): Prefix name of unsigned rbs. Defaults to "". + name (str, optional): Name of unsigned rbs. Defaults to "u_rbs". + """ + def __init__(self, a: Bus, b: Bus, prefix: str = "", name: str = "u_rbs", **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 subtractor components + for input_index in range(self.N): + # First adder is a half subtractor + if input_index == 0: + obj_subtractor = HalfSubtractor(self.a.get_wire(input_index), self.b.get_wire(input_index), prefix=self.prefix+"_hs") + # Rest adders are full subtractor + else: + obj_subtractor = FullSubtractor(self.a.get_wire(input_index), self.b.get_wire(input_index), obj_subtractor.get_borrow_wire(), prefix=self.prefix+"_fs"+str(input_index)) + + self.add_component(obj_subtractor) + self.out.connect(input_index, obj_subtractor.get_difference_wire()) + if input_index == (self.N-1): + self.out.connect(self.N, obj_subtractor.get_borrow_wire()) + + +class SignedRippleBorrowSubtractor(UnsignedRippleBorrowSubtractor, GeneralCircuit): + """Class representing signed ripple borrow subtractor. + + Signed ripple borrow subtractor represents N-bit signed subtractor which is composed of + N one bit subtractor, where the first is a half subtractor and rest are full subtractor. + 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 B2 A2 B1 A1 B0 A0 + β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ + β”Œβ”€β–Όβ”€β”€β–Όβ”€β” β”Œβ”€β–Όβ”€β”€β–Όβ”€β” β”Œβ”€β–Όβ”€β”€β–Όβ”€β” β”Œβ”€β–Όβ”€β”€β–Όβ”€β” β”Œβ”€β–Όβ”€β”€β–Όβ”€β” + β”‚ SIGN β”‚ B4β”‚ β”‚ B3β”‚ β”‚ B2β”‚ β”‚ B1β”‚ β”‚ + β”‚Extend│◄─── FS │◄─── FS │◄─── FS │◄─── HS β”‚ + β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ + β””β”€β”€β”¬β”€β”€β”€β”˜ β””β”€β”€β”¬β”€β”€β”€β”˜ β””β”€β”€β”¬β”€β”€β”€β”˜ β””β”€β”€β”¬β”€β”€β”€β”˜ β””β”€β”€β”¬β”€β”€β”€β”˜ + β–Ό β–Ό β–Ό β–Ό β–Ό + Bout D3 D2 D1 D0 + ``` + + Description of the __init__ method. + + Args: + a (Bus): First input bus. + b (Bus): Second input bus. + prefix (str, optional): Prefix name of signed rbs. Defaults to "". + name (str, optional): Name of signed rbs. Defaults to "s_rbs". + """ + def __init__(self, a: Bus, b: Bus, prefix: str = "", name: str = "s_rbs", **kwargs): + super().__init__(a=a, b=b, prefix=prefix, name=name, signed=True, **kwargs) + + # Additional XOR gates to ensure correct sign extension in case of sign addition + sign_xor_1 = XorGate(self.get_previous_component(1).a, self.get_previous_component(1).b, prefix=self.prefix+"_xor"+str(self.get_instance_num(cls=XorGate)), parent_component=self) + self.add_component(sign_xor_1) + sign_xor_2 = XorGate(sign_xor_1.out, self.get_previous_component(2).get_borrow_wire(), prefix=self.prefix+"_xor"+str(self.get_instance_num(cls=XorGate)), parent_component=self) + self.add_component(sign_xor_2) + self.out.connect(self.N, sign_xor_2.out) diff --git a/ariths_gen/multi_bit_circuits/subtractors/ripple_carry_subtractor.py b/ariths_gen/multi_bit_circuits/subtractors/ripple_carry_subtractor.py index 619e4bb..9d783c5 100644 --- a/ariths_gen/multi_bit_circuits/subtractors/ripple_carry_subtractor.py +++ b/ariths_gen/multi_bit_circuits/subtractors/ripple_carry_subtractor.py @@ -15,17 +15,17 @@ from ariths_gen.wire_components.wires import ConstantWireValue1 class UnsignedRippleCarrySubtractor(GeneralCircuit): - """Class representing unsigned ripple carry adder. + """Class representing unsigned ripple carry subtractor. - 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. + Unsigned ripple carry subtractor represents N-bit unsigned subtractor which is composed of + N one bit fulladders. The first FA has carry-in set to one and the B inputs of all FAs are inverted. ``` ── ── ── ── B3 A3 B2 A2 B1 A1 B0 A0 β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”Œβ”€β–Όβ”€β”€β–Όβ”€β” β”Œβ”€β–Όβ”€β”€β–Όβ”€β” β”Œβ”€β–Όβ”€β”€β–Όβ”€β” β”Œβ”€β–Όβ”€β”€β–Όβ”€β” - β”‚ β”‚ C3β”‚ β”‚ C2β”‚ β”‚ C1β”‚ β”‚ 1 - β”Œβ”€β”€β”€ FA │◄─── FA │◄─── FA │◄─── HA │◄── + β”‚ β”‚ C3β”‚ β”‚ C2β”‚ β”‚ C1β”‚ β”‚ 1 + β”Œβ”€β”€β”€ FA │◄─── FA │◄─── FA │◄─── FA │◄── β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”˜ β–Ό β–Ό β–Ό β–Ό β–Ό @@ -48,8 +48,6 @@ class UnsignedRippleCarrySubtractor(GeneralCircuit): 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))) @@ -68,26 +66,26 @@ class UnsignedRippleCarrySubtractor(GeneralCircuit): self.out.connect(self.N, self.get_previous_component().out) -class SignedRippleCarrySubtractor(GeneralCircuit): +class SignedRippleCarrySubtractor(UnsignedRippleCarrySubtractor, 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. + Signed ripple carry subtractor represents N-bit signed subtractor which is composed of + N one bit fulladders. The first FA has carry-in set to one and the B inputs of all FAs are inverted. 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 + __ __ __ __ __ + B3 A3 B3 A3 B2 A2 B1 A1 B0 A0 + β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ + β”Œβ”€β–Όβ”€β”€β–Όβ”€β” β”Œβ”€β–Όβ”€β”€β–Όβ”€β” β”Œβ”€β–Όβ”€β”€β–Όβ”€β” β”Œβ”€β–Όβ”€β”€β–Όβ”€β” β”Œβ”€β–Όβ”€β”€β–Όβ”€β” + β”‚ SIGN β”‚ C4β”‚ β”‚ C3β”‚ β”‚ C2β”‚ β”‚ C1β”‚ β”‚ 1 + β”‚Extend│◄─── FA │◄─── FA │◄─── FA │◄─── FA │◄── + β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ + β””β”€β”€β”¬β”€β”€β”€β”˜ β””β”€β”€β”¬β”€β”€β”€β”˜ β””β”€β”€β”¬β”€β”€β”€β”˜ β””β”€β”€β”¬β”€β”€β”€β”˜ β””β”€β”€β”¬β”€β”€β”€β”˜ + β–Ό β–Ό β–Ό β–Ό β–Ό + Cout S3 S2 S1 S0 ``` Description of the __init__ method. @@ -95,45 +93,16 @@ class SignedRippleCarrySubtractor(GeneralCircuit): 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". + prefix (str, optional): Prefix name of signed rcs. Defaults to "". + name (str, optional): Name of signed rcs. Defaults to "s_rcs". """ - def __init__(self, a: Bus, b: Bus, prefix: str = "", name: str = "u_rcs", **kwargs): + def __init__(self, a: Bus, b: Bus, prefix: str = "", name: str = "s_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) + super().__init__(a=a, b=b, prefix=prefix, name=name, 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 + # Additional XOR gates to ensure correct sign extension in case of sign addition + sign_xor_1 = XorGate(self.get_previous_component(2).a, self.get_previous_component(2).b, prefix=self.prefix+"_xor"+str(self.get_instance_num(cls=XorGate)), parent_component=self) + self.add_component(sign_xor_1) + sign_xor_2 = XorGate(sign_xor_1.out, self.get_previous_component(3).get_carry_wire(), prefix=self.prefix+"_xor"+str(self.get_instance_num(cls=XorGate)), parent_component=self) + self.add_component(sign_xor_2) + self.out.connect(self.N, sign_xor_2.out) diff --git a/generate_test.py b/generate_test.py index 6d1ebd9..4426239 100644 --- a/generate_test.py +++ b/generate_test.py @@ -54,6 +54,13 @@ from ariths_gen.multi_bit_circuits.adders import ( SignedCarryIncrementAdder ) +from ariths_gen.multi_bit_circuits.subtractors import ( + UnsignedRippleCarrySubtractor, + SignedRippleCarrySubtractor, + UnsignedRippleBorrowSubtractor, + SignedRippleBorrowSubtractor +) + from ariths_gen.multi_bit_circuits.multipliers import ( UnsignedDaddaMultiplier, UnsignedArrayMultiplier, @@ -222,6 +229,23 @@ if __name__ == "__main__": circuit = SignedLadnerFischerAdder(a, b, name=name, config_choice=1) export_circuit(circuit, name) + """ SUBTRACTORS """ + name = f"u_rcs{N}" + circuit = UnsignedRippleCarrySubtractor(a, b, name=name) + export_circuit(circuit, name) + + name = f"s_rcs{N}" + circuit = SignedRippleCarrySubtractor(a, b, name=name) + export_circuit(circuit, name) + + name = f"u_rbs{N}" + circuit = UnsignedRippleBorrowSubtractor(a, b, name=name) + export_circuit(circuit, name) + + name = f"s_rbs{N}" + circuit = SignedRippleBorrowSubtractor(a, b, name=name) + export_circuit(circuit, name) + """ MULTIPLIERS """ # Arrmul name = f"u_arrmul{N}" diff --git a/tests/subtractor_signed.c b/tests/subtractor_signed.c new file mode 100644 index 0000000..7bd2496 --- /dev/null +++ b/tests/subtractor_signed.c @@ -0,0 +1,18 @@ +#include +#include +#include + +int64_t CNAME(int64_t a, int64_t b); + + +int main() { + int result = 0; + for (int i = -128; i < 128; i++){ + for (int j = -128; j < 128; j++){ + result = i - j; + + assert(result == (int)CNAME(i,j)); + } + } + return 0; +} \ No newline at end of file diff --git a/tests/subtractor_unsigned.c b/tests/subtractor_unsigned.c new file mode 100644 index 0000000..decd779 --- /dev/null +++ b/tests/subtractor_unsigned.c @@ -0,0 +1,18 @@ +#include +#include +#include + +uint64_t CNAME(uint64_t a, uint64_t b); + + +int main() { + int result = 0; + for (int i = 0; i < 256; i++){ + for (int j = 0; j < 256; j++){ + result = i - j; + + assert(result == (int)CNAME(i,j)); + } + } + return 0; +} \ No newline at end of file diff --git a/tests/tb_adder_signed.v b/tests/tb_adder_signed.v index 25f7ef7..aaebec4 100644 --- a/tests/tb_adder_signed.v +++ b/tests/tb_adder_signed.v @@ -22,7 +22,7 @@ module add_signed_tb; #period; - //$assert(b == 0);c + //$assert(b == 0); if ( k + j != o) begin $display("Invalid output: %d + %d = %d", a, b, o); end diff --git a/tests/tb_subtractor_signed.v b/tests/tb_subtractor_signed.v new file mode 100644 index 0000000..92d2af2 --- /dev/null +++ b/tests/tb_subtractor_signed.v @@ -0,0 +1,33 @@ + +`timescale 1 ns/10 ps // time-unit = 1 ns, precision = 10 ps + +module sub_signed_tb; + reg signed [7:0] a; + reg signed [7:0] b; + wire signed [8:0] o; + + integer k, j; + localparam period = 20; + + `dut dut(a, b, o); //.input_a(a), .input_b(b), .cgp_circuit_out(o)); + + always + begin + + for(k = -127; k < 128; k = k+1) begin + for(j = -127; j < 128; j = j+1) begin + + assign a = k; + assign b = j; + + #period; + + //$assert(b == 0); + if ( k - j != o) begin + $display("Invalid output: %d - %d = %d", a, b, o); + end + end; + end; + $finish; + end +endmodule \ No newline at end of file diff --git a/tests/tb_subtractor_unsigned.v b/tests/tb_subtractor_unsigned.v new file mode 100644 index 0000000..fec214e --- /dev/null +++ b/tests/tb_subtractor_unsigned.v @@ -0,0 +1,33 @@ + +`timescale 1 ns/10 ps // time-unit = 1 ns, precision = 10 ps + +module sub_unsigned_tb; + reg [7:0] a; + reg [7:0] b; + wire signed [8:0] o; // output must be signed for a subtractor + + integer k, j; + localparam period = 20; + + `dut dut(a, b, o); //.input_a(a), .input_b(b), .cgp_circuit_out(o)); + + always + begin + + for(k = 0; k < 256; k = k+1) begin + for(j = 0; j < 256; j = j+1) begin + + assign a = k; + assign b = j; + + #period; + + //$assert(b == 0); + if ( k - j != o) begin + $display("Invalid output: %d - %d = %d", a, b, o); + end + end; + end; + $finish; + end +endmodule \ No newline at end of file diff --git a/tests/test_all.py b/tests/test_all.py index 51caec8..11ce60e 100644 --- a/tests/test_all.py +++ b/tests/test_all.py @@ -45,7 +45,8 @@ from ariths_gen.multi_bit_circuits.adders import ( ) from ariths_gen.multi_bit_circuits.subtractors import ( - UnsignedRippleCarrySubtractor, SignedRippleCarrySubtractor + UnsignedRippleCarrySubtractor, SignedRippleCarrySubtractor, + UnsignedRippleBorrowSubtractor, SignedRippleBorrowSubtractor ) from ariths_gen.multi_bit_circuits.multipliers import ( @@ -286,23 +287,6 @@ 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 @@ -332,9 +316,26 @@ def test_signed_add(): r = add(av, bv) 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 + + for c in [UnsignedRippleCarrySubtractor, UnsignedRippleBorrowSubtractor]: + sub = c(a, b) + r = sub(av, bv) + np.testing.assert_array_equal(expected, r) + + def test_signed_sub(): """ Test signed subtractor """ - N = 4 + N = 9 a = Bus(N=N, prefix="a") b = Bus(N=N, prefix="b") av = np.arange(-(2**(N-1)), 2**(N-1)) @@ -342,7 +343,7 @@ def test_signed_sub(): expected = av - bv # Non configurable multi-bit adders - for c in [SignedRippleCarrySubtractor]: + for c in [SignedRippleCarrySubtractor, SignedRippleBorrowSubtractor]: sub = c(a, b) r = sub(av, bv) np.testing.assert_array_equal(expected, r) @@ -442,6 +443,8 @@ if __name__ == "__main__": test_signed_mul() test_unsigned_add() test_signed_add() + test_unsigned_sub() + test_signed_sub() test_mac() test_direct() test_wire_as_bus() diff --git a/tests/test_cgp.py b/tests/test_cgp.py index dd1cb3a..c1f3b3a 100644 --- a/tests/test_cgp.py +++ b/tests/test_cgp.py @@ -46,6 +46,13 @@ from ariths_gen.multi_bit_circuits.adders import ( SignedCarryIncrementAdder ) +from ariths_gen.multi_bit_circuits.subtractors import ( + UnsignedRippleBorrowSubtractor, + UnsignedRippleCarrySubtractor, + SignedRippleBorrowSubtractor, + SignedRippleCarrySubtractor +) + from ariths_gen.multi_bit_circuits.multipliers import ( UnsignedDaddaMultiplier, UnsignedArrayMultiplier, @@ -205,6 +212,61 @@ def test_cgp_signed_add(): np.testing.assert_array_equal(expected, r) +def test_cgp_unsigned_sub(): + """ Test unsigned subtractors """ + N = 7 + 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 + + #for c in [UnsignedRippleBorrowSubtractor, UnsignedRippleCarrySubtractor]: + for c in [UnsignedRippleBorrowSubtractor]: + sub = c(a, b) + code = StringIO() + sub.get_cgp_code_flat(code) + cgp_code = code.getvalue() + + sub2 = UnsignedCGPCircuit(cgp_code, [N, N], signed_out=True) + o = StringIO() + sub2.get_v_code_flat(o) + print(o.getvalue()) + + r = sub2(av, bv) + assert sub(0, 0) == 0 + assert sub2(0, 0) == 0 + np.testing.assert_array_equal(expected, r) + + +def test_cgp_signed_sub(): + """ Test signed subtractors """ + N = 7 + 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 + + for c in [SignedRippleBorrowSubtractor, SignedRippleCarrySubtractor]: + sub = c(a, b) + r = sub(av, bv) + code = StringIO() + sub.get_cgp_code_flat(code) + cgp_code = code.getvalue() + print(cgp_code) + + sub2 = SignedCGPCircuit(cgp_code, [N, N]) + o = StringIO() + sub2.get_v_code_flat(o) + print(o.getvalue()) + + r = sub2(av, bv) + assert sub(0, 0) == 0 + assert sub2(0, 0) == 0 + np.testing.assert_array_equal(expected, r) + + def test_cgp_unsigned_mul(): """ Test unsigned multipliers """ N = 7 @@ -440,6 +502,8 @@ def test_cgp_variant1(): if __name__ == "__main__": test_cgp_unsigned_add() test_cgp_signed_add() + test_cgp_unsigned_sub() + test_cgp_signed_sub() test_cgp_unsigned_mul() test_cgp_signed_mul() test_cgp_variant1() diff --git a/tests/test_circuits.sh b/tests/test_circuits.sh index 0dda0d4..82b40b7 100755 --- a/tests/test_circuits.sh +++ b/tests/test_circuits.sh @@ -53,6 +53,14 @@ test_circuit "adder_unsigned" "u_ka8" test_circuit "adder_unsigned" "u_lfa8" +test_circuit "subtractor_signed" "s_rcs8" +test_circuit "subtractor_signed" "s_rbs8" + + +test_circuit "subtractor_unsigned" "u_rcs8" +test_circuit "subtractor_unsigned" "u_rbs8" + + test_circuit "multiplier_signed" "s_arrmul8" test_circuit "multiplier_signed" "s_csamul_cla8" test_circuit "multiplier_signed" "s_csamul_rca8" diff --git a/tests/test_circuits_cgp.sh b/tests/test_circuits_cgp.sh index 005b4b4..4a4bbbf 100755 --- a/tests/test_circuits_cgp.sh +++ b/tests/test_circuits_cgp.sh @@ -57,6 +57,10 @@ test_circuit "adder_unsigned" "u_ka8" test_circuit "adder_unsigned" "u_lfa8" +test_circuit "subtractor_signed" "s_rcs8" +test_circuit "subtractor_signed" "s_rbs8" + + test_circuit "multiplier_signed" "s_arrmul8" test_circuit "multiplier_signed" "s_csamul_cla8" test_circuit "multiplier_signed" "s_csamul_rca8" diff --git a/tests/test_circuits_verilog.sh b/tests/test_circuits_verilog.sh index 37b56e2..241ab65 100644 --- a/tests/test_circuits_verilog.sh +++ b/tests/test_circuits_verilog.sh @@ -66,6 +66,14 @@ test_circuit "adder_unsigned" "u_ka8" test_circuit "adder_unsigned" "u_lfa8" +test_circuit "subtractor_signed" "s_rcs8" +test_circuit "subtractor_signed" "s_rbs8" + + +test_circuit "subtractor_unsigned" "u_rcs8" +test_circuit "subtractor_unsigned" "u_rbs8" + + test_circuit "multiplier_signed" "s_arrmul8" test_circuit "multiplier_signed" "s_csamul_cla8" test_circuit "multiplier_signed" "s_csamul_rca8" diff --git a/tests/test_popcnt.py b/tests/test_popcnt.py index 6676d8d..21c0dda 100644 --- a/tests/test_popcnt.py +++ b/tests/test_popcnt.py @@ -21,7 +21,7 @@ from ariths_gen.multi_bit_circuits.others import ( import numpy as np -def test_popcount(): +def test_popcount(verbose: bool = False): """ Test unsigned adders """ N = 7 @@ -29,15 +29,13 @@ def test_popcount(): a = Bus(N=N, prefix="a") av = np.arange(2**N) - popcnt = UnsignedPopCount(a=a) #o = StringIO() #popcnt.get_v_code_hier(o) #print(o.getvalue()) - - print(popcnt(av)) - + if verbose: + print(popcnt(av)) # conv to binary r = [] @@ -46,12 +44,18 @@ def test_popcount(): r.append(a_s % 2) a_s = a_s // 2 r = np.dstack(r).reshape(-1, N) - print("r = ", r) + + if verbose: + print("r = ", r) + expected = np.sum(r, axis=1) + if verbose: + print("expected = ", expected) + np.testing.assert_array_equal(popcnt(av), expected) -def test_popcount_cgp(): +def test_popcount_cgp(verbose: bool = False): """ Test unsigned adders """ N = 7 @@ -59,16 +63,14 @@ def test_popcount_cgp(): a = Bus(N=N, prefix="a") av = np.arange(2**N) - popcnt = UnsignedPopCount(a=a) o = StringIO() popcnt.get_cgp_code_flat(o) cgp = UnsignedCGPCircuit(o.getvalue(), [N]) v = cgp(av) - - print(popcnt(av)) - + if verbose: + print(popcnt(av)) # conv to binary r = [] @@ -77,8 +79,19 @@ def test_popcount_cgp(): r.append(a_s % 2) a_s = a_s // 2 r = np.dstack(r).reshape(-1, N) - print("r = ", r) + + if verbose: + print("r = ", r) + expected = np.sum(r, axis=1) + if verbose: + print("expected = ", expected) + np.testing.assert_array_equal(v, expected) + +if __name__ == "__main__": + test_popcount() + test_popcount_cgp() + print("Python popcount tests were successful!") diff --git a/tests/test_popcount_compare.py b/tests/test_popcount_compare.py index 9f30875..c3dc51e 100644 --- a/tests/test_popcount_compare.py +++ b/tests/test_popcount_compare.py @@ -26,7 +26,7 @@ from io import StringIO from itertools import product -def test_popcountcompare_same(): +def test_popcountcompare_same(verbose: bool = False): N = 10 a = Bus(N=N, prefix="a") @@ -35,7 +35,9 @@ def test_popcountcompare_same(): test_cases = list(product(list(range(2**N)), repeat=2)) all = np.array(test_cases) - print(all) + if verbose: + print(all) + av = all[:, 0] bv = all[:, 1] def popcnt(x): @@ -43,28 +45,29 @@ def test_popcountcompare_same(): return np.sum(mask > 0, axis=0) cnta = (popcnt(av)) cntb = (popcnt(bv)) - print(cnta) - print(cntb) - + if verbose: + print(cnta) + print(cntb) cmp = PopCountCompare(a=a, b=b) cmp.get_v_code_hier(open("tmp.verilog", "w")) v = cmp(av, bv) - print("ret = ", v) + if verbose: + print("ret = ", v) expected = np.array(cnta >= cntb).astype(int) - print("expected = ", expected) - - #expected = np.sum(r, axis=1) + if verbose: + print("expected = ", expected) + #expected = np.sum(r, axis=1) np.testing.assert_array_equal(v, expected) -def test_popcountcompare_small(): +def test_popcountcompare_small(verbose: bool = False): N = 10 a = Bus(N=N, prefix="a") @@ -73,7 +76,9 @@ def test_popcountcompare_small(): test_cases = list(product(range(2**N), range(2**(N//2)))) all = np.array(test_cases) - print(all) + if verbose: + print(all) + av = all[:, 0] bv = all[:, 1] def popcnt(x): @@ -82,24 +87,28 @@ def test_popcountcompare_small(): cnta = (popcnt(av)) cntb = (popcnt(bv)) + if verbose: + print(cnta) + print(cntb) cmp = PopCountCompare(a=a, b=b) cmp.get_v_code_hier(open("tmp.verilog", "w")) v = cmp(av, bv) - print("ret = ", v) + if verbose: + print("ret = ", v) expected = np.array(cnta >= cntb).astype(int) - print("expected = ", expected) + if verbose: + print("expected = ", expected) #expected = np.sum(r, axis=1) - np.testing.assert_array_equal(v, expected) -def test_popcountcompare_small2(): +def test_popcountcompare_small2(verbose: bool = False): N = 10 a = Bus(N=N // 2, prefix="a") @@ -108,7 +117,9 @@ def test_popcountcompare_small2(): test_cases = list(product( range(2**(N//2)), range(2**N))) all = np.array(test_cases) - print(all) + if verbose: + print(all) + av = all[:, 0] bv = all[:, 1] def popcnt(x): @@ -117,25 +128,28 @@ def test_popcountcompare_small2(): cnta = (popcnt(av)) cntb = (popcnt(bv)) + if verbose: + print(cnta) + print(cntb) cmp = PopCountCompare(a=a, b=b) cmp.get_v_code_hier(open("tmp.verilog", "w")) v = cmp(av, bv) - print("ret = ", v) + if verbose: + print("ret = ", v) expected = np.array(cnta >= cntb).astype(int) - print("expected = ", expected) + if verbose: + print("expected = ", expected) #expected = np.sum(r, axis=1) - np.testing.assert_array_equal(v, expected) - -def test_popcountcompare_small2_cgp(): +def test_popcountcompare_small2_cgp(verbose: bool = False): N = 10 a = Bus(N=N // 2, prefix="a") @@ -144,7 +158,9 @@ def test_popcountcompare_small2_cgp(): test_cases = list(product( range(2**(N//2)), range(2**N))) all = np.array(test_cases) - print(all) + if verbose: + print(all) + av = all[:, 0] bv = all[:, 1] def popcnt(x): @@ -153,6 +169,9 @@ def test_popcountcompare_small2_cgp(): cnta = (popcnt(av)) cntb = (popcnt(bv)) + if verbose: + print(cnta) + print(cntb) cmp = PopCountCompare(a=a, b=b) cmp.get_v_code_hier(open("tmp.verilog", "w")) @@ -163,13 +182,22 @@ def test_popcountcompare_small2_cgp(): cgp = UnsignedCGPCircuit(o.getvalue(), [N//2, N]) v = cgp(av, bv) - print("ret = ", v) + if verbose: + print("ret = ", v) expected = np.array(cnta >= cntb).astype(int) - print("expected = ", expected) + if verbose: + print("expected = ", expected) #expected = np.sum(r, axis=1) - np.testing.assert_array_equal(v, expected) + + +if __name__ == "__main__": + test_popcountcompare_same() + test_popcountcompare_small() + test_popcountcompare_small2() + test_popcountcompare_small2_cgp() + print("Python popcount_compare tests were successful!") diff --git a/tests/test_reduce.py b/tests/test_reduce.py index b93e7c0..a4e3e90 100644 --- a/tests/test_reduce.py +++ b/tests/test_reduce.py @@ -24,7 +24,7 @@ from ariths_gen.multi_bit_circuits.others import ( ) -def test_orreduce(): +def test_orreduce(verbose: bool = False): """ Test unsigned adders """ N = 7 @@ -32,12 +32,12 @@ def test_orreduce(): a = Bus(N=N, prefix="a") av = np.arange(2**N) - reduce = OrReduce(a=a) o = StringIO() reduce.get_v_code_hier(o) - print(o.getvalue()) + if verbose: + print(o.getvalue()) #print(reduce(av)) @@ -48,13 +48,19 @@ def test_orreduce(): r.append(a_s % 2) a_s = a_s // 2 r = np.dstack(r).reshape(-1, N) - print("r = ", r) + + if verbose: + print("r = ", r) + expected = np.bitwise_or.reduce(r, axis=1) + + if verbose: + print("expected = ", expected) np.testing.assert_array_equal(reduce(av), expected) -def test_andreduce(): +def test_andreduce(verbose: bool = False): """ Test unsigned adders """ N = 7 @@ -62,12 +68,12 @@ def test_andreduce(): a = Bus(N=N, prefix="a") av = np.arange(2**N) - reduce = AndReduce(a=a) o = StringIO() reduce.get_v_code_hier(o) - print(o.getvalue()) + if verbose: + print(o.getvalue()) #print(reduce(av)) @@ -78,9 +84,15 @@ def test_andreduce(): r.append(a_s % 2) a_s = a_s // 2 r = np.dstack(r).reshape(-1, N) - print("r = ", r) + + if verbose: + print("r = ", r) + expected = np.bitwise_and.reduce(r, axis=1) + if verbose: + print("expected = ", expected) + np.testing.assert_array_equal(reduce(av), expected) From 03212a62f59d2e470e3ae3399ce2a108497ebccf Mon Sep 17 00:00:00 2001 From: honzastor Date: Tue, 1 Oct 2024 18:47:24 +0200 Subject: [PATCH 34/35] Actions fix --- .github/workflows/generate.yml | 2 +- setup.cfg | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/generate.yml b/.github/workflows/generate.yml index e1b6530..562dc74 100644 --- a/.github/workflows/generate.yml +++ b/.github/workflows/generate.yml @@ -35,7 +35,7 @@ jobs: - name: Run generating axmults run: python generate_axmuls.py - name: Upload results - uses: actions/upload-artifact@v1.0.0 + uses: actions/upload-artifact@v4 with: name: arithmetic-circuits-8 path: test_circuits diff --git a/setup.cfg b/setup.cfg index 028f4da..ce1a6e7 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,6 +1,6 @@ [metadata] name = ariths_gen -version = 0.0.1 +version = 0.0.2 description = "arithmetic circuits generator" From e804265a7b5fe468fce63197652ad4a7a6bc7376 Mon Sep 17 00:00:00 2001 From: honzastor Date: Wed, 2 Oct 2024 14:56:52 +0200 Subject: [PATCH 35/35] Updated git actions. --- .github/workflows/generate.yml | 38 +++++++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/.github/workflows/generate.yml b/.github/workflows/generate.yml index 562dc74..1935308 100644 --- a/.github/workflows/generate.yml +++ b/.github/workflows/generate.yml @@ -54,7 +54,7 @@ jobs: python-version: '3.x' - run: python -m pip install numpy - name: Download workflow run artifacts - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: name: arithmetic-circuits-8 path: test_circuits @@ -81,6 +81,42 @@ jobs: cd tests bash test_circuits_verilog.sh cd .. + + - name: Python circuits testing + run: | + cd tests + python test_all.py + cd .. + + - name: Python ax testing + run: | + cd tests + python test_ax.py + cd .. + + - name: Python CGP testing + run: | + cd tests + python test_cgp.py + cd .. + + - name: Python Compare testing + run: | + cd tests + python test_compare.py + cd .. + + - name: Python Popcount testing + run: | + cd tests + python test_popcnt.py + cd .. + + - name: Python Reduce testing + run: | + cd tests + python test_reduce.py + cd .. test_python: runs-on: ubuntu-latest