diff --git a/arithmetic_circuits.py b/arithmetic_circuits.py new file mode 100644 index 0000000..549135a --- /dev/null +++ b/arithmetic_circuits.py @@ -0,0 +1,314 @@ +from logic_gates import logic_gate, and_gate, nand_gate, or_gate, nor_gate, xor_gate, xnor_gate, not_gate +from wire_components import wire, bus +import itertools + +""" ARITHMETIC CIRCUITS """ + + +class arithmetic_circuit(): + def __init__(self): + self.components = [] + self.circuit_wires = [] + self.circuit_gates = [] + self.c_data_type = "uint64_t" + self.N = 1 + + # TODO delete? + self.carry_out_gate = None + self.sum_out_gates = [] + + def add_component(self, component): + self.components.append(component) + + def get_previous_component(self, number: int = 1): + return self.components[-number] + + def get_component_types(self): + return list({type(c): c for c in self.components}.values()) + + def get_unique_types(self): + hier_components = self.components_gates + self.component_types + return list({type(c): c for c in hier_components}.values()) + + def get_gate_types(self): + return list({type(g): g for g in self.components_gates}.values()) + + def get_previous_partial_product(self, a_index: int, b_index: int): + index = ((b_index-2) * (self.N*2)) + ((self.N-1)+2*(a_index+2)) + + if a_index == self.N-1: + index = index-2 + return self.components[index].get_carry_wire() + else: + return self.components[index].get_sum_wire() + + def get_sum_wire(self): + return self.out.get_wire(0) + + def get_carry_wire(self): + return self.out.get_wire(1) + + def get_circuit_wires(self): + for component in self.components: + if not [item for item in self.circuit_wires if item[1] == component.a.name]: + self.circuit_wires.append((component.a, component.a.name, len(self.circuit_wires))) + + if not [item for item in self.circuit_wires if item[1] == component.b.name]: + self.circuit_wires.append((component.b, component.b.name, len(self.circuit_wires))) + + if not [item for item in self.circuit_wires if item[1] == component.out.name]: + self.circuit_wires.append((component.out, component.out.name, len(self.circuit_wires))) + + # Search for circuit's wire unique index for cgp chromosome generation + def get_circuit_wire_index(self, wire: wire): + for w in self.circuit_wires: + if wire.name.endswith(w[1]): + return w[2] + + # Get list of all gates present in circuit + def get_circuit_gates(self): + gates = [] + # TODO CHANGE SIGNED MULTIPLIER IMPLEMENTATION (REPLACE CONSTANT WIRE FOR EXTRA GATES) FOR THIS TO BE DELETED + if hasattr(self, 'constant_wire'): + gates.append(xor_gate(a=self.a.get_wire(), b=self.b.get_wire(), prefix=self.prefix+"_xor_constant_wire")) + gates.append(xnor_gate(a=self.a.get_wire(), b=self.b.get_wire(), prefix=self.prefix+"_xnor_constant_wire")) + gates.append(or_gate(a=gates[0].out, b=gates[1].out, prefix=self.prefix+"_or_constant_wire")) + gates[2].out.name = "constant_wire" + + for c in self.components: + if isinstance(c, logic_gate): + gates.append(c) + else: + gates.extend((c.get_circuit_gates())) + return gates + + # Get list of all wires in circuit along with their index position for cgp chromosome generation + def get_cgp_wires(self): + if isinstance(self.a, bus): + [self.circuit_wires.append((w, f"_{w.name}", len(self.circuit_wires))) for w in self.a.bus] + [self.circuit_wires.append((w, f"_{w.name}", len(self.circuit_wires))) for w in self.b.bus] + else: + self.circuit_wires.append((self.a, f"_{self.a.name}", len(self.circuit_wires))) + self.circuit_wires.append((self.b, f"_{self.b.name}", len(self.circuit_wires))) + if hasattr(self, 'c'): + self.circuit_wires.append((self.c, f"_{self.c.name}", len(self.circuit_wires))) + + for gate in self.circuit_gates: + if not [item for item in self.circuit_wires if gate.a.name.endswith(item[1])]: + self.circuit_wires.append((gate.a, gate.a.name, len(self.circuit_wires))) + + if not [item for item in self.circuit_wires if gate.b.name.endswith(item[1])]: + self.circuit_wires.append((gate.b, gate.b.name, len(self.circuit_wires))) + + if not [item for item in self.circuit_wires if gate.out.name.endswith(item[1])]: + self.circuit_wires.append((gate.out, gate.out.name, len(self.circuit_wires))) + + """ C CODE GENERATION """ + # FLAT C # + @staticmethod + def get_includes_c(): + return f"#include \n#include \n\n" + + def get_prototype_c(self): + return f"{self.c_data_type} {self.prefix}({self.c_data_type} {self.a.prefix}, {self.c_data_type} {self.b.prefix})" + "{" + "\n" + + def get_declaration_c_flat(self): + return f"".join([c.get_declaration_c_flat() for c in self.components]) + + def get_init_c_flat(self): + return "".join([c.get_assign_c_flat(prefix_a=self.a.prefix, prefix_b=self.b.prefix) if isinstance(c, logic_gate) else c.get_init_c_flat() for c in self.components]) + + def get_function_out_c_flat(self): + return "".join([f" {self.out.prefix} |= {o.return_wire_value_c(offset=self.out.bus.index(o))};\n" for o in self.out.bus]) + + # Generating flat C code representation of circuit + def get_c_code_flat(self, file_object): + 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): + # Add unique 1-bit adder components (ha, fa) + self.component_types = self.get_component_types() + # Add unique logic gates composing subcomponents + self.components_gates = list(itertools.chain.from_iterable([c.get_component_types() for c in self.component_types])) + self.component_types = self.get_unique_types() + return "".join([c.get_function_block_c() for c in self.component_types]) + + def get_declaration_c_hier(self): + return "".join(self.a.get_wire_declaration_c()) + \ + "".join(self.b.get_wire_declaration_c()) + \ + "".join([c.out.get_declaration_c() if isinstance(c, logic_gate) else c.out.get_wire_declaration_c() for c in self.components]) + + def get_init_c_hier(self): + return ";\n".join([f" {w.name} = " + w.get_wire_value_c(offset=w.index, prefix=w.prefix) for w in self.a.bus]) + ";\n" + \ + ";\n".join([f" {w.name} = " + w.get_wire_value_c(offset=w.index, prefix=w.prefix) for w in self.b.bus]) + ";\n" + \ + "\n".join([f" {c.out.name} = {c.get_gate_invocation_c(a=c.a, b=c.b, get_index=True)}" if isinstance(c, logic_gate) else c.get_out_invocation_c() for c in self.components]) + + def get_function_out_c_hier(self): + return "".join([f" {self.out.prefix} |= {o.return_wire_value_c(offset=self.out.bus.index(o))};\n" for o in self.out.bus]) + + def get_circuit_c(self): + return f"{self.get_prototype_c()}" + \ + f"{self.out.get_declaration_c()}" + \ + f"{self.get_declaration_c_hier()}\n" + \ + f"{self.get_init_c_hier()}\n\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): + 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): + 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" + + def get_declaration_v_flat(self): + return f"".join([c.get_declaration_v_flat() for c in self.components]) + + def get_init_v_flat(self, offset: int = 0, array: bool = False): + return "\n".join([c.get_assign_v_flat(prefix_a=self.a.prefix, prefix_b=self.b.prefix, offset=offset, array=True) if isinstance(c, logic_gate) else c.get_init_v_flat(offset=self.components.index(c), array=array) for c in self.components]) + "\n" + + def get_function_out_v_flat(self): + return "".join([f" assign {self.out.prefix}[{self.out.bus.index(o)}] = {o.prefix};\n" for o in self.out.bus]) + + # Generating flat V code representation of circuit + def get_v_code_flat(self, file_object): + file_object.write(self.get_prototype_v()) + file_object.write(self.get_declaration_v_flat()+"\n") + file_object.write(self.get_init_v_flat(array=False)+"\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): + # Add unique 1-bit adder components (ha, fa) + self.component_types = self.get_component_types() + # Add unique logic gates composing subcomponents + self.components_gates = list(itertools.chain.from_iterable([c.get_component_types() for c in self.component_types])) + self.component_types = self.get_unique_types() + return "".join([c.get_function_block_v() for c in self.component_types]) + + def get_declaration_v_hier(self): + return "".join(self.a.get_wire_declaration_v()) + \ + "".join(self.b.get_wire_declaration_v()) + \ + "".join([c.out.get_declaration_v() if isinstance(c, logic_gate) else c.out.get_wire_declaration_v() for c in self.components]) + + def get_init_v_hier(self): + return ";\n".join([f" assign {w.name} = " + w.get_wire_value_v(offset=w.index, prefix=w.prefix, array=True) for w in self.a.bus]) + ";\n" + \ + ";\n".join([f" assign {w.name} = " + w.get_wire_value_v(offset=w.index, prefix=w.prefix, array=True) for w in self.b.bus]) + ";\n" + \ + "\n".join([f"{c.get_gate_invocation_v(a=c.a, b=c.b, out=c.out, get_index=True)}" if isinstance(c, logic_gate) else c.get_invocation_v() for c in self.components]) + "\n" + + def get_function_out_v_hier(self): + return "".join([f" assign {self.out.prefix}[{self.out.bus.index(o)}] = {o.name};\n" for o in self.out.bus]) + + def get_circuit_v(self): + return f"{self.get_prototype_v()}" + \ + f"{self.get_declaration_v_hier()}\n" + \ + f"{self.get_init_v_hier()}\n" + \ + f"{self.get_function_out_v_hier()}" + \ + f"endmodule" + + # Generating hierarchical C code representation of circuit + def get_v_code_hier(self, file_object): + file_object.write(self.get_function_blocks_v()) + file_object.write(self.get_circuit_v()) + file_object.close() + + """ CGP CODE GENERATION """ + # FLAT CGP # + def get_parameters_cgp(self): + self.circuit_gates = self.get_circuit_gates() + return f"{{1,{len(self.circuit_gates)},2,1,0}}" + + def get_triplet_cgp(self): + self.get_cgp_wires() + return "".join([g.get_triplet_cgp(a_index=self.get_circuit_wire_index(g.a), b_index=self.get_circuit_wire_index(g.b)) for g in self.circuit_gates]) + + def get_output_cgp(self): + return "(" + ",".join([str(self.get_circuit_wire_index(o)) for o in self.out.bus[::-1]]) + ")" + + def get_cgp_code(self, file_object): + file_object.write(self.get_parameters_cgp()) + file_object.write(self.get_triplet_cgp()) + file_object.write(self.get_output_cgp()) + file_object.close() + + +""" SIGNED ARITHMETIC CIRCUITS """ + + +class signed_adder_circuit(arithmetic_circuit): + def __init__(self): + super().__init__() + + # TODO TRY TO THINK ABOUT A WAY TO MORE EFFICIENTLY GENERATE OUTPUT FORMATS FOR EXTRA SIGN XOR INVOCATIONS + """ C CODE GENERATION """ + # FLAT C # + # Initialization of 1-bit adders and sign extension XOR gates + def get_init_c_flat(self): + return "".join([c.get_init_c_flat() for c in self.components if isinstance(c, arithmetic_circuit)]) + \ + f"{self.get_previous_component(number=2).get_assign_c_flat(prefix_a=self.a.prefix, prefix_b=self.b.prefix, offset=self.N-1)}" + \ + f"{self.get_previous_component().get_assign_c_flat(prefix_a=self.get_previous_component(number=2).out.prefix, prefix_b=self.get_previous_component(number=3).get_carry_wire().prefix)}" + + # HIERARCHICAL C # + def get_init_c_hier(self): + return ";\n".join([f" {w.name} = " + w.get_wire_value_c(offset=w.index, prefix=w.prefix) for w in self.a.bus]) + ";\n" + \ + ";\n".join([f" {w.name} = " + w.get_wire_value_c(offset=w.index, prefix=w.prefix) for w in self.b.bus]) + ";\n" + \ + "\n".join([f" {c.out.name} = {c.get_gate_invocation_c(a=c.a, b=c.b, get_index=True)}" if isinstance(c, logic_gate) else c.get_out_invocation_c() for c in self.components[:-1]]) + "\n" + \ + f" {self.get_previous_component().out.name} = {self.get_previous_component().get_gate_invocation_c(a=self.get_previous_component(number=2).out, b=self.get_previous_component(number=3).get_carry_wire())}" + + """ VERILOG CODE GENERATION """ + # FLAT VERILOG # + # Initialization of 1-bit adders and sign extension XOR gates + def get_init_v_flat(self, offset: int = 0, array: bool = False): + return f"\n".join([c.get_init_v_flat(offset=self.components.index(c), array=True) for c in self.components[:-2]]) + "\n" + \ + f"{self.get_previous_component(number=2).get_assign_v_flat(prefix_a=self.a.prefix, prefix_b=self.b.prefix, offset=self.N-1, array=True)}" + \ + f"{self.get_previous_component().get_assign_v_flat(prefix_a=self.get_previous_component(number=2).out.name, prefix_b=self.get_previous_component(number=3).get_carry_wire().name, offset=offset, array=array)}" + + # HIERARCHICAL VERILOG # + def get_init_v_hier(self): + return ";\n".join([f" assign {w.name} = " + w.get_wire_value_v(offset=w.index, prefix=w.prefix, array=True) for w in self.a.bus]) + ";\n" + \ + ";\n".join([f" assign {w.name} = " + w.get_wire_value_v(offset=w.index, prefix=w.prefix, array=True) for w in self.b.bus]) + ";\n" + \ + "\n".join([f"{c.get_gate_invocation_v(a=c.a, b=c.b, out=c.out, get_index=True)}" if isinstance(c, logic_gate) else c.get_invocation_v() for c in self.components[:-1]]) + "\n" + \ + f"{self.get_previous_component().get_gate_invocation_v(a=self.get_previous_component(number=2).out, b=self.get_previous_component(number=3).get_carry_wire(), out=self.get_previous_component().out)}\n" + + +class signed_multiplier_circuit(arithmetic_circuit): + def __init__(self): + super().__init__() + + # TODO CHANGE SIGNED MULTIPLIER CIRCUIT (REPLACE CONSTANT WIRE WITH LOGIC GATES) + # C CODE GENERATION + # FLAT C # + def get_declaration_c_flat(self): + return super().get_declaration_c_flat() + \ + f"{self.constant_wire.get_declaration_c()}" + + # HIERARCHICAL C # + def get_declaration_c_hier(self): + return super().get_declaration_c_hier() + \ + f"{self.constant_wire.get_declaration_c()}" + + # VERILOG CODE GENERATION + # FLAT VERILOG # + def get_declaration_v_flat(self): + return super().get_declaration_v_flat() + \ + f"{self.constant_wire.get_declaration_init_v()}" + + # HIERARCHICAL V # + def get_declaration_v_hier(self): + return super().get_declaration_v_hier() + \ + f"{self.constant_wire.get_declaration_init_v()}" diff --git a/arithmetic_circuits_generator.py b/arithmetic_circuits_generator.py index 11a410e..a945da5 100644 --- a/arithmetic_circuits_generator.py +++ b/arithmetic_circuits_generator.py @@ -1,580 +1,125 @@ from wire_components import wire, bus -from logic_gates_generator import not_gate, and_gate, xor_gate, or_gate, nand_gate, xnor_gate, nor_gate +from logic_gates import logic_gate, and_gate, nand_gate, or_gate, nor_gate, xor_gate, xnor_gate, not_gate +from one_bit_circuits import half_adder, full_adder +from multi_bit_circuits import unsigned_ripple_carry_adder, signed_ripple_carry_adder, unsigned_array_multiplier, signed_array_multiplier import sys -import itertools - - -""" ARITHMETIC CIRCUITS """ - - -class arithmetic_circuit(): - def __init__(self): - self.components = [] - self.circuit_wires = [] - self.c_data_type = "uint64_t" - self.N = 1 - - # TODO delete? - self.carry_out_gate = None - self.sum_out_gates = [] - - def add_component(self, component): - self.components.append(component) - - def get_previous_component(self): - return self.components[-1] - - def get_component_types(self): - return list({type(c): c for c in self.components}.values()) - - def get_unique_types(self): - hier_components = self.all_gates + self.component_types - return list({type(c): c for c in hier_components}.values()) - - def get_gate_types(self): - return list({type(g): g for g in self.all_gates}.values()) - - def get_sum_wire(self): - return self.out.get_wire(0) - - def get_carry_wire(self): - return self.out.get_wire(1) - - def get_circuit_wires(self): - for component in self.components: - if not [item for item in self.circuit_wires if item[1] == component.a.name]: - self.circuit_wires.append((component.a, component.a.name)) - - if not [item for item in self.circuit_wires if item[1] == component.b.name]: - self.circuit_wires.append((component.b, component.b.name)) - - if not [item for item in self.circuit_wires if item[1] == component.output.name]: - self.circuit_wires.append((component.output, component.output.name)) - - """ C CODE GENERATION """ - # FLAT C # - @staticmethod - def get_includes_c(): - return f"#include \n#include \n\n" - - def get_prototype_c(self): - return f"uint64_t {self.prefix}({self.c_data_type} {self.a.prefix}, {self.c_data_type} {self.b.prefix})" + "{" + "\n" - - def get_declaration_c_flat(self): - return f"".join([c.get_declaration_c_flat() for c in self.components]) - - def get_init_c_flat(self): - return "".join([c.get_init_c_flat() for c in self.components]) - - def get_function_sum_c_flat(self): - return "".join([c.get_function_sum_c_flat(offset=self.components.index(c)) for c in self.components]) - - def get_function_carry_c_flat(self): - return f"{self.get_previous_component().get_function_carry_c_flat(offset=self.out.N-1)}" - - # Generating flat C code representation of circuit - def get_c_code_flat(self, file_object): - 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_sum_c_flat()) - file_object.write(self.get_function_carry_c_flat()) - file_object.write(f" return {self.out.prefix}"+";\n}") - file_object.close() - - # HIERARCHICAL C # - def get_function_blocks_c(self): - # Add unique 1-bit adder components (ha, fa) - self.component_types = self.get_component_types() - # Add unique logic gates composing subcomponents - self.all_gates = list(itertools.chain.from_iterable([c.get_component_types() for c in self.component_types])) - self.component_types = self.get_unique_types() - return "".join([c.get_function_block_c() for c in self.component_types]) - - def get_declaration_c_hier(self): - self.cout = bus(N=self.N-1, prefix="cout") - return "".join(self.a.get_wire_declaration_c()) + \ - "".join(self.b.get_wire_declaration_c()) + \ - "".join(self.cout.get_wire_declaration_c()) - - def get_init_sum_c_hier(self): - self.in_wires = self.a.bus + self.b.bus - return ";\n".join([f" {w.name} = " + w.get_wire_value_c(offset=int(w.name[2:])) for w in self.in_wires]) - - def get_init_cout_c_hier(self): - return "\n".join([c.get_invocation_c(wire_n=self.cout.get_wire(self.components.index(c)).name, offset=self.components.index(c)) for c in self.components[:-1]]) - - def get_function_sum_c_hier(self): - return ";\n".join([c.get_adder_sum(offset=self.components.index(c)) for c in self.components])+";\n" - - def get_function_carry_c_hier(self): - return f"{self.get_previous_component().get_adder_cout(offset=self.out.N-1)};\n" - - def get_circuit_c(self): - return f"{self.get_prototype_c()}" + \ - f"{self.out.get_declaration_c()}" + \ - f"{self.get_declaration_c_hier()}\n" + \ - f"{self.get_init_sum_c_hier()};\n" + \ - f"{self.get_init_cout_c_hier()}\n\n" + \ - f"{self.get_function_sum_c_hier()}" + \ - f"{self.get_function_carry_c_hier()}" + \ - f" return {self.out.prefix}"+";\n}" - - # Generating hierarchical C code representation of circuit - def get_c_code_hier(self, file_object): - 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): - return f"module {self.prefix}(input [{self.N-1}:0] {self.a.prefix}, input [{self.N-1}:0] {self.b.prefix}, output [{self.N}:0] {self.out.prefix});\n" - - def get_declaration_v_flat(self): - return f"".join([c.get_declaration_v_flat() for c in self.components]) - - def get_init_v_flat(self, offset: int = 0, array: bool = False): - return "".join([c.get_init_v_flat(offset=self.components.index(c), array=True) for c in self.components]) - - def get_function_sum_cout_v_flat(self): - return "".join([c.get_function_sum_cout_v_flat(offset=self.components.index(c)) for c in self.components]) - - def get_function_sum_v_flat(self): - return "".join([c.get_function_sum_v_flat(offset=self.components.index(c)) for c in self.components]) - - def get_function_carry_v_flat(self): - return f"{self.get_previous_component().get_function_carry_v_flat(offset=self.out.N-1)}" - - # Generating flat V code representation of circuit - def get_v_code_flat(self, file_object): - file_object.write(self.get_prototype_v()) - file_object.write(self.get_declaration_v_flat()+"\n") - file_object.write(self.get_init_v_flat(array=False)+"\n") - file_object.write(self.get_function_sum_v_flat()) - file_object.write(self.get_function_carry_v_flat()) - file_object.write(f"endmodule") - file_object.close() - - -class half_adder(arithmetic_circuit): - def __init__(self, a: wire, b: wire, prefix: str = "ha"): - super().__init__() - self.c_data_type = "uint8_t" - self.prefix = prefix - self.a = a - self.b = b - # 2 wires for component's bus output (sum, cout) - self.out = bus("out", self.N+1) - - # Sum - # XOR gate for calculation of 1-bit sum - obj_xor_gate = xor_gate(a, b, prefix, outid=0) - self.add_component(obj_xor_gate) - self.out.connect(0, obj_xor_gate.output) - - # Cout - # AND gate for calculation of 1-bit cout - obj_and_gate = and_gate(a, b, prefix, outid=1) - self.add_component(obj_and_gate) - self.out.connect(1, obj_and_gate.output) - - """ C CODE GENERATION """ - # FLAT C # - # Half adder function prototype with two inputs - def get_prototype_c(self): - return f"{self.c_data_type} {self.prefix}({self.c_data_type} {self.a.name}, {self.c_data_type} {self.b.name})" + "{" + "\n" - - # Obtaining list of all the unique circuit wires from all contained logic gates - # to ensure non-recurring declaration of same wires - def get_declaration_c_flat(self): - self.get_circuit_wires() - # Unique declaration of all circuit's interconnections - return "".join([c[0].get_declaration_c() for c in self.circuit_wires]) - - # Half adder wires values initialization - def get_init_c_flat(self): - # self.a.prefix = self.a.name if self.a.prefix == "" else self.a.prefix - # self.b.prefix = self.b.name if self.b.prefix == "" else self.b.prefix - return f" {self.components[0].a.name} = {self.a.get_wire_value_c(offset=self.a.index)};\n" + \ - f" {self.components[0].b.name} = {self.b.get_wire_value_c(offset=self.b.index)};\n" + \ - f" {self.components[0].output.name} = {self.components[0].get_init_c_flat()};\n" + \ - f" {self.components[1].output.name} = {self.components[1].get_init_c_flat()};\n" - - def get_function_sum_c_flat(self, offset: int = 0): - return f" {self.out.prefix} |= {self.components[0].output.return_wire_value_c(offset=offset)};\n" - - def get_function_carry_c_flat(self, offset: int = 1): - return f" {self.out.prefix} |= {self.components[1].output.return_wire_value_c(offset=offset)};\n" - - # HIERARCHICAL C # - def get_function_block_c(self): - self.component_types = self.get_component_types() - self.prefix = "ha" - return f"{self.get_circuit_c()}\n\n" - - def get_function_blocks_c(self): - self.component_types = self.get_component_types() - return "".join([c.get_function_block_c() for c in self.component_types]) - - def get_invocation_c(self, wire_n: str, offset: int = 0): - self.prefix = "ha" if (offset == 0) else "fa" - return f" {wire_n} = ({self.prefix}({self.a.name}, {self.b.name}) >> 1) & 0x01;" - - def get_adder_sum(self, offset: int = 0): - return f" {self.out.prefix} |= (({self.prefix}({self.a.name}, {self.b.name}) >> 0) & 0x01) << {offset}" - - def get_adder_cout(self, offset: int = 1): - return f" {self.out.prefix} |= (({self.prefix}({self.a.name}, {self.b.name}) >> 1) & 0x01) << {offset}" - - def get_function_sum_c_hier(self, offset: int = 0): - return f" {self.out.prefix} |= {self.component_types[0].get_gate_output_c(a=self.a ,b=self.b, offset=offset)};\n" - - def get_function_carry_c_hier(self, offset: int = 1): - return f" {self.out.prefix} |= {self.component_types[1].get_gate_output_c(a=self.a ,b=self.b, offset=offset)};\n" - - def get_circuit_c(self): - return f"{self.get_prototype_c()}" + \ - f"{self.out.get_declaration_c()}\n" + \ - f"{self.get_function_sum_c_hier()}" + \ - f"{self.get_function_carry_c_hier()}" + \ - f" return {self.out.prefix}"+";\n}" - - """ VERILOG CODE GENERATION """ - # FLAT VERILOG # - def get_prototype_v(self): - return f"module {self.prefix}(input {self.a.name}, input {self.b.name}, output [{self.N}:0]{self.out.prefix});\n" - - def get_declaration_v_flat(self): - self.get_circuit_wires() - # Unique declaration of all circuit's interconnections - return "".join([c[0].get_declaration_v() for c in self.circuit_wires]) - - # Half adder wires values initialization - def get_init_v_flat(self, offset: int = 0, array: bool = False, ): - self.a.prefix = self.a.name if self.a.prefix == "" else self.a.prefix - self.b.prefix = self.b.name if self.b.prefix == "" else self.b.prefix - return f" assign {self.components[0].a.name} = {self.a.get_wire_value_v(offset=offset, array=array)};\n" + \ - f" assign {self.components[0].b.name} = {self.b.get_wire_value_v(offset=offset, array=array)};\n" + \ - f" assign {self.components[0].output.name} = {self.components[0].get_init_v_flat()};\n" + \ - f" assign {self.components[1].output.name} = {self.components[1].get_init_v_flat()};\n" - - def get_function_sum_v_flat(self, offset: int = 0): - return f" assign {self.out.prefix}[{offset}] = {self.components[0].output.name};\n" - - def get_function_carry_v_flat(self, offset: int = 1): - return f" assign {self.out.prefix}[{offset}] = {self.components[1].output.name};\n" - - -class full_adder(arithmetic_circuit): - def __init__(self, a: wire, b: wire, c: wire, prefix: str = "fa"): - super().__init__() - self.c_data_type = "uint8_t" - self.prefix = prefix - self.a = a - self.b = b - self.c = c - # 2 wires for component's bus output (sum, cout) - self.out = bus("out", self.N+1) - - # PG logic - propagate_xor_gate1 = xor_gate(a, b, prefix, outid=0) - self.add_component(propagate_xor_gate1) - generate_and_gate1 = and_gate(a, b, prefix, outid=1) - self.add_component(generate_and_gate1) - - # Sum - # XOR gate for calculation of 1-bit sum - obj_xor_gate2 = xor_gate(propagate_xor_gate1.output, c, prefix, outid=2) - self.add_component(obj_xor_gate2) - self.out.connect(0, obj_xor_gate2.output) - - # Cout - # AND gate for calculation of 1-bit cout - obj_and_gate2 = and_gate(propagate_xor_gate1.output, c, prefix, outid=3) - self.add_component(obj_and_gate2) - - obj_or_gate = or_gate(generate_and_gate1.output, obj_and_gate2.output, prefix, outid=4) - self.add_component(obj_or_gate) - - self.out.connect(1, obj_or_gate.output) - - # TODO delete or leave? - self.propagate = propagate_xor_gate1.output - self.generate = generate_and_gate1.output - - """ C CODE GENERATION """ - # FLAT C # - # Full adder function prototype with three inputs - def get_prototype_c(self): - return f"{self.c_data_type} {self.prefix}({self.c_data_type} {self.a.name}, {self.c_data_type} {self.b.name}, {self.c_data_type} {self.c.name})" + "{" + "\n" - - # Obtaining list of all the unique circuit wires from all contained logic gates - # to ensure non-recurring declaration of same wires - def get_declaration_c_flat(self): - self.get_circuit_wires() - # Unique declaration of all circuit's interconnections - return "".join([c[0].get_declaration_c() for c in self.circuit_wires]) - - # Full adder wires values initialization - def get_init_c_flat(self): - self.a.prefix = self.a.name if self.a.prefix == "" else self.a.prefix - self.b.prefix = self.b.name if self.b.prefix == "" else self.b.prefix - self.c.prefix = self.c.name - return f" {self.components[0].a.name} = {self.a.get_wire_value_c(offset=self.a.index)};\n" + \ - f" {self.components[0].b.name} = {self.b.get_wire_value_c(offset=self.b.index)};\n" + \ - f" {self.components[2].b.name} = {self.c.get_wire_value_c()};\n" + \ - f" {self.components[0].output.name} = {self.components[0].get_init_c_flat()};\n" + \ - f" {self.components[1].output.name} = {self.components[1].get_init_c_flat()};\n" + \ - f" {self.components[2].output.name} = {self.components[2].get_init_c_flat()};\n" + \ - f" {self.components[3].output.name} = {self.components[3].get_init_c_flat()};\n" + \ - f" {self.components[4].output.name} = {self.components[4].get_init_c_flat()};\n" - - def get_function_sum_c_flat(self, offset: int = 0): - return f" {self.out.prefix} |= {self.components[2].output.return_wire_value_c(offset=offset)};\n" - - def get_function_carry_c_flat(self, offset: int = 1): - return f" {self.out.prefix} |= {self.components[4].output.return_wire_value_c(offset=offset)};\n" - - # HIERARCHICAL C # - def get_function_block_c(self): - self.component_types = self.get_component_types() - self.prefix = "fa" - self.c.name = "cin" - return f"{self.get_circuit_c()}\n\n" - - def get_function_blocks_c(self): - self.component_types = self.get_component_types() - return "".join([c.get_function_block_c() for c in self.component_types]) - - def get_declaration_c_hier(self): - self.component_types[0].a.name = "xor_1" - self.component_types[2].a.name = "and_1" - self.component_types[2].b.name = "and_2" - return f"{self.component_types[0].a.get_declaration_c()}" + \ - f"{self.component_types[2].a.get_declaration_c()}" + \ - f"{self.component_types[2].b.get_declaration_c()}" - - def get_init_c_hier(self): - return f" {self.component_types[0].a.name} = {self.component_types[0].get_invocation_c(self.a, self.b)}\n" + \ - f" {self.component_types[2].a.name} = {self.component_types[1].get_invocation_c(self.a, self.b)}\n" + \ - f" {self.component_types[2].b.name} = {self.component_types[1].get_invocation_c(self.component_types[0].a, self.c)}\n" - - def get_invocation_c(self, wire_n: str, offset: int = 0): - self.prefix = "ha" if (offset == 0) else "fa" - self.c.name = "cout_"+str(offset-1) - return f" {wire_n} = ({self.prefix}({self.a.name}, {self.b.name}, {self.c.name}) >> 1) & 0x01;" - - def get_adder_sum(self, offset: int = 0): - self.c.name = "cout_"+str(offset-1) - return f" {self.out.prefix} |= (({self.prefix}({self.a.name}, {self.b.name}, {self.c.name}) >> 0) & 0x01) << {offset}" - - def get_adder_cout(self, offset: int = 1): - self.c.name = "cout_"+str(offset-2) - return f" {self.out.prefix} |= (({self.prefix}({self.a.name}, {self.b.name}, {self.c.name}) >> 1) & 0x01) << {offset}" - - def get_function_sum_c_hier(self, offset: int = 0): - return f" {self.out.prefix} |= {self.component_types[0].get_gate_output_c(a=self.component_types[0].a, b=self.c, offset=offset)};\n" - - def get_function_carry_c_hier(self, offset: int = 1): - return f" {self.out.prefix} |= {self.component_types[-1].get_gate_output_c(a=self.component_types[2].a, b=self.component_types[2].b, offset=offset)};\n" - - def get_circuit_c(self): - self.component_types = self.component_types - return f"{self.get_prototype_c()}" + \ - f"{self.out.get_declaration_c()}" + \ - f"{self.get_declaration_c_hier()}\n" + \ - f"{self.get_init_c_hier()}\n" + \ - f"{self.get_function_sum_c_hier()}" + \ - f"{self.get_function_carry_c_hier()}" + \ - f" return {self.out.prefix}"+";\n}" - - """ VERILOG CODE GENERATION """ - # FLAT VERILOG # - def get_prototype_v(self): - return f"module {self.prefix}(input {self.a.name}, input {self.b.name}, input {self.c.name}, output [{self.N}:0]{self.out.prefix});\n" - - def get_declaration_v_flat(self): - self.get_circuit_wires() - # Unique declaration of all circuit's interconnections - return "".join([c[0].get_declaration_v() for c in self.circuit_wires]) - - # Full adder wires values initialization - def get_init_v_flat(self, offset: int = 0, array: bool = False): - self.a.prefix = self.a.name if self.a.prefix == "" else self.a.prefix - self.b.prefix = self.b.name if self.b.prefix == "" else self.b.prefix - self.c.prefix = self.c.name - return f" assign {self.components[0].a.name} = {self.a.get_wire_value_v(offset=offset, array=array)};\n" + \ - f" assign {self.components[0].b.name} = {self.b.get_wire_value_v(offset=offset, array=array)};\n" + \ - f" assign {self.components[2].b.name} = {self.c.get_wire_value_v(offset=offset, array=array)};\n" + \ - f" assign {self.components[0].output.name} = {self.components[0].get_init_v_flat()};\n" + \ - f" assign {self.components[1].output.name} = {self.components[1].get_init_v_flat()};\n" + \ - f" assign {self.components[2].output.name} = {self.components[2].get_init_v_flat()};\n" + \ - f" assign {self.components[3].output.name} = {self.components[3].get_init_v_flat()};\n" + \ - f" assign {self.components[4].output.name} = {self.components[4].get_init_v_flat()};\n" - - def get_function_sum_v_flat(self, offset: int = 0): - return f" assign {self.out.prefix}[{offset}] = {self.components[2].output.name};\n" - - def get_function_carry_v_flat(self, offset: int = 1): - return f" assign {self.out.prefix}[{offset}] = {self.components[4].output.name};\n" - - -class signed_ripple_carry_adder(arithmetic_circuit): - def __init__(self, a: bus, b: bus, prefix: str = "s_rca"): - super().__init__() - self.N = max(a.N, b.N) - self.c_data_type = "int64_t" - # Bus sign extension in case buses have different lengths - a.bus_extend(N=self.N, prefix=a.prefix) - b.bus_extend(N=self.N, prefix=b.prefix) - self.a = a - self.b = b - if prefix == "s_rca": - self.prefix = prefix+str(self.N) - else: - self.prefix = prefix - - # Output wires for N sum bits and additional cout bit - self.out = bus("out", self.N+1) - - # Gradual addition of 1-bit adder components - for input_index in range(self.N): - # First one is a half adder - if input_index == 0: - obj_ha = half_adder(a.get_wire(input_index), b.get_wire(input_index), prefix=self.prefix+"_ha") - self.add_component(obj_ha) - self.out.connect(input_index, obj_ha) - # Rest are full adders - else: - obj_fa = full_adder(a.get_wire(input_index), b.get_wire(input_index), self.get_previous_component().get_carry_wire(), prefix=self.prefix+"_fa"+str(input_index)) - self.add_component(obj_fa) - self.out.connect(input_index, obj_fa.get_sum_wire()) - - if input_index == (self.N-1): - self.out.connect(self.N, obj_fa.get_carry_wire()) - - # Additional XOR gates to ensure correct sign extension in case of sign addition - sign_xor_1 = xor_gate(self.get_previous_component().a, self.get_previous_component().b, prefix=self.prefix+"_xor_1") - sign_xor_2 = xor_gate(sign_xor_1.output, self.get_previous_component().get_carry_wire(), prefix=self.prefix+"_xor_2") - self.add_component(sign_xor_1) - self.add_component(sign_xor_2) - - """ VERILOG CODE GENERATION """ - # FLAT C # - # Initialization of 1-bit adders and sign extension XOR gates - def get_init_c_flat(self): - self.components[-2].output.prefix = self.components[-2].output.name - self.components[-3].get_carry_wire().prefix = self.components[-3].get_carry_wire().name - return f"".join([c.get_init_c_flat() for c in self.components[:-2]]) + \ - f" {self.components[-2].a.name} = {self.a.get_wire_value_c(offset=self.N-1)};\n" + \ - f" {self.components[-2].b.name} = {self.b.get_wire_value_c(offset=self.N-1)};\n" + \ - f" {self.components[-2].output.name} = {self.components[-2].get_init_c_flat()};\n" + \ - f" {self.components[-1].a.name} = {self.components[-2].output.get_wire_value_c()};\n" + \ - f" {self.components[-1].b.name} = {self.components[-3].get_carry_wire().get_wire_value_c()};\n" + \ - f" {self.components[-1].output.name} = {self.components[-1].get_init_c_flat()};\n" - - def get_function_sum_c_flat(self): - return "".join([c.get_function_sum_c_flat(offset=self.components.index(c)) for c in self.components[:-2]]) - - def get_function_carry_c_flat(self): - return f" {self.out.prefix} |= {self.get_previous_component().output.return_wire_value_c(offset = self.N)};\n" - - # HIERARCHICAL C # - def get_declaration_c_hier(self): - self.cout = bus(N=self.N, prefix="cout") - return "".join(self.a.get_wire_declaration_c()) + \ - "".join(self.b.get_wire_declaration_c()) + \ - "".join(self.cout.get_wire_declaration_c()) + \ - f"{self.components[-2].output.get_declaration_c()}" - - def get_init_cout_c_hier(self): - return "\n".join([c.get_invocation_c(wire_n=self.cout.get_wire(self.components.index(c)).name, offset=self.components.index(c)) for c in self.components[:-2]]) + \ - f"\n {self.components[-2].output.name} = {self.components[-2].get_invocation_c(a=self.a.bus[-1], b=self.b.bus[-1])}" - - def get_function_sum_c_hier(self): - return ";\n".join([c.get_adder_sum(offset=self.components.index(c)) for c in self.components[:-2]])+";\n" - - def get_function_carry_c_hier(self): - return f" {self.out.prefix} |= {self.get_previous_component().get_gate_output_c(a=self.components[-2].output, b=self.cout.bus[-1], offset=self.out.N-1)};\n" - - """ VERILOG CODE GENERATION """ - # FLAT C # - # Initialization of 1-bit adders and sign extension XOR gates - def get_init_v_flat(self, offset: int = 0, array: bool = False): - self.components[-2].output.prefix = self.components[-2].output.name - self.components[-3].get_carry_wire().prefix = self.components[-3].get_carry_wire().name - return f"".join([c.get_init_v_flat(offset=self.components.index(c), array=True) for c in self.components[:-2]]) + \ - f" assign {self.components[-2].a.name} = {self.a.get_wire_value_v(offset=self.N-1, array=True)};\n" + \ - f" assign {self.components[-2].b.name} = {self.b.get_wire_value_v(offset=self.N-1, array=True)};\n" + \ - f" assign {self.components[-2].output.name} = {self.components[-2].get_init_v_flat()};\n" + \ - f" assign {self.components[-1].a.name} = {self.components[-2].output.get_wire_value_v(offset=offset, array=array)};\n" + \ - f" assign {self.components[-1].b.name} = {self.components[-3].get_carry_wire().get_wire_value_v()};\n" + \ - f" assign {self.components[-1].output.name} = {self.components[-1].get_init_v_flat()};\n" - - def get_function_sum_v_flat(self, offset: int = 0): - return "".join([c.get_function_sum_v_flat(offset=self.components.index(c)) for c in self.components[:-2]]) - - def get_function_carry_v_flat(self): - return f" assign {self.out.prefix}[{self.N}] = {self.get_previous_component().output.return_wire_value_v(offset=self.N)};\n" - - -class unsigned_ripple_carry_adder(arithmetic_circuit): - def __init__(self, a: bus, b: bus, prefix: str = "u_rca"): - super().__init__() - self.N = max(a.N, b.N) - # Bus sign extension in ase buses have different lengths - a.bus_extend(N=self.N, prefix=a.prefix) - b.bus_extend(N=self.N, prefix=b.prefix) - self.a = a - self.b = b - if prefix == "u_rca": - self.prefix = prefix+str(self.N) - else: - self.prefix = prefix - - # Output wires for N sum bits and additional cout bit - self.out = bus("out", self.N+1) - - # Gradual addition of 1-bit adder components - for input_index in range(self.N): - # First one is a half adder - if input_index == 0: - obj_ha = half_adder(a.get_wire(input_index), b.get_wire(input_index), prefix=self.prefix+"_ha") - self.add_component(obj_ha) - self.out.connect(input_index, obj_ha) - # Rest are full adders - else: - obj_fa = full_adder(a.get_wire(input_index), b.get_wire(input_index), self.get_previous_component().get_carry_wire(), prefix=self.prefix+"_fa"+str(input_index)) - self.add_component(obj_fa) - self.out.connect(input_index, obj_fa.get_sum_wire()) - - if input_index == (self.N-1): - self.out.connect(self.N, obj_fa.get_carry_wire()) +""" TESTING """ if __name__ == "__main__": - a = bus(N=8, prefix="a") - b = bus(N=8, prefix="b") - rca = unsigned_ripple_carry_adder(a, b, prefix="u_rca8") - # rca.get_c_code_hier(open("h_u_rca4.c", "w")) - # rca.get_c_code_flat(open("f_u_rca8.c", "w")) - rca.get_v_code_flat(open("f_u_rca8.v", "w")) + + a = bus(N=20, prefix="a") + b = bus(N=1, prefix="b") + rca = signed_ripple_carry_adder(a, b, prefix="f_s_rca2") + + rca.get_cgp_code(open("s_rca2.chr","w")) + #rca.get_v_code_hier(open("h_s_rca6.v", "w")) + #rca.get_v_code_flat(open("FLATTEST_u_rca3.v", "w")) + """ + rca.get_c_code_hier(open("h_s_rca8.c", "w")) + rca.get_c_code_flat(open("f_s_rca8.c", "w")) + rca.get_c_code_hier(open("h_s_rca8.c", "w")) + rca.get_c_code_flat(open("f_s_rca8.c", "w")) + + + rca.get_c_code_hier(open("h_s_rca8.c", "w")) + rca.get_v_code_hier(open("h_s_rca8.v", "w")) + rca.get_c_code_flat(open("f_s_rca8.c", "w")) + rca.get_v_code_flat(open("f_s_rca8.v", "w")) + """ + + #arrmul = signed_array_multiplier(a, b, prefix="f_s_arr_mul3") + u_arrmul = unsigned_array_multiplier(a, b, prefix="h_u_arr_mul5") + #u_arrmul.get_cgp_code(open("u_arr_mul5.chr","w")) + + s_arrmul = signed_array_multiplier(a, b) + s_arrmul.get_v_code_hier(open("testhier4.v","w")) + s_arrmul.get_v_code_flat(open("testflat4.v","w")) + + s_arrmul.get_cgp_code(open("s_arr_mul20.chr","w")) + #s_arrmul.get_v_code_hier(open("h_s_arr_mul3.v","w")) + + + s_arrmul.get_c_code_hier(open("h_s_arr_mul5.c","w")) + s_arrmul.get_c_code_flat(open("f_s_arr_mul5.c","w")) + s_arrmul.get_v_code_hier(open("h_s_arr_mul5.v","w")) + s_arrmul.get_v_code_flat(open("f_s_arr_mul5.v","w")) + s_arrmul.get_c_code_hier(open("h_s_arr_mul5.c","w")) + s_arrmul.get_c_code_flat(open("f_s_arr_mul5.c","w")) + s_arrmul.get_v_code_hier(open("h_s_arr_mul5.v","w")) + s_arrmul.get_v_code_flat(open("f_s_arr_mul5.v","w")) + + + #u_arrmul.get_v_code_hier(open("h_u_arr_mul3.v","w")) + #s_arrmul.get_v_code_hier(open("h_s_arr_mul3.v","w")) + + """ + arrmul.get_c_code_hier(open("h_s_arr_mul6.c","w")) + arrmul.get_c_code_flat(open("ff_s_arr_mul6.c","w")) + arrmul.get_c_code_hier(open("h_s_arr_mul6.c","w")) + arrmul.get_c_code_flat(open("ff_s_arr_mul6.c","w")) + """ + + + """ + rca.get_v_code_hier(open("h_s_rca8.v", "w")) + rca.get_v_code_flat(open("f_s_rca8.v", "w")) + + rca.get_c_code_hier(open("h_s_rca2.c", "w")) + rca.get_v_code_hier(open("h_s_rca2.v", "w")) + rca.get_c_code_flat(open("f_s_rca2.c", "w")) + rca.get_v_code_flat(open("f_s_rca2.v", "w")) + """ + #rca.get_v_code_hier(open("h_u_rca3.v", "w")) + #rca.get_c_code_flat(open("f_u_rca3.c", "w")) + + + #rca.get_c_code_hier(open("hier_srca8.c", "w")) + #rca.get_c_code_flat(open("flat_srca8.c", "w")) w1 = wire(name="a") w2 = wire(name="b") - w3 = wire(name="cout") - fa = full_adder(w1, w2, w3, prefix="f_fa") + w3 = wire(name="cin") ha = half_adder(w1, w2, prefix="f_ha") + fa = full_adder(w1, w2, w3, prefix="f_fa") - # ha.get_c_code_flat(open("f_ha.c","w")) - # ha.get_v_code_flat(open("f_ha.v","w")) - # fa.get_c_code_flat(open("f_fa.c","w")) - # fa.get_v_code_flat(open("f_fa.v","w")) + #ha.get_cgp_code(open("ha.chr","w")) + #fa.get_cgp_code(open("fa.chr","w")) - gate = not_gate(w1) - # gate.get_c_code(open("not_gate.c","w")) - # gate.get_v_code(open("not_gate.v","w")) + """ + ha.get_c_code_flat(open("f_ha.c","w")) + ha.get_v_code_flat(open("f_ha.v","w")) + fa.get_c_code_flat(open("f_fa.c","w")) + fa.get_v_code_hier(open("f_fa.v","w")) + """ + + ha.get_v_code_hier(open("h_ha.v","w")) + fa.get_v_code_hier(open("h_fa.v","w")) + + """ + ha.get_c_code_hier(open("h_ha.c","w")) + ha.get_c_code_flat(open("f_ha.c","w")) + ha.get_c_code_hier(open("h_ha.c","w")) + ha.get_c_code_flat(open("f_ha.c","w")) + + fa.get_c_code_hier(open("h_fa.c","w")) + fa.get_c_code_flat(open("f_fa.c","w")) + fa.get_c_code_hier(open("h_fa.c","w")) + fa.get_c_code_flat(open("f_fa.c","w")) + + ha.get_v_code_hier(open("h_ha.v","w")) + ha.get_v_code_flat(open("f_ha.v","w")) + ha.get_v_code_hier(open("h_ha.v","w")) + ha.get_v_code_flat(open("f_ha.v","w")) + + fa.get_v_code_hier(open("h_fa.v","w")) + fa.get_v_code_flat(open("f_fa.v","w")) + fa.get_v_code_hier(open("h_fa.v","w")) + fa.get_v_code_flat(open("f_fa.v","w")) + """ + + gate = and_gate(w1, w2) + #gate.get_cgp_code(open("and_gate.chr","w")) + #gate.get_c_code(open("and_gate.c","w")) + #gate.get_v_code(open("and_gate.v","w")) + \ No newline at end of file diff --git a/logic_gates.py b/logic_gates.py new file mode 100644 index 0000000..53d0af7 --- /dev/null +++ b/logic_gates.py @@ -0,0 +1,300 @@ +from wire_components import wire + +""" LOGIC GATE COMPONENTS """ + + +# Two-input # +class logic_gate(): + def __init__(self, a: wire, b: wire, prefix: str = "gate"): + self.a = wire(name=prefix+"_"+a.name.replace(prefix+"_", ""), value=a.value) + self.b = wire(name=prefix+"_"+b.name.replace(prefix+"_", ""), value=b.value) + self.prefix = prefix + + def get_component_types(self): + return list([self]) + + """ C CODE GENERATION """ + # FLAT C # + @staticmethod + def get_includes_c(): + return f"#include \n#include \n\n" + + def get_prototype_c(self): + return f"uint8_t {self.gate_type}(uint8_t {self.a.name}, uint8_t {self.b.name})" + "{" + "\n" + + def get_function_c(self): + return f"{self.a.get_wire_value_c()} {self.operator} {self.b.get_wire_value_c()}" + + def get_declaration_c_flat(self): + return f"{self.a.get_declaration_c()}{self.b.get_declaration_c()}{self.out.get_declaration_c()}" + + def get_init_c_flat(self): + return f"{self.a.name} {self.operator} {self.b.name}" + + def get_assign_c_flat(self, prefix_a: str = "a", prefix_b: str = "b", offset: int = 0): + return f" {self.a.name} = {self.a.get_wire_value_c(prefix=prefix_a, offset=offset)};\n" + \ + f" {self.b.name} = {self.b.get_wire_value_c(prefix=prefix_b, offset=offset)};\n" + \ + f" {self.out.prefix} = {self.a.name} {self.operator} {self.b.name};\n" + + # Generating flat C code representation of the logic gate itself + # (i.e. not as a component of bigger circuit) + def get_c_code(self, file_object): + file_object.write(self.get_includes_c()) + file_object.write(self.get_prototype_c()) + file_object.write(" return "+(self.get_function_c())+";\n}") + file_object.close() + + # HIERARCHICAL C # + def get_function_block_c(self): + return f"{self.get_prototype_c()}" + \ + f" return "+(self.get_function_c())+";\n}\n\n" + + def get_gate_invocation_c(self, a: wire, b: wire, sign: bool = False, get_index: bool = False): + a_name = a.prefix if sign is False else a.name + b_name = b.prefix if sign is False else b.name + a_name = a_name if get_index is False else "a" + a.name[a.name.rfind("_"):] + b_name = b_name if get_index is False else "b" + b.name[b.name.rfind("_"):] + return f"{self.gate_type}({a_name}, {b_name});" + + def get_gate_output_c(self, a: wire, b: wire, offset: int = 0, sign: bool = False): + a_name = a.prefix if sign is False else a.name + b_name = b.prefix if sign is False else b.name + return f"({self.gate_type}({a_name}, {b_name}) & 0x01) << {offset}" + + """ VERILOG CODE GENERATION """ + # FLAT VERILOG # + def get_prototype_v(self): + return f"module {self.gate_type}(input {self.a.name}, input {self.b.name}, output {self.out.name});\n" + + def get_declaration_v_flat(self): + return f"{self.a.get_declaration_v()}{self.b.get_declaration_v()}{self.out.get_declaration_v()}" + + def get_init_v_flat(self): + return f"{self.a.name} {self.operator} {self.b.name}" + + def get_assign_v_flat(self, prefix_a: str = "a", prefix_b: str = "b", offset: int = 0, array: bool = False): + return f" assign {self.a.name} = {self.a.get_wire_value_v(prefix=prefix_a, offset=offset, array=array)};\n" + \ + f" assign {self.b.name} = {self.b.get_wire_value_v(prefix=prefix_b, offset=offset, array=array)};\n" + \ + f" assign {self.out.prefix} = {self.a.name} {self.operator} {self.b.name};" + + # Generating flat Verilog code representation of the logic gate itself + # (i.e. not as a component of bigger circuit) + def get_v_code(self, file_object): + file_object.write(self.get_prototype_v()) + file_object.write(f" assign {self.out.name} = {self.get_init_v_flat()};\n") + file_object.write(f"endmodule") + file_object.close() + + # HIERARCHICAL VERILOG # + def get_function_block_v(self): + return f"{self.get_prototype_v()}" + \ + f" assign {self.out.name} = {self.get_init_v_flat()};\n" + \ + f"endmodule\n\n" + + def get_gate_invocation_v(self, a: wire, b: wire, out: wire, sign: bool = False, get_index: bool = False, out_array: bool = False, offset: int = 0): + a_name = a.prefix if sign is False else a.name + b_name = b.prefix if sign is False else b.name + a_name = a_name if get_index is False else "a" + a.name[a.name.rfind("_"):] + b_name = b_name if get_index is False else "b" + b.name[b.name.rfind("_"):] + return f" {self.gate_type} {self.gate_type}_{out.name}({a_name}, {b_name}, {out.get_wire_value_v(offset=offset, array=out_array)});" + + """ CGP CODE GENERATION """ + # FLAT CGP # + @staticmethod + def get_parameters_cgp(): + return "{1,1,2,1,0}" + + def get_triplet_cgp(self, a_index: int = 0, b_index: int = 1): + return f"({a_index},{b_index},{self.cgp_function})" + + @staticmethod + def get_output_cgp(out_index: int = 2): + return f"({out_index})" + + def get_cgp_code(self, file_object): + file_object.write(self.get_parameters_cgp()) + file_object.write(self.get_triplet_cgp()) + file_object.write(self.get_output_cgp()) + file_object.close() + + +class inverted_logic_gate(logic_gate): + def __init__(self, a: wire, b: wire, prefix: str = "gate"): + super().__init__(a, b, prefix) + + """ C CODE GENERATION """ + # FLAT C # + def get_function_c(self): + return "~("+(super().get_function_c())+") & 0x01 << 0" + + """ VERILOG CODE GENERATION """ + # FLAT VERILOG # + def get_init_v_flat(self): + return "~("+(super().get_init_v_flat())+")" + + +class and_gate(logic_gate): + def __init__(self, a: wire, b: wire, prefix: str = "", outid: int = 0): + super().__init__(a, b, prefix) + self.gate_type = "and_gate" + self.cgp_function = 2 + self.operator = "&" + self.out = wire(name=prefix+"_y"+str(outid)) + + """ C CODE GENERATION """ + # FLAT C # + def get_assign_c_flat(self, prefix_a: str = "a", prefix_b: str = "b"): + indexes = self.prefix[self.prefix.rfind("_", 0, self.prefix.rfind("_"))+1:] + offset_a = indexes[:indexes.rfind("_")] + offset_b = indexes[indexes.rfind("_")+1:] + return f" {self.a.name} = {self.a.get_wire_value_c(prefix=prefix_a, offset=offset_a)};\n" + \ + f" {self.b.name} = {self.b.get_wire_value_c(prefix=prefix_b, offset=offset_b)};\n" + \ + f" {self.out.prefix} = {self.a.name} {self.operator} {self.b.name};\n" + + """ VERILOG CODE GENERATION """ + # FLAT VERILOG # + def get_assign_v_flat(self, prefix_a: str = "a", prefix_b: str = "b", offset: int = 0, array: bool = False): + indexes = self.prefix[self.prefix.rfind("_", 0, self.prefix.rfind("_"))+1:] + offset_a = indexes[:indexes.rfind("_")] + offset_b = indexes[indexes.rfind("_")+1:] + return f" assign {self.a.name} = {self.a.get_wire_value_v(prefix=prefix_a, offset=offset_a, array=array)};\n" + \ + f" assign {self.b.name} = {self.b.get_wire_value_v(prefix=prefix_b, offset=offset_b, array=array)};\n" + \ + f" assign {self.out.prefix} = {self.a.name} {self.operator} {self.b.name};" + + +class nand_gate(inverted_logic_gate): + def __init__(self, a: wire, b: wire, prefix: str = "", outid: int = 0): + super().__init__(a, b, prefix) + self.gate_type = "nand_gate" + self.cgp_function = 5 + self.operator = "&" + self.out = wire(name=prefix+"_y"+str(outid)) + + """ C CODE GENERATION """ + # FLAT C # + def get_assign_c_flat(self, prefix_a: str = "a", prefix_b: str = "b"): + indexes = self.prefix[self.prefix.rfind("_", 0, self.prefix.rfind("_"))+1:] + offset_a = indexes[:indexes.rfind("_")] + offset_b = indexes[indexes.rfind("_")+1:] + return f" {self.a.name} = {self.a.get_wire_value_c(prefix=prefix_a, offset=offset_a)};\n" + \ + f" {self.b.name} = {self.b.get_wire_value_c(prefix=prefix_b, offset=offset_b)};\n" + \ + f" {self.out.prefix} = ~({self.a.name} {self.operator} {self.b.name});\n" + + """ VERILOG CODE GENERATION """ + # FLAT VERILOG # + def get_assign_v_flat(self, prefix_a: str = "a", prefix_b: str = "b", offset: int = 0, array: bool = False): + indexes = self.prefix[self.prefix.rfind("_", 0, self.prefix.rfind("_"))+1:] + offset_a = indexes[:indexes.rfind("_")] + offset_b = indexes[indexes.rfind("_")+1:] + return f" assign {self.a.name} = {self.a.get_wire_value_v(prefix=prefix_a, offset=offset_a, array=array)};\n" + \ + f" assign {self.b.name} = {self.b.get_wire_value_v(prefix=prefix_b, offset=offset_b, array=array)};\n" + \ + f" assign {self.out.prefix} = ~({self.a.name} {self.operator} {self.b.name});" + + +class or_gate(logic_gate): + def __init__(self, a: wire, b: wire, prefix: str = "", outid: int = 0): + super().__init__(a, b, prefix) + self.gate_type = "or_gate" + self.cgp_function = 3 + self.operator = "|" + self.out = wire(name=prefix+"_y"+str(outid)) + + +class nor_gate(inverted_logic_gate): + def __init__(self, a: wire, b: wire, prefix: str = "", outid: int = 0): + super().__init__(a, b, prefix) + self.gate_type = "nor_gate" + self.cgp_function = 6 + self.operator = "|" + self.out = wire(name=prefix+"_y"+str(outid)) + + +class xor_gate(logic_gate): + def __init__(self, a: wire, b: wire, prefix: str = "", outid: int = 0): + super().__init__(a, b, prefix) + self.gate_type = "xor_gate" + self.cgp_function = 4 + self.operator = "^" + self.out = wire(name=prefix+"_y"+str(outid)) + + +class xnor_gate(inverted_logic_gate): + def __init__(self, a: wire, b: wire, prefix: str = "", outid: int = 0): + super().__init__(a, b, prefix) + self.gate_type = "xnor_gate" + self.cgp_function = 7 + self.operator = "^" + self.out = wire(name=prefix+"_y"+str(outid)) + + +# Single-input # +class not_gate(inverted_logic_gate): + def __init__(self, a: wire, prefix: str = "", outid: int = 0): + self.gate_type = "not_gate" + self.cgp_function = 1 + self.operator = "~" + self.a = wire(name=prefix+"_"+a.name.replace(prefix+"_", ""), value=a.value) + self.prefix = prefix + self.out = wire(name=prefix+"_y"+str(outid)) + + """ C CODE GENERATION """ + # FLAT C # + def get_prototype_c(self): + return f"uint8_t {self.gate_type}(uint8_t {self.a.name})" + "{" + "\n" + + def get_function_c(self): + return f"{self.operator}{self.a.get_wire_value_c()} & 0x01 << 0" + + def get_declaration_c(self): + return f"{self.a.get_declaration_c()}{self.out.get_declaration_c()}" + + def get_init_c_flat(self): + return f"{self.operator}{self.a.name}" + + def get_assign_c_flat(self, prefix_a: str = "a", offset: int = 0): + return f" {self.a.name} = {self.a.get_wire_value_c(prefix=prefix_a, offset=offset)};\n" + \ + f" {self.out.prefix} = {self.operator}{self.a.name};\n" + + # HIERARCHICAL C # + def get_gate_invocation_c(self, a: wire, sign: bool = False, get_index: bool = False): + a_name = a.prefix if sign is False else a.name + a_name = a_name if get_index is False else "a" + a.name[a.name.rfind("_"):] + return f"{self.gate_type}({a_name});" + + def get_gate_output_c(self, a: wire, offset: int = 0, sign: bool = False): + a_name = a.prefix if sign is False else a.name + return f"({self.gate_type}({a_name}) & 0x01) << {offset}" + + """ VERILOG CODE GENERATION """ + # FLAT VERILOG # + def get_prototype_v(self): + return f"module {self.gate_type}(input {self.a.name}, output {self.out.name});\n" + + def get_declaration_v_flat(self): + return f"{self.a.get_declaration_v()}{self.out.get_declaration_v()}" + + def get_init_v_flat(self): + return f"{self.operator}{self.a.name}" + + def get_assign_v_flat(self, prefix_a: str = "a", offset: int = 0, array: bool = False): + return f" assign {self.a.name} = {self.a.get_wire_value_v(prefix=prefix_a, offset=offset, array=array)};\n" + \ + f" assign {self.out.prefix} = {self.operator}{self.a.name};" + + # HIERARCHICAL VERILOG # + def get_gate_invocation_v(self, a: wire, out: wire, sign: bool = False, get_index: bool = False, out_array: bool = False, offset: int = 0): + a_name = a.prefix if sign is False else a.name + a_name = a_name if get_index is False else "a" + a.name[a.name.rfind("_"):] + return f" {self.gate_type} {self.gate_type}_{out.name}({a_name}, {out.get_wire_value_v(offset=offset, array=out_array)});" + + """ CGP CODE GENERATION """ + # FLAT CGP # + @staticmethod + def get_parameters_cgp(): + return "{1,1,1,1,0}" + + def get_triplet_cgp(self, a_index: int = 0): + return f"({a_index},{self.cgp_function})" + + @staticmethod + def get_output_cgp(out_index: int = 1): + return f"({out_index})" diff --git a/multi_bit_circuits.py b/multi_bit_circuits.py new file mode 100644 index 0000000..8196f78 --- /dev/null +++ b/multi_bit_circuits.py @@ -0,0 +1,172 @@ +from arithmetic_circuits import arithmetic_circuit, signed_adder_circuit, signed_multiplier_circuit +from one_bit_circuits import half_adder, full_adder +from logic_gates import logic_gate, and_gate, nand_gate, or_gate, nor_gate, xor_gate, xnor_gate, not_gate +from wire_components import wire, bus + +""" MULTI BIT CIRCUITS """ + + +# ADDERS +class unsigned_ripple_carry_adder(arithmetic_circuit): + def __init__(self, a: bus, b: bus, prefix: str = "u_rca"): + super().__init__() + self.N = max(a.N, b.N) + # Bus sign extension in case buses have different lengths + a.bus_extend(N=self.N, prefix=a.prefix) + b.bus_extend(N=self.N, prefix=b.prefix) + self.a = a + self.b = b + if prefix == "u_rca" or prefix == "s_rca": + self.prefix = prefix+str(self.N) + else: + self.prefix = prefix + + # Output wires for N sum bits and additional cout bit + self.out = bus("out", self.N+1) + + # Gradual addition of 1-bit adder components + for input_index in range(self.N): + # First one is a half adder + if input_index == 0: + obj_ha = half_adder(a.get_wire(input_index), b.get_wire(input_index), prefix=self.prefix+"_ha") + self.add_component(obj_ha) + self.out.connect(input_index, obj_ha.get_sum_wire()) + # Rest are full adders + else: + obj_fa = full_adder(a.get_wire(input_index), b.get_wire(input_index), self.get_previous_component().get_carry_wire(), prefix=self.prefix+"_fa"+str(input_index)) + self.add_component(obj_fa) + self.out.connect(input_index, obj_fa.get_sum_wire()) + + if input_index == (self.N-1): + self.out.connect(self.N, obj_fa.get_carry_wire()) + + +class signed_ripple_carry_adder(unsigned_ripple_carry_adder, signed_adder_circuit): + def __init__(self, a: bus, b: bus, prefix: str = "s_rca"): + super().__init__(a=a, b=b, prefix=prefix) + self.c_data_type = "int64_t" + + # Additional XOR gates to ensure correct sign extension in case of sign addition + sign_xor_1 = xor_gate(self.get_previous_component().a, self.get_previous_component().b, prefix=self.prefix+"_xor_1") + sign_xor_2 = xor_gate(sign_xor_1.out, self.get_previous_component().get_carry_wire(), prefix=self.prefix+"_xor_2") + self.add_component(sign_xor_1) + self.add_component(sign_xor_2) + self.out.connect(self.N, sign_xor_2.out) + + +# MULTIPLIERS +class unsigned_array_multiplier(arithmetic_circuit): + def __init__(self, a: bus, b: bus, prefix: str = "u_arr_mul"): + super().__init__() + self.N = max(a.N, b.N) + # Bus sign extension in case buses have different lengths + a.bus_extend(N=self.N, prefix=a.prefix) + b.bus_extend(N=self.N, prefix=b.prefix) + self.a = a + self.b = b + if prefix == "u_arr_mul": + self.prefix = prefix+str(self.N) + else: + self.prefix = prefix + + # Output wires for multiplication product + self.out = bus("out", self.N*2) if self.N > 1 else bus("out", self.N) + + # Gradual generation of partial products + for b_multiplier_index in range(self.N): + for a_multiplicand_index in range(self.N): + # AND gates generation for calculation of partial products + obj_and = and_gate(a.get_wire(a_multiplicand_index), b.get_wire(b_multiplier_index), prefix=self.prefix+"_and_"+str(a_multiplicand_index)+"_"+str(b_multiplier_index)) + self.add_component(obj_and) + + if b_multiplier_index != 0: + previous_product = self.components[a_multiplicand_index + b_multiplier_index].out if b_multiplier_index == 1 else self.get_previous_partial_product(a_index=a_multiplicand_index, b_index=b_multiplier_index) + # HA generation for first 1-bit adder in each row starting from the second one + if a_multiplicand_index == 0: + obj_adder = half_adder(self.get_previous_component().out, previous_product, prefix=self.prefix+"_ha_"+str(a_multiplicand_index)+"_"+str(b_multiplier_index)) + self.add_component(obj_adder) + # Product generation + self.out.connect(b_multiplier_index, obj_adder.get_sum_wire()) + + # HA generation, last 1-bit adder in second row + elif a_multiplicand_index == self.N-1 and b_multiplier_index == 1: + obj_adder = half_adder(self.get_previous_component().out, self.get_previous_component(number=2).get_carry_wire(), prefix=self.prefix+"_ha_"+str(a_multiplicand_index)+"_"+str(b_multiplier_index)) + self.add_component(obj_adder) + + # FA generation + else: + obj_adder = full_adder(self.get_previous_component().out, previous_product, self.get_previous_component(number=2).get_carry_wire(), prefix=self.prefix+"_fa_"+str(a_multiplicand_index)+"_"+str(b_multiplier_index)) + self.add_component(obj_adder) + + # PRODUCT GENERATION + if a_multiplicand_index == 0 and b_multiplier_index == 0: + self.out.connect(a_multiplicand_index, obj_and.out) + + elif b_multiplier_index == self.N-1: + self.out.connect(b_multiplier_index + a_multiplicand_index, obj_adder.get_sum_wire()) + + if a_multiplicand_index == self.N-1: + self.out.connect(self.out.N-1, obj_adder.get_carry_wire()) + + +class signed_array_multiplier(signed_multiplier_circuit): + def __init__(self, a: bus, b: bus, prefix: str = "s_arr_mul"): + super().__init__() + self.c_data_type = "int64_t" + self.N = max(a.N, b.N) + # Bus sign extension in case buses have different lengths + a.bus_extend(N=self.N, prefix=a.prefix) + b.bus_extend(N=self.N, prefix=b.prefix) + self.a = a + self.b = b + if prefix == "s_arr_mul": + self.prefix = prefix+str(self.N) + else: + self.prefix = prefix + + # TODO CHANGE SIGNED MULTIPLIER CIRCUIT (REPLACE CONSTANT WIRE WITH LOGIC GATES) + self.constant_wire = wire(name="constant_wire", value=1) + + # Output wires for multiplication product + self.out = bus("out", self.N*2) if self.N > 1 else bus("out", self.N) + + # Gradual generation of partial products + for b_multiplier_index in range(self.N): + for a_multiplicand_index in range(self.N): + # AND and NAND gates generation for calculation of partial products and sign extension + if (b_multiplier_index == self.N-1 and a_multiplicand_index != self.N-1) or (b_multiplier_index != self.N-1 and a_multiplicand_index == self.N-1): + obj_nand = nand_gate(a.get_wire(a_multiplicand_index), b.get_wire(b_multiplier_index), prefix=self.prefix+"_nand_"+str(a_multiplicand_index)+"_"+str(b_multiplier_index)) + self.add_component(obj_nand) + else: + obj_and = and_gate(a.get_wire(a_multiplicand_index), b.get_wire(b_multiplier_index), prefix=self.prefix+"_and_"+str(a_multiplicand_index)+"_"+str(b_multiplier_index)) + self.add_component(obj_and) + + if b_multiplier_index != 0: + previous_product = self.components[a_multiplicand_index + b_multiplier_index].out if b_multiplier_index == 1 else self.get_previous_partial_product(a_index=a_multiplicand_index, b_index=b_multiplier_index) + # HA generation for first 1-bit adder in each row starting from the second one + if a_multiplicand_index == 0: + obj_adder = half_adder(self.get_previous_component().out, previous_product, prefix=self.prefix+"_ha_"+str(a_multiplicand_index)+"_"+str(b_multiplier_index)) + self.add_component(obj_adder) + # Product generation + self.out.connect(b_multiplier_index, obj_adder.get_sum_wire()) + + # FA generation + else: + if a_multiplicand_index == self.N-1 and b_multiplier_index == 1: + previous_product = self.constant_wire + + obj_adder = full_adder(self.get_previous_component().out, previous_product, self.get_previous_component(number=2).get_carry_wire(), prefix=self.prefix+"_fa_"+str(a_multiplicand_index)+"_"+str(b_multiplier_index)) + self.add_component(obj_adder) + + # PRODUCT GENERATION + if a_multiplicand_index == 0 and b_multiplier_index == 0: + self.out.connect(a_multiplicand_index, obj_and.out) + + elif b_multiplier_index == self.N-1: + self.out.connect(b_multiplier_index + a_multiplicand_index, obj_adder.get_sum_wire()) + + if a_multiplicand_index == self.N-1: + obj_adder = half_adder(self.get_previous_component().get_carry_wire(), self.constant_wire, prefix=self.prefix+"_ha_"+str(a_multiplicand_index+1)+"_"+str(b_multiplier_index)) + self.add_component(obj_adder) + + self.out.connect(self.out.N-1, obj_adder.get_sum_wire()) diff --git a/one_bit_circuits.py b/one_bit_circuits.py new file mode 100644 index 0000000..f52f707 --- /dev/null +++ b/one_bit_circuits.py @@ -0,0 +1,248 @@ +from arithmetic_circuits import arithmetic_circuit +from logic_gates import logic_gate, and_gate, nand_gate, or_gate, nor_gate, xor_gate, xnor_gate, not_gate +from wire_components import wire, bus + +""" ONE BIT CIRCUITS """ + + +class one_bit_circuit(arithmetic_circuit): + def __init__(self): + super().__init__() + + """ C CODE GENERATION """ + # FLAT C # + # Obtaining list of all the unique circuit wires from all contained logic gates + # to ensure non-recurring declaration of same wires + def get_declaration_c_flat(self): + self.circuit_wires = [] + self.get_circuit_wires() + # Unique declaration of all circuit's interconnections + return "".join([c[0].get_declaration_c() for c in self.circuit_wires]) + + # 2 bit input adder wire values initialization + def get_init_c_flat(self): + return f"{self.components[0].get_assign_c_flat(prefix_a=self.a.prefix, prefix_b=self.b.prefix)}" + \ + f" {self.components[1].out.name} = {self.components[1].get_init_c_flat()};\n" + + # HIERARCHICAL C # + def get_function_block_c(self): + self.component_types = self.get_component_types() + self.prefix = "ha" if isinstance(self, half_adder) else "fa" + return f"{self.get_circuit_c()}\n\n" + + def get_declaration_c_hier(self): + return f"{self.component_types[0].a.get_declaration_c()}" + \ + f"{self.component_types[2].a.get_declaration_c()}" + \ + f"{self.component_types[2].b.get_declaration_c()}" + + def get_init_c_hier(self): + return f" {self.component_types[0].a.name} = {self.component_types[0].get_gate_invocation_c(self.a, self.b)}\n" + \ + f" {self.component_types[2].a.name} = {self.component_types[1].get_gate_invocation_c(self.a, self.b)}\n" + \ + f" {self.component_types[2].b.name} = {self.component_types[1].get_gate_invocation_c(self.component_types[0].a, self.c)}\n" + + def get_out_invocation_c(self): + return self.get_sum_invocation_c()+"\n" + \ + self.get_cout_invocation_c() + + def get_sum_invocation_c(self): + return f" {self.get_sum_wire().name} = ({self.prefix}({self.a.name}, {self.b.name}) >> 0) & 0x01;" + + def get_cout_invocation_c(self): + return f" {self.get_carry_wire().name} = ({self.prefix}({self.a.name}, {self.b.name}) >> 1) & 0x01;" + + def get_function_sum_c_hier(self, offset: int = 0): + return f" {self.out.prefix} |= {self.component_types[0].get_gate_output_c(a=self.a ,b=self.b, offset=offset)};\n" + + def get_function_carry_c_hier(self, offset: int = 1): + return f" {self.out.prefix} |= {self.component_types[1].get_gate_output_c(a=self.a ,b=self.b, offset=offset)};\n" + + def get_circuit_c(self): + return f"{self.get_prototype_c()}" + \ + f"{self.out.get_declaration_c()}" + \ + f"{self.get_function_sum_c_hier()}" + \ + f"{self.get_function_carry_c_hier()}" + \ + f" return {self.out.prefix}"+";\n}" + + """ VERILOG CODE GENERATION """ + # FLAT VERILOG # + def get_prototype_v(self): + return f"module {self.prefix}(input {self.a.name}, input {self.b.name}, output {self.out.get_wire(0).name}, output {self.out.get_wire(1).name});\n" + + def get_declaration_v_flat(self): + self.circuit_wires = [] + self.get_circuit_wires() + # Unique declaration of all circuit's interconnections + return "".join([c[0].get_declaration_v() for c in self.circuit_wires]) + + # Half adder wires values initialization + def get_init_v_flat(self, offset: int = 0, array: bool = False): + return f"{self.components[0].get_assign_v_flat(prefix_a=self.a.prefix, prefix_b=self.b.prefix, offset=offset, array=array)}\n" + \ + f" assign {self.components[1].out.name} = {self.components[1].get_init_v_flat()};" + + def get_function_out_v_flat(self): + return "" + + # HIERARCHICAL VERILOG # + def get_function_block_v(self): + self.component_types = self.get_component_types() + self.prefix = "ha" if isinstance(self, half_adder) else "fa" + return f"{self.get_circuit_v()}\n\n" + + def get_declaration_v_hier(self): + return f"{self.component_types[0].a.get_declaration_v()}" + \ + f"{self.component_types[2].a.get_declaration_v()}" + \ + f"{self.component_types[2].b.get_declaration_v()}" + + def get_init_v_hier(self): + return f" {self.component_types[0].gate_type} {self.component_types[0].gate_type}_{self.component_types[0].a.name}({self.a.name}, {self.b.name}, {self.component_types[0].a.name});\n" + \ + f" {self.component_types[1].gate_type} {self.component_types[1].gate_type}_{self.component_types[2].a.name}({self.a.name}, {self.b.name}, {self.component_types[2].a.name});\n" + \ + f" {self.component_types[1].gate_type} {self.component_types[1].gate_type}_{self.component_types[2].b.name}({self.component_types[0].a.name}, {self.c.name}, {self.component_types[2].b.name});\n" + + def get_invocation_v(self): + return f" ha ha_{self.get_carry_wire().name}({self.a.name}, {self.b.name}, {self.get_sum_wire().name}, {self.get_carry_wire().name});" + + def get_function_sum_v_hier(self): + return f"{self.components[0].get_gate_invocation_v(a=self.a, b=self.b, out=self.components[0].out, sign=True)}\n" + + def get_function_carry_v_hier(self): + return f"{self.components[1].get_gate_invocation_v(a=self.a, b=self.b, out=self.components[1].out, sign=True)}\n" + + def get_circuit_v(self): + return f"{self.get_prototype_v()}" + \ + f"{self.get_function_sum_v_hier()}" + \ + f"{self.get_function_carry_v_hier()}" + \ + f"endmodule" + + +class half_adder(one_bit_circuit): + def __init__(self, a: wire, b: wire, prefix: str = "ha"): + super().__init__() + self.c_data_type = "uint8_t" + self.prefix = prefix + self.a = a + self.b = b + # 2 wires for component's bus output (sum, cout) + self.out = bus("out", self.N+1) + + # Sum + # XOR gate for calculation of 1-bit sum + obj_xor_gate = xor_gate(a, b, prefix, outid=0) + self.add_component(obj_xor_gate) + self.out.connect(0, obj_xor_gate.out) + + # Cout + # AND gate for calculation of 1-bit cout + obj_and_gate = and_gate(a, b, prefix, outid=1) + self.add_component(obj_and_gate) + self.out.connect(1, obj_and_gate.out) + + +class full_adder(one_bit_circuit): + def __init__(self, a: wire, b: wire, c: wire, prefix: str = "fa"): + super().__init__() + self.c_data_type = "uint8_t" + self.prefix = prefix + self.a = a + self.b = b + self.c = c + # 2 wires for component's bus output (sum, cout) + self.out = bus("out", self.N+1) + + # PG logic + propagate_xor_gate1 = xor_gate(a, b, prefix, outid=0) + self.add_component(propagate_xor_gate1) + generate_and_gate1 = and_gate(a, b, prefix, outid=1) + self.add_component(generate_and_gate1) + + # Sum + # XOR gate for calculation of 1-bit sum + obj_xor_gate2 = xor_gate(propagate_xor_gate1.out, c, prefix, outid=2) + self.add_component(obj_xor_gate2) + self.out.connect(0, obj_xor_gate2.out) + + # Cout + # AND gate for calculation of 1-bit cout + obj_and_gate2 = and_gate(propagate_xor_gate1.out, c, prefix, outid=3) + self.add_component(obj_and_gate2) + + obj_or_gate = or_gate(generate_and_gate1.out, obj_and_gate2.out, prefix, outid=4) + self.add_component(obj_or_gate) + + self.out.connect(1, obj_or_gate.out) + + # TODO delete or leave? + self.propagate = propagate_xor_gate1.out + self.generate = generate_and_gate1.out + + """ C CODE GENERATION """ + # FLAT C # + # Full adder function prototype with three inputs + def get_prototype_c(self): + 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" + + # Full adder wires values initialization + def get_init_c_flat(self): + return f" {self.components[0].a.name} = {self.a.get_wire_value_c(offset=self.a.index, prefix=self.a.prefix)};\n" + \ + f" {self.components[0].b.name} = {self.b.get_wire_value_c(offset=self.b.index, prefix=self.b.prefix)};\n" + \ + f" {self.components[2].b.name} = {self.c.get_wire_value_c()};\n" + \ + f" {self.components[0].out.name} = {self.components[0].get_init_c_flat()};\n" + \ + f" {self.components[1].out.name} = {self.components[1].get_init_c_flat()};\n" + \ + f" {self.components[2].out.name} = {self.components[2].get_init_c_flat()};\n" + \ + f" {self.components[3].out.name} = {self.components[3].get_init_c_flat()};\n" + \ + f" {self.components[4].out.name} = {self.components[4].get_init_c_flat()};\n" + + # HIERARCHICAL C # + def get_sum_invocation_c(self): + return f" {self.get_sum_wire().name} = ({self.prefix}({self.a.name}, {self.b.name}, {self.c.name}) >> 0) & 0x01;" + + def get_cout_invocation_c(self): + return f" {self.get_carry_wire().name} = ({self.prefix}({self.a.name}, {self.b.name}, {self.c.name}) >> 1) & 0x01;" + + def get_function_sum_c_hier(self, offset: int = 0): + return f" {self.out.prefix} |= {self.component_types[0].get_gate_output_c(a=self.component_types[0].a, b=self.c, offset=offset)};\n" + + def get_function_carry_c_hier(self, offset: int = 1): + return f" {self.out.prefix} |= {self.get_previous_component().get_gate_output_c(a=self.component_types[2].a, b=self.component_types[2].b, offset=offset)};\n" + + def get_circuit_c(self): + return f"{self.get_prototype_c()}" + \ + f"{self.out.get_declaration_c()}" + \ + f"{self.get_declaration_c_hier()}\n" + \ + f"{self.get_init_c_hier()}\n" + \ + f"{self.get_function_sum_c_hier()}" + \ + f"{self.get_function_carry_c_hier()}" + \ + f" return {self.out.prefix}"+";\n}" + + """ VERILOG CODE GENERATION """ + # FLAT VERILOG # + def get_prototype_v(self): + return f"module {self.prefix}(input {self.a.name}, input {self.b.name}, input {self.c.name}, output {self.out.get_wire(0).name}, output {self.out.get_wire(1).name});\n" + + # Full adder wires values initialization + def get_init_v_flat(self, offset: int = 0, array: bool = False): + return f" assign {self.components[0].a.name} = {self.a.get_wire_value_v(offset=offset, prefix=self.a.prefix, array=array)};\n" + \ + f" assign {self.components[0].b.name} = {self.b.get_wire_value_v(offset=offset, prefix=self.b.prefix, array=array)};\n" + \ + f" assign {self.components[2].b.name} = {self.c.get_wire_value_v(offset=offset)};\n" + \ + f" assign {self.components[0].out.name} = {self.components[0].get_init_v_flat()};\n" + \ + f" assign {self.components[1].out.name} = {self.components[1].get_init_v_flat()};\n" + \ + f" assign {self.components[2].out.name} = {self.components[2].get_init_v_flat()};\n" + \ + f" assign {self.components[3].out.name} = {self.components[3].get_init_v_flat()};\n" + \ + f" assign {self.components[4].out.name} = {self.components[4].get_init_v_flat()};\n" + + # HIERARCHICAL VERILOG # + def get_invocation_v(self): + return f" fa fa_{self.get_carry_wire().name}({self.a.name}, {self.b.name}, {self.c.name}, {self.get_sum_wire().name}, {self.get_carry_wire().name});" + + def get_function_sum_v_hier(self): + return f"{self.components[0].get_gate_invocation_v(a=self.component_types[0].a, b=self.c, out=self.out.get_wire(0))}\n" + + def get_function_carry_v_hier(self): + return f"{self.components[-1].get_gate_invocation_v(a=self.component_types[2].a, b=self.component_types[2].b, out=self.out.get_wire(1))}\n" + + def get_circuit_v(self): + return f"{self.get_prototype_v()}" + \ + f"{self.get_declaration_v_hier()}\n" + \ + f"{self.get_init_v_hier()}\n" + \ + f"{self.get_function_sum_v_hier()}" + \ + f"{self.get_function_carry_v_hier()}" + \ + f"endmodule"