From a336a683e706d79e5c3e77789d4abbf425201658 Mon Sep 17 00:00:00 2001 From: honzastor Date: Wed, 31 Mar 2021 04:40:54 +0200 Subject: [PATCH] Added some code documentation and updated git action to generate it. --- .github/workflows/generate.yml | 2 + README.md | 19 ++- Tests/README.md | 8 +- ariths_gen/core/arithmetic_circuit.py | 18 +- ariths_gen/core/multiplier_circuit.py | 15 +- .../core/three_input_one_bit_circuit.py | 9 +- ariths_gen/core/two_input_one_bit_circuit.py | 17 +- .../adders/carry_lookahead_adder.py | 86 ++++++++-- .../adders/pg_ripple_carry_adder.py | 86 +++++++++- .../adders/ripple_carry_adder.py | 68 +++++++- .../multipliers/array_multiplier.py | 40 ++++- .../multipliers/dadda_multiplier.py | 52 +++++- .../multipliers/wallace_multiplier.py | 50 +++++- .../logic_gates/logic_gates.py | 161 +++++++++++++++++- .../three_input_one_bit_components.py | 42 ++++- .../two_input_one_bit_components.py | 71 +++++++- ariths_gen/wire_components/buses.py | 90 +++++++++- ariths_gen/wire_components/wires.py | 79 ++++++++- 18 files changed, 831 insertions(+), 82 deletions(-) diff --git a/.github/workflows/generate.yml b/.github/workflows/generate.yml index f6e1558..9a54d33 100644 --- a/.github/workflows/generate.yml +++ b/.github/workflows/generate.yml @@ -30,6 +30,8 @@ jobs: run: python -c "import sys; print(sys.version)" - name: Run generating run: python ariths_gen.py + - name: Create documentation + run: pdoc --html ariths_gen - name: Listing run: ls build - name: Listing2 diff --git a/README.md b/README.md index ade3a8c..aeae52c 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,9 @@ ArithsGen presents an open source tool that enables generation of various arithmetic circuits along with the possibility to export them to various representations which all serve their specific purpose. C language for easy simulation, Verilog for logic synthesis, BLIF for formal verification possibilities and CGP to enable further global optimization. ### Usage - python3 ariths_gen.py +```bash +python3 ariths_gen.py +``` ### Example of generation #Example of 8-bit unsigned dadda multiplier that uses rca to provide the final product @@ -13,4 +15,17 @@ ArithsGen presents an open source tool that enables generation of various arithm b = Bus(N=8, prefix="b_bus") u_dadda = UnsignedDaddaMultiplier(a=a, b=b, prefix="h_u_dadda_rca8", unsigned_adder_class_name=UnsignedRippleCarryAdder) - u_dadda.get_v_code_hier(open("h_u_dadda_rca8.v", "w")) \ No newline at end of file + u_dadda.get_v_code_hier(open("h_u_dadda_rca8.v", "w")) + +## Documentation +Code documentation is provided using **pdoc** documentation generator tool. Source: https://pdoc3.github.io/pdoc/. + +### Instalation +```bash +pip3 install pdoc3 +``` + +### Usage +```bash +pdoc --html ariths_gen +``` \ No newline at end of file diff --git a/Tests/README.md b/Tests/README.md index f011d1a..95271c5 100644 --- a/Tests/README.md +++ b/Tests/README.md @@ -6,6 +6,10 @@ Script checks correct functionality of various architectures of unsigned/signed **Note** that these circuits were manually modified to allow such a testing (added main with nested loops and asserts)! ## Execute permission - chmod +x c_tests.sh +```bash +chmod +x c_tests.sh +``` ## Usage - ./c_tests.sh \ No newline at end of file +```bash +./c_tests.sh +``` \ No newline at end of file diff --git a/ariths_gen/core/arithmetic_circuit.py b/ariths_gen/core/arithmetic_circuit.py index 01639e9..64a810b 100644 --- a/ariths_gen/core/arithmetic_circuit.py +++ b/ariths_gen/core/arithmetic_circuit.py @@ -1,4 +1,4 @@ -from ariths_gen.one_bit_circuits.logic_gates import( +from ariths_gen.one_bit_circuits.logic_gates import ( LogicGate, AndGate, NandGate, @@ -6,16 +6,18 @@ from ariths_gen.one_bit_circuits.logic_gates import( NorGate, XorGate, XnorGate, - NotGate + NotGate ) -from ariths_gen.wire_components import( +from ariths_gen.wire_components import ( Wire, Bus ) -""" ARITHMETIC CIRCUITS """ + class ArithmeticCircuit(): + """Class represents a general arithmetic circuit and ensures their generation to various representations. + """ def __init__(self): self.components = [] self.circuit_wires = [] @@ -176,7 +178,7 @@ class ArithmeticCircuit(): def get_function_block_c(self): # Obtain proper adder name with its bit width - adder_prefix = self.__class__(a=Bus("a") , b=Bus("b")).prefix + str(self.N) + adder_prefix = self.__class__(a=Bus("a"), b=Bus("b")).prefix + str(self.N) adder_block = self.__class__(a=Bus(N=self.N, prefix="a"), b=Bus(N=self.N, prefix="b"), prefix=adder_prefix) return f"{adder_block.get_circuit_c()}\n\n" @@ -264,7 +266,7 @@ class ArithmeticCircuit(): def get_function_block_v(self): # Obtain proper adder name with its bit width - adder_prefix = self.__class__(a=Bus("a") , b=Bus("b")).prefix + str(self.N) + adder_prefix = self.__class__(a=Bus("a"), b=Bus("b")).prefix + str(self.N) adder_block = self.__class__(a=Bus(N=self.N, prefix="a"), b=Bus(N=self.N, prefix="b"), prefix=adder_prefix) return f"{adder_block.get_circuit_v()}\n\n" @@ -369,7 +371,7 @@ class ArithmeticCircuit(): def get_function_block_blif(self): # Obtain proper adder name with its bit width - adder_prefix = self.__class__(a=Bus("a") , b=Bus("b")).prefix + str(self.N) + adder_prefix = self.__class__(a=Bus("a"), b=Bus("b")).prefix + str(self.N) adder_block = self.__class__(a=Bus(N=self.N, prefix="a"), b=Bus(N=self.N, prefix="b"), prefix=adder_prefix) return f"{adder_block.get_circuit_blif()}" @@ -397,4 +399,4 @@ class ArithmeticCircuit(): file_object.write(self.get_parameters_cgp()) file_object.write(self.get_triplet_cgp()) file_object.write(self.get_output_cgp()) - file_object.close() \ No newline at end of file + file_object.close() diff --git a/ariths_gen/core/multiplier_circuit.py b/ariths_gen/core/multiplier_circuit.py index 5cf79b3..62c8a3a 100644 --- a/ariths_gen/core/multiplier_circuit.py +++ b/ariths_gen/core/multiplier_circuit.py @@ -1,8 +1,8 @@ -from .arithmetic_circuit import( +from .arithmetic_circuit import ( ArithmeticCircuit ) -from ariths_gen.one_bit_circuits.logic_gates import( +from ariths_gen.one_bit_circuits.logic_gates import ( LogicGate, AndGate, NandGate, @@ -10,18 +10,21 @@ from ariths_gen.one_bit_circuits.logic_gates import( NorGate, XorGate, XnorGate, - NotGate + NotGate ) -from ariths_gen.wire_components import( +from ariths_gen.wire_components import ( Wire, Bus ) import math -""" MULTIPLIER CIRCUITS """ + class MultiplierCircuit(ArithmeticCircuit): + """Class represents a general multiplier circuit derived from `ArithmeticCircuit` class. + """ + def __init__(self): super().__init__() @@ -84,7 +87,7 @@ class MultiplierCircuit(ArithmeticCircuit): column[-1] = NandGate(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] = [AndGate(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): diff --git a/ariths_gen/core/three_input_one_bit_circuit.py b/ariths_gen/core/three_input_one_bit_circuit.py index 1f30318..fc5952b 100644 --- a/ariths_gen/core/three_input_one_bit_circuit.py +++ b/ariths_gen/core/three_input_one_bit_circuit.py @@ -1,8 +1,11 @@ -from .two_input_one_bit_circuit import( +from .two_input_one_bit_circuit import ( TwoInputOneBitCircuit ) + class ThreeInputOneBitCircuit(TwoInputOneBitCircuit): + """Class represents a general three input one bit circuit and implements their generation to various representations. It is derived from `TwoInputOneBitCircuit` class. + """ def __init__(self): super().__init__() @@ -17,7 +20,7 @@ class ThreeInputOneBitCircuit(TwoInputOneBitCircuit): 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 @@ -49,4 +52,4 @@ class ThreeInputOneBitCircuit(TwoInputOneBitCircuit): # 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}}" \ No newline at end of file + return f"{{3,2,1,{len(self.circuit_gates)},2,1,0}}" diff --git a/ariths_gen/core/two_input_one_bit_circuit.py b/ariths_gen/core/two_input_one_bit_circuit.py index d79a5d0..3d9ba72 100644 --- a/ariths_gen/core/two_input_one_bit_circuit.py +++ b/ariths_gen/core/two_input_one_bit_circuit.py @@ -1,8 +1,11 @@ -from .arithmetic_circuit import( +from .arithmetic_circuit import ( ArithmeticCircuit ) + class TwoInputOneBitCircuit(ArithmeticCircuit): + """Class represents a general two input one bit circuit and implements their generation to various representations. It is derived from `ArithmeticCircuit` class. + """ def __init__(self): super().__init__() @@ -17,7 +20,7 @@ class TwoInputOneBitCircuit(ArithmeticCircuit): # Wires values initialization and assignment def get_init_c_flat(self): - return "".join([i.get_assign_c(name=i.get_wire_value_c(name=i.name.replace(self.prefix+"_",""))) for i in self.inputs]) + \ + 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 @@ -51,7 +54,7 @@ class TwoInputOneBitCircuit(ArithmeticCircuit): return "".join([c[0].get_declaration_c() for c in self.circuit_wires]) 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]) + \ + 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_out_c_hier(self): @@ -69,7 +72,7 @@ class TwoInputOneBitCircuit(ArithmeticCircuit): # Wires values initialization and assignment def get_init_v_flat(self): - return "".join([i.get_assign_v(name=i.name.replace(self.prefix+"_","")) for i in self.inputs]) + \ + 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]) # Generating flat Verilog code representation of circuit @@ -100,7 +103,7 @@ class TwoInputOneBitCircuit(ArithmeticCircuit): return "".join([c[0].get_declaration_v() for c in self.circuit_wires if c[0] not in self.out.bus]) def get_init_v_hier(self): - return "".join([i.get_assign_v(name=i.name.replace(self.prefix+"_","")) for i in self.inputs]) + return "".join([i.get_assign_v(name=i.name.replace(self.prefix+"_", "")) for i in self.inputs]) def get_function_out_v_hier(self): return "".join([f"{c.get_gate_invocation_v(remove_prefix=False)}" for c in self.components]) @@ -114,7 +117,7 @@ class TwoInputOneBitCircuit(ArithmeticCircuit): def get_wire_mapping_blif(self): # For unique mapping of all circuit's input interconnections self.get_circuit_wires() - return "".join([i.get_assign_blif(name=i.name.replace(self.prefix+"_","")) for i in self.inputs]) + 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]) @@ -141,4 +144,4 @@ class TwoInputOneBitCircuit(ArithmeticCircuit): # FLAT CGP # def get_parameters_cgp(self): self.circuit_gates = self.get_circuit_gates() - return f"{{2,2,1,{len(self.circuit_gates)},2,1,0}}" \ No newline at end of file + return f"{{2,2,1,{len(self.circuit_gates)},2,1,0}}" diff --git a/ariths_gen/multi_bit_circuits/adders/carry_lookahead_adder.py b/ariths_gen/multi_bit_circuits/adders/carry_lookahead_adder.py index 4fc8f28..5a61394 100644 --- a/ariths_gen/multi_bit_circuits/adders/carry_lookahead_adder.py +++ b/ariths_gen/multi_bit_circuits/adders/carry_lookahead_adder.py @@ -1,12 +1,12 @@ -from ariths_gen.wire_components import( +from ariths_gen.wire_components import ( Wire, Bus ) -from ariths_gen.core import( +from ariths_gen.core import ( ArithmeticCircuit, MultiplierCircuit ) -from ariths_gen.one_bit_circuits.one_bit_components import( +from ariths_gen.one_bit_circuits.one_bit_components import ( HalfAdder, PGLogicBlock, ConstantWireValue0, @@ -14,7 +14,7 @@ from ariths_gen.one_bit_circuits.one_bit_components import( FullAdder, FullAdderPG ) -from ariths_gen.one_bit_circuits.logic_gates import( +from ariths_gen.one_bit_circuits.logic_gates import ( LogicGate, AndGate, NandGate, @@ -25,7 +25,39 @@ from ariths_gen.one_bit_circuits.logic_gates import( NotGate ) + class UnsignedCarryLookaheadAdder(ArithmeticCircuit): + """Class representing unsigned carry look-ahead adder. + + Unsigned carry look-ahead adder represents faster adder circuit which is composed + of more complex circuitry but provides much less propagation delay as opposed to rca. + It is mainly composed of propagate/generate blocks and many AND/OR gates to calculate carries individually. + + ``` + B3 A3 B2 A2 B1 A1 B0 A0 + │ │ │ │ │ │ │ │ + ┌─▼──▼─┐ ┌─▼──▼─┐ ┌─▼──▼─┐ ┌─▼──▼─┐ + │ PG │ │ PG │ │ PG │ │ PG │ + │ block│ │ block│ │ block│ │ block│ + │ │ │ │ │ │ │ │ + └─┬──┬─┘ └─┬──┬─┘ └─┬──┬─┘ └─┬──┬─┘ + │ │ G3P3 │ │ G2P2 │ │ G1P1 │ │ G0P0 + ┌─▼──▼───────▼──▼───────▼──▼───────▼──▼─┐ + │ Carry Lookahead logic │ + │ │ + └┬────┬───────┬──────────┬──────────┬───┘ + │ │ │ │ │ + ▼ ▼ ▼ ▼ ▼ + Cout S3 S1 S0 S0 + ``` + + Description of the __init__ method. + + Args: + a (Bus): First input bus. + b (Bus): Second input bus. + prefix (str, optional): Prefix name of unsigned cla. Defaults to "u_cla". + """ def __init__(self, a: Bus, b: Bus, prefix: str = "u_cla"): super().__init__() self.N = max(a.N, b.N) @@ -49,7 +81,7 @@ class UnsignedCarryLookaheadAdder(ArithmeticCircuit): 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): pg_block = PGLogicBlock(self.a.get_wire(input_index), self.b.get_wire(input_index), prefix=self.prefix+"_pg_logic"+str(input_index)) @@ -73,7 +105,7 @@ class UnsignedCarryLookaheadAdder(ArithmeticCircuit): obj_sum_xor = XorGate(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 @@ -100,22 +132,54 @@ class UnsignedCarryLookaheadAdder(ArithmeticCircuit): 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 = OrGate(self.get_previous_component().out, composite_or_gates[a].out, prefix=self.prefix+"_or"+str(self.get_instance_num(cls=OrGate))) self.add_component(obj_or) - + # Carry bit generation obj_cout_or = OrGate(pg_block.get_generate_wire(), self.get_previous_component().out, prefix=self.prefix+"_or"+str(self.get_instance_num(cls=OrGate))) 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) - + class SignedCarryLookaheadAdder(UnsignedCarryLookaheadAdder, ArithmeticCircuit): + """Class representing signed carry look-ahead adder. + + Signed carry look-ahead adder represents faster adder circuit which is composed + of more complex circuitry but provides much less propagation delay as opposed to rca. + It is mainly composed of propagate/generate blocks and many AND/OR gates to calculate carries individually. + At last XOR gates are used to ensure proper sign extension. + + ``` + B3 A3 B2 A2 B1 A1 B0 A0 + │ │ │ │ │ │ │ │ + ┌─▼──▼─┐ ┌─▼──▼─┐ ┌─▼──▼─┐ ┌─▼──▼─┐ + │ PG │ │ PG │ │ PG │ │ PG │ + │ block│ │ block│ │ block│ │ block│ + │ │ │ │ │ │ │ │ + └─┬──┬─┘ └─┬──┬─┘ └─┬──┬─┘ └─┬──┬─┘ + │ │ G3P3 │ │ G2P2 │ │ G1P1 │ │ G0P0 + ┌─▼──▼───────▼──▼───────▼──▼───────▼──▼─┐ + │ Carry Lookahead logic │ + │ with sign extension │ + └┬────┬───────┬──────────┬──────────┬───┘ + │ │ │ │ │ + ▼ ▼ ▼ ▼ ▼ + Cout S3 S1 S0 S0 + ``` + + Description of the __init__ method. + + Args: + a (Bus): First input bus. + b (Bus): Second input bus. + prefix (str, optional): Prefix name of signed cla. Defaults to "s_cla". + """ 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" @@ -125,4 +189,4 @@ class SignedCarryLookaheadAdder(UnsignedCarryLookaheadAdder, ArithmeticCircuit): self.add_component(sign_xor_1) sign_xor_2 = XorGate(sign_xor_1.out, self.get_previous_component(2).out, prefix=self.prefix+"_xor"+str(self.get_instance_num(cls=XorGate))) self.add_component(sign_xor_2) - self.out.connect(self.N, sign_xor_2.out) \ No newline at end of file + self.out.connect(self.N, sign_xor_2.out) diff --git a/ariths_gen/multi_bit_circuits/adders/pg_ripple_carry_adder.py b/ariths_gen/multi_bit_circuits/adders/pg_ripple_carry_adder.py index 815c103..a418371 100644 --- a/ariths_gen/multi_bit_circuits/adders/pg_ripple_carry_adder.py +++ b/ariths_gen/multi_bit_circuits/adders/pg_ripple_carry_adder.py @@ -1,12 +1,12 @@ -from ariths_gen.wire_components import( +from ariths_gen.wire_components import ( Wire, Bus ) -from ariths_gen.core import( +from ariths_gen.core import ( ArithmeticCircuit, MultiplierCircuit ) -from ariths_gen.one_bit_circuits.one_bit_components import( +from ariths_gen.one_bit_circuits.one_bit_components import ( HalfAdder, PGLogicBlock, ConstantWireValue0, @@ -14,7 +14,7 @@ from ariths_gen.one_bit_circuits.one_bit_components import( FullAdder, FullAdderPG ) -from ariths_gen.one_bit_circuits.logic_gates import( +from ariths_gen.one_bit_circuits.logic_gates import ( LogicGate, AndGate, NandGate, @@ -25,7 +25,43 @@ from ariths_gen.one_bit_circuits.logic_gates import( NotGate ) + class UnsignedPGRippleCarryAdder(ArithmeticCircuit): + """Class representing unsigned ripple carry adder with propagate/generate logic. + + Unsigned ripple carry adder with PG logic represents slightly different rca implementation + of N-bit unsigned adder which is composed of N one bit full adders with P/G logic. + + ``` + B3 A3 B2 A2 B1 A1 B0 A0 + │ │ │ │ │ │ │ │ + ┌─▼──▼─┐ ┌─▼──▼─┐ ┌─▼──▼─┐ ┌─▼──▼─┐ + │ PG │ │ PG │ │ PG │ │ PG │ + │ FA │ │ FA │ │ FA │ │ FA │ + │ │ │ │ │ │ │ │ + └─┬──┬┬┘ └─┬┬┬──┘ └─┬┬┬──┘ └─┬┬┬──┘ + │ ││G3P3S3 │││G2P2S2 │││G1P1S1 │││ G0P0S0 + │ ┌▼▼───────▼▼▼─────────▼▼▼─────────▼▼▼──┐ + │ │ Group PG logic │ + │ │ │ + │ └─┬───────┬──────────┬──────────┬──────┘ + │ │ │ │ │ + ┌─▼───▼───────▼──────────▼──────────▼──────┐ + │ Sum logic │ + │ │ + └┬────┬───────┬──────────┬──────────┬──────┘ + │ │ │ │ │ + ▼ ▼ ▼ ▼ ▼ + Cout S3 S1 S0 S0 + ``` + + Description of the __init__ method. + + Args: + a (Bus): First input bus. + b (Bus): Second input bus. + prefix (str, optional): Prefix name of unsigned P/G rca. Defaults to "u_pg_rca". + """ def __init__(self, a: Bus, b: Bus, prefix: str = "u_pg_rca"): super().__init__() self.N = max(a.N, b.N) @@ -49,7 +85,7 @@ class UnsignedPGRippleCarryAdder(ArithmeticCircuit): obj_fa_cla = FullAdderPG(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)) else: obj_fa_cla = FullAdderPG(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()) @@ -57,13 +93,49 @@ class UnsignedPGRippleCarryAdder(ArithmeticCircuit): obj_or = OrGate(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 SignedPGRippleCarryAdder(UnsignedPGRippleCarryAdder, ArithmeticCircuit): + """Class representing signed ripple carry adder with propagate/generate logic. + + Signed ripple carry adder with PG logic represents slightly different rca implementation + of N-bit signed adder which is composed of N one bit full adders with P/G logic. + At last XOR gates are used to ensure proper sign extension. + + ``` + B3 A3 B2 A2 B1 A1 B0 A0 + │ │ │ │ │ │ │ │ + ┌─▼──▼─┐ ┌─▼──▼─┐ ┌─▼──▼─┐ ┌─▼──▼─┐ + │ PG │ │ PG │ │ PG │ │ PG │ + │ FA │ │ FA │ │ FA │ │ FA │ + │ │ │ │ │ │ │ │ + └─┬──┬┬┘ └─┬┬┬──┘ └─┬┬┬──┘ └─┬┬┬──┘ + │ ││G3P3S3 │││G2P2S2 │││G1P1S1 │││ G0P0S0 + │ ┌▼▼───────▼▼▼─────────▼▼▼─────────▼▼▼──┐ + │ │ Group PG logic │ + │ │ │ + │ └─┬───────┬──────────┬──────────┬──────┘ + │ │ │ │ │ + ┌─▼───▼───────▼──────────▼──────────▼──────┐ + │ Sum logic │ + │ with sign extension │ + └┬────┬───────┬──────────┬──────────┬──────┘ + │ │ │ │ │ + ▼ ▼ ▼ ▼ ▼ + Cout S3 S1 S0 S0 + ``` + + Description of the __init__ method. + + Args: + a (Bus): First input bus. + b (Bus): Second input bus. + prefix (str, optional): Prefix name of signed P/G rca. Defaults to "s_pg_rca". + """ 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" @@ -73,4 +145,4 @@ class SignedPGRippleCarryAdder(UnsignedPGRippleCarryAdder, ArithmeticCircuit): self.add_component(sign_xor_1) sign_xor_2 = XorGate(sign_xor_1.out, self.get_previous_component(2).out, prefix=self.prefix+"_xor"+str(self.get_instance_num(cls=XorGate))) self.add_component(sign_xor_2) - self.out.connect(self.N, sign_xor_2.out) \ No newline at end of file + self.out.connect(self.N, sign_xor_2.out) diff --git a/ariths_gen/multi_bit_circuits/adders/ripple_carry_adder.py b/ariths_gen/multi_bit_circuits/adders/ripple_carry_adder.py index 4926ba9..84966cb 100644 --- a/ariths_gen/multi_bit_circuits/adders/ripple_carry_adder.py +++ b/ariths_gen/multi_bit_circuits/adders/ripple_carry_adder.py @@ -1,12 +1,12 @@ -from ariths_gen.wire_components import( +from ariths_gen.wire_components import ( Wire, Bus ) -from ariths_gen.core import( +from ariths_gen.core import ( ArithmeticCircuit, MultiplierCircuit ) -from ariths_gen.one_bit_circuits.one_bit_components import( +from ariths_gen.one_bit_circuits.one_bit_components import ( HalfAdder, PGLogicBlock, ConstantWireValue0, @@ -14,7 +14,7 @@ from ariths_gen.one_bit_circuits.one_bit_components import( FullAdder, FullAdderPG ) -from ariths_gen.one_bit_circuits.logic_gates import( +from ariths_gen.one_bit_circuits.logic_gates import ( LogicGate, AndGate, NandGate, @@ -25,7 +25,34 @@ from ariths_gen.one_bit_circuits.logic_gates import( NotGate ) + class UnsignedRippleCarryAdder(ArithmeticCircuit): + """Class representing unsigned ripple carry adder. + + Unsigned ripple carry adder represents N-bit unsigned adder which is composed of + N one bit adders, where first is a half adder and rest are full adders. + + Its downside is its long propagation delay the bigger the circuit is. + + ``` + B3 A3 B2 A2 B1 A1 B0 A0 + │ │ │ │ │ │ │ │ + ┌─▼──▼─┐ ┌─▼──▼─┐ ┌─▼──▼─┐ ┌─▼──▼─┐ + │ │ C3│ │ C2│ │ C1│ │ + ┌──┤ FA │◄──┤ FA │◄──┤ FA │◄──┤ HA │ + │ │ │ │ │ │ │ │ │ + │ └──┬───┘ └──┬───┘ └──┬───┘ └──┬───┘ + ▼ ▼ ▼ ▼ ▼ + Cout S3 S2 S1 S0 + ``` + + Description of the __init__ method. + + Args: + a (Bus): First input bus. + b (Bus): Second input bus. + prefix (str, optional): Prefix name of unsigned rca. Defaults to "u_rca". + """ def __init__(self, a: Bus, b: Bus, prefix: str = "u_rca"): super().__init__() self.N = max(a.N, b.N) @@ -48,14 +75,41 @@ class UnsignedRippleCarryAdder(ArithmeticCircuit): # Rest adders are full adders else: obj_adder = FullAdder(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_adder) self.out.connect(input_index, obj_adder.get_sum_wire()) if input_index == (self.N-1): self.out.connect(self.N, obj_adder.get_carry_wire()) - + class SignedRippleCarryAdder(UnsignedRippleCarryAdder, ArithmeticCircuit): + """Class representing signed ripple carry adder. + + Signed ripple carry adder represents N-bit signed adder which is composed of + N one bit adders, where first is a half adder and rest are full adders. + At last XOR gates are used to ensure proper sign extension. + + Its downside is its long propagation delay the bigger the circuit is. + + ``` + B3 A3 B3 A3 B2 A2 B1 A1 B0 A0 + │ │ │ │ │ │ │ │ │ │ + ┌─▼──▼─┐ ┌─▼──▼─┐ ┌─▼──▼─┐ ┌─▼──▼─┐ ┌─▼──▼─┐ + │ SIGN │ C4│ │ C3│ │ C2│ │ C1│ │ + │Extend│◄──┤ FA │◄──┤ FA │◄──┤ FA │◄──┤ HA │ + │ │ │ │ │ │ │ │ │ │ + └──┬───┘ └──┬───┘ └──┬───┘ └──┬───┘ └──┬───┘ + ▼ ▼ ▼ ▼ ▼ + Cout S3 S2 S1 S0 + ``` + + Description of the __init__ method. + + Args: + a (Bus): First input bus. + b (Bus): Second input bus. + prefix (str, optional): Prefix name of signed rca. Defaults to "s_rca". + """ def __init__(self, a: Bus, b: Bus, prefix: str = "s_rca"): super().__init__(a=a, b=b, prefix=prefix) self.c_data_type = "int64_t" @@ -65,4 +119,4 @@ class SignedRippleCarryAdder(UnsignedRippleCarryAdder, ArithmeticCircuit): self.add_component(sign_xor_1) sign_xor_2 = XorGate(sign_xor_1.out, self.get_previous_component(2).get_carry_wire(), prefix=self.prefix+"_xor"+str(self.get_instance_num(cls=XorGate))) self.add_component(sign_xor_2) - self.out.connect(self.N, sign_xor_2.out) \ No newline at end of file + self.out.connect(self.N, sign_xor_2.out) diff --git a/ariths_gen/multi_bit_circuits/multipliers/array_multiplier.py b/ariths_gen/multi_bit_circuits/multipliers/array_multiplier.py index 298460e..e59075d 100644 --- a/ariths_gen/multi_bit_circuits/multipliers/array_multiplier.py +++ b/ariths_gen/multi_bit_circuits/multipliers/array_multiplier.py @@ -1,19 +1,19 @@ -from ariths_gen.wire_components import( +from ariths_gen.wire_components import ( Wire, Bus ) -from ariths_gen.core import( +from ariths_gen.core import ( ArithmeticCircuit, MultiplierCircuit ) -from ariths_gen.one_bit_circuits.one_bit_components import( +from ariths_gen.one_bit_circuits.one_bit_components import ( HalfAdder, ConstantWireValue0, ConstantWireValue1, FullAdder, FullAdderPG ) -from ariths_gen.one_bit_circuits.logic_gates import( +from ariths_gen.one_bit_circuits.logic_gates import ( LogicGate, AndGate, NandGate, @@ -24,8 +24,23 @@ from ariths_gen.one_bit_circuits.logic_gates import( NotGate ) -# MULTIPLIERS + class UnsignedArrayMultiplier(MultiplierCircuit): + """Class representing unsigned array multiplier. + + Unsigned array multiplier represents N-bit multiplier composed of + many AND gates and half/full adders to calculate partial products and + gradually sum them. + + Downside is its rather big area because it is composed of many logic gates. + + Description of the __init__ method. + + Args: + a (Bus): First input bus. + b (Bus): Second input bus. + prefix (str, optional): Prefix name of unsigned array multiplier. Defaults to "u_arrmul". + """ def __init__(self, a: Bus, b: Bus, prefix: str = "u_arrmul"): super().__init__() self.N = max(a.N, b.N) @@ -85,6 +100,21 @@ class UnsignedArrayMultiplier(MultiplierCircuit): class SignedArrayMultiplier(MultiplierCircuit): + """Class representing signed array multiplier. + + Signed array multiplier represents N-bit multiplier composed of + many AND/NAND gates and half/full adders to calculate partial products and + gradually sum them. + + Downside is its rather big area because it is composed of many logic gates. + + Description of the __init__ method. + + Args: + a (Bus): First input bus. + b (Bus): Second input bus. + prefix (str, optional): Prefix name of signed array multiplier. Defaults to "s_arrmul". + """ def __init__(self, a: Bus, b: Bus, prefix: str = "s_arrmul"): super().__init__() self.c_data_type = "int64_t" diff --git a/ariths_gen/multi_bit_circuits/multipliers/dadda_multiplier.py b/ariths_gen/multi_bit_circuits/multipliers/dadda_multiplier.py index c180d57..697c641 100644 --- a/ariths_gen/multi_bit_circuits/multipliers/dadda_multiplier.py +++ b/ariths_gen/multi_bit_circuits/multipliers/dadda_multiplier.py @@ -1,20 +1,20 @@ from ariths_gen.multi_bit_circuits.adders.ripple_carry_adder import UnsignedRippleCarryAdder -from ariths_gen.wire_components import( +from ariths_gen.wire_components import ( Wire, Bus ) -from ariths_gen.core import( +from ariths_gen.core import ( ArithmeticCircuit, MultiplierCircuit ) -from ariths_gen.one_bit_circuits.one_bit_components import( +from ariths_gen.one_bit_circuits.one_bit_components import ( HalfAdder, ConstantWireValue0, ConstantWireValue1, FullAdder, FullAdderPG ) -from ariths_gen.one_bit_circuits.logic_gates import( +from ariths_gen.one_bit_circuits.logic_gates import ( LogicGate, AndGate, NandGate, @@ -25,7 +25,26 @@ from ariths_gen.one_bit_circuits.logic_gates import( NotGate ) + class UnsignedDaddaMultiplier(MultiplierCircuit): + """Class representing unsigned dadda multiplier. + + Unsigned dadda multiplier represents fast N-bit multiplier which utilizes + the functionality of reduction algorithm proposed by Luigi Dadda. + Dadda algorithm is described more in detail here: + https://en.wikipedia.org/wiki/Dadda_multiplier + + It is composed of much less inner components (half/full adders, AND gates) as opposed + to e.g. wallace and array multipliers. + + Description of the __init__ method. + + Args: + a (Bus): First input bus. + b (Bus): Second input bus. + prefix (str, optional): Prefix name of unsigned dadda multiplier. Defaults to "u_dadda_rca". + unsigned_adder_class_name (str, optional): Unsigned multi bit adder used to obtain final sums of products. Defaults to UnsignedRippleCarryAdder. + """ def __init__(self, a: Bus, b: Bus, prefix: str = "u_dadda_rca", unsigned_adder_class_name: str = UnsignedRippleCarryAdder): super().__init__() self.N = max(a.N, b.N) @@ -103,7 +122,7 @@ class UnsignedDaddaMultiplier(MultiplierCircuit): # Final addition of remaining bits using chosen unsigned multi bit adder else: # Obtain proper adder name with its bit width (columns bit pairs minus the first alone bit) - adder_prefix = unsigned_adder_class_name(a=a , b=b).prefix + str(len(self.columns)-1) + adder_prefix = unsigned_adder_class_name(a=a, b=b).prefix + str(len(self.columns)-1) adder_a = Bus(prefix=f"{adder_prefix}_a", wires_list=[self.get_column_wire(column=col, bit=1) for col in range(1, len(self.columns))]) adder_b = Bus(prefix=f"{adder_prefix}_b", wires_list=[self.get_column_wire(column=col, bit=2) for col in range(1, len(self.columns))]) @@ -114,6 +133,25 @@ class UnsignedDaddaMultiplier(MultiplierCircuit): class SignedDaddaMultiplier(MultiplierCircuit): + """Class representing signed dadda multiplier. + + Signed dadda multiplier represents fast N-bit multiplier which utilizes + the functionality of reduction algorithm proposed by Luigi Dadda and uses Baugh-Wooley algorithm + to perform signed multiplication. + Dadda algorithm is described more in detail here: + https://en.wikipedia.org/wiki/Dadda_multiplier + + It is composed of much less inner components (half/full adders, AND/NAND gates) as opposed + to e.g. wallace and array multipliers. + + Description of the __init__ method. + + Args: + a (Bus): First input bus. + b (Bus): Second input bus. + prefix (str, optional): Prefix name of signed dadda multiplier. Defaults to "s_dadda_rca". + unsigned_adder_class_name (str, optional): Unsigned multi bit adder used to obtain final sums of products. Defaults to UnsignedRippleCarryAdder. + """ def __init__(self, a: Bus, b: Bus, prefix: str = "s_dadda_rca", unsigned_adder_class_name: str = UnsignedRippleCarryAdder): super().__init__() self.N = max(a.N, b.N) @@ -141,7 +179,7 @@ class SignedDaddaMultiplier(MultiplierCircuit): 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) + # (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) @@ -202,7 +240,7 @@ class SignedDaddaMultiplier(MultiplierCircuit): # Final addition of remaining bits using chosen unsigned multi bit adder else: # Obtain proper adder name with its bit width (columns bit pairs minus the first alone bit) - adder_prefix = unsigned_adder_class_name(a=a , b=b).prefix + str(len(self.columns)-1) + adder_prefix = unsigned_adder_class_name(a=a, b=b).prefix + str(len(self.columns)-1) adder_a = Bus(prefix=f"{adder_prefix}_a", wires_list=[self.get_column_wire(column=col, bit=1) for col in range(1, len(self.columns))]) adder_b = Bus(prefix=f"{adder_prefix}_b", wires_list=[self.get_column_wire(column=col, bit=2) for col in range(1, len(self.columns))]) diff --git a/ariths_gen/multi_bit_circuits/multipliers/wallace_multiplier.py b/ariths_gen/multi_bit_circuits/multipliers/wallace_multiplier.py index 277e68a..7f99982 100644 --- a/ariths_gen/multi_bit_circuits/multipliers/wallace_multiplier.py +++ b/ariths_gen/multi_bit_circuits/multipliers/wallace_multiplier.py @@ -1,20 +1,20 @@ from ariths_gen.multi_bit_circuits.adders.ripple_carry_adder import UnsignedRippleCarryAdder -from ariths_gen.wire_components import( +from ariths_gen.wire_components import ( Wire, Bus ) -from ariths_gen.core import( +from ariths_gen.core import ( ArithmeticCircuit, MultiplierCircuit ) -from ariths_gen.one_bit_circuits.one_bit_components import( +from ariths_gen.one_bit_circuits.one_bit_components import ( HalfAdder, ConstantWireValue0, ConstantWireValue1, FullAdder, FullAdderPG ) -from ariths_gen.one_bit_circuits.logic_gates import( +from ariths_gen.one_bit_circuits.logic_gates import ( LogicGate, AndGate, NandGate, @@ -25,7 +25,25 @@ from ariths_gen.one_bit_circuits.logic_gates import( NotGate ) + class UnsignedWallaceMultiplier(MultiplierCircuit): + """Class representing unsigned wallace multiplier. + + Unsigned wallace multiplier represents fast N-bit multiplier which utilizes + the functionality of wallace tree reduction algorithm proposed by Chris Wallace. + Wallace tree algorithm is described more in detail here: + https://en.wikipedia.org/wiki/Wallace_tree + + It presents smaller circuit in area opposed to array multiplier but is slightly bigger then dadda because of less reduction stages. + + Description of the __init__ method. + + Args: + a (Bus): First input bus. + b (Bus): Second input bus. + prefix (str, optional): Prefix name of unsigned wallace multiplier. Defaults to "u_wallace_rca". + unsigned_adder_class_name (str, optional): Unsigned multi bit adder used to obtain final sums of products. Defaults to UnsignedRippleCarryAdder. + """ def __init__(self, a: Bus, b: Bus, prefix: str = "u_wallace_rca", unsigned_adder_class_name: str = UnsignedRippleCarryAdder): super().__init__() self.N = max(a.N, b.N) @@ -98,7 +116,7 @@ class UnsignedWallaceMultiplier(MultiplierCircuit): # Final addition of remaining bits using chosen unsigned multi bit adder else: # Obtain proper adder name with its bit width (columns bit pairs minus the first alone bit) - adder_prefix = unsigned_adder_class_name(a=a , b=b).prefix + str(len(self.columns)-1) + adder_prefix = unsigned_adder_class_name(a=a, b=b).prefix + str(len(self.columns)-1) adder_a = Bus(prefix=f"{adder_prefix}_a", wires_list=[self.get_column_wire(column=col, bit=1) for col in range(1, len(self.columns))]) adder_b = Bus(prefix=f"{adder_prefix}_b", wires_list=[self.get_column_wire(column=col, bit=2) for col in range(1, len(self.columns))]) @@ -109,6 +127,24 @@ class UnsignedWallaceMultiplier(MultiplierCircuit): class SignedWallaceMultiplier(MultiplierCircuit): + """Class representing signed wallace multiplier. + + Signed wallace multiplier represents fast N-bit multiplier which utilizes + the functionality of wallace tree reduction algorithm proposed by Chris Wallace and uses Baugh-Wooley algorithm + to perform signed multiplication. + Wallace tree algorithm is described more in detail here: + https://en.wikipedia.org/wiki/Wallace_tree + + It presents smaller circuit in area opposed to array multiplier but is slightly bigger then dadda because of less reduction stages. + + Description of the __init__ method. + + Args: + a (Bus): First input bus. + b (Bus): Second input bus. + prefix (str, optional): Prefix name of signed wallace multiplier. Defaults to "s_wallace_rca". + unsigned_adder_class_name (str, optional): Unsigned multi bit adder used to obtain final sums of products. Defaults to UnsignedRippleCarryAdder. + """ def __init__(self, a: Bus, b: Bus, prefix: str = "s_wallace_rca", unsigned_adder_class_name: str = UnsignedRippleCarryAdder): super().__init__() self.N = max(a.N, b.N) @@ -134,7 +170,7 @@ class SignedWallaceMultiplier(MultiplierCircuit): 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) + # (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) @@ -194,7 +230,7 @@ class SignedWallaceMultiplier(MultiplierCircuit): # Final addition of remaining bits using chosen unsigned multi bit adder else: # Obtain proper adder name with its bit width (columns bit pairs minus the first alone bit) - adder_prefix = unsigned_adder_class_name(a=a , b=b).prefix + str(len(self.columns)-1) + adder_prefix = unsigned_adder_class_name(a=a, b=b).prefix + str(len(self.columns)-1) adder_a = Bus(prefix=f"{adder_prefix}_a", wires_list=[self.get_column_wire(column=col, bit=1) for col in range(1, len(self.columns))]) adder_b = Bus(prefix=f"{adder_prefix}_b", wires_list=[self.get_column_wire(column=col, bit=2) for col in range(1, len(self.columns))]) diff --git a/ariths_gen/one_bit_circuits/logic_gates/logic_gates.py b/ariths_gen/one_bit_circuits/logic_gates/logic_gates.py index 97993d6..567ec1a 100644 --- a/ariths_gen/one_bit_circuits/logic_gates/logic_gates.py +++ b/ariths_gen/one_bit_circuits/logic_gates/logic_gates.py @@ -1,10 +1,25 @@ from ariths_gen.wire_components import Wire -""" LOGIC GATE COMPONENTS """ - # Two-input # class LogicGate(): + """Class representing two input logic gates. + + ``` + ┌──────┐ + ──►│ FUNC │ + │ ├─► + ──►│ │ + └──────┘ + ``` + + Description of the __init__ method. + + Args: + a (Wire): First input wire. + b (Wire): Second input wire. + prefix (str, optional): Prefix name of logic gate. Defaults to "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) @@ -154,6 +169,23 @@ class LogicGate(): class InvertedLogicGate(LogicGate): + """Class representing two input inverted logic gates. + + ``` + ┌──────┐ + ──►│ FUNC │ + │ │O──► + ──►│ │ + └──────┘ + ``` + + Description of the __init__ method. + + Args: + a (Wire): First input wire. + b (Wire): Second input wire. + prefix (str, optional): Prefix name of logic gate. Defaults to "gate". + """ def __init__(self, a: Wire, b: Wire, prefix: str = "gate"): super().__init__(a, b, prefix) @@ -172,6 +204,24 @@ class InvertedLogicGate(LogicGate): class AndGate(LogicGate): + """Class representing two input and gate. + + ``` + ┌──────┐ + ──►│ & │ + │ ├─► + ──►│ │ + └──────┘ + ``` + + Description of the __init__ method. + + Args: + a (Wire): First input wire. + b (Wire): Second input wire. + prefix (str, optional): Prefix name of logic gate. Defaults to "". + outid (int, optional): Index of output wire. Defaults to 0. + """ def __init__(self, a: Wire, b: Wire, prefix: str = "", outid: int = 0): super().__init__(a, b, prefix) self.gate_type = "and_gate" @@ -186,6 +236,24 @@ class AndGate(LogicGate): class NandGate(InvertedLogicGate): + """Class representing two input nand gate. + + ``` + ┌──────┐ + ──►│ & │ + │ │O──► + ──►│ │ + └──────┘ + ``` + + Description of the __init__ method. + + Args: + a (Wire): First input wire. + b (Wire): Second input wire. + prefix (str, optional): Prefix name of logic gate. Defaults to "". + outid (int, optional): Index of output wire. Defaults to 0. + """ def __init__(self, a: Wire, b: Wire, prefix: str = "", outid: int = 0): super().__init__(a, b, prefix) self.gate_type = "nand_gate" @@ -200,6 +268,24 @@ class NandGate(InvertedLogicGate): class OrGate(LogicGate): + """Class representing two input or gate. + + ``` + ┌──────┐ + ──►│ ≥1 │ + │ ├─► + ──►│ │ + └──────┘ + ``` + + Description of the __init__ method. + + Args: + a (Wire): First input wire. + b (Wire): Second input wire. + prefix (str, optional): Prefix name of logic gate. Defaults to "". + outid (int, optional): Index of output wire. Defaults to 0. + """ def __init__(self, a: Wire, b: Wire, prefix: str = "", outid: int = 0): super().__init__(a, b, prefix) self.gate_type = "or_gate" @@ -214,6 +300,24 @@ class OrGate(LogicGate): class NorGate(InvertedLogicGate): + """Class representing two input nor gate. + + ``` + ┌──────┐ + ──►│ ≥1 │ + │ │O──► + ──►│ │ + └──────┘ + ``` + + Description of the __init__ method. + + Args: + a (Wire): First input wire. + b (Wire): Second input wire. + prefix (str, optional): Prefix name of logic gate. Defaults to "". + outid (int, optional): Index of output wire. Defaults to 0. + """ def __init__(self, a: Wire, b: Wire, prefix: str = "", outid: int = 0): super().__init__(a, b, prefix) self.gate_type = "nor_gate" @@ -228,6 +332,24 @@ class NorGate(InvertedLogicGate): class XorGate(LogicGate): + """Class representing two input xor gate. + + ``` + ┌──────┐ + ──►│ =1 │ + │ ├─► + ──►│ │ + └──────┘ + ``` + + Description of the __init__ method. + + Args: + a (Wire): First input wire. + b (Wire): Second input wire. + prefix (str, optional): Prefix name of logic gate. Defaults to "". + outid (int, optional): Index of output wire. Defaults to 0. + """ def __init__(self, a: Wire, b: Wire, prefix: str = "", outid: int = 0): super().__init__(a, b, prefix) self.gate_type = "xor_gate" @@ -242,6 +364,24 @@ class XorGate(LogicGate): class XnorGate(InvertedLogicGate): + """Class representing two input xnor gate. + + ``` + ┌──────┐ + ──►│ =1 │ + │ │O──► + ──►│ │ + └──────┘ + ``` + + Description of the __init__ method. + + Args: + a (Wire): First input wire. + b (Wire): Second input wire. + prefix (str, optional): Prefix name of logic gate. Defaults to "". + outid (int, optional): Index of output wire. Defaults to 0. + """ def __init__(self, a: Wire, b: Wire, prefix: str = "", outid: int = 0): super().__init__(a, b, prefix) self.gate_type = "xnor_gate" @@ -257,6 +397,23 @@ class XnorGate(InvertedLogicGate): # Single-input # class NotGate(InvertedLogicGate): + """Class representing one input not gate. + + ``` + ┌──────┐ + │ 1 │ + ──►│ │O─► + │ │ + └──────┘ + ``` + + Description of the __init__ method. + + Args: + a (Wire): Input wire. + prefix (str, optional): Prefix name of logic gate. Defaults to "". + outid (int, optional): Index of output wire. Defaults to 0. + """ def __init__(self, a: Wire, prefix: str = "", outid: int = 0): self.gate_type = "not_gate" self.cgp_function = 1 diff --git a/ariths_gen/one_bit_circuits/one_bit_components/three_input_one_bit_components.py b/ariths_gen/one_bit_circuits/one_bit_components/three_input_one_bit_components.py index 357af28..e706e86 100644 --- a/ariths_gen/one_bit_circuits/one_bit_components/three_input_one_bit_components.py +++ b/ariths_gen/one_bit_circuits/one_bit_components/three_input_one_bit_components.py @@ -2,8 +2,26 @@ from ariths_gen.core import ThreeInputOneBitCircuit from ariths_gen.one_bit_circuits.logic_gates import LogicGate, AndGate, NandGate, OrGate, NorGate, XorGate, XnorGate, NotGate from ariths_gen.wire_components import Wire, Bus -# THREE INPUT CIRCUITS + class FullAdder(ThreeInputOneBitCircuit): + """Class representing three input one bit full adder. + + ``` + ┌──────┐ + ──►│ ├─► Sum + ──►│ │ + ──►│ ├─► Cout + └──────┘ + ``` + + Description of the __init__ method. + + Args: + a (Wire, optional): First input wire. Defaults to Wire(name="a"). + b (Wire, optional): Second input wire. Defaults to Wire(name="b"). + c (Wire, optional): Carry input wire. Defaults to Wire(name="cin"). + prefix (str, optional): Prefix name of full adder. Defaults to "fa". + """ 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" @@ -38,6 +56,24 @@ class FullAdder(ThreeInputOneBitCircuit): class FullAdderPG(ThreeInputOneBitCircuit): + """Class representing modified three input one bit full adder with propagate/generate logic. + + ``` + ┌──────┐ + ──►│ ├─► P + ──►│ ├─► G + ──►│ ├─► Sum + └──────┘ + ``` + + Description of the __init__ method. + + Args: + a (Wire, optional): First input wire. Defaults to Wire(name="a"). + b (Wire, optional): Second input wire. Defaults to Wire(name="b"). + c (Wire, optional): Carry input wire. Defaults to Wire(name="cin"). + prefix (str, optional): Prefix name of full adder. Defaults to "fa_cla". + """ 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" @@ -61,7 +97,7 @@ class FullAdderPG(ThreeInputOneBitCircuit): sum_xor = XorGate(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) @@ -69,4 +105,4 @@ class FullAdderPG(ThreeInputOneBitCircuit): return self.out.get_wire(1) def get_sum_wire(self): - return self.out.get_wire(2) \ No newline at end of file + return self.out.get_wire(2) diff --git a/ariths_gen/one_bit_circuits/one_bit_components/two_input_one_bit_components.py b/ariths_gen/one_bit_circuits/one_bit_components/two_input_one_bit_components.py index a4a0de4..db4faf4 100644 --- a/ariths_gen/one_bit_circuits/one_bit_components/two_input_one_bit_components.py +++ b/ariths_gen/one_bit_circuits/one_bit_components/two_input_one_bit_components.py @@ -2,8 +2,25 @@ from ariths_gen.core import TwoInputOneBitCircuit from ariths_gen.one_bit_circuits.logic_gates import LogicGate, AndGate, NandGate, OrGate, NorGate, XorGate, XnorGate, NotGate from ariths_gen.wire_components import Wire, Bus -# TWO INPUT CIRCUITS + class HalfAdder(TwoInputOneBitCircuit): + """Class representing two input one bit half adder. + + ``` + ┌──────┐ + ──►│ ├─► Sum + │ │ + ──►│ ├─► Cout + └──────┘ + ``` + + Description of the __init__ method. + + Args: + a (Wire, optional): First input wire. Defaults to Wire(name="a"). + b (Wire, optional): Second input wire. Defaults to Wire(name="b"). + prefix (str, optional): Prefix name of full adder. Defaults to "ha". + """ def __init__(self, a: Wire = Wire(name="a"), b: Wire = Wire(name="b"), prefix: str = "ha"): super().__init__() self.c_data_type = "uint8_t" @@ -27,6 +44,23 @@ class HalfAdder(TwoInputOneBitCircuit): class PGLogicBlock(TwoInputOneBitCircuit): + """Class representing two input one bit propagate/generate logic block. + + ``` + ┌──────┐ + ──►│ ├─► P + │ │ + ──►│ ├─► G + └──────┘ + ``` + + Description of the __init__ method. + + Args: + a (Wire, optional): First input wire. Defaults to Wire(name="a"). + b (Wire, optional): Second input wire. Defaults to Wire(name="b"). + prefix (str, optional): Prefix name of full adder. Defaults to "pg_logic". + """ 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" @@ -57,7 +91,25 @@ class PGLogicBlock(TwoInputOneBitCircuit): def get_sum_wire(self): return self.out.get_wire(2) + class ConstantWireValue0(TwoInputOneBitCircuit): + """Class representing two input one bit constant wire with value 0 generation block. + + ``` + ┌──────┐ + ──►│ │ + │ ├─►0 + ──►│ │ + └──────┘ + ``` + + Description of the __init__ method. + + Args: + a (Wire, optional): First input wire. Defaults to Wire(name="a"). + b (Wire, optional): Second input wire. Defaults to Wire(name="b"). + prefix (str, optional): Prefix name of full adder. Defaults to "constant_wire_value_0". + """ 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" @@ -83,6 +135,23 @@ class ConstantWireValue0(TwoInputOneBitCircuit): class ConstantWireValue1(TwoInputOneBitCircuit): + """Class representing two input one bit constant wire with value 1 generation block. + + ``` + ┌──────┐ + ──►│ │ + │ ├─►1 + ──►│ │ + └──────┘ + ``` + + Description of the __init__ method. + + Args: + a (Wire, optional): First input wire. Defaults to Wire(name="a"). + b (Wire, optional): Second input wire. Defaults to Wire(name="b"). + prefix (str, optional): Prefix name of full adder. Defaults to "constant_wire_value_1". + """ 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" diff --git a/ariths_gen/wire_components/buses.py b/ariths_gen/wire_components/buses.py index 2e338ac..006bac8 100644 --- a/ariths_gen/wire_components/buses.py +++ b/ariths_gen/wire_components/buses.py @@ -1,7 +1,17 @@ from .wires import Wire + class Bus(): - def __init__(self, prefix: str, N: int = 1, wires_list: list = None): + """Class representing bus of wires used as inputs/outputs of bigger circuits. + + Description of the __init__ method. + + Args: + prefix (str, optional): Prefix name of the bus. Defaults to "bus". + N (int, optional): Number of wires in the bus. Defaults to 1. + wires_list (list, optional): List of Wire objects used to clone one bus to another. Defaults to 0. + """ + def __init__(self, prefix: str = "bus", N: int = 1, wires_list: list = None): if wires_list is None: self.bus = [Wire(name=prefix+"_"+str(i), index=i) for i in range(N)] self.prefix = prefix @@ -13,45 +23,119 @@ class Bus(): # Connecting output wire of the inner circuit component to the input of another component # (or to the wire of the circuit's output bus) - def connect(self, out_wire_index: int, inner_component_out_wire: Wire): - self.bus[out_wire_index] = inner_component_out_wire + def connect(self, bus_wire_index: int, inner_component_out_wire: Wire): + """Connects given 'Wire' object to a 'bus_wire_index' within this bus. + + Args: + bus_wire_index (int): Index in bus to store given wire in. + inner_component_out_wire (Wire): Wire of some other component (mostly its output) to store in the bus. + """ + self.bus[bus_wire_index] = inner_component_out_wire def get_wire(self, wire_index: int = 0): + """Retrieves a wire from the bus by a given index. + + Args: + wire_index (int, optional): Index of wire to retrieve from the bus. Defaults to 0. + + Returns: + Wire: Return wire from the bus. + """ return self.bus[wire_index] def bus_extend(self, N: int, prefix: str = "bus"): + """Provides bus extension to contain more wires. + + Args: + N (int): Number of wires in the bus. Defaults to 1. + prefix (str, optional): Prefix name of the bus. Defaults to "bus". + """ if self.N < N: self.bus += [Wire(name=prefix+"_"+str(i), index=i) for i in range(self.N, N)] self.N = N """ C CODE GENERATION """ def get_declaration_c(self): + """Bus declaration in C code. + + Returns: + str: C code for declaration and initialization of bus name. + """ if self.N > 8: return f" uint64_t {self.prefix} = 0;\n" else: return f" uint8_t {self.prefix} = 0;\n" def get_wire_declaration_c(self): + """Declare each wire from the bus independently. + + Returns: + str: C code for declaration and initialization of bus wires. + """ return "".join([w.get_declaration_c() for w in self.bus]) def get_wire_assign_c(self, bus_prefix: str = ""): + """Assign all bits from the bus to each individual wires in C code. + + Args: + bus_prefix (str, optional): Custom bus prefix to use for assignment. Defaults to "". + + Returns: + str: C code for bus wires assignments. + """ bus_prefix = self.prefix if bus_prefix == "" else bus_prefix return "".join([w.get_assign_c(name=w.get_wire_value_c(name=bus_prefix, offset=self.bus.index(w))) for w in self.bus]) def return_wire_value_c(self, offset: int = 0): + """Retrieve wire value from desired index position in the bus. + + Args: + offset (int, optional): Offset position of the wire to be retrieved. Defaults to 0. + """ self.get_wire(wire_index=offset).return_wire_value_c(offset=offset) """ VERILOG CODE GENERATION """ def get_wire_declaration_v(self): + """Declare each wire from the bus independently. + + Returns: + str: Verilog code for declaration of bus wires. + """ return "".join([w.get_declaration_v() for w in self.bus]) def get_wire_assign_v(self, bus_prefix: str = ""): + """Assign all bits from the bus to each individual wires in Verilog code. + + Args: + bus_prefix (str, optional): Custom bus prefix to use for assignment. Defaults to "". + + Returns: + str: Verilog code for bus wires assignments. + """ bus_prefix = self.prefix if bus_prefix == "" else bus_prefix return "".join([w.get_assign_v(name=self.prefix, offset=self.bus.index(w), array=True) for w in self.bus]) """ BLIF CODE GENERATION """ def get_wire_declaration_blif(self, array: bool = True): + """Declare each wire from the bus independently. + + Argument `array` specifies whether to declare wires from bus by their offset e.g. out[0] + or by their wire name e.g. out_0. + Args: + array (bool, optional): Determines in which manner bus wire names are declared. Defaults to True. + + Returns: + str: Blif code for declaration of bus wires. + """ return "".join([w.get_declaration_blif(name=self.prefix, offset=self.bus.index(w), array=array) for w in self.bus]) def get_wire_assign_blif(self, output: bool = False): + """Assign all bits from the bus as each individual wires in Blif code. + + Args: + output (bool, optional): Specifies whether bus wires are used as outputs (assigned to) or as inputs (assigned from). Defaults to False. + + Returns: + str: Blif code for bus wires assignments. + """ return "".join([w.get_assign_blif(name=self.prefix+f"[{self.bus.index(w)}]", output=output) for w in self.bus]) diff --git a/ariths_gen/wire_components/wires.py b/ariths_gen/wire_components/wires.py index 44b83c6..1bb1ab8 100644 --- a/ariths_gen/wire_components/wires.py +++ b/ariths_gen/wire_components/wires.py @@ -1,4 +1,14 @@ class Wire(): + """Class representing basic wire used to interconnect components. + + Description of the __init__ method. + + Args: + name (str): Name of the wire. + value (int, optional): Value it carries (0,1). Defaults to 0. + index (int, optional): Index position of wire (mainly used for indexing within a bus). Defaults to 0. + """ + def __init__(self, name: str, value: int = 0, index: int = 0): self.name = name if self.name.endswith("_"+str(index)): @@ -10,23 +20,68 @@ class Wire(): """ C CODE GENERATION """ def get_declaration_c(self): + """Wire declaration in C code. + + Returns: + str: C code for declaration and initialization of wire's name. + """ return f" uint8_t {self.name} = {self.value};\n" def get_wire_value_c(self, name: str = "", offset: int = 0): + """Access desired bit from wire represented in C code variable. + + Args: + name (str, optional): Name representing a wire. Defaults to "". + offset (int, optional): Access desired wire bit from C code variable (used to access wire bits from buses). Defaults to 0. + + Returns: + str: C code bitwise shift to get bit from `offset` position in `w_name`. + """ w_name = self.name if name == "" else name return f"(({w_name} >> {offset}) & 0x01)" def get_assign_c(self, name: str): + """Assign (connect) the value of wire to another wire. + + Args: + name (str): Name of wire to assign value from. + + Returns: + str: C code for assignment of one wire to another. + """ return f" {self.name} = {name};\n" def return_wire_value_c(self, offset: int = 0): + """Retrieve bit value from wire and shift it to desired position for storing it within a bus. + + Args: + offset (int, optional): Used to shift wire value in order to be stored in proper location in bus. Defaults to 0. + + Returns: + str: C code bitwise shift of retrieved wire value. + """ return f"({self.name} & 0x01) << {offset}" """ VERILOG CODE GENERATION """ def get_declaration_v(self): + """Wire declaration in Verilog code. + + Returns: + str: Verilog code for declaration of wire's name. + """ return f" wire {self.name};\n" def get_assign_v(self, name: str, offset: int = 0, array: bool = False): + """Assignment of wire value to another desired wire in Verilog. + + Args: + name (str): Name of wire/bus to assign value from. + offset (int, optional): Used to retrieve desired wire from a bus. Defaults to 0. + array (bool, optional): Tells whether wire value is assigned from within a bus or from basic wire. Defaults to False. + + Returns: + str: Verilog code for wire assignment. + """ if array is True: return f" assign {self.name} = {name}[{offset}];\n" else: @@ -34,15 +89,37 @@ class Wire(): """ BLIF CODE GENERATION """ def get_declaration_blif(self, name: str = "", offset: int = 0, array: bool = False): + """Wire declaration in Blif code. + + Declares basic wire name if wire is not a part of a bus, + or declares wire by an offset of its position within the input bus. + + Args: + name (str, optional): Name of a bus to be declared (if array is True). Defaults to "". + offset (int, optional): Offset wire location within a bus. Defaults to 0. + array (bool, optional): Tells whether a basic wire or a wire from within a bus is to be declared. Defaults to False. + + Returns: + str: Blif code for declaration of a wire. + """ if array is True: return f" {name}[{offset}]" else: return f" {self.name}" def get_assign_blif(self, name: str, output: bool = False): + """Assignment of wire value to another desired wire in Blif. + + Args: + name (str): Name of the source/destination wire to be assigned to. + output (bool, optional): Whether 'name' represents the destination or the source wire in the assignment. Defaults to False. + + Returns: + str: Blif code for assignment of one wire to another. + """ if output is True: return f".names {self.name} {name}\n" + \ f"1 1\n" else: return f".names {name} {self.name}\n" + \ - f"1 1\n" \ No newline at end of file + f"1 1\n"