From 792d0c5db12e6d7470500bb22e87fc1e99cb6d67 Mon Sep 17 00:00:00 2001 From: honzastor Date: Mon, 22 Mar 2021 00:22:01 +0100 Subject: [PATCH] Did some code refactoring concerning one bit circuits generation. Added multiple one/multi bit circuits. TBD: Generate and test different circuits, implement divider circuits, comment code, add verification/optimization of Verilog/BLIF files using yosys tool. --- arithmetic_circuits.py | 32 +- arithmetic_circuits_generator.py | 71 +++- multi_bit_circuits.py | 548 +++++++++++++++++++++++++------ one_bit_circuits.py | 433 +++++++++++++----------- 4 files changed, 763 insertions(+), 321 deletions(-) diff --git a/arithmetic_circuits.py b/arithmetic_circuits.py index a33bd29..59a50c5 100644 --- a/arithmetic_circuits.py +++ b/arithmetic_circuits.py @@ -9,6 +9,7 @@ class arithmetic_circuit(): def __init__(self): self.components = [] self.circuit_wires = [] + self.inputs = [] self.circuit_gates = [] self.c_data_type = "uint64_t" self.N = 1 @@ -75,6 +76,9 @@ class arithmetic_circuit(): 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))) + # Get unique names of all inner input circuits (mainly used in one bit circuits) + self.inputs = [i[0] for i in self.circuit_wires if i[0] not in [o.out for o in self.components]] + # 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: @@ -223,10 +227,10 @@ class arithmetic_circuit(): def get_inits_v_flat(self): return f"{self.a.get_wire_assign_v()}" + \ f"{self.b.get_wire_assign_v()}" + \ - "".join([c.get_assign_v_flat() if isinstance(c, logic_gate) else c.get_init_v_flat() for c in self.components]) + "\n" + "".join([c.get_assign_v_flat() if isinstance(c, logic_gate) else c.get_init_v_flat() for c in self.components]) def get_init_v_flat(self): - return "".join([c.get_assign_v_flat() if isinstance(c, logic_gate) else c.get_init_v_flat() for c in self.components]) + "\n" + return "".join([c.get_assign_v_flat() if isinstance(c, logic_gate) else c.get_init_v_flat() for c in self.components]) 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]) @@ -235,7 +239,7 @@ class arithmetic_circuit(): def get_v_code_flat(self, file_object): file_object.write(self.get_prototype_v()) file_object.write(self.get_declarations_v_flat()+"\n") - file_object.write(self.get_inits_v_flat()) + file_object.write(self.get_inits_v_flat() + "\n") file_object.write(self.get_function_out_v_flat()) file_object.write(f"endmodule") file_object.close() @@ -417,7 +421,7 @@ class multiplier_circuit(arithmetic_circuit): if d >= initial_value: return stage, max_height - def init_column_heights(self): + def init_column_heights(self, signed=False): columns = [[num] if num <= self.N else [num - (num - self.N)*2] for num in range(1, self.out.N)] columns = [self.add_column_wires(column=col, column_index=columns.index(col)) for col in columns] return columns @@ -431,7 +435,22 @@ class multiplier_circuit(arithmetic_circuit): [column[self.a.N-index].append(self.a.get_wire(index)) for index in range(self.a.N-1, self.a.N-column[0]-1, -1)] [column[index-(self.a.N-1-column[0])].append(self.b.get_wire(index)) for index in range(self.a.N-column[0], self.a.N)] - column[1:] = [and_gate(a=column[i][0], b=column[i][1], prefix=self.prefix+'_and_'+str(column[i][0].index)+'_'+str(column[i][1].index)) for i in range(1, len(column))] + # TODO check and refactor + # Filling unsigned pp matrix with AND gates + if self.__class__.__name__ == "unsigned_dadda_multiplier" or self.__class__.__name__ == "unsigned_wallace_multiplier": + column[1:] = [and_gate(a=column[i][0], b=column[i][1], prefix=self.prefix+'_and_'+str(column[i][0].index)+'_'+str(column[i][1].index)) for i in range(1, len(column))] + # Filling signed pp matrix with AND/NAND gates (based on Baugh-Wooley multiplication algorithm) + else: + # First half of partial product columns contains only AND gates + if column_index < self.N-1 or column_index == self.out.N-2: + column[1:] = [and_gate(a=column[i][0], b=column[i][1], prefix=self.prefix+'_and_'+str(column[i][0].index)+'_'+str(column[i][1].index)) for i in range(1, len(column))] + # Second half of partial product columns contains NAND/AND gates + else: + column[1] = nand_gate(a=column[1][0], b=column[1][1], prefix=self.prefix+'_nand_'+str(column[1][0].index)+'_'+str(column[1][1].index)) + column[-1] = nand_gate(a=column[-1][0], b=column[-1][1], prefix=self.prefix+'_nand_'+str(column[-1][0].index)+'_'+str(column[-1][1].index)) + if len(column[2:-1]) != 0: + column[2:-1] = [and_gate(a=column[i][0], b=column[i][1], prefix=self.prefix+'_and_'+str(column[i][0].index)+'_'+str(column[i][1].index)) for i in range(2, len(column)-1)] + return column def get_column_height(self, column_num: int): @@ -443,7 +462,7 @@ class multiplier_circuit(arithmetic_circuit): self.columns[next_column][0] = self.get_column_height(next_column)+next_height_change def get_column_wire(self, column: int, bit: int): - if isinstance(self.columns[column][bit], and_gate): + if isinstance(self.columns[column][bit], and_gate) or isinstance(self.columns[column][bit], nand_gate): self.add_component(self.columns[column][bit]) return self.get_previous_component(1).out else: @@ -460,6 +479,5 @@ class multiplier_circuit(arithmetic_circuit): self.columns[curr_column].pop(1) self.columns[curr_column].insert(self.get_column_height(curr_column), adder.get_sum_wire()) - # Add carry out from previous column's component to top of column wires if next_column-1 == curr_column: self.columns[next_column].insert(1, adder.get_carry_wire()) diff --git a/arithmetic_circuits_generator.py b/arithmetic_circuits_generator.py index 6bf4109..545a90b 100644 --- a/arithmetic_circuits_generator.py +++ b/arithmetic_circuits_generator.py @@ -1,7 +1,7 @@ from wire_components import wire, bus 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, unsigned_dadda_multiplier, unsigned_carry_lookahead_adder, signed_carry_lookahead_adder +from one_bit_circuits import constant_wire_value_1, constant_wire_value_0, half_adder, full_adder +from multi_bit_circuits import unsigned_ripple_carry_adder, signed_ripple_carry_adder, unsigned_pg_ripple_carry_adder, signed_pg_ripple_carry_adder, unsigned_array_multiplier, signed_array_multiplier, unsigned_dadda_multiplier, signed_dadda_multiplier, unsigned_wallace_multiplier, signed_wallace_multiplier, unsigned_carry_lookahead_adder, signed_carry_lookahead_adder import sys @@ -9,14 +9,21 @@ import sys if __name__ == "__main__": a = bus(N=8, prefix="a") b = bus(N=1, prefix="b") - rca = signed_ripple_carry_adder(a, b) + pg_rca = signed_pg_ripple_carry_adder(a, b) + pg_rca.get_c_code_flat(open("cla_test_flat.c", "w")) + pg_rca.get_c_code_hier(open("cla_test_hier.c", "w")) + pg_rca.get_cgp_code(open("cla_test.chr", "w")) + + #cla = unsigned_carry_lookahead_adder(a, b) + + #rca = signed_ripple_carry_adder(a, b) #rca.get_c_code_flat(open("rca_test.c", "w")) #rca.get_v_code_flat(open("rca_test.v", "w")) - #rca.get_blif_code_flat(open("rca_test.blif", "w")) + #rca.get_blif_code_hier(open("h_rca2_test.blif", "w")) #rca.get_cgp_code(open("rca_test.chr", "w")) - arrmul = signed_array_multiplier(a, b) + #arrmul = signed_array_multiplier(a, b) #arrmul.get_c_code_flat(open("arrmul_test.c", "w")) #arrmul.get_cgp_code(open("arrmul_test.chr", "w")) #arrmul.get_v_code_flat(open("arrmul_test.v", "w")) @@ -27,14 +34,28 @@ if __name__ == "__main__": w2 = wire(name="b") w3 = wire(name="cin") - ha = half_adder(w1, w2, prefix="f_ha") - fa = full_adder(w1, w2, w3, prefix="f_fa") + ha = half_adder(w1, w2, prefix="ha") + fa = full_adder(w1, w2, w3, prefix="fa") + + fa.get_v_code_flat(open("f_fa.v", "w")) + fa.get_v_code_hier(open("h_fa.v", "w")) + + fa.get_c_code_flat(open("f_fa.c", "w")) + fa.get_c_code_hier(open("h_fa.c", "w")) + + fa.get_blif_code_flat(open("f_fa.blif", "w")) + fa.get_blif_code_hier(open("h_fa.blif", "w")) + + fa.get_cgp_code(open("fa.chr", "w")) + + #fa.get_v_code_hier(open("h_fa.v", "w")) + #fa.get_blif_code_hier(open("h_fa2.blif", "w")) - ha.get_cgp_code(open(f"ha.chr", "w")) - fa.get_cgp_code(open(f"fa.chr", "w")) + #ha.get_cgp_code(open(f"ha.chr", "w")) + #fa.get_cgp_code(open(f"fa.chr", "w")) - fa.get_c_code_flat(open("f_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_blif_code_hier(open("h_ha.blif","w")) @@ -63,8 +84,36 @@ if __name__ == "__main__": #gate.get_cgp_code(open("and_gate.chr","w")) """ - dadda = unsigned_dadda_multiplier(a, b, prefix="h_u_dadda_mul2") + #dadda = unsigned_dadda_multiplier(a, b, prefix="h_u_dadda_mul2") #dadda.get_v_code_hier(open("h_u_dadda_mul2.v", "w")) #dadda.get_c_code_hier(open("h_u_dadda_mul2.c", "w")) #dadda.get_blif_code_hier(open("h_u_dadda_mul2.blif", "w")) #dadda.get_cgp_code(open("u_dadda_mul2.chr", "w")) + + + s_dadda = signed_dadda_multiplier(a, b, prefix="dadda_s_mul8") + + s_dadda.get_v_code_hier(open("dadda_s_mul8.v", "w")) + s_dadda.get_c_code_hier(open("dadda_s_mul82.c", "w")) + s_dadda.get_c_code_flat(open("f_dadda_s_mul82.c", "w")) + s_dadda.get_blif_code_hier(open("dadda_s_mul8.blif", "w")) + s_dadda.get_cgp_code(open("dadda_s_mul8.chr", "w")) + + + #dadda = unsigned_dadda_multiplier(a, b, prefix="h_f_dadda_mul3") + #dadda.get_c_code_hier(open("test_dadda_mul8.c", "w")) + + test = signed_carry_lookahead_adder(a, b, prefix="test") + + test.get_c_code_hier(open("htest.c", "w")) + test.get_c_code_flat(open("ftest.c", "w")) + + test.get_v_code_flat(open("ftest.v", "w")) + test.get_v_code_hier(open("htest.v", "w")) + + test.get_blif_code_flat(open("ftest.blif", "w")) + test.get_blif_code_hier(open("htest.blif", "w")) + + test.get_cgp_code(open("test.chr", "w")) + + diff --git a/multi_bit_circuits.py b/multi_bit_circuits.py index f124094..eb798d2 100644 --- a/multi_bit_circuits.py +++ b/multi_bit_circuits.py @@ -1,6 +1,7 @@ from itertools import combinations_with_replacement +from typing import ClassVar from arithmetic_circuits import arithmetic_circuit, multiplier_circuit -from one_bit_circuits import half_adder, full_adder +from one_bit_circuits import half_adder, full_adder, constant_wire_value_0, constant_wire_value_1, full_adder_pg, pg_logic_block 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 @@ -12,6 +13,7 @@ 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) + self.prefix = prefix self.a = bus(prefix=a.prefix, wires_list=a.bus) self.b = bus(prefix=b.prefix, wires_list=b.bus) @@ -19,19 +21,12 @@ class unsigned_ripple_carry_adder(arithmetic_circuit): self.a.bus_extend(N=self.N, prefix=a.prefix) self.b.bus_extend(N=self.N, prefix=b.prefix) - 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) - # TODO replace ha for fa??? - # Gradual addition of 1-bit adder components for input_index in range(self.N): - # First one is a half adder + # First adder is a half adder if input_index == 0: obj_ha = half_adder(self.a.get_wire(input_index), self.b.get_wire(input_index), prefix=self.prefix+"_ha") self.add_component(obj_ha) @@ -39,7 +34,7 @@ class unsigned_ripple_carry_adder(arithmetic_circuit): if input_index == (self.N-1): self.out.connect(self.N, obj_ha.get_carry_wire()) - # Rest are full adders + # Rest adders are full adders else: obj_fa = full_adder(self.a.get_wire(input_index), self.b.get_wire(input_index), self.get_previous_component().get_carry_wire(), prefix=self.prefix+"_fa"+str(input_index)) self.add_component(obj_fa) @@ -47,7 +42,7 @@ class unsigned_ripple_carry_adder(arithmetic_circuit): 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, arithmetic_circuit): def __init__(self, a: bus, b: bus, prefix: str = "s_rca"): @@ -62,11 +57,11 @@ class signed_ripple_carry_adder(unsigned_ripple_carry_adder, arithmetic_circuit) self.out.connect(self.N, sign_xor_2.out) -# TODO CHANGE!!! test, think about proper P/G gates generation -class unsigned_carry_lookahead_adder(arithmetic_circuit): - def __init__(self, a: bus, b: bus, prefix: str = "u_cla"): +class unsigned_pg_ripple_carry_adder(arithmetic_circuit): + def __init__(self, a: bus, b: bus, prefix: str = "u_pg_rca"): super().__init__() self.N = max(a.N, b.N) + self.prefix = prefix self.a = bus(prefix=a.prefix, wires_list=a.bus) self.b = bus(prefix=b.prefix, wires_list=b.bus) @@ -74,53 +69,146 @@ class unsigned_carry_lookahead_adder(arithmetic_circuit): self.a.bus_extend(N=self.N, prefix=a.prefix) self.b.bus_extend(N=self.N, prefix=b.prefix) - if prefix == "u_cla" or prefix == "s_cla": - 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): + if input_index == 0: + # Constant wire with value 0 for cin 0 + constant_wire_0 = constant_wire_value_0(self.a.get_wire(), self.b.get_wire()) + self.add_component(constant_wire_0) + obj_fa_cla = full_adder_pg(self.a.get_wire(input_index), self.b.get_wire(input_index), constant_wire_0.out.get_wire(), prefix=self.prefix+"_fa"+str(input_index)) + + self.add_component(obj_fa_cla) + self.out.connect(input_index, obj_fa_cla.get_sum_wire()) + else: + obj_fa_cla = full_adder_pg(self.a.get_wire(input_index), self.b.get_wire(input_index), self.get_previous_component().out, prefix=self.prefix+"_fa"+str(input_index)) + self.add_component(obj_fa_cla) + self.out.connect(input_index, obj_fa_cla.get_sum_wire()) + + obj_and = and_gate(self.get_previous_component().c, self.get_previous_component().get_propagate_wire(), prefix=self.prefix+"_and"+str(input_index)) + obj_or = or_gate(obj_and.out, self.get_previous_component().get_generate_wire(), prefix=self.prefix+"_or"+str(input_index)) + self.add_component(obj_and) + self.add_component(obj_or) + + # Connecting last output bit to last cout + if input_index == (self.N-1): + self.out.connect(self.N, obj_or.out) + + +class signed_pg_ripple_carry_adder(unsigned_pg_ripple_carry_adder, arithmetic_circuit): + def __init__(self, a: bus, b: bus, prefix: str = "s_pg_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.a.get_wire(self.N-1), self.b.get_wire(self.N-1), prefix=self.prefix+"_xor"+str(self.get_instance_num(cls=xor_gate))) + self.add_component(sign_xor_1) + sign_xor_2 = xor_gate(sign_xor_1.out, self.get_previous_component(2).out, prefix=self.prefix+"_xor"+str(self.get_instance_num(cls=xor_gate))) + self.add_component(sign_xor_2) + self.out.connect(self.N, sign_xor_2.out) + + +class unsigned_carry_lookahead_adder(arithmetic_circuit): + def __init__(self, a: bus, b: bus, prefix: str = "u_cla"): + super().__init__() + self.N = max(a.N, b.N) + self.prefix = prefix + self.a = bus(prefix=a.prefix, wires_list=a.bus) + self.b = bus(prefix=b.prefix, wires_list=b.bus) + + # Bus sign extension in case buses have different lengths + self.a.bus_extend(N=self.N, prefix=a.prefix) + self.b.bus_extend(N=self.N, prefix=b.prefix) + + # Lists containing all propagate/generate wires + self.propagate = [] + self.generate = [] # Output wires for N sum bits and additional cout bit self.out = bus("out", self.N+1) - # Generating wire with constant logic value 0 (output of the nor gate), used as cin to first fa - obj_xor = xor_gate(self.a.get_wire(), self.b.get_wire(), prefix=self.prefix+"_xor_constant_wire") - obj_xnor = xnor_gate(self.a.get_wire(), self.b.get_wire(), prefix=self.prefix+"_xnor_constant_wire") - obj_nor = nor_gate(obj_xor.out, obj_xnor.out, prefix=self.prefix+"_nor_constant_wire") - obj_nor.out.name = "constant_wire" - obj_nor.out.prefix = "constant_wire" - - self.add_component(obj_xor) - self.add_component(obj_xnor) - self.add_component(obj_nor) - - # Gradual addition of 1-bit adder components + # Constant wire with value 0 for cin 0 + constant_wire_0 = constant_wire_value_0(self.a.get_wire(), self.b.get_wire()) + self.add_component(constant_wire_0) + # Used as a first generate wire for obtaining next carry bits + self.generate.append(constant_wire_0.out.get_wire()) + + # Gradual addition of propagate/generate logic blocks and AND/OR gates for Cout bits generation, XOR gates for Sum bits generation for input_index in range(self.N): - obj_fa = full_adder(self.a.get_wire(input_index), self.b.get_wire(input_index), self.get_previous_component().out, prefix=self.prefix+"_fa"+str(input_index)) - self.add_component(obj_fa) - self.out.connect(input_index, obj_fa.get_sum_wire()) + pg_block = pg_logic_block(self.a.get_wire(input_index), self.b.get_wire(input_index), prefix=self.prefix+"_pg_logic"+str(input_index)) + self.propagate.append(pg_block.get_propagate_wire()) + self.generate.append(pg_block.get_generate_wire()) + self.add_component(pg_block) - if input_index == (self.N-1): - self.out.connect(self.N, obj_fa.get_carry_wire()) - else: - obj_and = and_gate(self.get_previous_component().get_carry_wire(), self.get_previous_component().propagate.out, prefix=self.prefix+"_and"+str(input_index)) - obj_xor = xor_gate(obj_and.out, self.get_previous_component().generate.out, prefix=self.prefix+"_xor"+str(input_index)) + if input_index == 0: + obj_sum_xor = xor_gate(pg_block.get_sum_wire(), constant_wire_0.out.get_wire(), prefix=self.prefix+"_xor"+str(input_index)) + self.add_component(obj_sum_xor) + self.out.connect(input_index, obj_sum_xor.out) - self.add_component(self.get_previous_component().propagate) - self.add_component(self.get_previous_component(2).generate) + # Carry propagation calculation + obj_and = and_gate(self.propagate[input_index], self.generate[input_index], prefix=self.prefix+"_and"+str(self.get_instance_num(cls=and_gate))) self.add_component(obj_and) - self.add_component(obj_xor) + # Carry bit generation + obj_cout_or = or_gate(pg_block.get_generate_wire(), self.get_previous_component().out, prefix=self.prefix+"_or"+str(self.get_instance_num(cls=or_gate))) + self.add_component(obj_cout_or) + else: + obj_sum_xor = xor_gate(pg_block.get_sum_wire(), self.get_previous_component(2).out, prefix=self.prefix+"_xor"+str(input_index)) + self.add_component(obj_sum_xor) + self.out.connect(input_index, obj_sum_xor.out) + + # For each pg pair values algorithmically combine two input AND gates to replace multiple input gates (resolves fan-in issue) + composite_and_gates = [] + # And combine AND gate pairs into OR gates + composite_or_gates = [] + + # Carry propagation calculation + for g_index in range(len(self.generate)-1): + for p_index in range(g_index, len(self.propagate)): + # No gate to cascade with, add to list + if len(composite_and_gates) == 0: + obj_and = and_gate(self.propagate[p_index], self.generate[g_index], prefix=self.prefix+"_and"+str(self.get_instance_num(cls=and_gate))) + # Combine 2 gates into another one to cascade them + else: + # Create new AND gate + obj_and = and_gate(self.propagate[p_index], self.generate[g_index], prefix=self.prefix+"_and"+str(self.get_instance_num(cls=and_gate))) + self.add_component(obj_and) + + # Combine new gate with previous one stored in list + obj_and = and_gate(self.get_previous_component(1).out, self.get_previous_component(2).out, prefix=self.prefix+"_and"+str(self.get_instance_num(cls=and_gate))) + composite_and_gates.pop(composite_and_gates.index(self.get_previous_component(2))) + + # Add gate to circuit components and to list of composite AND gates for this pg pair value + self.add_component(obj_and) + composite_and_gates.append(obj_and) + + composite_or_gates.append(composite_and_gates.pop()) + + # Final OR gates cascade using generated AND gates representing multiple input AND gates (cascade of multiple two input ones) + for a in range(len(composite_or_gates)-1): + obj_or = or_gate(self.get_previous_component().out, composite_or_gates[a].out, prefix=self.prefix+"_or"+str(self.get_instance_num(cls=or_gate))) + self.add_component(obj_or) + + # Carry bit generation + obj_cout_or = or_gate(pg_block.get_generate_wire(), self.get_previous_component().out, prefix=self.prefix+"_or"+str(self.get_instance_num(cls=or_gate))) + self.add_component(obj_cout_or) + + # Connecting last output bit to last cout + if input_index == (self.N-1): + self.out.connect(self.N, obj_cout_or.out) + -# TODO CHANGE!!! class signed_carry_lookahead_adder(unsigned_carry_lookahead_adder, arithmetic_circuit): def __init__(self, a: bus, b: bus, prefix: str = "s_cla"): 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(1).a, self.get_previous_component(1).b, prefix=self.prefix+"_xor"+str(self.get_instance_num(cls=xor_gate))) + sign_xor_1 = xor_gate(self.a.get_wire(self.N-1), self.b.get_wire(self.N-1), prefix=self.prefix+"_xor"+str(self.get_instance_num(cls=xor_gate))) self.add_component(sign_xor_1) - sign_xor_2 = xor_gate(sign_xor_1.out, self.get_previous_component(2).get_carry_wire(), prefix=self.prefix+"_xor"+str(self.get_instance_num(cls=xor_gate))) + sign_xor_2 = xor_gate(sign_xor_1.out, self.get_previous_component(2).out, prefix=self.prefix+"_xor"+str(self.get_instance_num(cls=xor_gate))) self.add_component(sign_xor_2) self.out.connect(self.N, sign_xor_2.out) @@ -130,6 +218,7 @@ class unsigned_array_multiplier(multiplier_circuit): def __init__(self, a: bus, b: bus, prefix: str = "u_arr_mul"): super().__init__() self.N = max(a.N, b.N) + self.prefix = prefix self.a = bus(prefix=a.prefix, wires_list=a.bus) self.b = bus(prefix=b.prefix, wires_list=b.bus) @@ -137,11 +226,6 @@ class unsigned_array_multiplier(multiplier_circuit): self.a.bus_extend(N=self.N, prefix=a.prefix) self.b.bus_extend(N=self.N, prefix=b.prefix) - 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) @@ -177,16 +261,10 @@ class unsigned_array_multiplier(multiplier_circuit): # 1 bit multiplier case if a_multiplicand_index == self.N-1: - obj_xor = xor_gate(self.a.get_wire(), self.b.get_wire(), prefix=self.prefix+"_xor_constant_wire") - obj_xnor = xnor_gate(self.a.get_wire(), self.b.get_wire(), prefix=self.prefix+"_xnor_constant_wire") - obj_and = and_gate(obj_xor.out, obj_xnor.out, prefix=self.prefix+"_and_constant_wire") - obj_and.out.name = "constant_wire" - obj_and.out.prefix = "constant_wire" - self.add_component(obj_xor) - self.add_component(obj_xnor) - self.add_component(obj_and) + constant_wire_0 = constant_wire_value_0(self.a.get_wire(), self.b.get_wire()) + self.add_component(constant_wire_0) - self.out.connect(a_multiplicand_index+1, obj_and.out) + self.out.connect(a_multiplicand_index+1, constant_wire_0.out.get_wire()) elif b_multiplier_index == self.N-1: self.out.connect(b_multiplier_index + a_multiplicand_index, obj_adder.get_sum_wire()) @@ -200,6 +278,7 @@ class signed_array_multiplier(multiplier_circuit): super().__init__() self.c_data_type = "int64_t" self.N = max(a.N, b.N) + self.prefix = prefix self.a = bus(prefix=a.prefix, wires_list=a.bus) self.b = bus(prefix=b.prefix, wires_list=b.bus) @@ -207,28 +286,16 @@ class signed_array_multiplier(multiplier_circuit): self.a.bus_extend(N=self.N, prefix=a.prefix) self.b.bus_extend(N=self.N, prefix=b.prefix) - if prefix == "s_arr_mul": - self.prefix = prefix+str(self.N) - else: - self.prefix = prefix - # Output wires for multiplication product self.out = bus("out", self.N*2) - # Generating wire with constant logic value 1 (output of the or gate) - obj_xor = xor_gate(self.a.get_wire(), self.b.get_wire(), prefix=self.prefix+"_xor_constant_wire") - obj_xnor = xnor_gate(self.a.get_wire(), self.b.get_wire(), prefix=self.prefix+"_xnor_constant_wire") - obj_or = or_gate(obj_xor.out, obj_xnor.out, prefix=self.prefix+"_or_constant_wire") - obj_or.out.name = "constant_wire" - obj_or.out.prefix = "constant_wire" - - self.add_component(obj_xor) - self.add_component(obj_xnor) - self.add_component(obj_or) + # Generating wire with constant logic value 1 + constant_wire_1 = constant_wire_value_1(self.a.get_wire(), self.b.get_wire()) + self.add_component(constant_wire_1) # To adjust proper wire connection between adders and AND/NAND gates - # we add offset equal to first 3 gates in circuits components list (that are present to prevent the need to use constant wire with logic value 1) - components_offset = 3 + # we add offset equal to first block in circuits components list (used for generation of wire with constant value 1) + components_offset = 1 # Gradual generation of partial products for b_multiplier_index in range(self.N): @@ -253,7 +320,7 @@ class signed_array_multiplier(multiplier_circuit): # FA generation else: if a_multiplicand_index == self.N-1 and b_multiplier_index == 1: - previous_product = obj_or.out + previous_product = constant_wire_1.out.get_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) @@ -264,7 +331,7 @@ class signed_array_multiplier(multiplier_circuit): # 1 bit multiplier case if a_multiplicand_index == self.N-1: - obj_nor = nor_gate(obj_or.out, self.get_previous_component().out, prefix=self.prefix+"_nor_zero_extend") + obj_nor = nor_gate(constant_wire_1.out.get_wire(), self.get_previous_component().out, prefix=self.prefix+"_nor_zero_extend") self.add_component(obj_nor) self.out.connect(a_multiplicand_index+1, obj_nor.out) @@ -273,16 +340,17 @@ class signed_array_multiplier(multiplier_circuit): self.out.connect(b_multiplier_index + a_multiplicand_index, obj_adder.get_sum_wire()) if a_multiplicand_index == self.N-1: - obj_xor = xor_gate(self.get_previous_component().get_carry_wire(), obj_or.out, prefix=self.prefix+"_xor"+str(a_multiplicand_index+1)+"_"+str(b_multiplier_index)) + obj_xor = xor_gate(self.get_previous_component().get_carry_wire(), constant_wire_1.out.get_wire(), prefix=self.prefix+"_xor"+str(a_multiplicand_index+1)+"_"+str(b_multiplier_index)) self.add_component(obj_xor) self.out.connect(self.out.N-1, obj_xor.out) -class unsigned_dadda_multiplier(multiplier_circuit): - def __init__(self, a: bus, b: bus, prefix: str = "u_dadda_mul"): +class unsigned_wallace_multiplier(multiplier_circuit): + def __init__(self, a: bus, b: bus, prefix: str = "u_wallace_rca", unsigned_adder_class_name: str = unsigned_ripple_carry_adder): super().__init__() self.N = max(a.N, b.N) + self.prefix = prefix self.a = bus(prefix=a.prefix, wires_list=a.bus) self.b = bus(prefix=b.prefix, wires_list=b.bus) @@ -290,16 +358,191 @@ class unsigned_dadda_multiplier(multiplier_circuit): self.a.bus_extend(N=self.N, prefix=a.prefix) self.b.bus_extend(N=self.N, prefix=b.prefix) - if prefix == "u_dadda_mul" or prefix == "s_dadda_mul": - self.prefix = prefix+str(self.N) + # Output wires for multiplication product + self.out = bus("out", self.N*2) + + # Initialize all columns partial products forming AND gates matrix + self.columns = self.init_column_heights() + + # Perform reduction until all columns have 2 or less bits in them + while not all(height <= 2 for (height, *_) in self.columns): + col = 0 + while col < len(self.columns): + # If column has exactly 3 bits in height and all previous columns has maximum of 2 bits in height, combine them in a half adder + if self.get_column_height(col) == 3 and all(height <= 2 for (height, *_) in self.columns[0:col-1]): + # Add half adder and also AND gates if neccesarry (via get_column_wire invocation) into list of circuit components + obj_adder = half_adder(self.get_column_wire(column=col, bit=1), self.get_column_wire(column=col, bit=2), prefix=self.prefix+"_ha"+str(self.get_instance_num(cls=half_adder))) + self.add_component(obj_adder) + + # Update the number of current and next column wires + self.update_column_heights(curr_column=col, curr_height_change=-1, next_column=col+1, next_height_change=1) + + # Update current and next column wires arrangement + # add ha's generated sum to the bottom of current column + # add ha's generated cout to the top of next column + self.update_column_wires(curr_column=col, next_column=col+1, adder=self.get_previous_component(1)) + + # If column has more than 3 bits in height, combine them in a full adder + elif self.get_column_height(col) > 3: + # Add full adder and also AND gates if neccesarry (via get_column_wire invocation) into list of circuit components + obj_adder = full_adder(self.get_column_wire(column=col, bit=1), self.get_column_wire(column=col, bit=2), self.get_column_wire(column=col, bit=3), prefix=self.prefix+"_fa"+str(self.get_instance_num(cls=full_adder))) + self.add_component(obj_adder) + + # Update the number of current and next column wires + self.update_column_heights(curr_column=col, curr_height_change=-2, next_column=col+1, next_height_change=1) + + # Update current and next column wires arrangement + # add fa's generated sum to the bottom of current column + # add fa's generated cout to the top of next column + self.update_column_wires(curr_column=col, next_column=col+1, adder=self.get_previous_component(1)) + col += 1 + + # Output generation + # First output bit from single first pp AND gate + self.out.connect(0, self.get_column_wire(column=0, bit=1)) + # Final addition of remaining bits + # 1 bit multiplier case + if self.N == 1: + constant_wire_0 = constant_wire_value_0(self.a.get_wire(), self.b.get_wire()) + self.add_component(constant_wire_0) + self.out.connect(1, constant_wire_0.out.get_wire()) + # 2 bit multiplier case + elif self.N == 2: + obj_ha = half_adder(self.get_column_wire(column=1, bit=1), self.get_column_wire(column=1, bit=2), prefix=self.prefix+"_ha"+str(self.get_instance_num(cls=half_adder))) + self.add_component(obj_ha) + self.out.connect(1, obj_ha.get_sum_wire()) + + obj_ha = half_adder(self.get_previous_component().get_carry_wire(), self.get_column_wire(column=2, bit=1), prefix=self.prefix+"_ha"+str(self.get_instance_num(cls=half_adder))) + self.add_component(obj_ha) + self.out.connect(2, obj_ha.get_sum_wire()) + self.out.connect(3, obj_ha.get_carry_wire()) + # Final addition of remaining bits using chosen unsigned multi bit adder else: - self.prefix = prefix + adder_type = unsigned_adder_class_name(a=a, b=b) + adder_a = bus(prefix=f"{adder_type.prefix}_a", wires_list=[self.get_column_wire(column=col, bit=1) for col in range(1, len(self.columns))]) + adder_b = bus(prefix=f"{adder_type.prefix}_b", wires_list=[self.get_column_wire(column=col, bit=2) for col in range(1, len(self.columns))]) + final_adder = unsigned_adder_class_name(a=adder_a, b=adder_b, prefix=self.prefix+f"_{adder_type.prefix}") + self.add_component(final_adder) + + [self.out.connect(o, final_adder.out.get_wire(o-1)) for o in range(1, len(self.out.bus))] + + +class signed_wallace_multiplier(multiplier_circuit): + def __init__(self, a: bus, b: bus, prefix: str = "s_wallace_rca", unsigned_adder_class_name: str = unsigned_ripple_carry_adder): + super().__init__() + self.N = max(a.N, b.N) + self.prefix = prefix + self.a = bus(prefix=a.prefix, wires_list=a.bus) + self.b = bus(prefix=b.prefix, wires_list=b.bus) + + # Bus sign extension in case buses have different lengths + self.a.bus_extend(N=self.N, prefix=a.prefix) + self.b.bus_extend(N=self.N, prefix=b.prefix) + + # Output wires for multiplication product + self.out = bus("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() + + # Generating wire with constant logic value 1 for signed multiplication + # Based on Baugh-Wooley multiplication algorithm + # Not used for 1 bit multiplier + if self.N != 1: + constant_wire_1 = constant_wire_value_1(self.a.get_wire(), self.b.get_wire()) + self.add_component(constant_wire_1) + + # Adding constant wire with value 1 to achieve signedness + # (adding constant value bit to last column (with one bit) to combine them in XOR gate to get the correct final multplication output bit at the end) + self.columns[self.N].insert(1, constant_wire_1.out.get_wire()) + self.update_column_heights(curr_column=self.N, curr_height_change=1) + + # Perform reduction until all columns have 2 or less bits in them + while not all(height <= 2 for (height, *_) in self.columns): + col = 0 + while col < len(self.columns): + # If column has exactly 3 bits in height and all previous columns has maximum of 2 bits in height, combine them in a half adder + if self.get_column_height(col) == 3 and all(height <= 2 for (height, *_) in self.columns[0:col-1]): + # Add half adder and also AND/NAND gates if neccesarry (via get_column_wire invocation) into list of circuit components + obj_adder = half_adder(self.get_column_wire(column=col, bit=1), self.get_column_wire(column=col, bit=2), prefix=self.prefix+"_ha"+str(self.get_instance_num(cls=half_adder))) + self.add_component(obj_adder) + + # Update the number of current and next column wires + self.update_column_heights(curr_column=col, curr_height_change=-1, next_column=col+1, next_height_change=1) + + # Update current and next column wires arrangement + # add ha's generated sum to the bottom of current column + # add ha's generated cout to the top of next column + self.update_column_wires(curr_column=col, next_column=col+1, adder=self.get_previous_component(1)) + + # If column has more than 3 bits in height, combine them in a full adder + elif self.get_column_height(col) > 3: + # Add full adder and also AND/NAND gates if neccesarry (via get_column_wire invocation) into list of circuit components + obj_adder = full_adder(self.get_column_wire(column=col, bit=1), self.get_column_wire(column=col, bit=2), self.get_column_wire(column=col, bit=3), prefix=self.prefix+"_fa"+str(self.get_instance_num(cls=full_adder))) + self.add_component(obj_adder) + + # Update the number of current and next column wires + self.update_column_heights(curr_column=col, curr_height_change=-2, next_column=col+1, next_height_change=1) + + # Update current and next column wires arrangement + # add fa's generated sum to the bottom of current column + # add fa's generated cout to the top of next column + self.update_column_wires(curr_column=col, next_column=col+1, adder=self.get_previous_component(1)) + col += 1 + + # Output generation + # First output bit from single first pp AND gate + self.out.connect(0, self.get_column_wire(column=0, bit=1)) + # Final addition of remaining bits + # 1 bit multiplier case + if self.N == 1: + constant_wire_0 = constant_wire_value_0(self.a.get_wire(), self.b.get_wire()) + self.add_component(constant_wire_0) + self.out.connect(1, constant_wire_0.out.get_wire()) + return + # 2 bit multiplier case + elif self.N == 2: + obj_ha = half_adder(self.get_column_wire(column=1, bit=1), self.get_column_wire(column=1, bit=2), prefix=self.prefix+"_ha"+str(self.get_instance_num(cls=half_adder))) + self.add_component(obj_ha) + self.out.connect(1, obj_ha.get_sum_wire()) + + obj_ha = half_adder(self.get_previous_component().get_carry_wire(), self.get_column_wire(column=2, bit=1), prefix=self.prefix+"_ha"+str(self.get_instance_num(cls=half_adder))) + self.add_component(obj_ha) + self.out.connect(2, obj_ha.get_sum_wire()) + self.out.connect(3, obj_ha.get_carry_wire()) + # Final addition of remaining bits using chosen unsigned multi bit adder + else: + adder_type = unsigned_adder_class_name(a=a, b=b) + adder_a = bus(prefix=f"{adder_type.prefix}_a", wires_list=[self.get_column_wire(column=col, bit=1) for col in range(1, len(self.columns))]) + adder_b = bus(prefix=f"{adder_type.prefix}_b", wires_list=[self.get_column_wire(column=col, bit=2) for col in range(1, len(self.columns))]) + final_adder = unsigned_adder_class_name(a=adder_a, b=adder_b, prefix=self.prefix+f"_{adder_type.prefix}") + self.add_component(final_adder) + + [self.out.connect(o, final_adder.out.get_wire(o-1)) for o in range(1, len(self.out.bus))] + + # Final XOR to ensure proper sign extension + obj_xor = xor_gate(constant_wire_1.out.get_wire(), self.out.get_wire(self.out.N-1), prefix=self.prefix+"_xor"+str(self.get_instance_num(cls=xor_gate))) + self.add_component(obj_xor) + self.out.connect(self.out.N-1, obj_xor.out) + +class unsigned_dadda_multiplier(multiplier_circuit): + def __init__(self, a: bus, b: bus, prefix: str = "u_dadda_rca", unsigned_adder_class_name: str = unsigned_ripple_carry_adder): + super().__init__() + self.N = max(a.N, b.N) + self.prefix = prefix + self.a = bus(prefix=a.prefix, wires_list=a.bus) + self.b = bus(prefix=b.prefix, wires_list=b.bus) + + # Bus sign extension in case buses have different lengths + self.a.bus_extend(N=self.N, prefix=a.prefix) + self.b.bus_extend(N=self.N, prefix=b.prefix) # Output wires for multiplication product self.out = bus("out", self.N*2) # Get starting stage and maximum possible column height self.stage, self.d = self.get_maximum_height(initial_value=min(self.a.N, self.b.N)) + # Initialize all columns partial products forming AND gates matrix self.columns = self.init_column_heights() # Perform reduction until stage 0 @@ -307,7 +550,7 @@ class unsigned_dadda_multiplier(multiplier_circuit): col = 0 while col < len(self.columns): if self.get_column_height(col) == self.d + 1: - # Add half adder and also and gates if neccesarry (via get_column_wire invocation) into list of circuits components + # Add half adder and also AND gates if neccesarry (via get_column_wire invocation) into list of circuit components obj_adder = half_adder(self.get_column_wire(column=col, bit=1), self.get_column_wire(column=col, bit=2), prefix=self.prefix+"_ha"+str(self.get_instance_num(cls=half_adder))) self.add_component(obj_adder) @@ -320,7 +563,7 @@ class unsigned_dadda_multiplier(multiplier_circuit): self.update_column_wires(curr_column=col, next_column=col+1, adder=self.get_previous_component(1)) elif self.get_column_height(col) > self.d: - # Add full adder and also and gates if neccesarry (via get_column_wire invocation) into list of circuits components + # Add full adder and also AND gates if neccesarry (via get_column_wire invocation) into list of circuit components obj_adder = full_adder(self.get_column_wire(column=col, bit=1), self.get_column_wire(column=col, bit=2), self.get_column_wire(column=col, bit=3), prefix=self.prefix+"_fa"+str(self.get_instance_num(cls=full_adder))) self.add_component(obj_adder) @@ -339,22 +582,16 @@ class unsigned_dadda_multiplier(multiplier_circuit): _, self.d = self.get_maximum_height(stage) # Output generation - # Final addition of remaining bits using RCA # TODO add CLA + # First output bit from single first pp AND gate self.out.connect(0, self.get_column_wire(column=0, bit=1)) + # Final addition of remaining bits # 1 bit multiplier case - if 1 == self.N: - obj_xor = xor_gate(self.a.get_wire(), self.b.get_wire(), prefix=self.prefix+"_xor_constant_wire") - obj_xnor = xnor_gate(self.a.get_wire(), self.b.get_wire(), prefix=self.prefix+"_xnor_constant_wire") - obj_and = and_gate(obj_xor.out, obj_xnor.out, prefix=self.prefix+"_and_constant_wire") - obj_and.out.name = "constant_wire" - obj_and.out.prefix = "constant_wire" - self.add_component(obj_xor) - self.add_component(obj_xnor) - self.add_component(obj_and) - - self.out.connect(1, obj_and.out) + if self.N == 1: + constant_wire_0 = constant_wire_value_0(self.a.get_wire(), self.b.get_wire()) + self.add_component(constant_wire_0) + self.out.connect(1, constant_wire_0.out.get_wire()) # 2 bit multiplier case - elif 2 == self.N: + elif self.N == 2: obj_ha = half_adder(self.get_column_wire(column=1, bit=1), self.get_column_wire(column=1, bit=2), prefix=self.prefix+"_ha"+str(self.get_instance_num(cls=half_adder))) self.add_component(obj_ha) self.out.connect(1, obj_ha.get_sum_wire()) @@ -363,13 +600,114 @@ class unsigned_dadda_multiplier(multiplier_circuit): self.add_component(obj_ha) self.out.connect(2, obj_ha.get_sum_wire()) self.out.connect(3, obj_ha.get_carry_wire()) - # Final addition of remaining bits using RCA # TODO add CLA + # Final addition of remaining bits using chosen unsigned multi bit adder else: - rca_a = bus(prefix="rca_a", wires_list=[self.get_column_wire(column=col, bit=1) for col in range(1, len(self.columns))]) - rca_b = bus(prefix="rca_b", wires_list=[self.get_column_wire(column=col, bit=2) for col in range(1, len(self.columns))]) - rca = unsigned_ripple_carry_adder(a=rca_a, b=rca_b, prefix=self.prefix+"_u_rca"+str(len(self.columns)-1)) - self.add_component(rca) + adder_type = unsigned_adder_class_name(a=a, b=b) + adder_a = bus(prefix=f"{adder_type.prefix}_a", wires_list=[self.get_column_wire(column=col, bit=1) for col in range(1, len(self.columns))]) + adder_b = bus(prefix=f"{adder_type.prefix}_b", wires_list=[self.get_column_wire(column=col, bit=2) for col in range(1, len(self.columns))]) + final_adder = unsigned_adder_class_name(a=adder_a, b=adder_b, prefix=self.prefix+f"_{adder_type.prefix}") + self.add_component(final_adder) - [self.out.connect(o, rca.out.get_wire(o-1)) for o in range(1, len(self.out.bus))] + [self.out.connect(o, final_adder.out.get_wire(o-1)) for o in range(1, len(self.out.bus))] -# TODO signed dadda_multiplier + +class signed_dadda_multiplier(multiplier_circuit): + def __init__(self, a: bus, b: bus, prefix: str = "s_dadda_rca", unsigned_adder_class_name: str = unsigned_ripple_carry_adder): + super().__init__() + self.N = max(a.N, b.N) + self.prefix = prefix + self.a = bus(prefix=a.prefix, wires_list=a.bus) + self.b = bus(prefix=b.prefix, wires_list=b.bus) + + # Bus sign extension in case buses have different lengths + self.a.bus_extend(N=self.N, prefix=a.prefix) + self.b.bus_extend(N=self.N, prefix=b.prefix) + + # Output wires for multiplication product + self.out = bus("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 + self.columns = self.init_column_heights() + + # Generating wire with constant logic value 1 for signed multiplication + # Based on Baugh-Wooley multiplication algorithm + # Not used for 1 bit multiplier + if self.N != 1: + constant_wire_1 = constant_wire_value_1(self.a.get_wire(), self.b.get_wire()) + self.add_component(constant_wire_1) + + # Adding constant wire with value 1 to achieve signedness + # (adding constant value bit to last column (with one bit) to combine them in XOR gate to get the correct final multplication output bit at the end) + self.columns[self.N].insert(1, constant_wire_1.out.get_wire()) + self.update_column_heights(curr_column=self.N, curr_height_change=1) + + # Perform reduction until stage 0 + for stage in range(self.stage, 0, -1): + col = 0 + while col < len(self.columns): + if self.get_column_height(col) == self.d + 1: + # Add half adder and also AND/NAND gates if neccesarry (via get_column_wire invocation) into list of circuit components + obj_adder = half_adder(self.get_column_wire(column=col, bit=1), self.get_column_wire(column=col, bit=2), prefix=self.prefix+"_ha"+str(self.get_instance_num(cls=half_adder))) + self.add_component(obj_adder) + + # Update the number of current and next column wires + self.update_column_heights(curr_column=col, curr_height_change=-1, next_column=col+1, next_height_change=1) + + # Update current and next column wires arrangement + # add ha's generated sum to the bottom of current column + # add ha's generated cout to the top of next column + self.update_column_wires(curr_column=col, next_column=col+1, adder=self.get_previous_component(1)) + elif self.get_column_height(col) > self.d: + # Add full adder and also AND/NAND gates if neccesarry (via get_column_wire invocation) into list of circuit components + obj_adder = full_adder(self.get_column_wire(column=col, bit=1), self.get_column_wire(column=col, bit=2), self.get_column_wire(column=col, bit=3), prefix=self.prefix+"_fa"+str(self.get_instance_num(cls=full_adder))) + self.add_component(obj_adder) + + # Update the number of current and next column wires + self.update_column_heights(curr_column=col, curr_height_change=-2, next_column=col+1, next_height_change=1) + + # Update current and next column wires arrangement + # add fa's generated sum to the bottom of current column + # add fa's generated cout to the top of next column + self.update_column_wires(curr_column=col, next_column=col+1, adder=self.get_previous_component(1)) + # Next iteration with same column in case there is need for further reduction + col -= 1 + col += 1 + # Update maximum possible column height + _, self.d = self.get_maximum_height(stage) + + # Output generation + # First output bit from single first pp AND gate + self.out.connect(0, self.get_column_wire(column=0, bit=1)) + # Final addition of remaining bits + # 1 bit multiplier case (no sign extension) + if self.N == 1: + constant_wire_0 = constant_wire_value_0(self.a.get_wire(), self.b.get_wire()) + self.add_component(constant_wire_0) + self.out.connect(1, constant_wire_0.out.get_wire()) + return + # 2 bit multiplier case + elif self.N == 2: + obj_ha = half_adder(self.get_column_wire(column=1, bit=1), self.get_column_wire(column=1, bit=2), prefix=self.prefix+"_ha"+str(self.get_instance_num(cls=half_adder))) + self.add_component(obj_ha) + self.out.connect(1, obj_ha.get_sum_wire()) + + obj_ha = half_adder(self.get_previous_component().get_carry_wire(), self.get_column_wire(column=2, bit=1), prefix=self.prefix+"_ha"+str(self.get_instance_num(cls=half_adder))) + self.add_component(obj_ha) + self.out.connect(2, obj_ha.get_sum_wire()) + self.out.connect(3, obj_ha.get_carry_wire()) + # Final addition of remaining bits using chosen unsigned multi bit adder + else: + adder_type = unsigned_adder_class_name(a=a, b=b) + adder_a = bus(prefix=f"{adder_type.prefix}_a", wires_list=[self.get_column_wire(column=col, bit=1) for col in range(1, len(self.columns))]) + adder_b = bus(prefix=f"{adder_type.prefix}_b", wires_list=[self.get_column_wire(column=col, bit=2) for col in range(1, len(self.columns))]) + final_adder = unsigned_adder_class_name(a=adder_a, b=adder_b, prefix=self.prefix+f"_{adder_type.prefix}") + self.add_component(final_adder) + + [self.out.connect(o, final_adder.out.get_wire(o-1)) for o in range(1, len(self.out.bus))] + + # Final XOR to ensure proper sign extension + obj_xor = xor_gate(constant_wire_1.out.get_wire(), self.out.get_wire(self.out.N-1), prefix=self.prefix+"_xor"+str(self.get_instance_num(cls=xor_gate))) + self.add_component(obj_xor) + self.out.connect(self.out.N-1, obj_xor.out) diff --git a/one_bit_circuits.py b/one_bit_circuits.py index f37ba11..80d6f4c 100644 --- a/one_bit_circuits.py +++ b/one_bit_circuits.py @@ -5,7 +5,7 @@ from wire_components import wire, bus """ ONE BIT CIRCUITS """ -class one_bit_circuit(arithmetic_circuit): +class two_input_one_bit_circuit(arithmetic_circuit): def __init__(self): super().__init__() @@ -18,10 +18,9 @@ class one_bit_circuit(arithmetic_circuit): # Unique declaration of all circuit's interconnections return "".join([c[0].get_declaration_c() for c in self.circuit_wires]) - # Half adder (2 inputs adder) wires values initialization + # Wires values initialization and assignment def get_init_c_flat(self): - return f"{self.components[0].a.get_assign_c(name=self.components[0].a.get_wire_value_c(name=self.a.name))}" + \ - f"{self.components[0].b.get_assign_c(name=self.components[0].b.get_wire_value_c(name=self.b.name))}" + \ + return "".join([i.get_assign_c(name=i.get_wire_value_c(name=i.name.replace(self.prefix+"_",""))) for i in self.inputs]) + \ "".join([f" {c.out.name} = {c.get_init_c_flat()};\n" for c in self.components]) # Generating flat C code representation of circuit @@ -36,116 +35,111 @@ class one_bit_circuit(arithmetic_circuit): file_object.close() # HIERARCHICAL C # + # Subcomponent generation def get_function_block_c(self): - adder_block = half_adder(a=wire(name="a"), b=wire(name="b")) if isinstance(self, half_adder) else full_adder(a=wire(name="a"), b=wire(name="b"), c=wire(name="cin")) + adder_block = self.__class__() return f"{adder_block.get_circuit_c()}\n\n" def get_wire_declaration_c_hier(self): return f"{self.out.get_wire_declaration_c()}" def get_out_invocation_c(self, **kwargs): - return self.get_sum_invocation_c()+"\n" + \ - self.get_cout_invocation_c()+"\n" + circuit_class = self.__class__() + return "".join([f' {o.name} = ({circuit_class.prefix}({self.a.name}, {self.b.name}) >> {self.out.bus.index(o)}) & 0x01;\n' for o in self.out.bus]) - def get_sum_invocation_c(self): - return f" {self.get_sum_wire().name} = (ha({self.a.name}, {self.b.name}) >> 0) & 0x01;" + # Self circuit hierarchical generation + def get_declaration_c_hier(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]) - def get_cout_invocation_c(self): - return f" {self.get_carry_wire().name} = (ha({self.a.name}, {self.b.name}) >> 1) & 0x01;" + def get_init_c_hier(self): + return "".join([i.get_assign_c(name=i.get_wire_value_c(name=i.name.replace(self.prefix+"_",""))) for i in self.inputs]) + \ + "".join([f" {c.out.name} = {c.get_gate_invocation_c(remove_prefix=False)}" for c in self.components]) - def get_function_sum_c_hier(self, offset: int = 0): - return f" {self.out.prefix} |= {self.components[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.components[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}" + 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]) """ 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" + return f"module {self.prefix}(input {self.a.name}, input {self.b.name}{''.join([f', output {o.name}' for o in self.out.bus])});\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 (2 inputs adder) wires values initialization + # Wires values initialization and assignment def get_init_v_flat(self): - return f"{self.components[0].get_assign_v_flat()}" + \ - f" assign {self.components[1].out.name} = {self.components[1].get_init_v_flat()};\n" + return "".join([i.get_assign_v(name=i.name.replace(self.prefix+"_","")) for i in self.inputs]) + \ + "".join([f" assign {c.out.name} = {c.get_init_v_flat()};\n" for c in self.components]) - # Not used in 1 bit circuits (no effect when calling while generating) - def get_function_out_v_flat(self): - return "" + # Generating flat Verilog 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()) + file_object.write(f"endmodule") + file_object.close() # HIERARCHICAL VERILOG # + # Subcomponent generation def get_function_block_v(self): - adder_block = half_adder(a=wire(name="a"), b=wire(name="b")) if isinstance(self, half_adder) else full_adder(a=wire(name="a"), b=wire(name="b"), c=wire(name="cin")) + adder_block = self.__class__() return f"{adder_block.get_circuit_v()}\n\n" def get_wire_declaration_v_hier(self): return f"{self.out.get_wire_declaration_v()}" def get_invocation_v(self, **kwargs): - 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});\n" + circuit_class = self.__class__() + return f" {circuit_class.prefix} {circuit_class.prefix}_{self.out.get_wire().name}({self.a.name}, {self.b.name}{''.join([f', {o.name}' for o in self.out.bus])});\n" - def get_function_sum_v_hier(self): - return f"{self.components[0].get_gate_invocation_v()}" + # Self circuit hierarchical generation + def get_declaration_v_hier(self): + self.get_circuit_wires() + # Unique declaration of all circuit's interconnections + return "".join([c.get_declaration_v() for c in self.inputs]) - def get_function_carry_v_hier(self): - return f"{self.components[1].get_gate_invocation_v()}" + def get_init_v_hier(self): + return "".join([i.get_assign_v(name=i.name.replace(self.prefix+"_","")) for i in self.inputs]) - 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" + def get_function_out_v_hier(self): + return "".join([f"{c.get_gate_invocation_v(remove_prefix=False)}" for c in self.components]) """ BLIF CODE GENERATION """ # FLAT BLIF # def get_declaration_blif(self): - return f".inputs {self.a.name} {self.b.name}" + \ - f"\n.outputs" + \ - "".join([f" {w.name}" for w in self.out.bus])+"\n" + return f".inputs {self.a.name} {self.b.name}\n" + \ + f".outputs{self.out.get_wire_declaration_blif()}\n" def get_wire_mapping_blif(self): # For unique mapping of all circuit's input interconnections self.get_circuit_wires() - return "".join([c[0].get_assign_blif(name=c[0].name.replace(self.prefix+'_', '')) for c in self.circuit_wires[:self.out.N]]) + return "".join([i.get_assign_blif(name=i.name.replace(self.prefix+"_","")) for i in self.inputs]) def get_function_blif_flat(self): return f"{self.get_wire_mapping_blif()}"+"".join([c.get_function_blif_flat() for c in self.components]) - # Not needed in 1-bit circuits def get_function_out_blif(self): - return "" + return f"{self.out.get_wire_assign_blif(output=True)}" # HIERARCHICAL BLIF # + # Subcomponent generation def get_function_block_blif(self): - adder_block = half_adder(a=wire(name="a"), b=wire(name="b")) if isinstance(self, half_adder) else full_adder(a=wire(name="a"), b=wire(name="b"), c=wire(name="cin")) + adder_block = self.__class__() return f"{adder_block.get_circuit_blif()}" - def get_function_blif_hier(self): - return "".join(c.get_invocation_blif_hier(init=False) for c in self.components) - def get_invocation_blif_hier(self, **kwargs): + circuit_class = self.__class__() return f"{self.get_wire_mapping_blif()}" + \ - f".subckt ha a={self.circuit_wires[0][0].name} b={self.circuit_wires[1][0].name} ha_y0={self.out.get_wire(0).name} ha_y1={self.out.get_wire(1).name}\n" + f".subckt {circuit_class.prefix} a={self.inputs[0].name} b={self.inputs[1].name}{''.join([f' {self.out.prefix}[{circuit_class.out.bus.index(o)}]={self.out.get_wire(circuit_class.out.bus.index(o)).name}' for o in circuit_class.out.bus])}\n" + + # Self circuit hierarchical generation + def get_function_blif_hier(self): + return f"{self.get_wire_mapping_blif()}"+"".join(c.get_invocation_blif_hier(init=False) for c in self.components) - def get_circuit_blif(self): - return f"{self.get_prototype_blif()}" + \ - f"{self.get_declaration_blif()}" + \ - f"{self.get_wire_mapping_blif()}" + \ - f"{self.get_function_blif_hier()}" + \ - f".end\n" - """ CGP CODE GENERATION """ # FLAT CGP # def get_parameters_cgp(self): @@ -153,8 +147,59 @@ class one_bit_circuit(arithmetic_circuit): return f"{{2,2,1,{len(self.circuit_gates)},2,1,0}}" -class half_adder(one_bit_circuit): - def __init__(self, a: wire, b: wire, prefix: str = "ha"): +class three_input_one_bit_circuit(two_input_one_bit_circuit): + def __init__(self): + super().__init__() + + """ C CODE GENERATION """ + # FLAT C # + # 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" + + # HIERARCHICAL C # + # Subcomponent generation (3 inputs) + def get_out_invocation_c(self, **kwargs): + circuit_class = self.__class__() + return "".join([f' {o.name} = ({circuit_class.prefix}({self.a.name}, {self.b.name}, {self.c.name}) >> {self.out.bus.index(o)}) & 0x01;\n' for o in self.out.bus]) + + """ VERILOG CODE GENERATION """ + # FLAT VERILOG # + # Module prototype with three inputs + def get_prototype_v(self): + return f"module {self.prefix}(input {self.a.name}, input {self.b.name}, input {self.c.name}{''.join([f', output {o.name}' for o in self.out.bus])});\n" + + # HIERARCHICAL VERILOG # + # Subcomponent generation (3 inputs) + def get_invocation_v(self, **kwargs): + circuit_class = self.__class__() + return f" {circuit_class.prefix} {circuit_class.prefix}_{self.out.get_wire().name}({self.a.name}, {self.b.name}, {self.c.name}{''.join([f', {o.name}' for o in self.out.bus])});\n" + + """ BLIF CODE GENERATION """ + # FLAT BLIF # + # Model prototype with three inputs + def get_declaration_blif(self): + return f".inputs {self.a.name} {self.b.name} {self.c.name}\n" + \ + f".outputs{self.out.get_wire_declaration_blif()}\n" + + # HIERARCHICAL BLIF # + # Subcomponent generation (3 inputs) + def get_invocation_blif_hier(self, **kwargs): + circuit_class = self.__class__() + return f"{self.get_wire_mapping_blif()}" + \ + f".subckt {circuit_class.prefix} a={self.inputs[0].name} b={self.inputs[1].name} cin={self.inputs[2].name}{''.join([f' {self.out.prefix}[{circuit_class.out.bus.index(o)}]={self.out.get_wire(circuit_class.out.bus.index(o)).name}' for o in circuit_class.out.bus])}\n" + + """ CGP CODE GENERATION """ + # FLAT CGP # + # Chromosome prototype with three inputs + def get_parameters_cgp(self): + self.circuit_gates = self.get_circuit_gates() + return f"{{3,2,1,{len(self.circuit_gates)},2,1,0}}" + + +# TWO INPUT CIRCUITS +class half_adder(two_input_one_bit_circuit): + def __init__(self, a: wire = wire(name="a"), b: wire = wire(name="b"), prefix: str = "ha"): super().__init__() self.c_data_type = "uint8_t" self.prefix = prefix @@ -165,19 +210,100 @@ class half_adder(one_bit_circuit): # 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) + obj_xor = xor_gate(a, b, prefix=self.prefix, outid=0) + self.add_component(obj_xor) + self.out.connect(0, obj_xor.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) + obj_and = and_gate(a, b, prefix=self.prefix, outid=1) + self.add_component(obj_and) + self.out.connect(1, obj_and.out) -class full_adder(one_bit_circuit): - def __init__(self, a: wire, b: wire, c: wire, prefix: str = "fa"): +class pg_logic_block(two_input_one_bit_circuit): + def __init__(self, a: wire = wire(name="a"), b: wire = wire(name="b"), prefix: str = "pg_logic"): + super().__init__() + self.c_data_type = "uint8_t" + self.prefix = prefix + self.a = a + self.b = b + # 3 wires for component's bus output (propagate, generate, sum) + self.out = bus("out", self.N+2) + + # PG logic + propagate_or = or_gate(a, b, prefix=self.prefix, outid=0) + self.add_component(propagate_or) + generate_and = and_gate(a, b, prefix=self.prefix, outid=1) + self.add_component(generate_and) + sum_xor = xor_gate(a, b, prefix=self.prefix, outid=2) + self.add_component(sum_xor) + + self.out.connect(0, propagate_or.out) + self.out.connect(1, generate_and.out) + self.out.connect(2, sum_xor.out) + + def get_propagate_wire(self): + return self.out.get_wire(0) + + def get_generate_wire(self): + return self.out.get_wire(1) + + def get_sum_wire(self): + return self.out.get_wire(2) + +class constant_wire_value_0(two_input_one_bit_circuit): + def __init__(self, a: wire = wire(name="a"), b: wire = wire(name="b"), prefix: str = "constant_wire_value_0"): + super().__init__() + self.c_data_type = "uint8_t" + self.prefix = prefix + self.a = a + self.b = b + # 1 wire for component's bus output (constant wire) + self.out = bus("out", self.N) + + # Generation of wire with constant value 0 + obj_xor = xor_gate(self.a, self.b, prefix=self.prefix, outid=0) + obj_xnor = xnor_gate(self.a, self.b, prefix=self.prefix, outid=1) + obj_nor = nor_gate(obj_xor.out, obj_xnor.out, prefix=self.prefix, outid=2) + obj_nor.out.name = "constant_wire_0" + obj_nor.out.prefix = "constant_wire_0" + + self.add_component(obj_xor) + self.add_component(obj_xnor) + self.add_component(obj_nor) + + # Constant wire output + self.out.connect(0, obj_nor.out) + + +class constant_wire_value_1(two_input_one_bit_circuit): + def __init__(self, a: wire = wire(name="a"), b: wire = wire(name="b"), prefix: str = "constant_wire_value_1"): + super().__init__() + self.c_data_type = "uint8_t" + self.prefix = prefix + self.a = a + self.b = b + # 1 wire for component's bus output (constant wire) + self.out = bus("out", self.N) + + # Generation of wire with constant value 1 + obj_xor = xor_gate(self.a, self.b, prefix=self.prefix, outid=0) + obj_xnor = xnor_gate(self.a, self.b, prefix=self.prefix, outid=1) + obj_or = or_gate(obj_xor.out, obj_xnor.out, prefix=self.prefix, outid=2) + obj_or.out.name = "constant_wire_1" + obj_or.out.prefix = "constant_wire_1" + self.add_component(obj_xor) + self.add_component(obj_xnor) + self.add_component(obj_or) + + # Constant wire output + self.out.connect(0, obj_or.out) + + +# THREE INPUT CIRCUITS +class full_adder(three_input_one_bit_circuit): + def __init__(self, a: wire = wire(name="a"), b: wire = wire(name="b"), c: wire = wire(name="cin"), prefix: str = "fa"): super().__init__() self.c_data_type = "uint8_t" self.prefix = prefix @@ -188,147 +314,58 @@ class full_adder(one_bit_circuit): 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) + propagate_xor = xor_gate(a, b, prefix=self.prefix, outid=0) + self.add_component(propagate_xor) + generate_and = and_gate(a, b, prefix=self.prefix, outid=1) + self.add_component(generate_and) # 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) + obj_xor = xor_gate(propagate_xor.out, c, prefix=self.prefix, outid=2) + self.add_component(obj_xor) + self.out.connect(0, obj_xor.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_and = and_gate(propagate_xor.out, c, prefix=self.prefix, outid=3) + self.add_component(obj_and) - obj_or_gate = or_gate(generate_and_gate1.out, obj_and_gate2.out, prefix, outid=4) - self.add_component(obj_or_gate) + obj_or = or_gate(generate_and.out, obj_and.out, prefix=self.prefix, outid=4) + self.add_component(obj_or) - self.out.connect(1, obj_or_gate.out) + self.out.connect(1, obj_or.out) - # TODO delete? - # Storing PG logic gates for better accessibility - self.propagate = propagate_xor_gate1 - self.generate = generate_and_gate1 - """ 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" +class full_adder_pg(three_input_one_bit_circuit): + def __init__(self, a: wire = wire(name="a"), b: wire = wire(name="b"), c: wire = wire(name="cin"), prefix: str = "fa_cla"): + super().__init__() + self.c_data_type = "uint8_t" + self.prefix = prefix + self.a = a + self.b = b + self.c = c + # 3 wires for component's bus output (sum, propagate, generate) + self.out = bus("out", self.N+2) - # Full adder wires values initialization - def get_init_c_flat(self): - return f"{self.components[0].a.get_assign_c(name=self.components[0].a.get_wire_value_c(name=self.a.name))}" + \ - f"{self.components[0].b.get_assign_c(name=self.components[0].b.get_wire_value_c(name=self.b.name))}" + \ - f"{self.components[2].b.get_assign_c(name=self.components[2].b.get_wire_value_c(name=self.c.name))}" + \ - "".join([f" {c.out.name} = {c.get_init_c_flat()};\n" for c in self.components]) + # PG logic + propagate_xor = xor_gate(a, b, prefix=self.prefix, outid=0) + self.add_component(propagate_xor) + self.out.connect(0, propagate_xor.out) - # HIERARCHICAL C # - def get_declaration_c_hier(self): - return f"{self.components[0].out.get_declaration_c()}" + \ - f"{self.components[1].out.get_declaration_c()}" + \ - f"{self.components[3].out.get_declaration_c()}" + generate_and = and_gate(a, b, prefix=self.prefix, outid=1) + self.add_component(generate_and) + self.out.connect(1, generate_and.out) - def get_init_c_hier(self): - # Temporarily change cin name for proper gate invocation - self.components[2].b.name = self.components[3].b.name = self.components[2].b.name.replace(self.prefix+"_", "") - return f" {self.components[0].out.name} = {self.components[0].get_gate_invocation_c()}" + \ - f" {self.components[1].out.name} = {self.components[1].get_gate_invocation_c()}" + \ - f" {self.components[3].out.name} = {self.components[3].get_gate_invocation_c(remove_prefix=False)}" + # Sum output + sum_xor = xor_gate(propagate_xor.out, c, prefix=self.prefix, outid=2) + self.add_component(sum_xor) + self.out.connect(2, sum_xor.out) + + def get_propagate_wire(self): + return self.out.get_wire(0) - def get_sum_invocation_c(self): - return f" {self.get_sum_wire().name} = (fa({self.a.name}, {self.b.name}, {self.c.name}) >> 0) & 0x01;" + def get_generate_wire(self): + return self.out.get_wire(1) - def get_cout_invocation_c(self): - return f" {self.get_carry_wire().name} = (fa({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.components[0].get_gate_output_c(a=self.components[0].out, b=self.c, offset=offset)};\n" - - def get_function_carry_c_hier(self, offset: int = 1): - # Return cin name to previous string value for sake of consistency - self.components[2].b.name = self.components[3].b.name = self.prefix+"_"+self.c.name - return f" {self.out.prefix} |= {self.get_previous_component().get_gate_output_c(a=self.components[1].out, b=self.components[3].out, 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): - return f"{self.components[0].a.get_assign_v(name=self.components[0].a.name.replace(self.prefix+'_', ''))}" + \ - f"{self.components[0].b.get_assign_v(name=self.components[0].b.name.replace(self.prefix+'_', ''))}" + \ - f"{self.components[2].b.get_assign_v(name=self.components[2].b.name.replace(self.prefix+'_', ''))}" + \ - "".join([f" assign {c.out.name} = {c.get_init_v_flat()};\n" for c in self.components]) - - # HIERARCHICAL VERILOG # - def get_invocation_v(self, **kwargs): - 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});\n" - - def get_declaration_v_hier(self): - return f"{self.components[0].out.get_declaration_v()}" + \ - f"{self.components[1].out.get_declaration_v()}" + \ - f"{self.components[3].out.get_declaration_v()}" - - def get_init_v_hier(self): - # Temporarily change cin name for proper gate invocation - self.components[2].b.name = self.components[3].b.name = self.components[2].b.name.replace(self.prefix+"_", "") - return f"{self.components[0].get_gate_invocation_v()}" + \ - f"{self.components[1].get_gate_invocation_v()}" + \ - f"{self.components[3].get_gate_invocation_v(remove_prefix=False)}" - - def get_function_sum_v_hier(self): - return f"{self.components[2].get_gate_invocation_v(remove_prefix=False)}" - - def get_function_carry_v_hier(self): - # Return cin name to previous string value for sake of consistency - self.components[2].b.name = self.components[3].b.name = self.prefix+"_"+self.c.name - return f"{self.components[4].get_gate_invocation_v(remove_prefix=False)}" - - 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" - - """ BLIF CODE GENERATION """ - # FLAT BLIF # - def get_declaration_blif(self): - return f".inputs {self.a.name} {self.b.name} {self.c.name}" + \ - f"\n.outputs" + \ - "".join([f" {w.name}" for w in self.out.bus])+"\n" - - def get_wire_mapping_blif(self): - # For unique mapping of all circuit's input interconnections - self.get_circuit_wires() - # getting desired inner wires and selecting first element from list of tuples containing wires and other info - return f"{self.circuit_wires[0][0].get_assign_blif(name=self.circuit_wires[0][0].name.replace(self.prefix+'_', ''))}" + \ - f"{self.circuit_wires[1][0].get_assign_blif(name=self.circuit_wires[1][0].name.replace(self.prefix+'_', ''))}" + \ - f"{self.circuit_wires[4][0].get_assign_blif(name=self.circuit_wires[4][0].name.replace(self.prefix+'_', ''))}" - - # HIERARCHICAL BLIF # - def get_invocation_blif_hier(self, **kwargs): - return f"{self.get_wire_mapping_blif()}" + \ - f".subckt fa a={self.circuit_wires[0][0].name} b={self.circuit_wires[1][0].name} cin={self.circuit_wires[4][0].name} fa_y2={self.out.get_wire(0).name} fa_y4={self.out.get_wire(1).name}\n" - - """ CGP CODE GENERATION """ - # FLAT CGP # - def get_parameters_cgp(self): - self.circuit_gates = self.get_circuit_gates() - return f"{{3,2,1,{len(self.circuit_gates)},2,1,0}}" \ No newline at end of file + def get_sum_wire(self): + return self.out.get_wire(2) \ No newline at end of file