diff --git a/ariths_gen/core/arithmetic_circuits/general_circuit.py b/ariths_gen/core/arithmetic_circuits/general_circuit.py index 42af3f6..c4fb720 100644 --- a/ariths_gen/core/arithmetic_circuits/general_circuit.py +++ b/ariths_gen/core/arithmetic_circuits/general_circuit.py @@ -1,3 +1,4 @@ +from typing import Dict from ariths_gen.core.logic_gate_circuits.logic_gate_circuit import OneInputLogicGate, TwoInputLogicGate from ariths_gen.wire_components import ( @@ -52,12 +53,22 @@ class GeneralCircuit(): # super().__init__(prefix, name, out_N, inner_component, inputs=[a, b], signed=signed, **kwargs) + + def get_circuit_def(self) -> Dict[str, Wire]: + """ returns IDs and wires of the inputs and output""" + #.{circuit_block.a.prefix}({self.a.prefix}), .{circuit_block.b.prefix}({self.b.prefix}), .{circuit_block.out.prefix}({self.out.prefix}));\n" + r = {chr(97 + i): self.inputs[i] for i in range(len(self.inputs))} + r['out'] = self.get_global_prefix() + "_out" + return r + def add_component(self, component): """Adds a component into list of circuit's inner subcomponents. Args: component: Subcomponent to be added into list of components composing described circuit. """ + prefixes = [c.prefix for c in self.components] + #assert component.prefix not in prefixes, f"Component with prefix {component.prefix} already exists in the circuit." self.components.append(component) return component @@ -550,6 +561,8 @@ class GeneralCircuit(): Returns: str: Hierarchical Verilog code of subcomponent arithmetic circuit's wires declaration. """ + return "".join(w.get_wire_declaration_v() for w in self.inputs + [self.out]) + "\n" + return f" wire [{self.a.N-1}:0] {self.a.prefix};\n" + \ f" wire [{self.b.N-1}:0] {self.b.prefix};\n" + \ f" wire [{self.out.N-1}:0] {self.out.prefix};\n" @@ -576,8 +589,9 @@ class GeneralCircuit(): circuit_type = self.__class__(a=Bus("a"), b=Bus("b")).prefix + str(self.N) circuit_block = self.__class__(a=Bus(N=self.N, prefix="a"), b=Bus( N=self.N, prefix="b"), name=circuit_type) - return self.a.return_bus_wires_values_v_hier() + self.b.return_bus_wires_values_v_hier() + \ - f" {circuit_type} {circuit_type}_{self.out.prefix}(.{circuit_block.a.prefix}({self.a.prefix}), .{circuit_block.b.prefix}({self.b.prefix}), .{circuit_block.out.prefix}({self.out.prefix}));\n" + return "".join([c.return_bus_wires_values_v_hier() for c in self.inputs]) + \ + f" {circuit_type} {circuit_type}_{self.out.prefix}(" + ",".join([f".{a.prefix}({b.prefix})" for a, b in zip(circuit_block.inputs, self.inputs)]) + f", .{circuit_block.out.prefix}({self.out.prefix}));\n" + #.{circuit_block.a.prefix}({self.a.prefix}), .{circuit_block.b.prefix}({self.b.prefix}), .{circuit_block.out.prefix}({self.out.prefix}));\n" def get_function_out_v_hier(self): """Generates hierarchical Verilog code assignment of corresponding arithmetic circuit's output bus wires. diff --git a/ariths_gen/multi_bit_circuits/adders/__init__.py b/ariths_gen/multi_bit_circuits/adders/__init__.py index f0ba494..0ba1f34 100644 --- a/ariths_gen/multi_bit_circuits/adders/__init__.py +++ b/ariths_gen/multi_bit_circuits/adders/__init__.py @@ -67,4 +67,4 @@ from ariths_gen.multi_bit_circuits.adders.conditional_sum_adder import ( from ariths_gen.multi_bit_circuits.adders.carry_increment_adder import ( UnsignedCarryIncrementAdder, SignedCarryIncrementAdder -) +) \ No newline at end of file diff --git a/ariths_gen/multi_bit_circuits/others/__init__.py b/ariths_gen/multi_bit_circuits/others/__init__.py new file mode 100644 index 0000000..44ed65b --- /dev/null +++ b/ariths_gen/multi_bit_circuits/others/__init__.py @@ -0,0 +1,12 @@ + +from ariths_gen.multi_bit_circuits.others.popcount import ( + UnsignedPopCount +) + +from ariths_gen.multi_bit_circuits.others.bit_reduce import ( + BitReduce, AndReduce, OrReduce +) + +from ariths_gen.multi_bit_circuits.others.compare import ( + UnsignedCompareLT +) \ No newline at end of file diff --git a/ariths_gen/multi_bit_circuits/others/bit_reduce.py b/ariths_gen/multi_bit_circuits/others/bit_reduce.py new file mode 100644 index 0000000..45da4d7 --- /dev/null +++ b/ariths_gen/multi_bit_circuits/others/bit_reduce.py @@ -0,0 +1,89 @@ +""" + +""" + +from ariths_gen.wire_components import ( + Wire, + ConstantWireValue0, + ConstantWireValue1, + Bus, + wires +) +from ariths_gen.core.arithmetic_circuits import ( + ArithmeticCircuit, + GeneralCircuit, + MultiplierCircuit +) +from ariths_gen.core.logic_gate_circuits import ( + MultipleInputLogicGate +) +from ariths_gen.one_bit_circuits.one_bit_components import ( + HalfAdder, + FullAdder, + FullAdderP, + TwoOneMultiplexer +) +from ariths_gen.one_bit_circuits.logic_gates import ( + AndGate, + NandGate, + OrGate, + NorGate, + XorGate, + XnorGate, + NotGate +) + +from ariths_gen.core.logic_gate_circuits import TwoInputLogicGate, TwoInputInvertedLogicGate, OneInputLogicGate +from ariths_gen.multi_bit_circuits.adders import UnsignedRippleCarryAdder + +from math import log2, ceil + +class BitReduce(GeneralCircuit): + """Class representing tree reducer circuit. Doent work for NAND gate! + """ + + def __init__(self, a: Bus, gate : TwoInputLogicGate, prefix : str = "", name : str = "bitreduce", **kwargs): + self.N = a.N + self.a = a + + outc = 1 + super().__init__(name=name, prefix=prefix, inputs = [self.a], out_N=outc) + + # tree reduction + def create_tree(a: Bus, depth: int, branch="A"): + + #print(a) + if a.N == 1: + return a[0] + else: + half = a.N // 2 + b_in = Bus(N=half, prefix=f"b_inn{depth}A") + c_in = Bus(N=a.N - half, prefix=f"b_inn{depth}B") + #print(a, half, a.N) + + + for i, j in enumerate(range(half)): + b_in[i] = a[j] + + for i, j in enumerate(range(half, a.N)): + c_in[i] = a[j] + + b = create_tree(b_in, depth=depth + 1, branch = "A") + c = create_tree(c_in, depth= depth + 1, branch = "B") + d = gate(a=b, b=c, prefix = f"{self.prefix}_red_{branch}_{depth}") + self.add_component(d) + return d.out + + sumwire = create_tree(self.a, 0, "X") + #print(sumbus) + self.out[0] = sumwire + + +class OrReduce(BitReduce): + def __init__(self, a: Bus, prefix : str = "", name : str = "orreduce", **kwargs): + super().__init__(a=a, gate=OrGate, prefix=prefix, name=name, **kwargs) + + +class AndReduce(BitReduce): + def __init__(self, a: Bus, prefix : str = "", name : str = "orreduce", **kwargs): + super().__init__(a=a, gate=AndGate, prefix=prefix, name=name, **kwargs) \ No newline at end of file diff --git a/ariths_gen/multi_bit_circuits/others/compare.py b/ariths_gen/multi_bit_circuits/others/compare.py new file mode 100644 index 0000000..6c62970 --- /dev/null +++ b/ariths_gen/multi_bit_circuits/others/compare.py @@ -0,0 +1,83 @@ +""" + +""" + +from ariths_gen.wire_components import ( + Wire, + ConstantWireValue0, + ConstantWireValue1, + Bus, + wires +) +from ariths_gen.core.arithmetic_circuits import ( + ArithmeticCircuit, + GeneralCircuit, + MultiplierCircuit +) + +from ariths_gen.core.logic_gate_circuits import ( + MultipleInputLogicGate +) +from ariths_gen.one_bit_circuits.one_bit_components import ( + HalfAdder, + FullAdder, + FullAdderP, + TwoOneMultiplexer +) +from ariths_gen.one_bit_circuits.logic_gates import ( + AndGate, + NandGate, + OrGate, + NorGate, + XorGate, + XnorGate, + NotGate +) + +from ariths_gen.multi_bit_circuits.others import OrReduce + + +from math import log2, ceil + +class UnsignedCompareLT(GeneralCircuit): + """Class representing unsigned compare + + + Returns true if a < b + + """ + + def __init__(self, a: Bus, b: Bus, prefix : str = "", name : str = "cmp_lt", **kwargs): + self.a = a + self.b = b + self.N = max(a.N, b.N) + + #print("outc", outc) + super().__init__(name=name, prefix=prefix, + inputs = [self.a, self.b], out_N=1) + + + self.a.bus_extend(self.N, prefix=a.prefix) + self.b.bus_extend(self.N, prefix=b.prefix) + + + # create wires + psum = ConstantWireValue1() + + res = Bus(N = self.N, prefix=self.prefix + "res") + + + for i in reversed(range(self.N)): + + i1 = self.add_component(NotGate(self.a[i], f"{self.prefix}_i1_{i}")).out + i2 = self.b[i] + + and1 = self.add_component(AndGate(i1, i2, f"{self.prefix}_and1_{i}")).out + res[i] = self.add_component(AndGate(and1, psum, f"{self.prefix}_and2_{i}")).out + + pi = self.add_component(XnorGate(self.a[i], self.b[i], f"{self.prefix}_pi_{i}")).out + psum = self.add_component(AndGate(pi, psum, f"{self.prefix}_psum_{i}")).out + + + self.out = self.add_component(OrReduce(res, prefix=f"{self.prefix}_orred")).out + #self.out.connect_bus(sumbus ) \ No newline at end of file diff --git a/ariths_gen/multi_bit_circuits/others/popcount.py b/ariths_gen/multi_bit_circuits/others/popcount.py new file mode 100644 index 0000000..cdca327 --- /dev/null +++ b/ariths_gen/multi_bit_circuits/others/popcount.py @@ -0,0 +1,89 @@ +""" + +""" + +from ariths_gen.wire_components import ( + Wire, + ConstantWireValue0, + ConstantWireValue1, + Bus, + wires +) +from ariths_gen.core.arithmetic_circuits import ( + ArithmeticCircuit, + GeneralCircuit, + MultiplierCircuit +) +from ariths_gen.core.logic_gate_circuits import ( + MultipleInputLogicGate +) +from ariths_gen.one_bit_circuits.one_bit_components import ( + HalfAdder, + FullAdder, + FullAdderP, + TwoOneMultiplexer +) +from ariths_gen.one_bit_circuits.logic_gates import ( + AndGate, + NandGate, + OrGate, + NorGate, + XorGate, + XnorGate, + NotGate +) + +from ariths_gen.multi_bit_circuits.adders import UnsignedRippleCarryAdder + +from math import log2, ceil + +class UnsignedPopCount(GeneralCircuit): + """Class representing unsigned popcount circuit. + + Popcount circuit is a circuit that counts the number of 1s in a binary number. + + """ + + def __init__(self, a: Bus, adder : ArithmeticCircuit|None = None, prefix : str = "", name : str = "popcnt", **kwargs): + self.N = a.N + self.a = a + + outc = ceil(log2(self.N + 1)) + #print("outc", outc) + super().__init__(name=name, prefix=prefix, inputs = [self.a], out_N=outc) + + + self.a.bus_extend(2**(outc - 1), prefix=a.prefix) + #print(self.a) + self.adder = adder + if not self.adder: + self.adder = UnsignedRippleCarryAdder + + # tree reduction + def create_tree(a: Bus, depth: int, branch="A"): + + #print(a) + if a.N == 1: + return a + else: + half = a.N // 2 + b_in = Bus(N=half, prefix=f"b_inn{depth}A") + c_in = Bus(N=a.N - half, prefix=f"b_inn{depth}B") + #print(a, half, a.N) + + + for i, j in enumerate(range(half)): + b_in[i] = a[j] + + for i, j in enumerate(range(half, a.N)): + c_in[i] = a[j] + + b = create_tree(b_in, depth=depth + 1, branch = "A") + c = create_tree(c_in, depth= depth + 1, branch = "B") + d = self.adder(a=b, b=c, prefix = f"{self.prefix}_add{branch}_{depth}") + self.add_component(d) + return d.out + + sumbus = create_tree(self.a,0, "X") + #print(sumbus) + self.out.connect_bus(sumbus ) \ No newline at end of file diff --git a/ariths_gen/wire_components/buses.py b/ariths_gen/wire_components/buses.py index f7c91f5..930d834 100644 --- a/ariths_gen/wire_components/buses.py +++ b/ariths_gen/wire_components/buses.py @@ -242,6 +242,15 @@ class Bus(): [unique_out_wires.append(w.prefix) if w.prefix not in unique_out_wires else None for w in self.bus] return "".join([f", .{circuit_block.out.get_wire(self.bus.index(o)).prefix}({unique_out_wires.pop(unique_out_wires.index(o.prefix))})" if o.prefix in unique_out_wires else f", .{circuit_block.out.get_wire(self.bus.index(o)).prefix}()" for o in self.bus]) + def get_wire_declaration_v(self): + """Declare the wire in Verilog code representation. + + Returns: + str: Verilog code for declaration of individual bus wires. + """ + return f" wire [{self.N-1}:0] {self.prefix};\n" + + """ BLIF CODE GENERATION """ def get_wire_declaration_blif(self, array: bool = True): """Declare each wire from the bus independently in Blif code representation. diff --git a/tests/test_compare.py b/tests/test_compare.py new file mode 100644 index 0000000..02e1312 --- /dev/null +++ b/tests/test_compare.py @@ -0,0 +1,50 @@ +from ariths_gen.wire_components import ( + Wire, + ConstantWireValue0, + ConstantWireValue1, + Bus +) + +from ariths_gen.core.arithmetic_circuits import GeneralCircuit + +from ariths_gen.multi_bit_circuits.others import ( + UnsignedCompareLT +) + +import numpy as np +import math +from io import StringIO + + +def test_compare(): + """ Test unsigned comparator """ + N = 8 + + a = Bus(N=N, prefix="a") + b = Bus(N=N, prefix="b") + av = np.arange(2**N).reshape(1, -1) + bv = np.arange(2**N).reshape(-1, 1) + + + cmp = UnsignedCompareLT(a=a, b=b) + o = StringIO() + cmp.get_v_code_hier(open("tmp.verilog", "w")) + print(o.getvalue()) + +# av = 0 + # bv = 5 + + + v = cmp(av, bv) + print("ret = ", v) + + expected = np.array(av < bv).astype(int) + + print("expected = ", expected) + + #expected = np.sum(r, axis=1) + + np.testing.assert_array_equal(v, expected) + + + \ No newline at end of file diff --git a/tests/test_popcnt.py b/tests/test_popcnt.py new file mode 100644 index 0000000..8d66628 --- /dev/null +++ b/tests/test_popcnt.py @@ -0,0 +1,50 @@ +from ariths_gen.wire_components import ( + Wire, + ConstantWireValue0, + ConstantWireValue1, + Bus +) + +from ariths_gen.core.arithmetic_circuits import GeneralCircuit + +from ariths_gen.multi_bit_circuits.others import ( + UnsignedPopCount +) + +import numpy as np +import math +from io import StringIO + + +def test_popcount(): + """ Test unsigned adders """ + N = 7 + + for N in [3, 7, 8, 9, 16]: + a = Bus(N=N, prefix="a") + av = np.arange(2**N) + + + popcnt = UnsignedPopCount(a=a) + #o = StringIO() + #popcnt.get_v_code_hier(o) + #print(o.getvalue()) + + + print(popcnt(av)) + + + # conv to binary + r = [] + a_s = av.copy() + for i in range(N): + r.append(a_s % 2) + a_s = a_s // 2 + r = np.dstack(r).reshape(-1, N) + print("r = ", r) + expected = np.sum(r, axis=1) + + np.testing.assert_array_equal(popcnt(av), expected) + + + \ No newline at end of file diff --git a/tests/test_reduce.py b/tests/test_reduce.py new file mode 100644 index 0000000..9ec4b7e --- /dev/null +++ b/tests/test_reduce.py @@ -0,0 +1,78 @@ +from ariths_gen.wire_components import ( + Wire, + ConstantWireValue0, + ConstantWireValue1, + Bus +) + +from ariths_gen.core.arithmetic_circuits import GeneralCircuit + +from ariths_gen.multi_bit_circuits.others import ( + BitReduce, AndReduce, OrReduce +) + +import numpy as np +import math +from io import StringIO + + +def test_orreduce(): + """ Test unsigned adders """ + N = 7 + + for N in [3, 7, 8, 9, 16]: + a = Bus(N=N, prefix="a") + av = np.arange(2**N) + + + reduce = OrReduce(a=a) + o = StringIO() + reduce.get_v_code_hier(o) + print(o.getvalue()) + + + #print(reduce(av)) + + # conv to binary + r = [] + a_s = av.copy() + for i in range(N): + r.append(a_s % 2) + a_s = a_s // 2 + r = np.dstack(r).reshape(-1, N) + print("r = ", r) + expected = np.bitwise_or.reduce(r, axis=1) + + np.testing.assert_array_equal(reduce(av), expected) + + +def test_andreduce(): + """ Test unsigned adders """ + N = 7 + + for N in [3, 7, 8, 9, 16]: + a = Bus(N=N, prefix="a") + av = np.arange(2**N) + + + reduce = AndReduce(a=a) + o = StringIO() + reduce.get_v_code_hier(o) + print(o.getvalue()) + + + #print(reduce(av)) + + # conv to binary + r = [] + a_s = av.copy() + for i in range(N): + r.append(a_s % 2) + a_s = a_s // 2 + r = np.dstack(r).reshape(-1, N) + print("r = ", r) + expected = np.bitwise_and.reduce(r, axis=1) + + np.testing.assert_array_equal(reduce(av), expected) + + \ No newline at end of file