From bee208670584226f0cdbac5b1d9995ee3196880a Mon Sep 17 00:00:00 2001 From: Vojta Mrazek Date: Sat, 18 Sep 2021 12:55:31 +0200 Subject: [PATCH] Devel (#2) * CGP format * CGP format minor * General MAC circuit * Modified definition of MAC class to allow proper generation of output representations. * auto test MAC * test all * Made some minor changes and updated creation of MAC circuit. * Updated logic behind generating export representations, mainly focused around circuit and its buses and subcomponents namings. * Made some minor changes concerning proper exportation of multiplier circuits. Co-authored-by: honzastor --- .github/workflows/generate.yml | 7 + .gitignore | 1 + .testall.sh | 6 + .../core/arithmetic_circuits/__init__.py | 2 + .../arithmetic_circuits/arithmetic_circuit.py | 88 ++- .../arithmetic_circuits/general_circuit.py | 733 ++++++++++++++++++ .../arithmetic_circuits/multiplier_circuit.py | 4 +- .../logic_gate_circuits/logic_gate_circuit.py | 14 +- .../two_input_one_bit_circuit.py | 2 +- .../adders/carry_lookahead_adder.py | 20 +- .../adders/carry_skip_adder.py | 20 +- .../adders/pg_ripple_carry_adder.py | 22 +- .../adders/ripple_carry_adder.py | 20 +- .../dividers/array_divider.py | 13 +- .../multipliers/array_multiplier.py | 30 +- .../multipliers/dadda_multiplier.py | 46 +- .../multipliers/wallace_multiplier.py | 44 +- ariths_gen/wire_components/buses.py | 20 +- chr2c.py | 7 +- generate_mac.py | 24 + generate_test.py | 54 +- tests/mac.c | 23 + tests/test_mac.sh | 35 + 23 files changed, 1035 insertions(+), 200 deletions(-) create mode 100644 .testall.sh create mode 100644 ariths_gen/core/arithmetic_circuits/general_circuit.py create mode 100644 generate_mac.py create mode 100644 tests/mac.c create mode 100644 tests/test_mac.sh diff --git a/.github/workflows/generate.yml b/.github/workflows/generate.yml index 1da9788..ae1a56e 100644 --- a/.github/workflows/generate.yml +++ b/.github/workflows/generate.yml @@ -30,6 +30,8 @@ jobs: run: python -c "import sys; print(sys.version)" - name: Run generating run: python generate_test.py + - name: Run generating + run: python generate_mac.py - name: Upload results uses: actions/upload-artifact@v1.0.0 with: @@ -62,6 +64,11 @@ jobs: cd tests bash test_circuits_cgp.sh cd .. + - name: Run MAC testing + run: | + cd tests + bash test_mac.sh + cd .. # Only on main thread documentation: diff --git a/.gitignore b/.gitignore index 285bb0b..13f41cf 100644 --- a/.gitignore +++ b/.gitignore @@ -8,5 +8,6 @@ dist/ test_circuits/ tests/tmp.exe tests/tmp.c +html/ *.egg-info \ No newline at end of file diff --git a/.testall.sh b/.testall.sh new file mode 100644 index 0000000..7fe681f --- /dev/null +++ b/.testall.sh @@ -0,0 +1,6 @@ +python generate_test.py +python generate_mac.py +cd tests +bash test_mac.sh +bash test_circuits.sh +bash test_circuits_cgp.sh \ No newline at end of file diff --git a/ariths_gen/core/arithmetic_circuits/__init__.py b/ariths_gen/core/arithmetic_circuits/__init__.py index b1de929..9c792a3 100644 --- a/ariths_gen/core/arithmetic_circuits/__init__.py +++ b/ariths_gen/core/arithmetic_circuits/__init__.py @@ -2,6 +2,8 @@ from .arithmetic_circuit import ( ArithmeticCircuit ) +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 index 17468e3..1e76475 100644 --- a/ariths_gen/core/arithmetic_circuits/arithmetic_circuit.py +++ b/ariths_gen/core/arithmetic_circuits/arithmetic_circuit.py @@ -14,12 +14,34 @@ class ArithmeticCircuit(): The __init__ method fills some mandatory attributes concerning arithmetic circuit that are later used for generation into various representations. """ - def __init__(self): + + def __init__(self, a, b, prefix: str, name: str, out_N: int, inner_component: bool = False, one_bit_circuit: bool = False): + 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) + + # N output wires for given circuit + self.out = Bus(self.prefix+"_out", out_N, out_bus=True) + self.components = [] self.circuit_wires = [] self.circuit_gates = [] self.c_data_type = "uint64_t" - self.N = 1 def add_component(self, component): """Adds a component into list of circuit's inner subcomponents. @@ -123,8 +145,10 @@ class ArithmeticCircuit(): list: List of unique component types describing the circuit. """ gate_comps = self.get_unique_types(components=self.get_circuit_gates()) - one_bit_comps = self.get_unique_types(components=self.get_one_bit_components()) - multi_bit_comps = self.get_unique_types(components=self.get_multi_bit_components()) + one_bit_comps = self.get_unique_types( + components=self.get_one_bit_components()) + multi_bit_comps = self.get_unique_types( + components=self.get_multi_bit_components()) all_components = gate_comps + one_bit_comps + multi_bit_comps return all_components @@ -172,23 +196,31 @@ class ArithmeticCircuit(): """ self.circuit_wires = [] 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] + [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] 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))) + 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))) if hasattr(self, 'c'): - self.circuit_wires.append((self.c, f"{self.c.name}", self.save_wire_id(wire=self.c))) + self.circuit_wires.append( + (self.c, f"{self.c.name}", self.save_wire_id(wire=self.c))) for gate in self.circuit_gates: if not [item for item in self.circuit_wires if gate.a.name == item[1]]: - self.circuit_wires.append((gate.a, gate.a.name, self.save_wire_id(wire=gate.a))) + self.circuit_wires.append( + (gate.a, gate.a.name, self.save_wire_id(wire=gate.a))) if hasattr(gate, 'b') and not [item for item in self.circuit_wires if gate.b.name == item[1]]: - self.circuit_wires.append((gate.b, gate.b.name, self.save_wire_id(wire=gate.b))) + self.circuit_wires.append( + (gate.b, gate.b.name, self.save_wire_id(wire=gate.b))) if not [item for item in self.circuit_wires if gate.out.name == item[1]]: - self.circuit_wires.append((gate.out, gate.out.name, self.save_wire_id(wire=gate.out))) + self.circuit_wires.append( + (gate.out, gate.out.name, self.save_wire_id(wire=gate.out))) def get_circuit_wire_index(self, wire: Wire): """Searches for circuit's wire unique index position within the circuit. Used for cgp chromosome generation. @@ -283,8 +315,10 @@ class ArithmeticCircuit(): 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")).prefix + str(self.N) - circuit_block = self.__class__(a=Bus(N=self.N, prefix="a"), b=Bus(N=self.N, prefix="b"), prefix=circuit_prefix) + circuit_prefix = 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_prefix) return f"{circuit_block.get_circuit_c()}\n\n" def get_declarations_c_hier(self): @@ -369,6 +403,7 @@ class ArithmeticCircuit(): """ 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. @@ -433,8 +468,10 @@ class ArithmeticCircuit(): 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")).prefix + str(self.N) - circuit_block = self.__class__(a=Bus(N=self.N, prefix="a"), b=Bus(N=self.N, prefix="b"), prefix=circuit_prefix) + circuit_prefix = 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_prefix) return f"{circuit_block.get_circuit_v()}\n\n" def get_declarations_v_hier(self): @@ -483,8 +520,10 @@ class ArithmeticCircuit(): circuit_type = self.prefix.replace(circuit_prefix+"_", "") # Obtain proper circuit name with its bit width - circuit_prefix = 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"), prefix=circuit_prefix) + circuit_prefix = 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_prefix) 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" @@ -521,6 +560,7 @@ class ArithmeticCircuit(): """ BLIF CODE GENERATION """ # FLAT BLIF # + def get_prototype_blif(self): """Generates Blif code model name of described arithmetic circuit. @@ -603,7 +643,8 @@ class ArithmeticCircuit(): 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. @@ -635,8 +676,8 @@ class ArithmeticCircuit(): 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")).prefix + str(self.N) - circuit_block = self.__class__(a=Bus(N=self.N, prefix="a"), b=Bus(N=self.N, prefix="b"), prefix=circuit_prefix) + circuit_block = self.__class__(a=Bus(N=self.N, prefix="a"), b=Bus( + N=self.N, prefix="b")) return f"{circuit_block.get_circuit_blif()}" # Generating hierarchical BLIF code representation of circuit @@ -652,6 +693,7 @@ class ArithmeticCircuit(): """ CGP CODE GENERATION """ # FLAT CGP # + def get_parameters_cgp(self): """Generates CGP chromosome parameters of corresponding arithmetic circuit. @@ -678,8 +720,8 @@ class ArithmeticCircuit(): 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() - return "".join([g.get_triplet_cgp(a_id=self.get_circuit_wire_index(g.a)) 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)) for g in self.circuit_gates]) + 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]) def get_outputs_cgp(self): """Generates list of output wires indexes of described arithmetic circuit from MSB to LSB. diff --git a/ariths_gen/core/arithmetic_circuits/general_circuit.py b/ariths_gen/core/arithmetic_circuits/general_circuit.py new file mode 100644 index 0000000..e92bf17 --- /dev/null +++ b/ariths_gen/core/arithmetic_circuits/general_circuit.py @@ -0,0 +1,733 @@ +from ariths_gen.core.logic_gate_circuits.logic_gate_circuit import OneInputLogicGate, TwoInputLogicGate + +from ariths_gen.wire_components import ( + Wire, + ConstantWireValue0, + ConstantWireValue1, + Bus +) + + +class GeneralCircuit(): + """Class represents a general arithmetic circuit and ensures their 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, prefix: str, name: str, out_N: int, inner_component: bool = False, inputs: list=[]): + if prefix == "": + self.prefix = name + else: + self.prefix = prefix + "_" + name + self.inner_component = inner_component + self.inputs = inputs + self.out = Bus(self.prefix+"_out", out_N, out_bus=True) + + self.components = [] + self.circuit_wires = [] + self.circuit_gates = [] + self.c_data_type = "uint64_t" + + 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. + """ + self.components.append(component) + return component + + def get_previous_component(self, number: int = 1): + """Retrieves previously added composite subcomponent from circuit's list of components. + + Args: + number (int, optional): Offset indicating which lastly added component will be retrieved. Defaults to 1. + + Returns: + component: Desired previously added composite component. + """ + return self.components[-number] + + def get_instance_num(self, cls, count_disabled_gates: bool = True): + """Informs how many instances of the same type are already present inside circuit's components list. + + Args: + cls (type): Class type for which to count the number of instances in the components list. + count_disabled_gates (bool, optional): Indicates whether logic gates that aren't generated should be also counted. Defaults to True. + Returns: + int: Number of instances of the same class type. + """ + if issubclass(cls, TwoInputLogicGate) and count_disabled_gates is False: + 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): + """Gets a list of all the logic gates in circuit that should be generated. + + Returns: + list: List of composite logic gates. + """ + gates = [] + for c in self.components: + if isinstance(c, TwoInputLogicGate): + if c.disable_generation is False: + gates.append(c) + else: + gates.extend((c.get_circuit_gates())) + return gates + + def get_one_bit_components(self): + """Retrieves a list of all the one bit circuits (besides logic gates) present as subcomponents inside the circuit. + + Returns: + list: List of composite one bit circuits. + """ + one_bit_comps = [] + for c in self.components: + if isinstance(c, TwoInputLogicGate): + continue + elif isinstance(getattr(c, 'a'), Wire): + one_bit_comps.append(c) + else: + one_bit_comps.extend(c.get_one_bit_components()) + + return one_bit_comps + + def get_multi_bit_components(self): + """Retrieves a list of all the multi bit circuits present as subcomponents inside the circuit. + + Returns: + list: List of composite multi bit circuits. + """ + multi_bit_comps = [] + for c in self.components: + if isinstance(c, TwoInputLogicGate): + continue + elif isinstance(getattr(c, 'a'), Wire): + continue + else: + multi_bit_comps.append(c) + return multi_bit_comps + + @staticmethod + def get_unique_types(components: list): + """Retrieves just the unique representatives of class types present inside the provided components list. + + Args: + components (list): List of components to be filtered. + + Returns: + list: List of unique composite class types. + """ + return list({type(c): c for c in components}.values()) + + def get_component_types(self): + """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. + + Returns: + list: List of unique component types describing the circuit. + """ + gate_comps = self.get_unique_types(components=self.get_circuit_gates()) + one_bit_comps = self.get_unique_types( + components=self.get_one_bit_components()) + multi_bit_comps = self.get_unique_types( + components=self.get_multi_bit_components()) + + all_components = gate_comps + one_bit_comps + multi_bit_comps + return all_components + + def get_sum_wire(self): + """Get output wire carrying sum value. + + Returns: + Wire: Return sum wire. + """ + return self.out.get_wire(0) + + def get_carry_wire(self): + """Get output wire carrying carry out value. + + Returns: + Wire: Return carry out wire. + """ + return self.out.get_wire(1) + + def save_wire_id(self, wire: Wire): + """Returns appropriate wire index position within the circuit. + + 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. + + Args: + wire (Wire): Wire that will be stored at this circuit index position. + + Returns: + int: Wire's index position within circuit. + """ + if wire.is_const(): + return wire.cgp_const + else: + return len([w[0] for w in self.circuit_wires if w[0].is_const() is False]) + 2 + + def get_cgp_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 = [] + 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] + 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))) + if hasattr(self, 'c'): + self.circuit_wires.append( + (self.c, f"{self.c.name}", self.save_wire_id(wire=self.c))) + + for gate in self.circuit_gates: + if not [item for item in self.circuit_wires if gate.a.name == item[1]]: + self.circuit_wires.append( + (gate.a, gate.a.name, self.save_wire_id(wire=gate.a))) + + if hasattr(gate, 'b') and not [item for item in self.circuit_wires if gate.b.name == item[1]]: + self.circuit_wires.append( + (gate.b, gate.b.name, self.save_wire_id(wire=gate.b))) + + if not [item for item in self.circuit_wires if gate.out.name == item[1]]: + self.circuit_wires.append( + (gate.out, gate.out.name, self.save_wire_id(wire=gate.out))) + + def get_circuit_wire_index(self, wire: Wire): + """Searches for circuit's wire unique index position within the circuit. Used for cgp chromosome generation. + + Args: + wire (Wire): Wire to retrieve index position of. + + Returns: + int: Wire's index position number within the circuit. + """ + if wire.is_const(): + return wire.cgp_const + else: + for w in self.circuit_wires: + if wire.name == w[1]: + return w[2] + + """ C CODE GENERATION """ + # FLAT C # + @staticmethod + def get_includes_c(): + """Generates necessary C library includes for output representation. + + Returns: + str: C code library includes. + """ + return f"#include \n#include \n\n" + + 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}(" + ",".join([f"{self.c_data_type} {x.prefix}" for x in self.inputs]) + ")" + "{" + "\n" + + def get_declaration_c_flat(self): + """Generates flat C code declaration of input/output circuit wires. + + Returns: + str: Flat C code arithmetic circuit's wires declaration. + """ + return f"".join([c.get_declaration_c_flat() for c in self.components]) + + def get_init_c_flat(self): + """Generates flat C code initialization and assignment of corresponding arithmetic circuit's input/output wires. + + Returns: + str: Flat C code initialization of arithmetic circuit wires. + """ + return "".join([c.get_assign_c_flat() if isinstance(c, TwoInputLogicGate) else c.get_init_c_flat() for c in self.components]) + + def get_function_out_c_flat(self): + """Generates flat C code assignment of corresponding arithmetic circuit's output bus wires. + + Returns: + str: Flat C code containing output bus wires assignment. + """ + return self.out.return_bus_wires_values_c_flat() + + # Generating flat C code representation of circuit + def get_c_code_flat(self, file_object): + """Generates flat C code representation of corresponding arithmetic circuit. + + Args: + file_object (TextIOWrapper): Destination file object where circuit's representation will be written to. + """ + file_object.write(self.get_includes_c()) + file_object.write(self.get_prototype_c()) + file_object.write(self.out.get_declaration_c()) + file_object.write(self.get_declaration_c_flat()+"\n") + file_object.write(self.get_init_c_flat()+"\n") + file_object.write(self.get_function_out_c_flat()) + file_object.write(f" return {self.out.prefix}"+";\n}") + file_object.close() + + # HIERARCHICAL C # + def get_function_blocks_c(self): + """Generates hierarchical C code representation of all subcomponents function blocks present in corresponding arithmetic circuit. + + Returns: + str: Hierarchical C code of all subcomponents function blocks description. + """ + # Retrieve all unique component types composing this circuit + self.component_types = self.get_component_types() + return "".join([c.get_function_block_c() for c in self.component_types]) + + 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")).prefix + str(self.N) + circuit_block = self.__class__(a=Bus(N=self.N, prefix="a"), b=Bus( + N=self.N, prefix="b"), prefix=circuit_prefix) + return f"{circuit_block.get_circuit_c()}\n\n" + + def get_declarations_c_hier(self): + """Generates hierarchical C code declaration of input/output circuit wires. + + Returns: + str: Hierarchical C code containing unique declaration of arithmetic circuit wires. + """ + return "".join([c.get_declaration_c_hier() for c in self.components]) + + 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.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. + + Returns: + str: Hierarchical C code initialization of arithmetic circuit wires. + """ + return "".join([c.get_gate_invocation_c() if isinstance(c, TwoInputLogicGate) else c.get_out_invocation_c(circuit_prefix=self.prefix) for c in self.components]) + + def get_out_invocation_c(self, circuit_prefix: str): + """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. + + Args: + circuit_prefix (str): Prefix name of the upper component from which function block is being 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.prefix.replace(circuit_prefix+"_", "") + 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" + + def get_function_out_c_hier(self): + """Generates hierarchical C code assignment of corresponding arithmetic circuit's output bus wires. + + Returns: + str: Hierarchical C code containing output bus wires assignment. + """ + return self.out.return_bus_wires_values_c_hier() + + def get_circuit_c(self): + """Generates hierarchical C code subcomponent's function block. + + Returns: + str: Hierarchical C code of subcomponent's function block. + """ + return f"{self.get_prototype_c()}" + \ + f"{self.out.get_declaration_c()}" + \ + f"{self.get_declarations_c_hier()}\n" + \ + f"{self.get_init_c_hier()}\n" + \ + f"{self.get_function_out_c_hier()}" + \ + f" return {self.out.prefix}"+";\n}" + + # Generating hierarchical C code representation of circuit + def get_c_code_hier(self, file_object): + """Generates hierarchical C code representation of corresponding arithmetic circuit. + + Args: + file_object (TextIOWrapper): Destination file object where circuit's representation will be written to. + """ + file_object.write(self.get_includes_c()) + file_object.write(self.get_function_blocks_c()) + file_object.write(self.get_circuit_c()) + file_object.close() + + """ 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" + + def get_declaration_v_flat(self): + """Generates flat Verilog code declaration of input/output circuit wires. + + Returns: + str: Flat Verilog code arithmetic circuit's wires declaration. + """ + return f"".join([c.get_declaration_v_flat() for c in self.components]) + + def get_init_v_flat(self): + """Generates flat Verilog code initialization and assignment of corresponding arithmetic circuit's input/output buses wires. + + Returns: + str: Flat Verilog code initialization of arithmetic circuit wires. + """ + return "".join([c.get_assign_v_flat() if isinstance(c, TwoInputLogicGate) else c.get_init_v_flat() for c in self.components]) + + def get_function_out_v_flat(self): + """Generates flat Verilog code assignment of corresponding arithmetic circuit's output bus wires. + + Returns: + str: Flat Verilog code containing output bus wires assignment. + """ + return self.out.return_bus_wires_values_v_flat() + + # Generating flat Verilog code representation of circuit + def get_v_code_flat(self, file_object): + """Generates flat Verilog code representation of corresponding arithmetic circuit. + + Args: + file_object (TextIOWrapper): Destination file object where circuit's representation will be written to. + """ + file_object.write(self.get_prototype_v()) + file_object.write(self.get_declaration_v_flat()+"\n") + file_object.write(self.get_init_v_flat() + "\n") + file_object.write(self.get_function_out_v_flat()) + file_object.write(f"endmodule") + file_object.close() + + # HIERARCHICAL VERILOG # + def get_function_blocks_v(self): + """Generates hierarchical Verilog code representation of all subcomponents function blocks present in corresponding arithmetic circuit. + + Returns: + str: Hierarchical Verilog code of all subcomponents function blocks description. + """ + # Retrieve all unique component types composing this circuit + self.component_types = self.get_component_types() + return "".join([c.get_function_block_v() for c in self.component_types]) + + 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")).prefix + str(self.N) + circuit_block = self.__class__(a=Bus(N=self.N, prefix="a"), b=Bus( + N=self.N, prefix="b"), prefix=circuit_prefix) + return f"{circuit_block.get_circuit_v()}\n\n" + + def get_declarations_v_hier(self): + """Generates hierarchical Verilog code declaration of input/output circuit wires. + + Returns: + str: Hierarchical Verilog code containing unique declaration of arithmetic circuit wires. + """ + return "".join([c.get_declaration_v_hier() for c in self.components]) + + 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.out.N-1}:0] {self.out.prefix};\n" + + def get_init_v_hier(self): + """Generates hierarchical Verilog code initialization and assignment of corresponding arithmetic circuit's input/output wires. + + Returns: + str: Hierarchical Verilog code initialization of arithmetic circuit wires. + """ + return "".join([c.get_gate_invocation_v() if isinstance(c, TwoInputLogicGate) else c.get_out_invocation_v(circuit_prefix=self.prefix) for c in self.components]) + + def get_out_invocation_v(self, circuit_prefix: str): + """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. + + Args: + circuit_prefix (str): Prefix name of the upper component from which function block is being 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.prefix.replace(circuit_prefix+"_", "") + + # Obtain proper circuit name with its bit width + circuit_prefix = 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"), prefix=circuit_prefix) + 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" + + def get_function_out_v_hier(self): + """Generates hierarchical Verilog code assignment of corresponding arithmetic circuit's output bus wires. + + Returns: + str: Hierarchical Verilog code containing output bus wires assignment. + """ + return self.out.return_bus_wires_values_v_hier() + + def get_circuit_v(self): + """Generates hierarchical Verilog code subcomponent's function block. + + Returns: + str: Hierarchical Verilog code of subcomponent's function block. + """ + return f"{self.get_prototype_v()}" + \ + f"{self.get_declarations_v_hier()}\n" + \ + f"{self.get_init_v_hier()}\n" + \ + f"{self.get_function_out_v_hier()}" + \ + f"endmodule" + + # Generating hierarchical Verilog code representation of circuit + def get_v_code_hier(self, file_object): + """Generates hierarchical Verilog code representation of corresponding arithmetic circuit. + + Args: + file_object (TextIOWrapper): Destination file object where circuit's representation will be written to. + """ + file_object.write(self.get_function_blocks_v()) + file_object.write(self.get_circuit_v()) + file_object.close() + + """ BLIF CODE GENERATION """ + # FLAT BLIF # + + def get_prototype_blif(self): + """Generates Blif code model name of described arithmetic circuit. + + Returns: + str: Model's name in Blif code. + """ + return f".model {self.prefix}\n" + + 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" + + def get_function_blif_flat(self): + """Generates flat Blif code with invocation of subcomponents logic gates functions via their corresponding truth tables. + + Returns: + str: Flat Blif code containing invocation of inner subcomponents logic gates Boolean functions. + """ + return "".join(c.get_function_blif_flat() for c in self.components) + + def get_function_out_blif(self): + """Generates flat Blif code assignment of corresponding arithmetic circuit's output bus wires. + + Returns: + str: Flat Blif code containing output bus wires assignment. + """ + return f"{self.out.get_wire_assign_blif(output=True)}" + + # Generating flat BLIF code representation of circuit + def get_blif_code_flat(self, file_object): + """Generates flat Blif code representation of corresponding arithmetic circuit. + + Args: + file_object (TextIOWrapper): Destination file object where circuit's representation will be written to. + """ + file_object.write(self.get_prototype_blif()) + file_object.write(self.get_declaration_blif()) + file_object.write(self.get_function_blif_flat()) + file_object.write(self.get_function_out_blif()) + file_object.write(f".end\n") + file_object.close() + + # HIERARCHICAL BLIF # + def get_invocations_blif_hier(self): + """Generates hierarchical Blif code with invocations of subcomponents function blocks. + + Returns: + str: Hierarchical Blif code containing invocations of inner subcomponents function blocks. + """ + return "".join(c.get_invocation_blif_hier(circuit_prefix=self.prefix) for c in self.components) + + def get_invocation_blif_hier(self, circuit_prefix: str): + """Generates hierarchical Blif code invocation of corresponding arithmetic circuit's generated function block. + + Used for multi-bit subcomponent's modul invocation. + + Args: + circuit_prefix (str): Prefix name of the upper component from which function block is being invoked. + + 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.prefix.replace(circuit_prefix+"_", "") + 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" + + def get_circuit_blif(self): + """Generates hierarchical Blif code subcomponent's function block. + + Returns: + str: Hierarchical Blif code of subcomponent's function block. + """ + return f"{self.get_prototype_blif()}" + \ + f"{self.get_declaration_blif()}" + \ + f"{self.get_invocations_blif_hier()}" + \ + f"{self.get_function_out_blif()}" + \ + f".end\n" + + def get_function_blocks_blif(self): + """Generates hierarchical Blif code representation of all subcomponents function blocks present in corresponding arithmetic circuit. + + Returns: + str: Hierarchical Blif code of all subcomponents function blocks description. + """ + # Retrieve all unique component types composing this circuit + # (iterating backwards as opposed to other representations so the top modul is always above its subcomponents) + self.component_types = self.get_component_types() + return "\n".join([c.get_function_block_blif() for c in self.component_types[::-1]]) + + 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")).prefix + str(self.N) + circuit_block = self.__class__(a=Bus(N=self.N, prefix="a"), b=Bus( + N=self.N, prefix="b"), prefix=circuit_prefix) + return f"{circuit_block.get_circuit_blif()}" + + # Generating hierarchical BLIF code representation of circuit + def get_blif_code_hier(self, file_object): + """Generates hierarchical Blif code representation of corresponding arithmetic circuit. + + Args: + file_object (TextIOWrapper): Destination file object where circuit's representation will be written to. + """ + file_object.write(self.get_circuit_blif()+"\n") + file_object.write(self.get_function_blocks_blif()) + file_object.close() + + """ CGP CODE GENERATION """ + # FLAT CGP # + + def get_parameters_cgp(self): + """Generates CGP chromosome parameters of corresponding arithmetic circuit. + + In total seven parameters represent: total inputs, total outputs, number of rows, number of columns (gates), + number of each gate's inputs, number of each gate's outputs, quality constant value. + + Returns: + str: CGP chromosome parameters of described arithmetic circuit. + """ + self.circuit_gates = self.get_circuit_gates() + return f"{{{self.a.N+self.a.N},{self.out.N},1,{len(self.circuit_gates)},2,1,0}}" + + def get_triplets_cgp(self): + """Generates list of logic gate triplets (first input wire, second input wire, logic gate function) using wires unique position indexes within the described circuit. + + Each triplet represents unique logic gate within the described arithmetic circuit. Besides the contained input wires indexes and gate's inner logic function, an output wire + with incremented index position is also created and remembered to be appropriately driven as an input to another logic gate or as the circuit's output. + + 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. + + 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() + 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]) + + def get_outputs_cgp(self): + """Generates list of output wires indexes of described arithmetic circuit from MSB to LSB. + + Returns: + str: List of arithmetic circuit's output wire indexes. + """ + return "(" + ",".join([str(self.get_circuit_wire_index(o)) for o in self.out.bus]) + ")" + + # Generating flat CGP chromosome representation of circuit + def get_cgp_code_flat(self, file_object): + """Generates flat CGP chromosome representation of corresponding arithmetic circuit. + + Args: + file_object (TextIOWrapper): Destination file object where circuit's representation will be written to. + """ + file_object.write(self.get_parameters_cgp()) + file_object.write(self.get_triplets_cgp()) + file_object.write(self.get_outputs_cgp()) + file_object.close() diff --git a/ariths_gen/core/arithmetic_circuits/multiplier_circuit.py b/ariths_gen/core/arithmetic_circuits/multiplier_circuit.py index af46fe4..e565ed6 100644 --- a/ariths_gen/core/arithmetic_circuits/multiplier_circuit.py +++ b/ariths_gen/core/arithmetic_circuits/multiplier_circuit.py @@ -29,8 +29,8 @@ class MultiplierCircuit(ArithmeticCircuit): that are later used for generation into various representations. """ - def __init__(self): - super().__init__() + def __init__(self, a, b, prefix, name, out_N, **kwargs): + super().__init__(a=a, b=b, prefix=prefix, name=name, out_N=out_N, **kwargs) # Array multipliers def get_previous_partial_product(self, a_index: int, b_index: int): 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 84cb3bb..5c847e7 100644 --- a/ariths_gen/core/logic_gate_circuits/logic_gate_circuit.py +++ b/ariths_gen/core/logic_gate_circuits/logic_gate_circuit.py @@ -416,7 +416,7 @@ class TwoInputLogicGate(): """ return "{2,1,1,1,2,1,0}" - def get_triplet_cgp(self, a_id: int, b_id: int): + def get_triplet_cgp(self, a_id: int, b_id: int, out_id: int): """Generates logic gate triplet (first input wire, second input wire, logic gate function) using wires unique position indexes within the described circuit. Each triplet represents unique logic gate within the described circuit. Besides the contained input wires indexes and gate's inner logic function, an output wire @@ -429,11 +429,12 @@ class TwoInputLogicGate(): Args: a_id (int): First input wire index position. b_id (int): Second input wire index position. + out_id (int): The output wire index position Returns: str: Triplet describing function of corresponding two input logic gate. """ - return f"({a_id},{b_id},{self.cgp_function})" + return f"([{out_id}]{a_id},{b_id},{self.cgp_function})" @staticmethod def get_output_cgp(out_id: int): @@ -470,7 +471,7 @@ class TwoInputLogicGate(): out_id = self.out.cgp_const else: out_id = a_id+1 if a_id > b_id else b_id+1 - return self.get_triplet_cgp(a_id=a_id, b_id=b_id) + self.get_output_cgp(out_id=out_id) + return self.get_triplet_cgp(a_id=a_id, b_id=b_id, out_id=out_id) + self.get_output_cgp(out_id=out_id) def get_cgp_code(self, file_object): """Generates flat CGP chromosome representation of corresponding logic gate itself. @@ -706,7 +707,7 @@ class OneInputLogicGate(TwoInputLogicGate): """ CGP CODE GENERATION """ # FLAT CGP # - def get_triplet_cgp(self, a_id: int): + def get_triplet_cgp(self, a_id: int, out_id: int): """Generates logic gate triplet (first input wire, second input wire, logic gate function) using wires unique position indexes within the described circuit. Each triplet represents unique logic gate within the described circuit. In this case of one input logic gate, the same input wire index is driven to both inputs. @@ -719,11 +720,12 @@ class OneInputLogicGate(TwoInputLogicGate): Args: a_id (int): First (used also as the second) input wire index position. + out_id (int): Outpu wire index position Returns: str: Triplet describing function of corresponding one input logic gate. """ - return f"({a_id},{a_id},{self.cgp_function})" + return f"([{out_id}]{a_id},{a_id},{self.cgp_function})" @staticmethod def get_output_cgp(out_id: int): @@ -752,4 +754,4 @@ class OneInputLogicGate(TwoInputLogicGate): out_id = self.out.cgp_const else: out_id = a_id+1 if a_id == 2 else 2 - return self.get_triplet_cgp(a_id=a_id) + self.get_output_cgp(out_id=out_id) + return self.get_triplet_cgp(a_id=a_id, out_id=out_id) + self.get_output_cgp(out_id=out_id) 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 8331d13..b13106d 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 @@ -16,7 +16,7 @@ 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__() + super().__init__(a=a, b=b, prefix=prefix, name="", out_N=1, one_bit_circuit = True) self.c_data_type = "uint8_t" self.prefix = prefix self.a = a 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 d4d664f..ccd8aa4 100644 --- a/ariths_gen/multi_bit_circuits/adders/carry_lookahead_adder.py +++ b/ariths_gen/multi_bit_circuits/adders/carry_lookahead_adder.py @@ -62,22 +62,17 @@ class UnsignedCarryLookaheadAdder(ArithmeticCircuit): a (Bus): First input bus. b (Bus): Second input bus. cla_block_size (int, optional): Size of each composite cla adder block size. Defaults to 4. - prefix (str, optional): Prefix name of unsigned cla. Defaults to "u_cla". + prefix (str, optional): Prefix name of unsigned cla. Defaults to "". + name (str, optional): Name of unsigned cla. Defaults to "u_cla". """ - def __init__(self, a: Bus, b: Bus, cla_block_size: int = 4, prefix: str = "u_cla"): - super().__init__() + 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) - self.prefix = prefix - self.a = Bus(prefix=a.prefix, wires_list=a.bus) - self.b = Bus(prefix=b.prefix, wires_list=b.bus) + 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) - # Output wires for N sum bits and additional cout bit - self.out = Bus(self.prefix+"_out", self.N+1) - # To signify current number of blocks and number of bits that remain to be added into function blocks N_blocks = 0 N_wires = self.N @@ -185,10 +180,11 @@ class SignedCarryLookaheadAdder(UnsignedCarryLookaheadAdder, ArithmeticCircuit): a (Bus): First input bus. b (Bus): Second input bus. cla_block_size (int, optional): Size of each composite cla adder block size. Defaults to 4. - prefix (str, optional): Prefix name of signed cla. Defaults to "s_cla". + prefix (str, optional): Prefix name of signed cla. Defaults to "". + name (str, optional): Name of signed cla. Defaults to "s_cla". """ - def __init__(self, a: Bus, b: Bus, cla_block_size: int = 4, prefix: str = "s_cla"): - super().__init__(a=a, b=b, cla_block_size=cla_block_size, prefix=prefix) + def __init__(self, a: Bus, b: Bus, cla_block_size: int = 4, prefix: str = "", name: str = "s_cla", **kwargs): + super().__init__(a=a, b=b, cla_block_size=cla_block_size, prefix=prefix, name=name, **kwargs) self.c_data_type = "int64_t" # Additional XOR gates to ensure correct sign extension in case of sign addition 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 1fa3be2..b033dcd 100644 --- a/ariths_gen/multi_bit_circuits/adders/carry_skip_adder.py +++ b/ariths_gen/multi_bit_circuits/adders/carry_skip_adder.py @@ -69,22 +69,17 @@ class UnsignedCarrySkipAdder(ArithmeticCircuit): a (Bus): First input bus. b (Bus): Second input bus. bypass_block_size (int, optional): Size of each composite bypass adder block size. Defaults to 4. - prefix (str, optional): Prefix name of unsigned cska. Defaults to "u_cska". + prefix (str, optional): Prefix name of unsigned cska. Defaults to "". + name (str, optional): Name of unsigned cska. Defaults to "u_cska". """ - def __init__(self, a: Bus, b: Bus, bypass_block_size: int = 4, prefix: str = "u_cska"): - super().__init__() + 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) - self.prefix = prefix - self.a = Bus(prefix=a.prefix, wires_list=a.bus) - self.b = Bus(prefix=b.prefix, wires_list=b.bus) + 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) - # Output wires for N sum bits and additional cout bit - self.out = Bus(self.prefix+"_out", self.N+1) - # To signify current number of blocks and number of bits that remain to be added into function blocks N_blocks = 0 N_wires = self.N @@ -168,10 +163,11 @@ class SignedCarrySkipAdder(UnsignedCarrySkipAdder, ArithmeticCircuit): a (Bus): First input bus. b (Bus): Second input bus. bypass_block_size (int, optional): Size of each composite bypass adder block size. Defaults to 4. - prefix (str, optional): Prefix name of signed cska. Defaults to "s_cska". + prefix (str, optional): Prefix name of signed cska. Defaults to "". + name (str, optional): Name of signed cska. Defaults to "s_cska". """ - def __init__(self, a: Bus, b: Bus, bypass_block_size: int = 4, prefix: str = "s_cska"): - super().__init__(a=a, b=b, bypass_block_size=bypass_block_size, prefix=prefix) + def __init__(self, a: Bus, b: Bus, bypass_block_size: int = 4, prefix: str = "", name: str = "s_cska", **kwargs): + super().__init__(a=a, b=b, bypass_block_size=bypass_block_size, prefix=prefix, name=name, **kwargs) self.c_data_type = "int64_t" # Additional XOR gates to ensure correct sign extension in case of sign addition 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 5988a0e..5e99a1c 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 @@ -59,22 +59,17 @@ class UnsignedPGRippleCarryAdder(ArithmeticCircuit): Args: a (Bus): First input bus. b (Bus): Second input bus. - prefix (str, optional): Prefix name of unsigned P/G rca. Defaults to "u_pg_rca". + prefix (str, optional): Prefix name of unsigned P/G rca. Defaults to "". + name (str, optional): Name of unsigned P/G rca. Defaults to "u_pg_rca". """ - def __init__(self, a: Bus, b: Bus, prefix: str = "u_pg_rca"): - super().__init__() + def __init__(self, a: Bus, b: Bus, prefix: str = "", name: str = "u_pg_rca", **kwargs): self.N = max(a.N, b.N) - self.prefix = prefix - self.a = Bus(prefix=a.prefix, wires_list=a.bus) - self.b = Bus(prefix=b.prefix, wires_list=b.bus) - + 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) - # Output wires for N sum bits and additional cout bit - self.out = Bus(self.prefix+"_out", self.N+1) - # Gradual addition of 1-bit adder components for input_index in range(self.N): if input_index == 0: @@ -131,10 +126,11 @@ class SignedPGRippleCarryAdder(UnsignedPGRippleCarryAdder, ArithmeticCircuit): Args: a (Bus): First input bus. b (Bus): Second input bus. - prefix (str, optional): Prefix name of signed P/G rca. Defaults to "s_pg_rca". + prefix (str, optional): Prefix name of signed P/G rca. Defaults to "". + name (str, optional): Name of signed P/G rca. Defaults to "s_pg_rca". """ - def __init__(self, a: Bus, b: Bus, prefix: str = "s_pg_rca"): - super().__init__(a=a, b=b, prefix=prefix) + def __init__(self, a: Bus, b: Bus, prefix: str = "", name: str = "s_pg_rca", **kwargs): + super().__init__(a=a, b=b, prefix=prefix, name=name, **kwargs) self.c_data_type = "int64_t" # Additional XOR gates to ensure correct sign extension in case of sign addition 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 48a3df6..35af939 100644 --- a/ariths_gen/multi_bit_circuits/adders/ripple_carry_adder.py +++ b/ariths_gen/multi_bit_circuits/adders/ripple_carry_adder.py @@ -50,22 +50,17 @@ class UnsignedRippleCarryAdder(ArithmeticCircuit): Args: a (Bus): First input bus. b (Bus): Second input bus. - prefix (str, optional): Prefix name of unsigned rca. Defaults to "u_rca". + prefix (str, optional): Prefix name of unsigned rca. Defaults to "". + name (str, optional): Name of unsigned rca. Defaults to "u_rca". """ - def __init__(self, a: Bus, b: Bus, prefix: str = "u_rca"): - super().__init__() + def __init__(self, a: Bus, b: Bus, prefix: str = "", name: str = "u_rca", **kwargs): self.N = max(a.N, b.N) - self.prefix = prefix - self.a = Bus(prefix=a.prefix, wires_list=a.bus) - self.b = Bus(prefix=b.prefix, wires_list=b.bus) + 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) - # Output wires for N sum bits and additional cout bit - self.out = Bus(self.prefix+"_out", self.N+1) - # Gradual addition of 1-bit adder components for input_index in range(self.N): # First adder is a half adder @@ -107,10 +102,11 @@ class SignedRippleCarryAdder(UnsignedRippleCarryAdder, ArithmeticCircuit): Args: a (Bus): First input bus. b (Bus): Second input bus. - prefix (str, optional): Prefix name of signed rca. Defaults to "s_rca". + prefix (str, optional): Prefix name of signed rca. Defaults to "". + name (str, optional): Name of signed rca. Defaults to "s_rca". """ - def __init__(self, a: Bus, b: Bus, prefix: str = "s_rca"): - super().__init__(a=a, b=b, prefix=prefix) + def __init__(self, a: Bus, b: Bus, prefix: str = "", name: str = "s_rca", **kwargs): + super().__init__(a=a, b=b, prefix=prefix, name=name, **kwargs) self.c_data_type = "int64_t" # Additional XOR gates to ensure correct sign extension in case of sign addition diff --git a/ariths_gen/multi_bit_circuits/dividers/array_divider.py b/ariths_gen/multi_bit_circuits/dividers/array_divider.py index 0c9f71b..4c31ac1 100644 --- a/ariths_gen/multi_bit_circuits/dividers/array_divider.py +++ b/ariths_gen/multi_bit_circuits/dividers/array_divider.py @@ -92,22 +92,17 @@ class ArrayDivider(ArithmeticCircuit): Args: a (Bus): First input bus. b (Bus): Second input bus. - prefix (str, optional): Prefix name of array divider. Defaults to "arrdiv". + prefix (str, optional): Prefix name of array divider. Defaults to "". + name (str, optional): Name of array divider. Defaults to "arrdiv". """ - def __init__(self, a: Bus, b: Bus, prefix: str = "arrdiv"): - super().__init__() + def __init__(self, a: Bus, b: Bus, prefix: str = "", name: str = "arrdiv", **kwargs): self.N = max(a.N, b.N) - self.prefix = prefix - self.a = Bus(prefix=a.prefix, wires_list=a.bus) - self.b = Bus(prefix=b.prefix, wires_list=b.bus) + super().__init__(a=a, b=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) self.b.bus_extend(N=self.N, prefix=b.prefix) - # Output wires for quotient result - self.out = Bus(self.prefix+"_out", self.N) - # Performing series of iterative subtractions # Gradually shifting the divisor for a_index in reversed(range(self.N)): diff --git a/ariths_gen/multi_bit_circuits/multipliers/array_multiplier.py b/ariths_gen/multi_bit_circuits/multipliers/array_multiplier.py index 67daa2a..92b0666 100644 --- a/ariths_gen/multi_bit_circuits/multipliers/array_multiplier.py +++ b/ariths_gen/multi_bit_circuits/multipliers/array_multiplier.py @@ -79,22 +79,17 @@ class UnsignedArrayMultiplier(MultiplierCircuit): Args: a (Bus): First input bus. b (Bus): Second input bus. - prefix (str, optional): Prefix name of unsigned array multiplier. Defaults to "u_arrmul". + prefix (str, optional): Prefix name of unsigned array multiplier. Defaults to "". + name (str, optional): Name of unsigned array multiplier. Defaults to "u_arrmul". """ - def __init__(self, a: Bus, b: Bus, prefix: str = "u_arrmul"): - super().__init__() + def __init__(self, a: Bus, b: Bus, prefix: str = "", name: str = "u_arrmul", **kwargs): self.N = max(a.N, b.N) - self.prefix = prefix - self.a = Bus(prefix=a.prefix, wires_list=a.bus) - self.b = Bus(prefix=b.prefix, wires_list=b.bus) + 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) - # Output wires for multiplication product - self.out = Bus(self.prefix+"_out", self.N*2) - # Gradual generation of partial products for b_multiplier_index in range(self.N): for a_multiplicand_index in range(self.N): @@ -127,7 +122,7 @@ class UnsignedArrayMultiplier(MultiplierCircuit): # 1 bit multiplier case if a_multiplicand_index == self.N-1: - self.out.connect(a_multiplicand_index+1, ConstantWireValue0) + self.out.connect(a_multiplicand_index+1, ConstantWireValue0()) elif b_multiplier_index == self.N-1: self.out.connect(b_multiplier_index + a_multiplicand_index, obj_adder.get_sum_wire()) @@ -191,23 +186,18 @@ class SignedArrayMultiplier(MultiplierCircuit): Args: a (Bus): First input bus. b (Bus): Second input bus. - prefix (str, optional): Prefix name of signed array multiplier. Defaults to "s_arrmul". + prefix (str, optional): Prefix name of signed array multiplier. Defaults to "". + name (str, optional): Name of signed array multiplier. Defaults to "s_arrmul". """ - def __init__(self, a: Bus, b: Bus, prefix: str = "s_arrmul"): - super().__init__() - self.c_data_type = "int64_t" + def __init__(self, a: Bus, b: Bus, prefix: str = "", name: str = "s_arrmul", **kwargs): self.N = max(a.N, b.N) - self.prefix = prefix - self.a = Bus(prefix=a.prefix, wires_list=a.bus) - self.b = Bus(prefix=b.prefix, wires_list=b.bus) + super().__init__(a=a, b=b, prefix=prefix, name=name, out_N=self.N*2, **kwargs) + self.c_data_type = "int64_t" # Bus sign extension in case buses have different lengths self.a.bus_extend(N=self.N, prefix=a.prefix) self.b.bus_extend(N=self.N, prefix=b.prefix) - # Output wires for multiplication product - self.out = Bus(self.prefix+"_out", self.N*2) - # Gradual generation of partial products for b_multiplier_index in range(self.N): for a_multiplicand_index in range(self.N): diff --git a/ariths_gen/multi_bit_circuits/multipliers/dadda_multiplier.py b/ariths_gen/multi_bit_circuits/multipliers/dadda_multiplier.py index af7f182..7cf1fc9 100644 --- a/ariths_gen/multi_bit_circuits/multipliers/dadda_multiplier.py +++ b/ariths_gen/multi_bit_circuits/multipliers/dadda_multiplier.py @@ -45,23 +45,18 @@ class UnsignedDaddaMultiplier(MultiplierCircuit): Args: a (Bus): First input bus. b (Bus): Second input bus. - prefix (str, optional): Prefix name of unsigned dadda multiplier. Defaults to "u_dadda_cla". + prefix (str, optional): Prefix name of unsigned dadda multiplier. Defaults to "". + name (str, optional): Name of unsigned dadda multiplier. Defaults to "u_dadda_cla". unsigned_adder_class_name (str, optional): Unsigned multi bit adder used to obtain final sums of products. Defaults to UnsignedCarryLookaheadAdder. """ - def __init__(self, a: Bus, b: Bus, prefix: str = "u_dadda_cla", unsigned_adder_class_name: str = UnsignedCarryLookaheadAdder): - super().__init__() + 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) - self.prefix = prefix - self.a = Bus(prefix=a.prefix, wires_list=a.bus) - self.b = Bus(prefix=b.prefix, wires_list=b.bus) + 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) - # Output wires for multiplication product - self.out = Bus(self.prefix+"_out", self.N*2) - # Get starting stage and maximum possible column height self.stage, self.d = self.get_maximum_height(initial_value=min(self.a.N, self.b.N)) # Initialize all columns partial products forming AND gates matrix @@ -123,11 +118,10 @@ class UnsignedDaddaMultiplier(MultiplierCircuit): # Final addition of remaining bits using chosen unsigned multi bit adder else: # Obtain proper adder name with its bit width (columns bit pairs minus the first alone bit) - adder_prefix = self.prefix + "_" + unsigned_adder_class_name(a=a, b=b).prefix + str(len(self.columns)-1) - - adder_a = Bus(prefix=f"{adder_prefix}_a", wires_list=[self.add_column_wire(column=col, bit=0) for col in range(1, len(self.columns))]) - adder_b = Bus(prefix=f"{adder_prefix}_b", wires_list=[self.add_column_wire(column=col, bit=1) for col in range(1, len(self.columns))]) - final_adder = unsigned_adder_class_name(a=adder_a, b=adder_b, prefix=adder_prefix) + adder_name = unsigned_adder_class_name(a=a, b=b).prefix + str(len(self.columns)-1) + adder_a = Bus(prefix=f"a", wires_list=[self.add_column_wire(column=col, bit=0) for col in range(1, len(self.columns))]) + adder_b = Bus(prefix=f"b", wires_list=[self.add_column_wire(column=col, bit=1) for col in range(1, len(self.columns))]) + final_adder = unsigned_adder_class_name(a=adder_a, b=adder_b, prefix=self.prefix, name=adder_name, inner_component=True) self.add_component(final_adder) [self.out.connect(o, final_adder.out.get_wire(o-1), inserted_wire_desired_index=o-1) for o in range(1, len(self.out.bus))] @@ -155,24 +149,19 @@ class SignedDaddaMultiplier(MultiplierCircuit): Args: a (Bus): First input bus. b (Bus): Second input bus. - prefix (str, optional): Prefix name of signed dadda multiplier. Defaults to "s_dadda_cla". + prefix (str, optional): Prefix name of signed dadda multiplier. Defaults to "". + name (str, optional): Name of signed dadda multiplier. Defaults to "s_dadda_cla". unsigned_adder_class_name (str, optional): Unsigned multi bit adder used to obtain final sums of products. Defaults to UnsignedCarryLookaheadAdder. """ - def __init__(self, a: Bus, b: Bus, prefix: str = "s_dadda_cla", unsigned_adder_class_name: str = UnsignedCarryLookaheadAdder): - super().__init__() - self.c_data_type = "int64_t" + 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) - self.prefix = prefix - self.a = Bus(prefix=a.prefix, wires_list=a.bus) - self.b = Bus(prefix=b.prefix, wires_list=b.bus) + super().__init__(a=a, b=b, prefix=prefix, name=name, out_N=self.N*2, **kwargs) + self.c_data_type = "int64_t" # Bus sign extension in case buses have different lengths self.a.bus_extend(N=self.N, prefix=a.prefix) self.b.bus_extend(N=self.N, prefix=b.prefix) - # Output wires for multiplication product - self.out = Bus(self.prefix+"_out", self.N*2) - # Get starting stage and maximum possible column height self.stage, self.d = self.get_maximum_height(initial_value=min(self.a.N, self.b.N)) # Initialize all columns partial products forming AND/NAND gates matrix based on Baugh-Wooley multiplication @@ -241,11 +230,10 @@ class SignedDaddaMultiplier(MultiplierCircuit): # Final addition of remaining bits using chosen unsigned multi bit adder else: # Obtain proper adder name with its bit width (columns bit pairs minus the first alone bit) - adder_prefix = self.prefix + "_" + unsigned_adder_class_name(a=a, b=b).prefix + str(len(self.columns)-1) - - adder_a = Bus(prefix=f"{adder_prefix}_a", wires_list=[self.add_column_wire(column=col, bit=0) for col in range(1, len(self.columns))]) - adder_b = Bus(prefix=f"{adder_prefix}_b", wires_list=[self.add_column_wire(column=col, bit=1) for col in range(1, len(self.columns))]) - final_adder = unsigned_adder_class_name(a=adder_a, b=adder_b, prefix=adder_prefix) + adder_name = unsigned_adder_class_name(a=a, b=b).prefix + str(len(self.columns)-1) + adder_a = Bus(prefix=f"a", wires_list=[self.add_column_wire(column=col, bit=0) for col in range(1, len(self.columns))]) + adder_b = Bus(prefix=f"b", wires_list=[self.add_column_wire(column=col, bit=1) for col in range(1, len(self.columns))]) + final_adder = unsigned_adder_class_name(a=adder_a, b=adder_b, prefix=self.prefix, name=adder_name, inner_component=True) self.add_component(final_adder) [self.out.connect(o, final_adder.out.get_wire(o-1), inserted_wire_desired_index=o-1) for o in range(1, len(self.out.bus))] diff --git a/ariths_gen/multi_bit_circuits/multipliers/wallace_multiplier.py b/ariths_gen/multi_bit_circuits/multipliers/wallace_multiplier.py index cfabf4c..aa22119 100644 --- a/ariths_gen/multi_bit_circuits/multipliers/wallace_multiplier.py +++ b/ariths_gen/multi_bit_circuits/multipliers/wallace_multiplier.py @@ -44,23 +44,18 @@ class UnsignedWallaceMultiplier(MultiplierCircuit): Args: a (Bus): First input bus. b (Bus): Second input bus. - prefix (str, optional): Prefix name of unsigned wallace multiplier. Defaults to "u_wallace_cla". + prefix (str, optional): Prefix name of unsigned wallace multiplier. Defaults to "". + name (str, optional): Name of unsigned wallace multiplier. Defaults to "u_wallace_cla". unsigned_adder_class_name (str, optional): Unsigned multi bit adder used to obtain final sums of products. Defaults to UnsignedCarryLookaheadAdder. """ - def __init__(self, a: Bus, b: Bus, prefix: str = "u_wallace_cla", unsigned_adder_class_name: str = UnsignedCarryLookaheadAdder): - super().__init__() + def __init__(self, a: Bus, b: Bus, prefix: str = "", name: str = "u_wallace_cla", unsigned_adder_class_name: str = UnsignedCarryLookaheadAdder, **kwargs): self.N = max(a.N, b.N) - self.prefix = prefix - self.a = Bus(prefix=a.prefix, wires_list=a.bus) - self.b = Bus(prefix=b.prefix, wires_list=b.bus) + 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) - # Output wires for multiplication product - self.out = Bus(self.prefix+"_out", self.N*2) - # Initialize all columns partial products forming AND gates matrix self.columns = self.init_column_heights() @@ -117,11 +112,10 @@ class UnsignedWallaceMultiplier(MultiplierCircuit): # Final addition of remaining bits using chosen unsigned multi bit adder else: # Obtain proper adder name with its bit width (columns bit pairs minus the first alone bit) - adder_prefix = self.prefix + "_" + unsigned_adder_class_name(a=a, b=b).prefix + str(len(self.columns)-1) - - adder_a = Bus(prefix=f"{adder_prefix}_a", wires_list=[self.add_column_wire(column=col, bit=0) for col in range(1, len(self.columns))]) - adder_b = Bus(prefix=f"{adder_prefix}_b", wires_list=[self.add_column_wire(column=col, bit=1) for col in range(1, len(self.columns))]) - final_adder = unsigned_adder_class_name(a=adder_a, b=adder_b, prefix=adder_prefix) + adder_name = unsigned_adder_class_name(a=a, b=b).prefix + str(len(self.columns)-1) + adder_a = Bus(prefix=f"a", wires_list=[self.add_column_wire(column=col, bit=0) for col in range(1, len(self.columns))]) + adder_b = Bus(prefix=f"b", wires_list=[self.add_column_wire(column=col, bit=1) for col in range(1, len(self.columns))]) + final_adder = unsigned_adder_class_name(a=adder_a, b=adder_b, prefix=self.prefix, name=adder_name, inner_component=True) self.add_component(final_adder) [self.out.connect(o, final_adder.out.get_wire(o-1), inserted_wire_desired_index=o-1) for o in range(1, len(self.out.bus))] @@ -148,24 +142,19 @@ class SignedWallaceMultiplier(MultiplierCircuit): Args: a (Bus): First input bus. b (Bus): Second input bus. - prefix (str, optional): Prefix name of signed wallace multiplier. Defaults to "s_wallace_cla". + prefix (str, optional): Prefix name of signed wallace multiplier. Defaults to "". + name (str, optional): Name of signed wallace multiplier. Defaults to "s_wallace_cla". unsigned_adder_class_name (str, optional): Unsigned multi bit adder used to obtain final sums of products. Defaults to UnsignedCarryLookaheadAdder. """ - def __init__(self, a: Bus, b: Bus, prefix: str = "s_wallace_cla", unsigned_adder_class_name: str = UnsignedCarryLookaheadAdder): - super().__init__() + def __init__(self, a: Bus, b: Bus, prefix: str = "", name: str = "s_wallace_cla", unsigned_adder_class_name: str = UnsignedCarryLookaheadAdder, **kwargs): self.N = max(a.N, b.N) - self.prefix = prefix + super().__init__(a=a, b=b, prefix=prefix, name=name, out_N=self.N*2, **kwargs) self.c_data_type = "int64_t" - self.a = Bus(prefix=a.prefix, wires_list=a.bus) - self.b = Bus(prefix=b.prefix, wires_list=b.bus) # Bus sign extension in case buses have different lengths self.a.bus_extend(N=self.N, prefix=a.prefix) self.b.bus_extend(N=self.N, prefix=b.prefix) - # Output wires for multiplication product - self.out = Bus(self.prefix+"_out", self.N*2) - # Initialize all columns partial products forming AND/NAND gates matrix based on Baugh-Wooley multiplication self.columns = self.init_column_heights(signed=True) @@ -231,11 +220,10 @@ class SignedWallaceMultiplier(MultiplierCircuit): # Final addition of remaining bits using chosen unsigned multi bit adder else: # Obtain proper adder name with its bit width (columns bit pairs minus the first alone bit) - adder_prefix = self.prefix + "_" + unsigned_adder_class_name(a=a, b=b).prefix + str(len(self.columns)-1) - - adder_a = Bus(prefix=f"{adder_prefix}_a", wires_list=[self.add_column_wire(column=col, bit=0) for col in range(1, len(self.columns))]) - adder_b = Bus(prefix=f"{adder_prefix}_b", wires_list=[self.add_column_wire(column=col, bit=1) for col in range(1, len(self.columns))]) - final_adder = unsigned_adder_class_name(a=adder_a, b=adder_b, prefix=adder_prefix) + adder_name = unsigned_adder_class_name(a=a, b=b).prefix + str(len(self.columns)-1) + adder_a = Bus(prefix=f"a", wires_list=[self.add_column_wire(column=col, bit=0) for col in range(1, len(self.columns))]) + adder_b = Bus(prefix=f"b", wires_list=[self.add_column_wire(column=col, bit=1) for col in range(1, len(self.columns))]) + final_adder = unsigned_adder_class_name(a=adder_a, b=adder_b, prefix=self.prefix, name=adder_name, inner_component=True) self.add_component(final_adder) [self.out.connect(o, final_adder.out.get_wire(o-1), inserted_wire_desired_index=o-1) for o in range(1, len(self.out.bus))] diff --git a/ariths_gen/wire_components/buses.py b/ariths_gen/wire_components/buses.py index 9d707bb..50b263e 100644 --- a/ariths_gen/wire_components/buses.py +++ b/ariths_gen/wire_components/buses.py @@ -10,8 +10,10 @@ class Bus(): prefix (str, optional): Prefix name of the bus. Defaults to "bus". N (int, optional): Number of wires in the bus. Defaults to 1. wires_list (list, optional): List of Wire objects used to clone one bus to another. Defaults to 0. + out_bus (bool, optional): Specifies whether this Bus is an output bus of some previous component. Defaults to False. """ - def __init__(self, prefix: str = "bus", N: int = 1, wires_list: list = None): + def __init__(self, prefix: str = "bus", N: int = 1, wires_list: list = None, out_bus: bool = False): + self.out_bus = out_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) @@ -22,6 +24,14 @@ class Bus(): self.bus = wires_list self.N = len(self.bus) + def is_output_bus(self): + """Tells whether this Bus is an output bus. + + Returns: + bool: Returns True if it is an output bus of some component. + """ + return self.out_bus + def bus_extend(self, N: int, prefix: str = "bus"): """Provides bus extension to contain more wires. @@ -69,6 +79,14 @@ class Bus(): elif inserted_wire_desired_index != -1: self.bus[bus_wire_index] = Wire(name=inner_component_out_wire.name, prefix=inner_component_out_wire.parent_bus.prefix, index=inserted_wire_index, value=inner_component_out_wire.value, parent_bus=self) + # TODO + def connect_bus(self, connecting_bus, start_connection_pos: int = 0, end_connection_pos: int = -1): + if end_connection_pos == -1: + end_connection_pos = self.N + + # Nakonec je potřeba napojit výstup adderu na výstup mac + [self.connect(o, connecting_bus.get_wire(o), inserted_wire_desired_index=o) for o in range(start_connection_pos, end_connection_pos)] + """ C CODE GENERATION """ def get_declaration_c(self): """Bus declaration in C code. diff --git a/chr2c.py b/chr2c.py index 457824c..43ac444 100644 --- a/chr2c.py +++ b/chr2c.py @@ -10,7 +10,7 @@ import argparse # Parse all nodes present in input CGP def parse_node(n): - return list(map(int, re.match(r"(\d+),(\d+),(\d+)", n).groups())) + return list(map(int, re.match(r"\[(\d+)\](\d+),(\d+),(\d+)", n).groups())) # Recursively detect wires required to get the result wire with id 'id' and activate them (will be generated) @@ -56,10 +56,7 @@ def parse_chromosome(chromosome, signed=False, function=None): cdata_dict = [None for i in range(0, c_cols * c_rows)] for id, chromosome in enumerate(cdata): - a = chromosome[0] - b = chromosome[1] - f = chromosome[2] - + cid, a, b, f = chromosome cdata_dict[id] = (id + 2 + c_in, a, b, f) # Reserve position for all wires present in the genotype diff --git a/generate_mac.py b/generate_mac.py new file mode 100644 index 0000000..2cb69a1 --- /dev/null +++ b/generate_mac.py @@ -0,0 +1,24 @@ + +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 +import os + +class MAC(GeneralCircuit): + def __init__(self, a: Bus, b: Bus, r: Bus, prefix: str = "", name: str = "mac", **kwargs): + super().__init__(prefix=prefix, name=name, out_N=2*a.N+1, inputs=[a, b, r], **kwargs) + 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.out.connect_bus(connecting_bus=self.add.out) + +# usage +os.makedirs("test_circuits/mac", exist_ok=True) +mymac = MAC(Bus("a", 8), Bus("b", 8), Bus("acc", 16)) +mymac.get_v_code_hier(open("test_circuits/mac/mac_hier.v", "w")) +mymac.get_c_code_hier(open("test_circuits/mac/mac_hier.c", "w")) +mymac.get_c_code_flat(open("test_circuits/mac/mac_flat.c", "w")) diff --git a/generate_test.py b/generate_test.py index 8d6c16e..85f2c21 100644 --- a/generate_test.py +++ b/generate_test.py @@ -80,116 +80,116 @@ if __name__ == "__main__": # RCA name = f"u_rca{N}" - circuit = UnsignedRippleCarryAdder(a, b, prefix=name) + circuit = UnsignedRippleCarryAdder(a, b, name=name) export_circuit(circuit, name) name = f"s_rca{N}" - circuit = SignedRippleCarryAdder(a, b, prefix=name) + circuit = SignedRippleCarryAdder(a, b, name=name) export_circuit(circuit, name) # RCA with PG name = f"u_pg_rca{N}" - circuit = UnsignedPGRippleCarryAdder(a, b, prefix=name) + circuit = UnsignedPGRippleCarryAdder(a, b, name=name) export_circuit(circuit, name) name = f"s_pg_rca{N}" - circuit = SignedPGRippleCarryAdder(a, b, prefix=name) + circuit = SignedPGRippleCarryAdder(a, b, name=name) export_circuit(circuit, name) # CSKA with 4 bit CSKA blocks (default) name = f"u_cska{N}" - circuit = UnsignedCarrySkipAdder(a, b, prefix=name) + circuit = UnsignedCarrySkipAdder(a, b, name=name) export_circuit(circuit, name) name = f"s_cska{N}" - circuit = SignedCarrySkipAdder(a, b, prefix=name) + circuit = SignedCarrySkipAdder(a, b, name=name) export_circuit(circuit, name) # CLA with 4 bit CLA blocks (default) name = f"u_cla{N}" - circuit = UnsignedCarryLookaheadAdder(a, b, prefix=name) + circuit = UnsignedCarryLookaheadAdder(a, b, name=name) export_circuit(circuit, name) name = f"s_cla{N}" - circuit = SignedCarryLookaheadAdder(a, b, prefix=name) + circuit = SignedCarryLookaheadAdder(a, b, name=name) export_circuit(circuit, name) # Arrmul name = f"u_arrmul{N}" - circuit = UnsignedArrayMultiplier(a, b, prefix=name) + circuit = UnsignedArrayMultiplier(a, b, name=name) export_circuit(circuit, name) name = f"s_arrmul{N}" - circuit = SignedArrayMultiplier(a, b, prefix=name) + circuit = SignedArrayMultiplier(a, b, name=name) export_circuit(circuit, name) # Wallace name = f"u_wallace_cla{N}" - circuit = UnsignedWallaceMultiplier(a, b, prefix=name, unsigned_adder_class_name=UnsignedCarryLookaheadAdder) + circuit = UnsignedWallaceMultiplier(a, b, name=name, unsigned_adder_class_name=UnsignedCarryLookaheadAdder) export_circuit(circuit, name) name = f"s_wallace_cla{N}" - circuit = SignedWallaceMultiplier(a, b, prefix=name, unsigned_adder_class_name=UnsignedCarryLookaheadAdder) + circuit = SignedWallaceMultiplier(a, b, name=name, unsigned_adder_class_name=UnsignedCarryLookaheadAdder) export_circuit(circuit, name) name = f"u_wallace_rca{N}" - circuit = UnsignedWallaceMultiplier(a, b, prefix=name, unsigned_adder_class_name=UnsignedRippleCarryAdder) + circuit = UnsignedWallaceMultiplier(a, b, name=name, unsigned_adder_class_name=UnsignedRippleCarryAdder) export_circuit(circuit, name) name = f"s_wallace_rca{N}" - circuit = SignedWallaceMultiplier(a, b, prefix=name, unsigned_adder_class_name=UnsignedRippleCarryAdder) + circuit = SignedWallaceMultiplier(a, b, name=name, unsigned_adder_class_name=UnsignedRippleCarryAdder) export_circuit(circuit, name) name = f"u_wallace_pg_rca{N}" - circuit = UnsignedWallaceMultiplier(a, b, prefix=name, unsigned_adder_class_name=UnsignedPGRippleCarryAdder) + circuit = UnsignedWallaceMultiplier(a, b, name=name, unsigned_adder_class_name=UnsignedPGRippleCarryAdder) export_circuit(circuit, name) name = f"s_wallace_pg_rca{N}" - circuit = SignedWallaceMultiplier(a, b, prefix=name, unsigned_adder_class_name=UnsignedPGRippleCarryAdder) + circuit = SignedWallaceMultiplier(a, b, name=name, unsigned_adder_class_name=UnsignedPGRippleCarryAdder) export_circuit(circuit, name) name = f"u_wallace_cska{N}" - circuit = UnsignedWallaceMultiplier(a, b, prefix=name, unsigned_adder_class_name=UnsignedCarrySkipAdder) + circuit = UnsignedWallaceMultiplier(a, b, name=name, unsigned_adder_class_name=UnsignedCarrySkipAdder) export_circuit(circuit, name) name = f"s_wallace_cska{N}" - circuit = SignedWallaceMultiplier(a, b, prefix=name, unsigned_adder_class_name=UnsignedCarrySkipAdder) + circuit = SignedWallaceMultiplier(a, b, name=name, unsigned_adder_class_name=UnsignedCarrySkipAdder) export_circuit(circuit, name) # Dadda name = f"u_dadda_cla{N}" - circuit = UnsignedDaddaMultiplier(a, b, prefix=name, unsigned_adder_class_name=UnsignedCarryLookaheadAdder) + circuit = UnsignedDaddaMultiplier(a, b, name=name, unsigned_adder_class_name=UnsignedCarryLookaheadAdder) export_circuit(circuit, name) name = f"s_dadda_cla{N}" - circuit = SignedDaddaMultiplier(a, b, prefix=name, unsigned_adder_class_name=UnsignedCarryLookaheadAdder) + circuit = SignedDaddaMultiplier(a, b, name=name, unsigned_adder_class_name=UnsignedCarryLookaheadAdder) export_circuit(circuit, name) name = f"u_dadda_rca{N}" - circuit = UnsignedDaddaMultiplier(a, b, prefix=name, unsigned_adder_class_name=UnsignedRippleCarryAdder) + circuit = UnsignedDaddaMultiplier(a, b, name=name, unsigned_adder_class_name=UnsignedRippleCarryAdder) export_circuit(circuit, name) name = f"s_dadda_rca{N}" - circuit = SignedDaddaMultiplier(a, b, prefix=name, unsigned_adder_class_name=UnsignedRippleCarryAdder) + circuit = SignedDaddaMultiplier(a, b, name=name, unsigned_adder_class_name=UnsignedRippleCarryAdder) export_circuit(circuit, name) name = f"u_dadda_pg_rca{N}" - circuit = UnsignedDaddaMultiplier(a, b, prefix=name, unsigned_adder_class_name=UnsignedPGRippleCarryAdder) + circuit = UnsignedDaddaMultiplier(a, b, name=name, unsigned_adder_class_name=UnsignedPGRippleCarryAdder) export_circuit(circuit, name) name = f"s_dadda_pg_rca{N}" - circuit = SignedDaddaMultiplier(a, b, prefix=name, unsigned_adder_class_name=UnsignedPGRippleCarryAdder) + circuit = SignedDaddaMultiplier(a, b, name=name, unsigned_adder_class_name=UnsignedPGRippleCarryAdder) export_circuit(circuit, name) name = f"u_dadda_cska{N}" - circuit = UnsignedDaddaMultiplier(a, b, prefix=name, unsigned_adder_class_name=UnsignedCarrySkipAdder) + circuit = UnsignedDaddaMultiplier(a, b, name=name, unsigned_adder_class_name=UnsignedCarrySkipAdder) export_circuit(circuit, name) name = f"s_dadda_cska{N}" - circuit = SignedDaddaMultiplier(a, b, prefix=name, unsigned_adder_class_name=UnsignedCarrySkipAdder) + circuit = SignedDaddaMultiplier(a, b, name=name, unsigned_adder_class_name=UnsignedCarrySkipAdder) export_circuit(circuit, name) # Arrdiv name = f"arrdiv{N}" - circuit = ArrayDivider(a, b, prefix=name) + circuit = ArrayDivider(a, b, name=name) export_circuit(circuit, name) diff --git a/tests/mac.c b/tests/mac.c new file mode 100644 index 0000000..653a6a9 --- /dev/null +++ b/tests/mac.c @@ -0,0 +1,23 @@ +#include +#include +#include +#include + +uint64_t mac(uint64_t a,uint64_t b,uint64_t acc); + + +int main() { + int result = 0; + + srand(42); + for(int i = 0; i < 10000; i++) { + uint64_t a, b, acc; + a = rand() % 256; + b = rand() % 256; + acc = rand() % 65536; + result = (a * b) + acc; + + assert(result == mac(a, b, acc)); + } + return 0; +} \ No newline at end of file diff --git a/tests/test_mac.sh b/tests/test_mac.sh new file mode 100644 index 0000000..8aa6689 --- /dev/null +++ b/tests/test_mac.sh @@ -0,0 +1,35 @@ +#!/usr/bin/bash + +valid=1 + +test_circuit_mac () { + local type=$1 + + + + for mode in "flat" "hier"; do + echo -e "===== Testing \e[33mMAC\e[0m ($mode) ======" + + g++ -std=c++11 -pedantic -g -std=c++11 -pedantic -DCNAME="$circuit" $type.c ../test_circuits/mac/mac_$mode.c -o tmp.exe + if ./tmp.exe ; then + echo -e "[\e[32mok\e[0m]" + else + echo -e "[\e[31mfail\e[0m]" + valid=0 + fi + done +} + + + +test_circuit_mac "mac" + + + +if [ $valid -eq 1 ]; then + echo "all tests passed" + exit 0 +else + echo "some of tests failed" + exit 1 +fi