mirror of
https://github.com/ehw-fit/ariths-gen.git
synced 2025-04-04 06:11:41 +01:00
commit
397876b265
60
.github/workflows/generate.yml
vendored
60
.github/workflows/generate.yml
vendored
@ -17,9 +17,9 @@ jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Set up Python 3.x
|
||||
uses: actions/setup-python@v4
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
# Semantic version range syntax or exact version of a Python version
|
||||
python-version: '3.x'
|
||||
@ -35,7 +35,7 @@ jobs:
|
||||
- name: Run generating axmults
|
||||
run: python generate_axmuls.py
|
||||
- name: Upload results
|
||||
uses: actions/upload-artifact@v1.0.0
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: arithmetic-circuits-8
|
||||
path: test_circuits
|
||||
@ -44,17 +44,17 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
needs: build
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install iverilog
|
||||
run: sudo apt install iverilog
|
||||
- name: Set up Python 3.x
|
||||
uses: actions/setup-python@v4
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
# Semantic version range syntax or exact version of a Python version
|
||||
python-version: '3.x'
|
||||
- run: python -m pip install numpy
|
||||
- name: Download workflow run artifacts
|
||||
uses: actions/download-artifact@v3
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: arithmetic-circuits-8
|
||||
path: test_circuits
|
||||
@ -81,6 +81,42 @@ jobs:
|
||||
cd tests
|
||||
bash test_circuits_verilog.sh
|
||||
cd ..
|
||||
|
||||
- name: Python circuits testing
|
||||
run: |
|
||||
cd tests
|
||||
python test_all.py
|
||||
cd ..
|
||||
|
||||
- name: Python ax testing
|
||||
run: |
|
||||
cd tests
|
||||
python test_ax.py
|
||||
cd ..
|
||||
|
||||
- name: Python CGP testing
|
||||
run: |
|
||||
cd tests
|
||||
python test_cgp.py
|
||||
cd ..
|
||||
|
||||
- name: Python Compare testing
|
||||
run: |
|
||||
cd tests
|
||||
python test_compare.py
|
||||
cd ..
|
||||
|
||||
- name: Python Popcount testing
|
||||
run: |
|
||||
cd tests
|
||||
python test_popcnt.py
|
||||
cd ..
|
||||
|
||||
- name: Python Reduce testing
|
||||
run: |
|
||||
cd tests
|
||||
python test_reduce.py
|
||||
cd ..
|
||||
|
||||
test_python:
|
||||
runs-on: ubuntu-latest
|
||||
@ -90,9 +126,9 @@ jobs:
|
||||
python-version: [ '3.7', '3.8', '3.9', '3.10', '3.11' ]
|
||||
name: Python ${{ matrix.python-version }} test
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Setup python
|
||||
uses: actions/setup-python@v4
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
architecture: x64
|
||||
@ -107,9 +143,9 @@ jobs:
|
||||
needs: test
|
||||
if: github.ref == 'refs/heads/main'
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Set up Python 3.x
|
||||
uses: actions/setup-python@v4
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
# Semantic version range syntax or exact version of a Python version
|
||||
python-version: '3.x'
|
||||
@ -123,12 +159,12 @@ jobs:
|
||||
- name: Generate documentation
|
||||
run: pdoc --html ariths_gen
|
||||
- name: Upload results
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: documentation
|
||||
path: html
|
||||
- name: Deploy 🚀
|
||||
uses: JamesIves/github-pages-deploy-action@4.4.1
|
||||
uses: JamesIves/github-pages-deploy-action@4
|
||||
with:
|
||||
branch: gh-pages # The branch the action should deploy to.
|
||||
folder: html/ariths_gen # The folder the action should deploy.
|
||||
|
@ -5,4 +5,10 @@ cd tests
|
||||
bash test_mac.sh
|
||||
bash test_circuits.sh
|
||||
bash test_circuits_verilog.sh
|
||||
bash test_circuits_cgp.sh
|
||||
bash test_circuits_cgp.sh
|
||||
python test_all.py
|
||||
python test_ax.py
|
||||
python test_cgp.py
|
||||
python test_compare.py
|
||||
python test_popcnt.py
|
||||
python test_reduce.py
|
@ -141,6 +141,11 @@ print("Mean average error", np.abs(r - (va * vb)).mean())
|
||||
The `yosys_equiv_check.sh` script enables to formally check the equivalence of generated Verilog and BLIF representations of the same circuit.
|
||||
It uses the Yosys Open SYnthesis Suite tool by Claire Xenia Wolf. For further information, please visit: https://yosyshq.readthedocs.io/projects/yosys/en/latest/index.html.
|
||||
|
||||
## Install Yosys
|
||||
```bash
|
||||
sudo apt-get install yosys
|
||||
```
|
||||
|
||||
## Execute permission
|
||||
```bash
|
||||
chmod +x yosys_equiv_check.sh
|
||||
|
@ -1,10 +1,7 @@
|
||||
from .arithmetic_circuit import (
|
||||
ArithmeticCircuit,
|
||||
ThreeInputArithmeticCircuit
|
||||
from .general_circuit import (
|
||||
GeneralCircuit
|
||||
)
|
||||
|
||||
from .general_circuit import (GeneralCircuit)
|
||||
|
||||
from .multiplier_circuit import (
|
||||
MultiplierCircuit
|
||||
)
|
||||
|
@ -1,265 +0,0 @@
|
||||
from ariths_gen.core.logic_gate_circuits.logic_gate_circuit import OneInputLogicGate, TwoInputLogicGate
|
||||
|
||||
from ariths_gen.wire_components import (
|
||||
Wire,
|
||||
ConstantWireValue0,
|
||||
ConstantWireValue1,
|
||||
Bus
|
||||
)
|
||||
|
||||
from ariths_gen.core.arithmetic_circuits.general_circuit import GeneralCircuit
|
||||
|
||||
|
||||
class ArithmeticCircuit(GeneralCircuit):
|
||||
"""Class represents a general arithmetic circuit and ensures its generation to various representations.
|
||||
|
||||
The __init__ method fills some mandatory attributes concerning arithmetic circuit
|
||||
that are later used for generation into various representations.
|
||||
"""
|
||||
|
||||
def __init__(self, a, b, prefix: str, name: str, out_N: int, inner_component: bool = False, one_bit_circuit: bool = False, signed: bool = False, **kwargs):
|
||||
super().__init__(prefix, name, out_N, inner_component, inputs=[a, b], signed=signed, **kwargs)
|
||||
if one_bit_circuit is False:
|
||||
if prefix == "":
|
||||
self.prefix = name
|
||||
else:
|
||||
self.prefix = prefix + "_" + name
|
||||
|
||||
self.inner_component = inner_component
|
||||
if self.inner_component is True:
|
||||
self.a = Bus(prefix=f"{self.prefix}_{a.prefix}", wires_list=a.bus)
|
||||
self.b = Bus(prefix=f"{self.prefix}_{b.prefix}", wires_list=b.bus)
|
||||
|
||||
if a.is_output_bus():
|
||||
self.a.connect_bus(connecting_bus=a)
|
||||
if b.is_output_bus():
|
||||
self.b.connect_bus(connecting_bus=b)
|
||||
else:
|
||||
self.a = Bus(prefix=f"{a.prefix}", wires_list=a.bus)
|
||||
self.b = Bus(prefix=f"{b.prefix}", wires_list=b.bus)
|
||||
|
||||
""" C CODE GENERATION """
|
||||
def get_prototype_c(self):
|
||||
"""Generates C code function header to describe corresponding arithmetic circuit's interface in C code.
|
||||
|
||||
Returns:
|
||||
str: Function's name and parameters in C code.
|
||||
"""
|
||||
return f"{self.c_data_type} {self.prefix}({self.c_data_type} {self.a.prefix}, {self.c_data_type} {self.b.prefix})" + "{" + "\n"
|
||||
|
||||
""" VERILOG CODE GENERATION """
|
||||
def get_prototype_v(self):
|
||||
"""Generates Verilog code module header to describe corresponding arithmetic circuit's interface in Verilog code.
|
||||
|
||||
Returns:
|
||||
str: Module's name and parameters in Verilog code.
|
||||
"""
|
||||
return f"module {self.prefix}(input [{self.N-1}:0] {self.a.prefix}, input [{self.N-1}:0] {self.b.prefix}, output [{self.out.N-1}:0] {self.out.prefix});\n"
|
||||
|
||||
""" BLIF CODE GENERATION """
|
||||
def get_declaration_blif(self):
|
||||
"""Generates flat Blif code declaration of input/output circuit wires.
|
||||
|
||||
Returns:
|
||||
str: Flat Blif code containing declaration of circuit's wires.
|
||||
"""
|
||||
if self.N == 1:
|
||||
return f".inputs {self.a.prefix} {self.b.prefix}\n" + \
|
||||
f".outputs{self.out.get_wire_declaration_blif()}\n" + \
|
||||
f".names vdd\n1\n" + \
|
||||
f".names gnd\n0\n"
|
||||
else:
|
||||
return f".inputs{self.a.get_wire_declaration_blif()}{self.b.get_wire_declaration_blif()}\n" + \
|
||||
f".outputs{self.out.get_wire_declaration_blif()}\n" + \
|
||||
f".names vdd\n1\n" + \
|
||||
f".names gnd\n0\n"
|
||||
|
||||
|
||||
class ThreeInputArithmeticCircuit(GeneralCircuit):
|
||||
"""Class represents a general three input arithmetic circuit and ensures its generation to various representations.
|
||||
|
||||
The __init__ method fills some mandatory attributes concerning arithmetic circuit
|
||||
that are later used for generation into various representations.
|
||||
"""
|
||||
|
||||
def __init__(self, a, b, c, prefix: str, name: str, out_N: int, inner_component: bool = False, one_bit_circuit: bool = False, signed: bool = False, **kwargs):
|
||||
super().__init__(prefix, name, out_N, inner_component, inputs=[a, b, c], signed=signed, **kwargs)
|
||||
if one_bit_circuit is False:
|
||||
if prefix == "":
|
||||
self.prefix = name
|
||||
else:
|
||||
self.prefix = prefix + "_" + name
|
||||
|
||||
self.inner_component = inner_component
|
||||
if self.inner_component is True:
|
||||
self.a = Bus(prefix=f"{self.prefix}_{a.prefix}", wires_list=a.bus)
|
||||
self.b = Bus(prefix=f"{self.prefix}_{b.prefix}", wires_list=b.bus)
|
||||
self.c = Bus(prefix=f"{self.prefix}_{c.prefix}", wires_list=c.bus)
|
||||
|
||||
if a.is_output_bus():
|
||||
self.a.connect_bus(connecting_bus=a)
|
||||
if b.is_output_bus():
|
||||
self.b.connect_bus(connecting_bus=b)
|
||||
if c.is_output_bus():
|
||||
self.c.connect_bus(connecting_bus=c)
|
||||
else:
|
||||
self.a = Bus(prefix=f"{a.prefix}", wires_list=a.bus)
|
||||
self.b = Bus(prefix=f"{b.prefix}", wires_list=b.bus)
|
||||
self.c = Bus(prefix=f"{c.prefix}", wires_list=c.bus)
|
||||
|
||||
""" C CODE GENERATION """
|
||||
def get_prototype_c(self):
|
||||
"""Generates C code function header to describe corresponding arithmetic circuit's interface in C code.
|
||||
|
||||
Returns:
|
||||
str: Function's name and parameters in C code.
|
||||
"""
|
||||
return f"{self.c_data_type} {self.prefix}({self.c_data_type} {self.a.prefix}, {self.c_data_type} {self.b.prefix}, {self.c_data_type} {self.c.prefix})" + "{" + "\n"
|
||||
|
||||
def get_function_block_c(self):
|
||||
"""Generates hierarchical C code representation of corresponding multi-bit arithmetic circuit used as function block in hierarchical circuit description.
|
||||
|
||||
Returns:
|
||||
str: Hierarchical C code of multi-bit arithmetic circuit's function block description.
|
||||
"""
|
||||
# Obtain proper circuit name with its bit width
|
||||
circuit_prefix = self.__class__(
|
||||
a=Bus("a"), b=Bus("b"), c=Bus("c")).prefix + str(self.N)
|
||||
circuit_block = self.__class__(a=Bus(N=self.N, prefix="a"), b=Bus(
|
||||
N=self.N, prefix="b"), c=Bus(
|
||||
N=self.N, prefix="c"), name=circuit_prefix, **self._parent_kwargs)
|
||||
return f"{circuit_block.get_circuit_c()}\n\n"
|
||||
|
||||
def get_declaration_c_hier(self):
|
||||
"""Generates hierarchical C code declaration of corresponding subcomponent input/output wires inside the upper component.
|
||||
|
||||
Generates wires used to connect input/output values to/from invocation of the corresponding function block into inner wires present
|
||||
inside the upper component from which function block has been invoked.
|
||||
|
||||
Returns:
|
||||
str: Hierarchical C code of subcomponent arithmetic circuit's wires declaration.
|
||||
"""
|
||||
return f" {self.c_data_type} {self.a.prefix} = 0;\n" + \
|
||||
f" {self.c_data_type} {self.b.prefix} = 0;\n" + \
|
||||
f" {self.c_data_type} {self.c.prefix} = 0;\n" + \
|
||||
f" {self.c_data_type} {self.out.prefix} = 0;\n"
|
||||
|
||||
def get_out_invocation_c(self):
|
||||
"""Generates hierarchical C code invocation of corresponding arithmetic circuit's generated function block.
|
||||
|
||||
Assigns input values from other subcomponents into multi-bit input buses used as inputs for function block invocation.
|
||||
Assigns output values from invocation of the corresponding function block into inner wires present inside
|
||||
the upper component from which function block has been invoked.
|
||||
|
||||
Returns:
|
||||
str: Hierarchical C code of subcomponent's C function invocation and output assignment.
|
||||
"""
|
||||
# Getting name of circuit type for proper C code generation without affecting actual generated composition
|
||||
circuit_type = self.__class__(a=Bus("a"), b=Bus("b"), c=Bus("c")).prefix + str(self.N)
|
||||
return self.a.return_bus_wires_values_c_hier() + self.b.return_bus_wires_values_c_hier() + self.c.return_bus_wires_values_c_hier() + \
|
||||
f" {self.out.prefix} = {circuit_type}({self.a.prefix}, {self.b.prefix}, {self.c.prefix});\n"
|
||||
|
||||
""" VERILOG CODE GENERATION """
|
||||
def get_prototype_v(self):
|
||||
"""Generates Verilog code module header to describe corresponding arithmetic circuit's interface in Verilog code.
|
||||
|
||||
Returns:
|
||||
str: Module's name and parameters in Verilog code.
|
||||
"""
|
||||
return f"module {self.prefix}(input [{self.N-1}:0] {self.a.prefix}, input [{self.N-1}:0] {self.b.prefix}, input [{self.N-1}:0] {self.c.prefix}, output [{self.out.N-1}:0] {self.out.prefix});\n"
|
||||
|
||||
def get_function_block_v(self):
|
||||
"""Generates hierarchical Verilog code representation of corresponding multi-bit arithmetic circuit used as function block in hierarchical circuit description.
|
||||
|
||||
Returns:
|
||||
str: Hierarchical Verilog code of multi-bit arithmetic circuit's function block description.
|
||||
"""
|
||||
# Obtain proper circuit name with its bit width
|
||||
circuit_prefix = self.__class__(
|
||||
a=Bus("a"), b=Bus("b"), c=Bus("c")).prefix + str(self.N)
|
||||
circuit_block = self.__class__(a=Bus(N=self.N, prefix="a"), b=Bus(
|
||||
N=self.N, prefix="b"), c=Bus(N=self.N, prefix="c"), name=circuit_prefix, **self._parent_kwargs)
|
||||
return f"{circuit_block.get_circuit_v()}\n\n"
|
||||
|
||||
def get_declaration_v_hier(self):
|
||||
"""Generates hierarchical Verilog code declaration of corresponding subcomponent input/output wires inside the upper component.
|
||||
|
||||
Generates wires used to connect input/output values to/from invocation of the corresponding function block into inner wires present
|
||||
inside the upper component from which function block has been invoked.
|
||||
|
||||
Returns:
|
||||
str: Hierarchical Verilog code of subcomponent arithmetic circuit's wires declaration.
|
||||
"""
|
||||
return f" wire [{self.a.N-1}:0] {self.a.prefix};\n" + \
|
||||
f" wire [{self.b.N-1}:0] {self.b.prefix};\n" + \
|
||||
f" wire [{self.c.N-1}:0] {self.c.prefix};\n" + \
|
||||
f" wire [{self.out.N-1}:0] {self.out.prefix};\n"
|
||||
|
||||
def get_out_invocation_v(self):
|
||||
"""Generates hierarchical Verilog code invocation of corresponding arithmetic circuit's generated function block.
|
||||
|
||||
Assigns input values from other subcomponents into multi-bit input buses used as inputs for function block invocation.
|
||||
Assigns output values from invocation of the corresponding function block into inner wires present inside
|
||||
the upper component from which function block has been invoked.
|
||||
|
||||
Returns:
|
||||
str: Hierarchical Verilog code of subcomponent's module invocation and output assignment.
|
||||
"""
|
||||
# Getting name of circuit type and insitu copying out bus for proper Verilog code generation without affecting actual generated composition
|
||||
circuit_type = self.__class__(a=Bus("a"), b=Bus("b"), c=Bus("c")).prefix + str(self.N)
|
||||
circuit_block = self.__class__(a=Bus(N=self.N, prefix="a"), b=Bus(
|
||||
N=self.N, prefix="b"), c=Bus(
|
||||
N=self.N, prefix="c"), name=circuit_type)
|
||||
return self.a.return_bus_wires_values_v_hier() + self.b.return_bus_wires_values_v_hier() + self.c.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.c.prefix}({self.c.prefix}), .{circuit_block.out.prefix}({self.out.prefix}));\n"
|
||||
|
||||
""" BLIF CODE GENERATION """
|
||||
def get_declaration_blif(self):
|
||||
"""Generates flat Blif code declaration of input/output circuit wires.
|
||||
|
||||
Returns:
|
||||
str: Flat Blif code containing declaration of circuit's wires.
|
||||
"""
|
||||
if self.N == 1:
|
||||
return f".inputs {self.a.prefix} {self.b.prefix} {self.c.prefix}\n" + \
|
||||
f".outputs{self.out.get_wire_declaration_blif()}\n" + \
|
||||
f".names vdd\n1\n" + \
|
||||
f".names gnd\n0\n"
|
||||
else:
|
||||
return f".inputs{self.a.get_wire_declaration_blif()}{self.b.get_wire_declaration_blif()}{self.c.get_wire_declaration_blif()}\n" + \
|
||||
f".outputs{self.out.get_wire_declaration_blif()}\n" + \
|
||||
f".names vdd\n1\n" + \
|
||||
f".names gnd\n0\n"
|
||||
|
||||
def get_invocation_blif_hier(self):
|
||||
"""Generates hierarchical Blif code invocation of corresponding arithmetic circuit's generated function block.
|
||||
|
||||
Used for multi-bit subcomponent's modul invocation.
|
||||
|
||||
Returns:
|
||||
str: Hierarchical Blif code of subcomponent's model invocation and output assignment.
|
||||
"""
|
||||
# Getting name of circuit type for proper Blif code generation without affecting actual generated composition
|
||||
circuit_type = self.__class__(a=Bus("a"), b=Bus("b"), c=Bus("c")).prefix + str(self.N)
|
||||
return f"{self.a.get_wire_assign_blif(output=True)}" + \
|
||||
f"{self.b.get_wire_assign_blif(output=True)}" + \
|
||||
f"{self.c.get_wire_assign_blif(output=True)}" + \
|
||||
f".subckt {circuit_type}" + \
|
||||
"".join([f" a[{self.a.bus.index(w)}]={self.a.prefix}[{self.a.bus.index(w)}]" for w in self.a.bus]) + \
|
||||
"".join([f" b[{self.b.bus.index(w)}]={self.b.prefix}[{self.b.bus.index(w)}]" for w in self.b.bus]) + \
|
||||
"".join([f" c[{self.c.bus.index(w)}]={self.c.prefix}[{self.c.bus.index(w)}]" for w in self.c.bus]) + \
|
||||
"".join(
|
||||
[f" {circuit_type}_out[{self.out.bus.index(o)}]={o.name}" for o in self.out.bus]) + "\n"
|
||||
|
||||
def get_function_block_blif(self):
|
||||
"""Generates hierarchical Blif code representation of corresponding multi-bit arithmetic circuit used as function block in hierarchical circuit description.
|
||||
|
||||
Returns:
|
||||
str: Hierarchical Blif code of multi-bit arithmetic circuit's function block description.
|
||||
"""
|
||||
# Obtain proper circuit name with its bit width
|
||||
circuit_prefix = self.__class__(
|
||||
a=Bus("a"), b=Bus("b"), c=Bus("c")).prefix + str(self.N)
|
||||
circuit_block = self.__class__(a=Bus(N=self.N, prefix="a"), b=Bus(
|
||||
N=self.N, prefix="b"), c=Bus(N=self.N, prefix="c"), name=circuit_prefix, **self._parent_kwargs)
|
||||
return f"{circuit_block.get_circuit_blif()}"
|
@ -1,12 +1,14 @@
|
||||
from ariths_gen.core.logic_gate_circuits.logic_gate_circuit import OneInputLogicGate, TwoInputLogicGate
|
||||
|
||||
from ariths_gen.core.logic_gate_circuits.logic_gate_circuit import (
|
||||
OneInputLogicGate,
|
||||
TwoInputLogicGate
|
||||
)
|
||||
from ariths_gen.wire_components import (
|
||||
Wire,
|
||||
ConstantWireValue0,
|
||||
ConstantWireValue1,
|
||||
Bus
|
||||
)
|
||||
|
||||
from typing import Dict
|
||||
import inspect
|
||||
import copy
|
||||
from io import StringIO
|
||||
|
||||
|
||||
@ -16,22 +18,40 @@ class GeneralCircuit():
|
||||
The __init__ method fills some mandatory attributes concerning arithmetic circuit
|
||||
that are later used for generation into various representations.
|
||||
"""
|
||||
|
||||
def __init__(self, prefix: str, name: str, out_N: int, inner_component: bool = False, inputs: list = [], signed: bool = False, outname=None, **kwargs):
|
||||
def __init__(self, prefix: str, name: str, out_N: int, inner_component: bool = False, inputs: list = [], one_bit_circuit: bool = False, signed: bool = False, signed_out = None, outname=None, **kwargs):
|
||||
if prefix == "":
|
||||
self.prefix = name
|
||||
else:
|
||||
self.prefix = prefix + "_" + name
|
||||
self.inner_component = inner_component
|
||||
self.inputs = inputs
|
||||
|
||||
# Dynamic input bus assignment
|
||||
self.inputs = []
|
||||
for i, input in enumerate(inputs):
|
||||
attr_name = chr(97+i)
|
||||
full_prefix = f"{self.prefix}_{input.prefix}" if self.inner_component else f"{input.prefix}"
|
||||
if isinstance(input, Bus) or isinstance(input, Wire):
|
||||
circuit_input = input
|
||||
circuit_input.prefix = full_prefix
|
||||
setattr(self, attr_name, circuit_input)
|
||||
self.inputs.append(circuit_input)
|
||||
# If the input bus is an output bus, connect it
|
||||
if isinstance(input, Bus) and input.is_output_bus():
|
||||
getattr(self, attr_name).connect_bus(connecting_bus=input)
|
||||
|
||||
if not outname:
|
||||
outname = self.prefix+"_out"
|
||||
self.out = Bus(outname, out_N, out_bus=True, signed=signed)
|
||||
|
||||
if signed_out is None:
|
||||
signed_out = signed
|
||||
self.out = Bus(outname, out_N, out_bus=True, signed=signed_out)
|
||||
|
||||
self.components = []
|
||||
self.circuit_wires = []
|
||||
self._prefixes = [] # TODO rename to fullname and add distinct attr for prefix, name, suffix
|
||||
self.circuit_gates = []
|
||||
self.circuit_wires = []
|
||||
self.signed = signed
|
||||
self.signed_out = signed_out
|
||||
self.c_data_type = "int64_t" if self.signed is True else "uint64_t"
|
||||
self.pyc = None # Python compiled function
|
||||
self.kwargs = kwargs
|
||||
@ -49,15 +69,72 @@ class GeneralCircuit():
|
||||
|
||||
def __str__(self):
|
||||
return f"<{type(self).__name__} prefix={self.prefix} " + (", ".join([f"input={i}" for i in self.inputs])) + ">"
|
||||
|
||||
# super().__init__(prefix, name, out_N, inner_component, inputs=[a, b], signed=signed, **kwargs)
|
||||
|
||||
def get_hier_subcomponent_def(self, parent_kwargs: dict = {}):
|
||||
""" Creates and returns a new instance of the current circuit block used for definition of a subcomponent in a hierarchical circuit.
|
||||
|
||||
Args:
|
||||
parent_kwargs (dict): Dictionary containing all the configuration settings of the parent circuit block.
|
||||
|
||||
Returns:
|
||||
GeneralCircuit: A new instance of the current circuit block with proper prefix and input wires.
|
||||
"""
|
||||
# Obtain proper circuit name with its input bit widths
|
||||
init_signature = inspect.signature(self.__class__.__init__)
|
||||
init_params = list(init_signature.parameters.keys())
|
||||
default_circuit_name = init_signature.parameters['name'].default
|
||||
circuit_type = default_circuit_name + "x".join(str(getattr(self, chr(97+i)).N) for i, _ in enumerate(self.inputs))
|
||||
# Initialize and fill args for the new instance based on the current instance
|
||||
init_args = {}
|
||||
|
||||
for param in init_params[1:]: # Skip 'self'
|
||||
attr = getattr(self, param, None) # Get the attribute from the current instance
|
||||
|
||||
if attr is not None: # If attribute does not exist, it will use default value from the signature
|
||||
if isinstance(attr, Bus): # If the input is a Bus, create a copy of the Bus object with same length, but proper prefix
|
||||
init_args[param] = Bus(N=attr.N, prefix=param)
|
||||
elif isinstance(attr, Wire): # If the input is a Wire, create a copy of the Wire object with proper prefix
|
||||
init_args[param] = Wire(name=param)
|
||||
else: # Copy other types of attributes
|
||||
init_args[param] = copy.deepcopy(attr)
|
||||
|
||||
init_args['name'] = circuit_type
|
||||
init_args['prefix'] = ""
|
||||
|
||||
circuit_block = self.__class__(**init_args, **parent_kwargs)
|
||||
return circuit_block
|
||||
|
||||
def get_circuit_def(self) -> Dict[str, Wire]:
|
||||
""" returns IDs and wires of the inputs and output"""
|
||||
# TODO delete? (probably replaced by get_hier_subcomponent_def)
|
||||
#.{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.
|
||||
|
||||
Additionally it adds all the gates of the component to the circuit's list of gates and all
|
||||
sbcomponents prefixes to check for naming duplicates which could cause issues in the circuit generation.
|
||||
|
||||
Args:
|
||||
component: Subcomponent to be added into list of components composing described circuit.
|
||||
"""
|
||||
# TODO should be refactored in ArithsGen rework
|
||||
# We should probably check also wire names for especially hierarchical generation
|
||||
if isinstance(component, TwoInputLogicGate):
|
||||
if component.disable_generation is False:
|
||||
self.circuit_gates.append(component)
|
||||
else:
|
||||
self.circuit_gates.extend(component.get_circuit_gates())
|
||||
for prefix in component._prefixes:
|
||||
assert prefix not in self._prefixes, f"Component with prefix {prefix} already exists in the circuit."
|
||||
self._prefixes.extend(component._prefixes)
|
||||
|
||||
assert component.prefix not in self._prefixes, f"Component with prefix {component.prefix} already exists in the circuit."
|
||||
self._prefixes.append(component.prefix)
|
||||
self.components.append(component)
|
||||
return component
|
||||
|
||||
@ -85,20 +162,25 @@ class GeneralCircuit():
|
||||
return sum(isinstance(c, cls) for c in self.components if isinstance(c, cls) and c.disable_generation is False)
|
||||
else:
|
||||
return sum(isinstance(c, cls) for c in self.components)
|
||||
|
||||
def get_circuit_gates(self):
|
||||
|
||||
def get_circuit_gates(self, verilog_output: bool = False):
|
||||
"""Gets a list of all the logic gates in circuit that should be generated.
|
||||
|
||||
Args:
|
||||
verilog_output (bool): Specifies whether the call has been invoked by a verilog output generation method.
|
||||
Returns:
|
||||
list: List of composite logic gates.
|
||||
"""
|
||||
gates = []
|
||||
for c in self.components:
|
||||
if isinstance(c, TwoInputLogicGate):
|
||||
if c.disable_generation is False:
|
||||
if (c.disable_generation is False) and (verilog_output is False or getattr(c, "use_verilog_instance", False) is False):
|
||||
gates.append(c)
|
||||
else:
|
||||
gates.extend((c.get_circuit_gates()))
|
||||
# Check whether it is necessary to use gates for the Verilog component
|
||||
# description (ArithsGen internally defined comp) or not (technology specific instance)
|
||||
if verilog_output is False or ((hasattr(c, "use_verilog_instance") and c.use_verilog_instance is False) or hasattr(c, "use_verilog_instance") is False):
|
||||
gates.extend(c.get_circuit_gates(verilog_output))
|
||||
return gates
|
||||
|
||||
def get_one_bit_components(self):
|
||||
@ -111,7 +193,7 @@ class GeneralCircuit():
|
||||
for c in self.components:
|
||||
if isinstance(c, TwoInputLogicGate):
|
||||
continue
|
||||
elif isinstance(getattr(c, 'a'), Wire):
|
||||
elif all(isinstance(i, Wire) for i in self.inputs):
|
||||
one_bit_comps.append(c)
|
||||
else:
|
||||
one_bit_comps.extend(c.get_one_bit_components())
|
||||
@ -128,10 +210,11 @@ class GeneralCircuit():
|
||||
for c in self.components:
|
||||
if isinstance(c, TwoInputLogicGate):
|
||||
continue
|
||||
elif isinstance(getattr(c, 'a'), Wire):
|
||||
elif all(isinstance(i, Wire) for i in self.inputs):
|
||||
continue
|
||||
else:
|
||||
multi_bit_comps.append(c)
|
||||
multi_bit_comps.extend(c.get_multi_bit_components())
|
||||
return multi_bit_comps
|
||||
|
||||
@staticmethod
|
||||
@ -146,23 +229,25 @@ class GeneralCircuit():
|
||||
list: List of unique composite class types.
|
||||
"""
|
||||
if multi_bit is True:
|
||||
return list({(type(c), c.N): c for c in components}.values())
|
||||
return list({(type(c), tuple(i.N for i in c.inputs)): c for c in components[::-1]}.values())
|
||||
else:
|
||||
return list({type(c): c for c in components}.values())
|
||||
|
||||
def get_component_types(self):
|
||||
def get_component_types(self, verilog_output: bool = False):
|
||||
"""Retrieves a list of all the unique types of subcomponents composing the circuit.
|
||||
|
||||
Returning list consists of only the unique types of logic gates, one bit circuits and multi bit circuits.
|
||||
|
||||
Args:
|
||||
verilog_output (bool): Specifies whether the call has been invoked by a verilog output generation method.
|
||||
Returns:
|
||||
list: List of unique component types describing the circuit.
|
||||
"""
|
||||
gate_comps = self.get_unique_types(components=self.get_circuit_gates())
|
||||
gate_comps = self.get_unique_types(components=self.get_circuit_gates(verilog_output))
|
||||
one_bit_comps = self.get_unique_types(
|
||||
components=self.get_one_bit_components())
|
||||
multi_bit_comps = self.get_unique_types(
|
||||
components=self.get_multi_bit_components(), multi_bit=True)
|
||||
multi_bit_comps = self.get_unique_types(components=self.get_multi_bit_components(),
|
||||
multi_bit=True)
|
||||
|
||||
all_components = gate_comps + one_bit_comps + multi_bit_comps
|
||||
return all_components
|
||||
@ -183,39 +268,28 @@ class GeneralCircuit():
|
||||
if wire.is_const():
|
||||
return wire.cgp_const
|
||||
else:
|
||||
return len(self.circuit_wires)+2
|
||||
# [1] is reservation for a constant wire with value 1
|
||||
pos = max([1] + [x[2] for x in self.circuit_wires])
|
||||
return pos + 1
|
||||
|
||||
def get_cgp_wires(self):
|
||||
def get_circuit_wires(self):
|
||||
"""Gets a list of all wires in circuit along with their index position for cgp chromosome generation and stores them inside `self.circuit_wires` list.
|
||||
|
||||
Constant wire with value 0 has constant index of 0.
|
||||
Constant wire with value 1 has constant index of 1.
|
||||
Other wires indexes start counting from 2 and up.
|
||||
"""
|
||||
self.circuit_wires = []
|
||||
circuit_wires_names = []
|
||||
if isinstance(self.a, Bus):
|
||||
[self.circuit_wires.append(
|
||||
(w, f"{w.name}", self.save_wire_id(wire=w))) for w in self.a.bus]
|
||||
[self.circuit_wires.append(
|
||||
(w, f"{w.name}", self.save_wire_id(wire=w))) for w in self.b.bus]
|
||||
[circuit_wires_names.append(w.name) for w in self.a.bus]
|
||||
[circuit_wires_names.append(w.name) for w in self.b.bus]
|
||||
if hasattr(self, 'c'):
|
||||
|
||||
for input in self.inputs:
|
||||
if isinstance(input, Bus):
|
||||
[self.circuit_wires.append(
|
||||
(w, f"{w.name}", self.save_wire_id(wire=w))) for w in self.c.bus]
|
||||
[circuit_wires_names.append(w.name) for w in self.c.bus]
|
||||
else:
|
||||
self.circuit_wires.append(
|
||||
(self.a, f"{self.a.name}", self.save_wire_id(wire=self.a)))
|
||||
self.circuit_wires.append(
|
||||
(self.b, f"{self.b.name}", self.save_wire_id(wire=self.b)))
|
||||
circuit_wires_names.append(self.a.name)
|
||||
circuit_wires_names.append(self.b.name)
|
||||
if hasattr(self, 'c'):
|
||||
(w, f"{w.name}", self.save_wire_id(wire=w))) for w in input.bus]
|
||||
[circuit_wires_names.append(w.name) for w in input.bus]
|
||||
else:
|
||||
self.circuit_wires.append(
|
||||
(self.c, f"{self.c.name}", self.save_wire_id(wire=self.c)))
|
||||
circuit_wires_names.append(self.c.name)
|
||||
(input, f"{input.name}", self.save_wire_id(wire=input)))
|
||||
circuit_wires_names.append(input.name)
|
||||
|
||||
for gate in self.circuit_gates:
|
||||
if gate.a.name not in circuit_wires_names:
|
||||
@ -232,6 +306,7 @@ class GeneralCircuit():
|
||||
self.circuit_wires.append(
|
||||
(gate.out, gate.out.name, self.save_wire_id(wire=gate.out)))
|
||||
circuit_wires_names.append(gate.out.name)
|
||||
|
||||
|
||||
def get_circuit_wire_index(self, wire: Wire):
|
||||
"""Searches for circuit's wire unique index position within the circuit. Used for cgp chromosome generation.
|
||||
@ -276,18 +351,18 @@ class GeneralCircuit():
|
||||
return self.out.return_bus_wires_values_python_flat()
|
||||
|
||||
# Generating flat Python code representation of circuit
|
||||
def get_python_code_flat(self, file_object):
|
||||
|
||||
def get_python_code_flat(self, file_object, retype=True):
|
||||
"""Generates flat Python code representation of corresponding arithmetic circuit.
|
||||
|
||||
Args:
|
||||
file_object (TextIOWrapper): Destination file object where circuit's representation will be written to.
|
||||
retype (bool) specifies if signed output should return int64_t
|
||||
"""
|
||||
file_object.write(self.get_prototype_python())
|
||||
# file_object.write(self.out.get_declaration_python())
|
||||
# file_object.write(self.get_declaration_python_flat()+"\n")
|
||||
file_object.write(self.get_init_python_flat()+"\n")
|
||||
file_object.write(self.get_function_out_python_flat())
|
||||
file_object.write(self.out.return_bus_wires_sign_extend_python_flat())
|
||||
file_object.write(self.out.return_bus_wires_sign_extend_python_flat(retype=True))
|
||||
file_object.write(f" return {self.out.prefix}"+"\n")
|
||||
|
||||
""" C CODE GENERATION """
|
||||
@ -358,8 +433,6 @@ class GeneralCircuit():
|
||||
"""
|
||||
# Retrieve all unique component types composing this circuit and add them kwargs from the parent circuit to allow propagatation of config settings for subcomponents
|
||||
self.component_types = self.get_component_types()
|
||||
for c in self.component_types:
|
||||
c._parent_kwargs = self.kwargs
|
||||
return "".join([c.get_function_block_c() for c in self.component_types])
|
||||
|
||||
def get_function_block_c(self):
|
||||
@ -368,13 +441,7 @@ class GeneralCircuit():
|
||||
Returns:
|
||||
str: Hierarchical C code of multi-bit arithmetic circuit's function block description.
|
||||
"""
|
||||
# Obtain proper circuit name with its bit width
|
||||
circuit_prefix = self.__class__(
|
||||
a=Bus("a"), b=Bus("b")).prefix + str(self.N)
|
||||
print(self._parent_kwargs)
|
||||
circuit_block = self.__class__(a=Bus(N=self.N, prefix="a"), b=Bus(
|
||||
N=self.N, prefix="b"), name=circuit_prefix, **self._parent_kwargs)
|
||||
return f"{circuit_block.get_circuit_c()}\n\n"
|
||||
return f"{self.get_hier_subcomponent_def(parent_kwargs=self.kwargs).get_circuit_c()}\n\n"
|
||||
|
||||
def get_declarations_c_hier(self):
|
||||
"""Generates hierarchical C code declaration of input/output circuit wires.
|
||||
@ -393,8 +460,7 @@ class GeneralCircuit():
|
||||
Returns:
|
||||
str: Hierarchical C code of subcomponent arithmetic circuit's wires declaration.
|
||||
"""
|
||||
return f" {self.c_data_type} {self.a.prefix} = 0;\n" + \
|
||||
f" {self.c_data_type} {self.b.prefix} = 0;\n" + \
|
||||
return "".join([f" {self.c_data_type} {i.prefix} = 0;\n" for i in self.inputs if ((isinstance(i, Wire)) or (not all((w.is_const()) or (w.parent_bus is not None and w.prefix == i.prefix) for w in i.bus)))]) + \
|
||||
f" {self.c_data_type} {self.out.prefix} = 0;\n"
|
||||
|
||||
def get_init_c_hier(self):
|
||||
@ -416,9 +482,12 @@ class GeneralCircuit():
|
||||
str: Hierarchical C code of subcomponent's C function invocation and output assignment.
|
||||
"""
|
||||
# Getting name of circuit type for proper C code generation without affecting actual generated composition
|
||||
circuit_type = self.__class__(a=Bus("a"), b=Bus("b")).prefix + str(self.N)
|
||||
return self.a.return_bus_wires_values_c_hier() + self.b.return_bus_wires_values_c_hier() + \
|
||||
f" {self.out.prefix} = {circuit_type}({self.a.prefix}, {self.b.prefix});\n"
|
||||
init_signature = inspect.signature(self.__class__.__init__)
|
||||
default_circuit_name = init_signature.parameters['name'].default
|
||||
circuit_type = default_circuit_name + "x".join(str(getattr(self, chr(97+i)).N) for i, _ in enumerate(self.inputs))
|
||||
# TODO .. now only works for input buses
|
||||
return "".join(w.return_bus_wires_values_c_hier() for w in self.inputs) + \
|
||||
f" {self.out.prefix} = {circuit_type}({', '.join(w.prefix if isinstance(w, Bus) else w.get_wire_value_c_hier() for w in self.inputs)});\n"
|
||||
|
||||
def get_function_out_c_hier(self):
|
||||
"""Generates hierarchical C code assignment of corresponding arithmetic circuit's output bus wires.
|
||||
@ -455,14 +524,13 @@ class GeneralCircuit():
|
||||
|
||||
""" VERILOG CODE GENERATION """
|
||||
# FLAT VERILOG #
|
||||
|
||||
def get_prototype_v(self):
|
||||
"""Generates Verilog code module header to describe corresponding arithmetic circuit's interface in Verilog code.
|
||||
|
||||
Returns:
|
||||
str: Module's name and parameters in Verilog code.
|
||||
"""
|
||||
return f"module {self.prefix}(" + ",".join(f"input [{x.N-1}:0] {x.prefix}" for x in self.inputs) + f", output [{self.out.N-1}:0] {self.out.prefix});\n"
|
||||
return f"module {self.prefix}(" + ", ".join(f"input [{x.N-1}:0] {x.prefix}" for x in self.inputs) + f", output [{self.out.N-1}:0] {self.out.prefix});\n"
|
||||
|
||||
def get_declaration_v_flat(self):
|
||||
"""Generates flat Verilog code declaration of input/output circuit wires.
|
||||
@ -509,9 +577,7 @@ class GeneralCircuit():
|
||||
str: Hierarchical Verilog code of all subcomponents function blocks description.
|
||||
"""
|
||||
# Retrieve all unique component types composing this circuit and add them kwargs from the parent circuit to allow propagatation of config settings for subcomponents
|
||||
self.component_types = self.get_component_types()
|
||||
for c in self.component_types:
|
||||
c._parent_kwargs = self.kwargs
|
||||
self.component_types = self.get_component_types(verilog_output=True)
|
||||
return "".join([c.get_function_block_v() for c in self.component_types])
|
||||
|
||||
def get_function_block_v(self):
|
||||
@ -521,11 +587,7 @@ class GeneralCircuit():
|
||||
str: Hierarchical Verilog code of multi-bit arithmetic circuit's function block description.
|
||||
"""
|
||||
# Obtain proper circuit name with its bit width
|
||||
circuit_prefix = self.__class__(
|
||||
a=Bus("a"), b=Bus("b")).prefix + str(self.N)
|
||||
circuit_block = self.__class__(a=Bus(N=self.N, prefix="a"), b=Bus(
|
||||
N=self.N, prefix="b"), name=circuit_prefix, **self._parent_kwargs)
|
||||
return f"{circuit_block.get_circuit_v()}\n\n"
|
||||
return f"{self.get_hier_subcomponent_def(parent_kwargs=self.kwargs).get_circuit_v()}\n\n"
|
||||
|
||||
def get_declarations_v_hier(self):
|
||||
"""Generates hierarchical Verilog code declaration of input/output circuit wires.
|
||||
@ -544,9 +606,7 @@ class GeneralCircuit():
|
||||
Returns:
|
||||
str: Hierarchical Verilog code of subcomponent arithmetic circuit's wires declaration.
|
||||
"""
|
||||
return f" wire [{self.a.N-1}:0] {self.a.prefix};\n" + \
|
||||
f" wire [{self.b.N-1}:0] {self.b.prefix};\n" + \
|
||||
f" wire [{self.out.N-1}:0] {self.out.prefix};\n"
|
||||
return "".join(b.get_wire_declaration_v() for b in self.inputs + [self.out] if (b == self.out) or (not all((w.is_const()) or (w.parent_bus is not None and w.prefix == b.prefix) for w in b.bus)))
|
||||
|
||||
def get_init_v_hier(self):
|
||||
"""Generates hierarchical Verilog code initialization and assignment of corresponding arithmetic circuit's input/output wires.
|
||||
@ -567,11 +627,13 @@ class GeneralCircuit():
|
||||
str: Hierarchical Verilog code of subcomponent's module invocation and output assignment.
|
||||
"""
|
||||
# Getting name of circuit type and insitu copying out bus for proper Verilog code generation without affecting actual generated composition
|
||||
circuit_type = self.__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"
|
||||
init_signature = inspect.signature(self.__class__.__init__)
|
||||
default_circuit_name = init_signature.parameters['name'].default
|
||||
circuit_type = default_circuit_name + "x".join(str(getattr(self, chr(97+i)).N) for i, _ in enumerate(self.inputs))
|
||||
circuit_block = self.get_hier_subcomponent_def(parent_kwargs=self.kwargs)
|
||||
# TODO .. now only works for input buses
|
||||
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"
|
||||
|
||||
def get_function_out_v_hier(self):
|
||||
"""Generates hierarchical Verilog code assignment of corresponding arithmetic circuit's output bus wires.
|
||||
@ -620,7 +682,7 @@ class GeneralCircuit():
|
||||
Returns:
|
||||
str: Flat Blif code containing declaration of circuit's wires.
|
||||
"""
|
||||
return f".inputs{''.join([w.get_wire_declaration_blif() for w in self.inputs])}\n" + \
|
||||
return f".inputs {''.join([w.get_wire_declaration_blif() for w in self.inputs])}\n" + \
|
||||
f".outputs{self.out.get_wire_declaration_blif()}\n" + \
|
||||
f".names vdd\n1\n" + \
|
||||
f".names gnd\n0\n"
|
||||
@ -654,6 +716,14 @@ class GeneralCircuit():
|
||||
file_object.write(self.get_function_out_blif())
|
||||
file_object.write(f".end\n")
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# HIERARCHICAL BLIF #
|
||||
def get_invocations_blif_hier(self):
|
||||
"""Generates hierarchical Blif code with invocations of subcomponents function blocks.
|
||||
@ -672,14 +742,27 @@ class GeneralCircuit():
|
||||
str: Hierarchical Blif code of subcomponent's model invocation and output assignment.
|
||||
"""
|
||||
# Getting name of circuit type for proper Blif code generation without affecting actual generated composition
|
||||
circuit_type = self.__class__(a=Bus("a"), b=Bus("b")).prefix + str(self.N)
|
||||
init_signature = inspect.signature(self.__class__.__init__)
|
||||
default_circuit_name = init_signature.parameters['name'].default
|
||||
circuit_type = default_circuit_name + "x".join(str(getattr(self, chr(97+i)).N) for i, _ in enumerate(self.inputs))
|
||||
if self.out.N > 1:
|
||||
return "".join([w.get_wire_assign_blif(output=True) for w in self.inputs]) + \
|
||||
f".subckt {circuit_type}" + \
|
||||
"".join([f" {chr(97+i)}[{b.bus.index(w)}]={b.prefix}[{b.bus.index(w)}]" if b.N > 1 else f" {chr(97+i)}={b.prefix}" for i, b in enumerate(self.inputs) for w in b.bus]) + \
|
||||
"".join([f" {circuit_type}_out[{self.out.bus.index(o)}]={o.name}" for o in self.out.bus if not o.is_const()]) + "\n"
|
||||
else:
|
||||
return "".join([w.get_wire_assign_blif(output=True) for w in self.inputs]) + \
|
||||
f".subckt {circuit_type}" + \
|
||||
"".join([f" {chr(97+i)}[{b.bus.index(w)}]={b.prefix}[{b.bus.index(w)}]" if b.N > 1 else f" {chr(97+i)}={b.prefix}" for i, b in enumerate(self.inputs) for w in b.bus]) + \
|
||||
"".join([f" {circuit_type}_out={o.name}" for o in self.out.bus if not o.is_const()]) + "\n"
|
||||
|
||||
# TODO delete
|
||||
return f"{self.a.get_wire_assign_blif(output=True)}" + \
|
||||
f"{self.b.get_wire_assign_blif(output=True)}" + \
|
||||
f".subckt {circuit_type}" + \
|
||||
"".join([f" a[{self.a.bus.index(w)}]={self.a.prefix}[{self.a.bus.index(w)}]" for w in self.a.bus]) + \
|
||||
"".join([f" b[{self.b.bus.index(w)}]={self.b.prefix}[{self.b.bus.index(w)}]" for w in self.b.bus]) + \
|
||||
"".join(
|
||||
[f" {circuit_type}_out[{self.out.bus.index(o)}]={o.name}" for o in self.out.bus]) + "\n"
|
||||
"".join([f" {circuit_type}_out[{self.out.bus.index(o)}]={o.name}" for o in self.out.bus]) + "\n"
|
||||
|
||||
def get_circuit_blif(self):
|
||||
"""Generates hierarchical Blif code subcomponent's function block.
|
||||
@ -702,8 +785,6 @@ class GeneralCircuit():
|
||||
# Retrieve all unique component types composing this circuit and add them kwargs from the parent circuit to allow propagatation of config settings for subcomponents
|
||||
# (iterating backwards as opposed to other representations so the top modul is always above its subcomponents)
|
||||
self.component_types = self.get_component_types()
|
||||
for c in self.component_types:
|
||||
c._parent_kwargs = self.kwargs
|
||||
return "\n".join([c.get_function_block_blif() for c in self.component_types[::-1]])
|
||||
|
||||
def get_function_block_blif(self):
|
||||
@ -713,11 +794,7 @@ class GeneralCircuit():
|
||||
str: Hierarchical Blif code of multi-bit arithmetic circuit's function block description.
|
||||
"""
|
||||
# Obtain proper circuit name with its bit width
|
||||
circuit_prefix = self.__class__(
|
||||
a=Bus("a"), b=Bus("b")).prefix + str(self.N)
|
||||
circuit_block = self.__class__(a=Bus(N=self.N, prefix="a"), b=Bus(
|
||||
N=self.N, prefix="b"), name=circuit_prefix, **self._parent_kwargs)
|
||||
return f"{circuit_block.get_circuit_blif()}"
|
||||
return f"{self.get_hier_subcomponent_def(parent_kwargs=self.kwargs).get_circuit_blif()}"
|
||||
|
||||
# Generating hierarchical BLIF code representation of circuit
|
||||
def get_blif_code_hier(self, file_object):
|
||||
@ -741,7 +818,7 @@ class GeneralCircuit():
|
||||
Returns:
|
||||
str: CGP chromosome parameters of described arithmetic circuit.
|
||||
"""
|
||||
self.circuit_gates = self.get_circuit_gates()
|
||||
# self.circuit_gates = self.get_circuit_gates() TODO delete
|
||||
return f"{{{sum(input_bus.N for input_bus in self.inputs)},{self.out.N},1,{len(self.circuit_gates)},2,1,0}}"
|
||||
|
||||
def get_triplets_cgp(self):
|
||||
@ -757,7 +834,7 @@ class GeneralCircuit():
|
||||
Returns:
|
||||
str: List of triplets each describing logic function of corresponding two input logic gate and as a whole describe the arithmetic circuit.
|
||||
"""
|
||||
self.get_cgp_wires()
|
||||
self.get_circuit_wires()
|
||||
return "".join([g.get_triplet_cgp(a_id=self.get_circuit_wire_index(g.a), out_id=self.get_circuit_wire_index(g.out)) if isinstance(g, OneInputLogicGate) else
|
||||
g.get_triplet_cgp(a_id=self.get_circuit_wire_index(g.a), b_id=self.get_circuit_wire_index(g.b), out_id=self.get_circuit_wire_index(g.out)) for g in self.circuit_gates])
|
||||
|
||||
|
@ -1,36 +1,24 @@
|
||||
from .arithmetic_circuit import (
|
||||
ArithmeticCircuit
|
||||
from .general_circuit import (
|
||||
GeneralCircuit
|
||||
)
|
||||
|
||||
from ariths_gen.one_bit_circuits.logic_gates import (
|
||||
AndGate,
|
||||
NandGate,
|
||||
OrGate,
|
||||
NorGate,
|
||||
XorGate,
|
||||
XnorGate,
|
||||
NotGate
|
||||
)
|
||||
|
||||
from ariths_gen.wire_components import (
|
||||
Wire,
|
||||
ConstantWireValue0,
|
||||
ConstantWireValue1,
|
||||
Bus
|
||||
)
|
||||
|
||||
from ariths_gen.one_bit_circuits.logic_gates import (
|
||||
AndGate,
|
||||
NandGate
|
||||
)
|
||||
import math
|
||||
|
||||
|
||||
class MultiplierCircuit(ArithmeticCircuit):
|
||||
"""Class represents a general multiplier circuit derived from `ArithmeticCircuit` class.
|
||||
class MultiplierCircuit(GeneralCircuit):
|
||||
"""Class represents a general multiplier circuit derived from `GeneralCircuit` class.
|
||||
|
||||
The __init__ method calls parent class __init__ method which fills some mandatory attributes concerning arithmetic circuit
|
||||
The __init__ method calls parent class __init__ method which fills some mandatory attributes concerning general circuit
|
||||
that are later used for generation into various representations.
|
||||
"""
|
||||
|
||||
def __init__(self, a, b, prefix: str, name: str, out_N: int, **kwargs):
|
||||
super().__init__(a=a, b=b, prefix=prefix, name=name, out_N=out_N, **kwargs)
|
||||
def __init__(self, prefix: str, name: str, out_N: int, inner_component: bool = False, inputs: list = [], one_bit_circuit: bool = False, signed: bool = False, outname: str = "", **kwargs):
|
||||
super().__init__(prefix=prefix, name=name, out_N=out_N, inner_component=inner_component, inputs=inputs, one_bit_circuit=one_bit_circuit, signed=signed, outname=outname, **kwargs)
|
||||
|
||||
# Array/approximate multipliers
|
||||
def get_previous_partial_product(self, a_index: int, b_index: int, mult_type=""):
|
||||
@ -266,7 +254,7 @@ class MultiplierCircuit(ArithmeticCircuit):
|
||||
else:
|
||||
return self.columns[column][bit+1]
|
||||
|
||||
def update_column_wires(self, curr_column: int, adder: ArithmeticCircuit, next_column: int = 0):
|
||||
def update_column_wires(self, curr_column: int, adder: GeneralCircuit, next_column: int = 0):
|
||||
"""Provides bit height reduction of the chosen column.
|
||||
|
||||
Inserts chosen column's top bits into an `adder` circuit to reduce its bit height.
|
||||
@ -274,7 +262,7 @@ class MultiplierCircuit(ArithmeticCircuit):
|
||||
|
||||
Args:
|
||||
curr_column (int): Current pp column index.
|
||||
adder (ArithmeticCircuit): Two/three input one bit adder.
|
||||
adder (GeneralCircuit): Two/three input one bit adder.
|
||||
next_column (int, optional): Subsequent pp column index. Defaults to 0.
|
||||
"""
|
||||
if hasattr(adder, "c"):
|
||||
|
@ -1,5 +1,4 @@
|
||||
from ariths_gen.wire_components import (
|
||||
Wire,
|
||||
ConstantWireValue0,
|
||||
ConstantWireValue1,
|
||||
Bus
|
||||
@ -7,14 +6,6 @@ from ariths_gen.wire_components import (
|
||||
from ariths_gen.core.arithmetic_circuits import (
|
||||
GeneralCircuit
|
||||
)
|
||||
|
||||
from ariths_gen.core.logic_gate_circuits import (
|
||||
MultipleInputLogicGate
|
||||
)
|
||||
from ariths_gen.one_bit_circuits.one_bit_components import (
|
||||
HalfAdder,
|
||||
FullAdder
|
||||
)
|
||||
from ariths_gen.one_bit_circuits.logic_gates import (
|
||||
AndGate,
|
||||
NandGate,
|
||||
@ -30,18 +21,37 @@ import re
|
||||
class UnsignedCGPCircuit(GeneralCircuit):
|
||||
"""Unsigned circuit variant that loads CGP code and is able to export it to C/verilog/Blif/CGP."""
|
||||
|
||||
def __init__(self, code: str, input_widths: list, prefix: str = "", name: str = "cgp", **kwargs):
|
||||
def __init__(self, code: str = "", input_widths: list = None, inputs: list = None, prefix: str = "", name: str = "cgp", **kwargs):
|
||||
cgp_prefix, cgp_core, cgp_outputs = re.match(
|
||||
r"{(.*)}(.*)\(([^()]+)\)", code).groups()
|
||||
|
||||
c_in, c_out, c_rows, c_cols, c_ni, c_no, c_lback = map(
|
||||
int, cgp_prefix.split(","))
|
||||
|
||||
assert inputs is not None or input_widths is not None, "Either inputs or input_widths must be provided"
|
||||
|
||||
assert sum(
|
||||
input_widths) == c_in, f"CGP input width {c_in} doesn't match input_widths {input_widths}"
|
||||
if inputs:
|
||||
assert input_widths is None, "Only one of inputs or input_widths must be provided"
|
||||
|
||||
inputs = [Bus(N=bw, prefix=f"input_{chr(i)}")
|
||||
for i, bw in enumerate(input_widths, start=0x61)]
|
||||
input_widths =[i.N for i in inputs]
|
||||
assert sum(input_widths) == c_in, f"CGP input width {c_in} doesn't match inputs {inputs_widths}"
|
||||
|
||||
|
||||
|
||||
else:
|
||||
|
||||
assert sum(
|
||||
input_widths) == c_in, f"CGP input width {c_in} doesn't match input_widths {input_widths}"
|
||||
|
||||
inputs = [Bus(N=bw, prefix=f"input_{chr(i)}")
|
||||
for i, bw in enumerate(input_widths, start=0x61)]
|
||||
|
||||
# Assign each Bus object in self.inputs to a named attribute of self
|
||||
for bus in inputs:
|
||||
# Here, bus.prefix is 'input_a', 'input_b', etc.
|
||||
# We strip 'input_' and use the remaining part (e.g., 'a', 'b') to create the attribute name
|
||||
attr_name = bus.prefix.replace('input_', '')
|
||||
setattr(self, attr_name, bus)
|
||||
|
||||
# Adding values to the list
|
||||
self.vals = {}
|
||||
@ -53,6 +63,10 @@ class UnsignedCGPCircuit(GeneralCircuit):
|
||||
j += 1
|
||||
|
||||
super().__init__(prefix=prefix, name=name, out_N=c_out, inputs=inputs, **kwargs)
|
||||
|
||||
if not code:
|
||||
return # only for getting the name
|
||||
|
||||
cgp_core = cgp_core.split(")(")
|
||||
|
||||
i = 0
|
||||
@ -60,8 +74,12 @@ class UnsignedCGPCircuit(GeneralCircuit):
|
||||
i, in_a, in_b, fn = map(int, re.match(
|
||||
r"\(?\[(\d+)\](\d+),(\d+),(\d+)\)?", definition).groups())
|
||||
|
||||
assert in_a < i
|
||||
assert in_b < i
|
||||
if in_a > i or in_b > i:
|
||||
raise ValueError(f"Backward connection in CGP gene \"{definition}\", maxid = {i}")
|
||||
|
||||
if in_a == i or in_b == i:
|
||||
raise ValueError(f"Loop connection in CGP gene: \"{definition}\", maxid = {i}")
|
||||
|
||||
comp_set = dict(prefix=f"{self.prefix}_core_{i:03d}", parent_component=self)
|
||||
|
||||
a, b = self._get_wire(in_a), self._get_wire(in_b)
|
||||
@ -91,6 +109,9 @@ class UnsignedCGPCircuit(GeneralCircuit):
|
||||
|
||||
# Output connection
|
||||
for i, o in enumerate(map(int, cgp_outputs.split(","))):
|
||||
if o >= c_in + c_rows * c_cols + 2:
|
||||
raise ValueError(
|
||||
f"Output {i} is connected to wire {o} which is not in the range of CGP wires ({c_in + c_rows * c_cols + 2})")
|
||||
w = self._get_wire(o)
|
||||
self.out.connect(i, w)
|
||||
|
||||
@ -109,11 +130,17 @@ class UnsignedCGPCircuit(GeneralCircuit):
|
||||
return ConstantWireValue0()
|
||||
if i == 1:
|
||||
return ConstantWireValue1()
|
||||
return self.vals[i]
|
||||
try:
|
||||
return self.vals[i]
|
||||
except KeyError:
|
||||
|
||||
raise KeyError(f"Key {i} not found in " + ", ".join(
|
||||
[f"{i}: {v}" for i, v in self.vals.items()]
|
||||
))
|
||||
|
||||
|
||||
class SignedCGPCircuit(UnsignedCGPCircuit):
|
||||
"""Signed circuit variant that loads CGP code and is able to export it to C/verilog/Blif/CGP."""
|
||||
def __init__(self, code: str, input_widths: list, prefix: str = "", name: str = "cgp", **kwargs):
|
||||
super().__init__(code=code, input_widths=input_widths, prefix=prefix, name=name, signed=True, **kwargs)
|
||||
def __init__(self, code: str, input_widths: list = None, inputs: list=None, prefix: str = "", name: str = "cgp", **kwargs):
|
||||
super().__init__(code=code, input_widths=input_widths, inputs=inputs, prefix=prefix, name=name, signed=True, **kwargs)
|
||||
self.c_data_type = "int64_t"
|
||||
|
@ -1,4 +1,4 @@
|
||||
from ariths_gen.wire_components.wires import Wire, ConstantWireValue0, ConstantWireValue1
|
||||
from ariths_gen.wire_components.wires import Wire
|
||||
from ariths_gen.wire_components.buses import Bus
|
||||
import math
|
||||
|
||||
@ -33,14 +33,16 @@ class MultipleInputLogicGate():
|
||||
prefix (str, optional): Prefix used to name inner composite logic gates. Defaults to "".
|
||||
"""
|
||||
def __init__(self, a: Bus, two_input_gate_cls, parent_component: object, prefix: str = ""):
|
||||
i = 0
|
||||
while a.N != 1:
|
||||
N = math.floor(a.N/2)
|
||||
out_wires = []
|
||||
# Creation of composite two input logic gates from bus `a`'s bit pairs and addition of generated blocks outputs for next iteration
|
||||
for bus_index in range(0, N):
|
||||
gate = two_input_gate_cls(a=a.get_wire(bus_index), b=a.get_wire(bus_index+N), prefix=prefix+str(parent_component.get_instance_num(cls=two_input_gate_cls, count_disabled_gates=False)), parent_component=parent_component)
|
||||
gate = two_input_gate_cls(a=a.get_wire(bus_index), b=a.get_wire(bus_index+N), prefix=prefix+ "_" + str(i) + "_" + str(parent_component.get_instance_num(cls=two_input_gate_cls, count_disabled_gates=False)), parent_component=parent_component)
|
||||
parent_component.add_component(gate)
|
||||
out_wires.append(gate.out)
|
||||
i += 1
|
||||
|
||||
# In case bus `a` has odd number of wires
|
||||
if a.N % 2 != 0:
|
||||
@ -110,6 +112,8 @@ class TwoInputLogicGate():
|
||||
Returns:
|
||||
str: C code description of logic gate's Boolean function (with bitwise shifted inputs).
|
||||
"""
|
||||
if self.out.is_const():
|
||||
return self.out.get_wire_value_c_flat()
|
||||
return f"{self.a.get_wire_value_c_flat()} {self.operator} {self.b.get_wire_value_c_flat()}"
|
||||
|
||||
def get_declaration_c_flat(self):
|
||||
@ -206,6 +210,8 @@ class TwoInputLogicGate():
|
||||
# No gate logic is generated if one of the inputs is a wire with constant value.
|
||||
# I.e. either the constant or the second input wire is propagated to the output for the corresponding logic gate's logic function.
|
||||
if self.disable_generation:
|
||||
#return f" {self.out.prefix} = {self.get_function_c()} # DD {self.prefix} \n"
|
||||
|
||||
return ""
|
||||
else:
|
||||
return f" {self.out.prefix} = {self.get_function_c()}\n"
|
||||
@ -338,7 +344,7 @@ class TwoInputLogicGate():
|
||||
Returns:
|
||||
str: Blif logic gate's wires declaration.
|
||||
"""
|
||||
return f".inputs {self.a.get_declaration_blif()} {self.b.get_declaration_blif()}\n" + \
|
||||
return f".inputs {self.a.get_wire_declaration_blif()}{self.b.get_wire_declaration_blif()}\n" + \
|
||||
f".outputs" + \
|
||||
"".join([f" {self.out.name}\n" if self.disable_generation is False else f" {self.out.name}_out\n" for _ in range(1)]) + \
|
||||
f".names vdd\n1\n" + \
|
||||
@ -681,7 +687,7 @@ class OneInputLogicGate(TwoInputLogicGate):
|
||||
Returns:
|
||||
str: Blif logic gate's wires declaration.
|
||||
"""
|
||||
return f".inputs {self.a.get_declaration_blif()}\n" + \
|
||||
return f".inputs {self.a.get_wire_declaration_blif()}\n" + \
|
||||
f".outputs" + \
|
||||
"".join([f" {self.out.name}\n" if self.disable_generation is False else f" {self.out.name}_out\n" for _ in range(1)]) + \
|
||||
f".names vdd\n1\n" + \
|
||||
|
@ -1,11 +1,13 @@
|
||||
from .two_input_one_bit_circuit import (
|
||||
TwoInputOneBitCircuit
|
||||
)
|
||||
|
||||
from ariths_gen.core.arithmetic_circuits import (
|
||||
GeneralCircuit
|
||||
)
|
||||
from ariths_gen.wire_components.wires import Wire
|
||||
|
||||
|
||||
class FourInputOneBitCircuit(TwoInputOneBitCircuit):
|
||||
class FourInputOneBitCircuit(TwoInputOneBitCircuit, GeneralCircuit):
|
||||
"""Class represents a general four input one bit circuit and implements their generation to various representations. It is derived from `TwoInputOneBitCircuit` class.
|
||||
|
||||
Description of the __init__ method.
|
||||
@ -15,16 +17,12 @@ class FourInputOneBitCircuit(TwoInputOneBitCircuit):
|
||||
b (Wire): Second input wire.
|
||||
c (Wire): Third input wire.
|
||||
d (Wire): Fourth input wire.
|
||||
prefix (str, optional): Prefix name of circuit. Defaults to "four_input_one_bit_circuit".
|
||||
prefix (str, optional): Prefix name of circuit. Defaults to "".
|
||||
name (str, optional): Name of circuit. Defaults to "four_input_one_bit_circuit".
|
||||
"""
|
||||
def __init__(self, a: Wire = Wire(name="a"), b: Wire = Wire(name="b"), c: Wire = Wire(name="c"), d: Wire = Wire(name="d"), prefix: str = "four_input_one_bit_circuit"):
|
||||
super().__init__()
|
||||
def __init__(self, a: Wire = Wire(name="a"), b: Wire = Wire(name="b"), c: Wire = Wire(name="c"), d: Wire = Wire(name="d"), prefix: str = "", name: str = "four_input_one_bit_circuit"):
|
||||
GeneralCircuit.__init__(self, inputs=[a, b, c, d], prefix=prefix, name=name, out_N=1)
|
||||
self.c_data_type = "uint8_t"
|
||||
self.prefix = prefix
|
||||
self.a = a
|
||||
self.b = b
|
||||
self.c = c
|
||||
self.d = d
|
||||
|
||||
""" C CODE GENERATION """
|
||||
# FLAT C #
|
||||
@ -39,7 +37,7 @@ class FourInputOneBitCircuit(TwoInputOneBitCircuit):
|
||||
|
||||
# HIERARCHICAL C #
|
||||
# Subcomponent generation (four inputs)
|
||||
def get_out_invocation_c(self, *args, **kwargs):
|
||||
def get_out_invocation_c(self):
|
||||
"""Generates hierarchical C code invocation of corresponding four input one bit circuit's generated function block.
|
||||
|
||||
Assigns output values from invocation of the corresponding function block into inner wires present inside the upper
|
||||
@ -120,7 +118,7 @@ class FourInputOneBitCircuit(TwoInputOneBitCircuit):
|
||||
f"" for c in self.components])
|
||||
|
||||
# Subcomponent generation
|
||||
def get_out_invocation_v(self, *args, **kwargs):
|
||||
def get_out_invocation_v(self):
|
||||
"""Generates hierarchical Verilog code invocation of corresponding four input one bit circuit's generated function block.
|
||||
|
||||
Assigns output values from invocation of the corresponding function block into inner wires present inside the upper
|
||||
@ -146,7 +144,7 @@ class FourInputOneBitCircuit(TwoInputOneBitCircuit):
|
||||
"""
|
||||
unique_out_wires = []
|
||||
[unique_out_wires.append(o.name+"_outid"+str(self.out.bus.index(o))) if o.is_const() or o.name in [self.a.name, self.b.name, self.c.name, self.d.name] else unique_out_wires.append(o.name) for o in self.out.bus]
|
||||
return f".inputs {self.a.get_declaration_blif()} {self.b.get_declaration_blif()} {self.c.get_declaration_blif()} {self.d.get_declaration_blif()}\n" + \
|
||||
return f".inputs {self.a.get_wire_declaration_blif()}{self.b.get_wire_declaration_blif()}{self.c.get_wire_declaration_blif()}{self.d.get_wire_declaration_blif()}\n" + \
|
||||
f".outputs" + \
|
||||
"".join([f" {o}" for o in unique_out_wires]) + "\n" + \
|
||||
f".names vdd\n1\n" + \
|
||||
@ -190,7 +188,7 @@ class FourInputOneBitCircuit(TwoInputOneBitCircuit):
|
||||
c.out.get_assign_blif(prefix=f"{unique_out_wires.pop(unique_out_wires.index(c.out.name+'_outid'+str(c.outid)))}", output=True) if f"{c.out.name+'_outid'+str(c.outid)}" in unique_out_wires else
|
||||
"" for c in self.components])
|
||||
|
||||
def get_invocation_blif_hier(self, *args, **kwargs):
|
||||
def get_invocation_blif_hier(self):
|
||||
"""Generates hierarchical Blif code invocation of corresponding four input one bit circuit's generated function block.
|
||||
|
||||
Returns:
|
||||
|
@ -1,11 +1,13 @@
|
||||
from .two_input_one_bit_circuit import (
|
||||
TwoInputOneBitCircuit
|
||||
)
|
||||
|
||||
from ariths_gen.core.arithmetic_circuits import (
|
||||
GeneralCircuit
|
||||
)
|
||||
from ariths_gen.wire_components.wires import Wire
|
||||
|
||||
|
||||
class ThreeInputOneBitCircuit(TwoInputOneBitCircuit):
|
||||
class ThreeInputOneBitCircuit(TwoInputOneBitCircuit, GeneralCircuit):
|
||||
"""Class represents a general three input one bit circuit and implements their generation to various representations. It is derived from `TwoInputOneBitCircuit` class.
|
||||
|
||||
Description of the __init__ method.
|
||||
@ -14,15 +16,12 @@ class ThreeInputOneBitCircuit(TwoInputOneBitCircuit):
|
||||
a (Wire): First input wire.
|
||||
b (Wire): Second input wire.
|
||||
c (Wire): Third input wire.
|
||||
prefix (str, optional): Prefix name of circuit. Defaults to "three_input_one_bit_circuit".
|
||||
prefix (str, optional): Prefix name of circuit. Defaults to "".
|
||||
name (str, optional): Name of circuit. Defaults to "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 = "three_input_one_bit_circuit"):
|
||||
super().__init__()
|
||||
def __init__(self, a: Wire = Wire(name="a"), b: Wire = Wire(name="b"), c: Wire = Wire(name="cin"), prefix: str = "", name: str = "three_input_one_bit_circuit"):
|
||||
GeneralCircuit.__init__(self, inputs=[a, b, c], prefix=prefix, name=name, out_N=1)
|
||||
self.c_data_type = "uint8_t"
|
||||
self.prefix = prefix
|
||||
self.a = a
|
||||
self.b = b
|
||||
self.c = c
|
||||
|
||||
""" C CODE GENERATION """
|
||||
# FLAT C #
|
||||
@ -37,7 +36,7 @@ class ThreeInputOneBitCircuit(TwoInputOneBitCircuit):
|
||||
|
||||
# HIERARCHICAL C #
|
||||
# Subcomponent generation (three inputs)
|
||||
def get_out_invocation_c(self, *args, **kwargs):
|
||||
def get_out_invocation_c(self):
|
||||
"""Generates hierarchical C code invocation of corresponding three input one bit circuit's generated function block.
|
||||
|
||||
Assigns output values from invocation of the corresponding function block into inner wires present inside the upper
|
||||
@ -118,7 +117,7 @@ class ThreeInputOneBitCircuit(TwoInputOneBitCircuit):
|
||||
f"" for c in self.components])
|
||||
|
||||
# Subcomponent generation
|
||||
def get_out_invocation_v(self, *args, **kwargs):
|
||||
def get_out_invocation_v(self):
|
||||
"""Generates hierarchical Verilog code invocation of corresponding three input one bit circuit's generated function block.
|
||||
|
||||
Assigns output values from invocation of the corresponding function block into inner wires present inside the upper
|
||||
@ -144,7 +143,7 @@ class ThreeInputOneBitCircuit(TwoInputOneBitCircuit):
|
||||
"""
|
||||
unique_out_wires = []
|
||||
[unique_out_wires.append(o.name+"_outid"+str(self.out.bus.index(o))) if o.is_const() or o.name in [self.a.name, self.b.name, self.c.name] else unique_out_wires.append(o.name) for o in self.out.bus]
|
||||
return f".inputs {self.a.get_declaration_blif()} {self.b.get_declaration_blif()} {self.c.get_declaration_blif()}\n" + \
|
||||
return f".inputs {self.a.get_wire_declaration_blif()}{self.b.get_wire_declaration_blif()}{self.c.get_wire_declaration_blif()}\n" + \
|
||||
f".outputs" + \
|
||||
"".join([f" {o}" for o in unique_out_wires]) + "\n" + \
|
||||
f".names vdd\n1\n" + \
|
||||
@ -188,7 +187,7 @@ class ThreeInputOneBitCircuit(TwoInputOneBitCircuit):
|
||||
c.out.get_assign_blif(prefix=f"{unique_out_wires.pop(unique_out_wires.index(c.out.name+'_outid'+str(c.outid)))}", output=True) if f"{c.out.name+'_outid'+str(c.outid)}" in unique_out_wires else
|
||||
"" for c in self.components])
|
||||
|
||||
def get_invocation_blif_hier(self, *args, **kwargs):
|
||||
def get_invocation_blif_hier(self):
|
||||
"""Generates hierarchical Blif code invocation of corresponding three input one bit circuit's generated function block.
|
||||
|
||||
Returns:
|
||||
|
@ -1,11 +1,11 @@
|
||||
from ariths_gen.core.arithmetic_circuits import (
|
||||
ArithmeticCircuit
|
||||
GeneralCircuit
|
||||
)
|
||||
|
||||
from ariths_gen.wire_components.wires import Wire
|
||||
|
||||
|
||||
class TwoInputOneBitCircuit(ArithmeticCircuit):
|
||||
class TwoInputOneBitCircuit(GeneralCircuit):
|
||||
"""Class represents a general two input one bit circuit and implements their generation to various representations. It is derived from `ArithmeticCircuit` class.
|
||||
|
||||
Description of the __init__ method.
|
||||
@ -13,14 +13,12 @@ class TwoInputOneBitCircuit(ArithmeticCircuit):
|
||||
Args:
|
||||
a (Wire): First input wire.
|
||||
b (Wire): Second input wire.
|
||||
prefix (str, optional): Prefix name of circuit. Defaults to "two_input_one_bit_circuit".
|
||||
prefix (str, optional): Prefix name of circuit. Defaults to "".
|
||||
name (str, optional): Name of circuit. Defaults to "two_input_one_bit_circuit".
|
||||
"""
|
||||
def __init__(self, a: Wire = Wire(name="a"), b: Wire = Wire(name="b"), prefix: str = "two_input_one_bit_circuit"):
|
||||
super().__init__(a=a, b=b, prefix=prefix, name="", out_N=1, one_bit_circuit=True)
|
||||
def __init__(self, a: Wire = Wire(name="a"), b: Wire = Wire(name="b"), prefix: str = "", name: str = "two_input_one_bit_circuit"):
|
||||
super().__init__(inputs=[a, b], prefix=prefix, name=name, out_N=1, one_bit_circuit=True)
|
||||
self.c_data_type = "uint8_t"
|
||||
self.prefix = prefix
|
||||
self.a = a
|
||||
self.b = b
|
||||
|
||||
""" C CODE GENERATION """
|
||||
# FLAT C #
|
||||
@ -53,7 +51,7 @@ class TwoInputOneBitCircuit(ArithmeticCircuit):
|
||||
adder_block = self.__class__()
|
||||
return f"{adder_block.get_circuit_c()}\n\n"
|
||||
|
||||
def get_out_invocation_c(self, *args, **kwargs):
|
||||
def get_out_invocation_c(self):
|
||||
"""Generates hierarchical C code invocation of corresponding two input one bit circuit's generated function block.
|
||||
|
||||
Assigns output values from invocation of the corresponding function block into inner wires present inside the upper
|
||||
@ -168,7 +166,7 @@ class TwoInputOneBitCircuit(ArithmeticCircuit):
|
||||
adder_block = self.__class__()
|
||||
return f"{adder_block.get_circuit_v()}\n\n"
|
||||
|
||||
def get_out_invocation_v(self, *args, **kwargs):
|
||||
def get_out_invocation_v(self):
|
||||
"""Generates hierarchical Verilog code invocation of corresponding two input one bit circuit's generated function block.
|
||||
|
||||
Assigns output values from invocation of the corresponding function block into inner wires present inside the upper
|
||||
@ -253,7 +251,7 @@ class TwoInputOneBitCircuit(ArithmeticCircuit):
|
||||
"""
|
||||
unique_out_wires = []
|
||||
[unique_out_wires.append(o.name+"_outid"+str(self.out.bus.index(o))) if o.is_const() or o.name in [self.a.name, self.b.name] else unique_out_wires.append(o.name) for o in self.out.bus]
|
||||
return f".inputs {self.a.get_declaration_blif()} {self.b.get_declaration_blif()}\n" + \
|
||||
return f".inputs {self.a.get_wire_declaration_blif()}{self.b.get_wire_declaration_blif()}\n" + \
|
||||
f".outputs" + \
|
||||
"".join([f" {o}" for o in unique_out_wires]) + "\n" + \
|
||||
f".names vdd\n1\n" + \
|
||||
@ -329,7 +327,7 @@ class TwoInputOneBitCircuit(ArithmeticCircuit):
|
||||
c.out.get_assign_blif(prefix=f"{unique_out_wires.pop(unique_out_wires.index(c.out.name+'_outid'+str(c.outid)))}", output=True) if f"{c.out.name+'_outid'+str(c.outid)}" in unique_out_wires else
|
||||
"" for c in self.components])
|
||||
|
||||
def get_invocation_blif_hier(self, *args, **kwargs):
|
||||
def get_invocation_blif_hier(self):
|
||||
"""Generates hierarchical Blif code invocation of corresponding two input one bit circuit's generated function block.
|
||||
|
||||
Returns:
|
||||
|
@ -1,41 +1,22 @@
|
||||
from ariths_gen.wire_components import (
|
||||
Wire,
|
||||
ConstantWireValue0,
|
||||
ConstantWireValue1,
|
||||
Bus
|
||||
)
|
||||
from ariths_gen.core.arithmetic_circuits import (
|
||||
ArithmeticCircuit,
|
||||
MultiplierCircuit
|
||||
GeneralCircuit
|
||||
)
|
||||
from ariths_gen.one_bit_circuits.one_bit_components import (
|
||||
HalfAdder,
|
||||
FullAdder,
|
||||
PGSumLogic,
|
||||
GreyCell,
|
||||
BlackCell
|
||||
)
|
||||
from ariths_gen.one_bit_circuits.logic_gates import (
|
||||
AndGate,
|
||||
NandGate,
|
||||
OrGate,
|
||||
NorGate,
|
||||
XorGate,
|
||||
XnorGate,
|
||||
NotGate
|
||||
)
|
||||
from ariths_gen.multi_bit_circuits.adders import (
|
||||
UnsignedCarryLookaheadAdder,
|
||||
UnsignedPGRippleCarryAdder,
|
||||
UnsignedRippleCarryAdder,
|
||||
SignedCarryLookaheadAdder,
|
||||
SignedPGRippleCarryAdder,
|
||||
SignedRippleCarryAdder
|
||||
XorGate
|
||||
)
|
||||
import math
|
||||
|
||||
|
||||
class UnsignedBrentKungAdder(ArithmeticCircuit):
|
||||
class UnsignedBrentKungAdder(GeneralCircuit):
|
||||
"""Class representing unsigned Brent-Kung adder (using valency-2 logic gates).
|
||||
|
||||
The Brent-Kung adder belongs to a type of tree (parallel-prefix) adders.
|
||||
@ -87,7 +68,7 @@ class UnsignedBrentKungAdder(ArithmeticCircuit):
|
||||
"""
|
||||
def __init__(self, a: Bus, b: Bus, prefix: str = "", name: str = "u_bka", **kwargs):
|
||||
self.N = max(a.N, b.N)
|
||||
super().__init__(a=a, b=b, prefix=prefix, name=name, out_N=self.N+1, **kwargs)
|
||||
super().__init__(inputs=[a, b], prefix=prefix, name=name, out_N=self.N+1, **kwargs)
|
||||
|
||||
# Bus sign extension in case buses have different lengths
|
||||
self.a.bus_extend(N=self.N, prefix=a.prefix)
|
||||
@ -155,7 +136,7 @@ class UnsignedBrentKungAdder(ArithmeticCircuit):
|
||||
self.generate_sig[i_wire+1].append(self.get_previous_component().get_generate_wire())
|
||||
|
||||
|
||||
class SignedBrentKungAdder(UnsignedBrentKungAdder, ArithmeticCircuit):
|
||||
class SignedBrentKungAdder(UnsignedBrentKungAdder, GeneralCircuit):
|
||||
"""Class representing signed Brent-Kung adder (using valency-2 logic gates).
|
||||
|
||||
The Brent-Kung adder belongs to a type of tree (parallel-prefix) adders.
|
||||
|
@ -1,13 +1,9 @@
|
||||
from ariths_gen.wire_components import (
|
||||
Wire,
|
||||
ConstantWireValue0,
|
||||
ConstantWireValue1,
|
||||
Bus,
|
||||
wires
|
||||
Bus
|
||||
)
|
||||
from ariths_gen.core.arithmetic_circuits import (
|
||||
ArithmeticCircuit,
|
||||
MultiplierCircuit
|
||||
GeneralCircuit
|
||||
)
|
||||
from ariths_gen.core.logic_gate_circuits import (
|
||||
MultipleInputLogicGate
|
||||
@ -15,21 +11,16 @@ from ariths_gen.core.logic_gate_circuits import (
|
||||
from ariths_gen.one_bit_circuits.one_bit_components import (
|
||||
HalfAdder,
|
||||
FullAdder,
|
||||
FullAdderP,
|
||||
TwoOneMultiplexer
|
||||
FullAdderP
|
||||
)
|
||||
from ariths_gen.one_bit_circuits.logic_gates import (
|
||||
AndGate,
|
||||
NandGate,
|
||||
OrGate,
|
||||
NorGate,
|
||||
XorGate,
|
||||
XnorGate,
|
||||
NotGate
|
||||
XorGate
|
||||
)
|
||||
|
||||
|
||||
class UnsignedCarryIncrementAdder(ArithmeticCircuit):
|
||||
class UnsignedCarryIncrementAdder(GeneralCircuit):
|
||||
"""Class representing unsigned carry increment adder.
|
||||
|
||||
Carry increment adder represents a modified carry select adder that achieves about the same critical delay
|
||||
@ -71,7 +62,7 @@ class UnsignedCarryIncrementAdder(ArithmeticCircuit):
|
||||
"""
|
||||
def __init__(self, a: Bus, b: Bus, increment_block_size: int = 4, prefix: str = "", name: str = "u_cia", **kwargs):
|
||||
self.N = max(a.N, b.N)
|
||||
super().__init__(a=a, b=b, prefix=prefix, name=name, out_N=self.N+1, **kwargs)
|
||||
super().__init__(inputs=[a, b], prefix=prefix, name=name, out_N=self.N+1, **kwargs)
|
||||
|
||||
# Bus sign extension in case buses have different lengths
|
||||
self.a.bus_extend(N=self.N, prefix=a.prefix)
|
||||
@ -115,7 +106,7 @@ class UnsignedCarryIncrementAdder(ArithmeticCircuit):
|
||||
self.out.connect(self.N, cin)
|
||||
|
||||
|
||||
class SignedCarryIncrementAdder(UnsignedCarryIncrementAdder, ArithmeticCircuit):
|
||||
class SignedCarryIncrementAdder(UnsignedCarryIncrementAdder, GeneralCircuit):
|
||||
"""Class representing signed carry increment adder.
|
||||
|
||||
Carry increment adder represents a modified carry select adder that achieves about the same critical delay
|
||||
|
@ -1,34 +1,24 @@
|
||||
from ariths_gen.wire_components import (
|
||||
Wire,
|
||||
ConstantWireValue0,
|
||||
ConstantWireValue1,
|
||||
Bus
|
||||
)
|
||||
from ariths_gen.core.arithmetic_circuits import (
|
||||
ArithmeticCircuit,
|
||||
MultiplierCircuit
|
||||
GeneralCircuit
|
||||
)
|
||||
from ariths_gen.core.logic_gate_circuits import (
|
||||
MultipleInputLogicGate
|
||||
)
|
||||
from ariths_gen.one_bit_circuits.one_bit_components import (
|
||||
HalfAdder,
|
||||
FullAdder,
|
||||
FullAdderPG,
|
||||
PGLogicBlock
|
||||
)
|
||||
from ariths_gen.one_bit_circuits.logic_gates import (
|
||||
AndGate,
|
||||
NandGate,
|
||||
OrGate,
|
||||
NorGate,
|
||||
XorGate,
|
||||
XnorGate,
|
||||
NotGate
|
||||
XorGate
|
||||
)
|
||||
|
||||
|
||||
class UnsignedCarryLookaheadAdder(ArithmeticCircuit):
|
||||
class UnsignedCarryLookaheadAdder(GeneralCircuit):
|
||||
"""Class representing unsigned carry-lookahead adder.
|
||||
|
||||
Unsigned carry-lookahead adder represents faster adder circuit which is composed
|
||||
@ -67,7 +57,7 @@ class UnsignedCarryLookaheadAdder(ArithmeticCircuit):
|
||||
"""
|
||||
def __init__(self, a: Bus, b: Bus, cla_block_size: int = 4, prefix: str = "", name: str = "u_cla", **kwargs):
|
||||
self.N = max(a.N, b.N)
|
||||
super().__init__(a=a, b=b, prefix=prefix, name=name, out_N=self.N+1, **kwargs)
|
||||
super().__init__(inputs=[a, b], prefix=prefix, name=name, out_N=self.N+1, **kwargs)
|
||||
|
||||
# Bus sign extension in case buses have different lengths
|
||||
self.a.bus_extend(N=self.N, prefix=a.prefix)
|
||||
@ -109,14 +99,14 @@ class UnsignedCarryLookaheadAdder(ArithmeticCircuit):
|
||||
|
||||
# For each pg pair values algorithmically combine two input AND gates to replace multiple input gates (resolves fan-in issue)
|
||||
pg_wires = Bus(wires_list=composite_wires)
|
||||
multi_bit_and_gate = MultipleInputLogicGate(a=pg_wires, two_input_gate_cls=AndGate, prefix=self.prefix+"_and", parent_component=self)
|
||||
multi_bit_and_gate = MultipleInputLogicGate(a=pg_wires, two_input_gate_cls=AndGate, prefix=self.prefix+f"_and{block_n}_{i}_{g_index}", parent_component=self)
|
||||
composite_or_gates_inputs.append(multi_bit_and_gate.out)
|
||||
|
||||
# Final OR gates cascade using generated AND gates output wires
|
||||
composite_or_wires = Bus(wires_list=composite_or_gates_inputs)
|
||||
multi_bit_or_gate = MultipleInputLogicGate(a=composite_or_wires, two_input_gate_cls=OrGate, prefix=self.prefix+"_or", parent_component=self)
|
||||
multi_bit_or_gate = MultipleInputLogicGate(a=composite_or_wires, two_input_gate_cls=OrGate, prefix=self.prefix+f"_orred{block_n}_{i}_{g_index}_", parent_component=self)
|
||||
# Carry bit generation
|
||||
obj_cout_or = OrGate(pg_block.get_generate_wire(), multi_bit_or_gate.out, prefix=self.prefix+"_or"+str(self.get_instance_num(cls=OrGate, count_disabled_gates=False)), parent_component=self)
|
||||
obj_cout_or = OrGate(pg_block.get_generate_wire(), multi_bit_or_gate.out, prefix=self.prefix+f"_or{block_n}_{i}_{g_index}_"+str(self.get_instance_num(cls=OrGate, count_disabled_gates=False)), parent_component=self)
|
||||
self.add_component(obj_cout_or)
|
||||
# Updating cin for the the next cla block/sum XOR
|
||||
cin = obj_cout_or.out
|
||||
@ -128,7 +118,7 @@ class UnsignedCarryLookaheadAdder(ArithmeticCircuit):
|
||||
self.out.connect(self.N, cin)
|
||||
|
||||
|
||||
class SignedCarryLookaheadAdder(UnsignedCarryLookaheadAdder, ArithmeticCircuit):
|
||||
class SignedCarryLookaheadAdder(UnsignedCarryLookaheadAdder, GeneralCircuit):
|
||||
"""Class representing signed carry-lookahead adder.
|
||||
|
||||
Signed carry-lookahead adder represents faster adder circuit which is composed
|
||||
|
@ -1,38 +1,20 @@
|
||||
from ariths_gen.wire_components import (
|
||||
Wire,
|
||||
ConstantWireValue0,
|
||||
ConstantWireValue1,
|
||||
Bus
|
||||
)
|
||||
from ariths_gen.core.arithmetic_circuits import (
|
||||
ArithmeticCircuit,
|
||||
ThreeInputArithmeticCircuit,
|
||||
MultiplierCircuit
|
||||
GeneralCircuit
|
||||
)
|
||||
from ariths_gen.one_bit_circuits.one_bit_components import (
|
||||
HalfAdder,
|
||||
FullAdder
|
||||
)
|
||||
from ariths_gen.one_bit_circuits.logic_gates import (
|
||||
AndGate,
|
||||
NandGate,
|
||||
OrGate,
|
||||
NorGate,
|
||||
XorGate,
|
||||
XnorGate,
|
||||
NotGate
|
||||
)
|
||||
from ariths_gen.multi_bit_circuits.adders import (
|
||||
UnsignedCarryLookaheadAdder,
|
||||
UnsignedPGRippleCarryAdder,
|
||||
UnsignedRippleCarryAdder,
|
||||
SignedCarryLookaheadAdder,
|
||||
SignedPGRippleCarryAdder,
|
||||
SignedRippleCarryAdder
|
||||
UnsignedCarryLookaheadAdder
|
||||
)
|
||||
|
||||
|
||||
class CarrySaveAdderComponent(ThreeInputArithmeticCircuit):
|
||||
class CarrySaveAdderComponent(GeneralCircuit):
|
||||
"""Class representing carry save adder component.
|
||||
|
||||
The carry save adder component is especially useful when constructing tree multiplier architectures to reduce the propagation delay as opposed to traditional implementation of tree multipliers with half/full adders.
|
||||
@ -70,7 +52,8 @@ class CarrySaveAdderComponent(ThreeInputArithmeticCircuit):
|
||||
"""
|
||||
def __init__(self, a: Bus, b: Bus, c: Bus, prefix: str = "", name: str = "csa_component", signed: bool = False, **kwargs):
|
||||
self.N = max(a.N, b.N, c.N)
|
||||
super().__init__(a=a, b=b, c=c, prefix=prefix, name=name, out_N=(2*self.N)+2, signed=signed, **kwargs)
|
||||
super().__init__(inputs=[a, b, c], prefix=prefix, name=name, out_N=(2*self.N)+2, signed=signed, **kwargs)
|
||||
self.out.signed = False # CSA component has always unsigned output
|
||||
|
||||
bus_extension_wire = ConstantWireValue1() if self.signed is True else ConstantWireValue0()
|
||||
self.a.bus_extend(N=self.N, prefix=a.prefix, desired_extension_wire=bus_extension_wire)
|
||||
@ -94,7 +77,7 @@ class CarrySaveAdderComponent(ThreeInputArithmeticCircuit):
|
||||
[self.out.connect(o, self.carry_bits.get_wire(o-int(self.out.N/2))) for o in range(int(self.out.N/2), self.out.N)]
|
||||
|
||||
|
||||
class UnsignedCarrySaveAdder(ThreeInputArithmeticCircuit):
|
||||
class UnsignedCarrySaveAdder(GeneralCircuit):
|
||||
"""Class representing unsigned carry save adder.
|
||||
|
||||
Unsigned carry save adder represents 3 input N-bit unsigned adder which is composed of
|
||||
@ -132,7 +115,7 @@ class UnsignedCarrySaveAdder(ThreeInputArithmeticCircuit):
|
||||
"""
|
||||
def __init__(self, a: Bus, b: Bus, c: Bus, prefix: str = "", name: str = "u_csa", unsigned_adder_class_name: str = UnsignedCarryLookaheadAdder, **kwargs):
|
||||
self.N = max(a.N, b.N, c.N)
|
||||
super().__init__(a=a, b=b, c=c, prefix=prefix, name=name, out_N=self.N+2, **kwargs)
|
||||
super().__init__(inputs=[a, b, c], prefix=prefix, name=name, out_N=self.N+2, **kwargs)
|
||||
|
||||
# Bus sign extension in case buses have different lengths
|
||||
self.a.bus_extend(N=self.N, prefix=a.prefix)
|
||||
|
@ -1,34 +1,23 @@
|
||||
from ariths_gen.wire_components import (
|
||||
Wire,
|
||||
ConstantWireValue0,
|
||||
ConstantWireValue1,
|
||||
Bus,
|
||||
wires
|
||||
Bus
|
||||
)
|
||||
from ariths_gen.core.arithmetic_circuits import (
|
||||
ArithmeticCircuit,
|
||||
MultiplierCircuit
|
||||
)
|
||||
from ariths_gen.core.logic_gate_circuits import (
|
||||
MultipleInputLogicGate
|
||||
GeneralCircuit
|
||||
)
|
||||
from ariths_gen.one_bit_circuits.one_bit_components import (
|
||||
HalfAdder,
|
||||
FullAdder,
|
||||
TwoOneMultiplexer
|
||||
)
|
||||
from ariths_gen.one_bit_circuits.logic_gates import (
|
||||
AndGate,
|
||||
NandGate,
|
||||
OrGate,
|
||||
NorGate,
|
||||
XorGate,
|
||||
XnorGate,
|
||||
NotGate
|
||||
XorGate
|
||||
)
|
||||
|
||||
|
||||
class UnsignedCarrySelectAdder(ArithmeticCircuit):
|
||||
class UnsignedCarrySelectAdder(GeneralCircuit):
|
||||
"""Class representing unsigned carry select adder.
|
||||
|
||||
Carry select adder's logic is divided into a number of carry select blocks.
|
||||
@ -75,7 +64,7 @@ class UnsignedCarrySelectAdder(ArithmeticCircuit):
|
||||
"""
|
||||
def __init__(self, a: Bus, b: Bus, select_block_size: int = 4, prefix: str = "", name: str = "u_csla", **kwargs):
|
||||
self.N = max(a.N, b.N)
|
||||
super().__init__(a=a, b=b, prefix=prefix, name=name, out_N=self.N+1, **kwargs)
|
||||
super().__init__(inputs=[a, b], prefix=prefix, name=name, out_N=self.N+1, **kwargs)
|
||||
|
||||
# Bus sign extension in case buses have different lengths
|
||||
self.a.bus_extend(N=self.N, prefix=a.prefix)
|
||||
@ -125,7 +114,7 @@ class UnsignedCarrySelectAdder(ArithmeticCircuit):
|
||||
self.out.connect(self.N, cin)
|
||||
|
||||
|
||||
class SignedCarrySelectAdder(UnsignedCarrySelectAdder, ArithmeticCircuit):
|
||||
class SignedCarrySelectAdder(UnsignedCarrySelectAdder, GeneralCircuit):
|
||||
"""Class representing signed carry select adder.
|
||||
|
||||
Carry select adder's logic is divided into a number of carry select blocks.
|
||||
|
@ -1,13 +1,9 @@
|
||||
from ariths_gen.wire_components import (
|
||||
Wire,
|
||||
ConstantWireValue0,
|
||||
ConstantWireValue1,
|
||||
Bus,
|
||||
wires
|
||||
Bus
|
||||
)
|
||||
from ariths_gen.core.arithmetic_circuits import (
|
||||
ArithmeticCircuit,
|
||||
MultiplierCircuit
|
||||
GeneralCircuit
|
||||
)
|
||||
from ariths_gen.core.logic_gate_circuits import (
|
||||
MultipleInputLogicGate
|
||||
@ -19,16 +15,11 @@ from ariths_gen.one_bit_circuits.one_bit_components import (
|
||||
)
|
||||
from ariths_gen.one_bit_circuits.logic_gates import (
|
||||
AndGate,
|
||||
NandGate,
|
||||
OrGate,
|
||||
NorGate,
|
||||
XorGate,
|
||||
XnorGate,
|
||||
NotGate
|
||||
XorGate
|
||||
)
|
||||
|
||||
|
||||
class UnsignedCarrySkipAdder(ArithmeticCircuit):
|
||||
class UnsignedCarrySkipAdder(GeneralCircuit):
|
||||
"""Class representing unsigned carry skip (bypass) adder composed of smaller carry bypass blocks of chosen size to reduce propagation delay.
|
||||
|
||||
Unsigned carry skip (bypass) adder represents faster adder circuit which is composed
|
||||
@ -72,7 +63,7 @@ class UnsignedCarrySkipAdder(ArithmeticCircuit):
|
||||
"""
|
||||
def __init__(self, a: Bus, b: Bus, bypass_block_size: int = 4, prefix: str = "", name: str = "u_cska", **kwargs):
|
||||
self.N = max(a.N, b.N)
|
||||
super().__init__(a=a, b=b, prefix=prefix, name=name, out_N=self.N+1, **kwargs)
|
||||
super().__init__(inputs=[a, b], prefix=prefix, name=name, out_N=self.N+1, **kwargs)
|
||||
|
||||
# Bus sign extension in case buses have different lengths
|
||||
self.a.bus_extend(N=self.N, prefix=a.prefix)
|
||||
@ -120,7 +111,7 @@ class UnsignedCarrySkipAdder(ArithmeticCircuit):
|
||||
self.out.connect(self.N, cin)
|
||||
|
||||
|
||||
class SignedCarrySkipAdder(UnsignedCarrySkipAdder, ArithmeticCircuit):
|
||||
class SignedCarrySkipAdder(UnsignedCarrySkipAdder, GeneralCircuit):
|
||||
"""Class representing signed carry skip (bypass) adder composed of smaller carry bypass blocks of chosen size to reduce propagation delay.
|
||||
|
||||
Signed carry skip (bypass) adder represents faster adder circuit which is composed
|
||||
|
@ -1,35 +1,22 @@
|
||||
from ariths_gen.wire_components import (
|
||||
Wire,
|
||||
ConstantWireValue0,
|
||||
ConstantWireValue1,
|
||||
Bus,
|
||||
wires
|
||||
Bus
|
||||
)
|
||||
from ariths_gen.core.arithmetic_circuits import (
|
||||
ArithmeticCircuit,
|
||||
MultiplierCircuit
|
||||
)
|
||||
from ariths_gen.core.logic_gate_circuits import (
|
||||
MultipleInputLogicGate
|
||||
GeneralCircuit
|
||||
)
|
||||
from ariths_gen.one_bit_circuits.one_bit_components import (
|
||||
HalfAdder,
|
||||
FullAdder,
|
||||
TwoOneMultiplexer
|
||||
)
|
||||
from ariths_gen.one_bit_circuits.logic_gates import (
|
||||
AndGate,
|
||||
NandGate,
|
||||
OrGate,
|
||||
NorGate,
|
||||
XorGate,
|
||||
XnorGate,
|
||||
NotGate
|
||||
XorGate
|
||||
)
|
||||
import math
|
||||
|
||||
|
||||
class UnsignedConditionalSumAdder(ArithmeticCircuit):
|
||||
class UnsignedConditionalSumAdder(GeneralCircuit):
|
||||
"""Class representing unsigned conditional sum adder.
|
||||
|
||||
Conditional sum adder performs carry-select addition starting with
|
||||
@ -108,7 +95,7 @@ class UnsignedConditionalSumAdder(ArithmeticCircuit):
|
||||
"""
|
||||
def __init__(self, a: Bus, b: Bus, prefix: str = "", name: str = "u_cosa", **kwargs):
|
||||
self.N = max(a.N, b.N)
|
||||
super().__init__(a=a, b=b, prefix=prefix, name=name, out_N=self.N+1, **kwargs)
|
||||
super().__init__(inputs=[a, b], prefix=prefix, name=name, out_N=self.N+1, **kwargs)
|
||||
|
||||
# Bus sign extension in case buses have different lengths
|
||||
self.a.bus_extend(N=self.N, prefix=a.prefix)
|
||||
@ -191,7 +178,7 @@ class UnsignedConditionalSumAdder(ArithmeticCircuit):
|
||||
self.out.connect(self.N, self.carry_sig[i_wire+1][0][-1])
|
||||
|
||||
|
||||
class SignedConditionalSumAdder(UnsignedConditionalSumAdder, ArithmeticCircuit):
|
||||
class SignedConditionalSumAdder(UnsignedConditionalSumAdder, GeneralCircuit):
|
||||
"""Class representing signed conditional sum adder.
|
||||
|
||||
Conditional sum adder performs carry-select addition starting with
|
||||
|
@ -1,41 +1,22 @@
|
||||
from ariths_gen.wire_components import (
|
||||
Wire,
|
||||
ConstantWireValue0,
|
||||
ConstantWireValue1,
|
||||
Bus
|
||||
)
|
||||
from ariths_gen.core.arithmetic_circuits import (
|
||||
ArithmeticCircuit,
|
||||
MultiplierCircuit
|
||||
GeneralCircuit
|
||||
)
|
||||
from ariths_gen.one_bit_circuits.one_bit_components import (
|
||||
HalfAdder,
|
||||
FullAdder,
|
||||
PGSumLogic,
|
||||
GreyCell,
|
||||
BlackCell
|
||||
)
|
||||
from ariths_gen.one_bit_circuits.logic_gates import (
|
||||
AndGate,
|
||||
NandGate,
|
||||
OrGate,
|
||||
NorGate,
|
||||
XorGate,
|
||||
XnorGate,
|
||||
NotGate
|
||||
)
|
||||
from ariths_gen.multi_bit_circuits.adders import (
|
||||
UnsignedCarryLookaheadAdder,
|
||||
UnsignedPGRippleCarryAdder,
|
||||
UnsignedRippleCarryAdder,
|
||||
SignedCarryLookaheadAdder,
|
||||
SignedPGRippleCarryAdder,
|
||||
SignedRippleCarryAdder
|
||||
XorGate
|
||||
)
|
||||
import math
|
||||
|
||||
|
||||
class UnsignedHanCarlsonAdder(ArithmeticCircuit):
|
||||
class UnsignedHanCarlsonAdder(GeneralCircuit):
|
||||
"""Class representing unsigned Han-Carlson adder (using valency-2 logic gates).
|
||||
|
||||
The Han-Carlson adder belongs to a type of tree (parallel-prefix) adders.
|
||||
@ -88,7 +69,7 @@ class UnsignedHanCarlsonAdder(ArithmeticCircuit):
|
||||
"""
|
||||
def __init__(self, a: Bus, b: Bus, prefix: str = "", name: str = "u_hca", config_choice: int = 1, **kwargs):
|
||||
self.N = max(a.N, b.N)
|
||||
super().__init__(a=a, b=b, prefix=prefix, name=name, out_N=self.N+1, **kwargs)
|
||||
super().__init__(inputs=[a, b], prefix=prefix, name=name, out_N=self.N+1, **kwargs)
|
||||
|
||||
# Bus sign extension in case buses have different lengths
|
||||
self.a.bus_extend(N=self.N, prefix=a.prefix)
|
||||
@ -184,7 +165,7 @@ class UnsignedHanCarlsonAdder(ArithmeticCircuit):
|
||||
self.generate_sig[i_wire+1].append(self.get_previous_component().get_generate_wire())
|
||||
|
||||
|
||||
class SignedHanCarlsonAdder(UnsignedHanCarlsonAdder, ArithmeticCircuit):
|
||||
class SignedHanCarlsonAdder(UnsignedHanCarlsonAdder, GeneralCircuit):
|
||||
"""Class representing signed Han-Carlson adder (using valency-2 logic gates).
|
||||
|
||||
The Han-Carlson adder belongs to a type of tree (parallel-prefix) adders.
|
||||
|
@ -1,41 +1,22 @@
|
||||
from ariths_gen.wire_components import (
|
||||
Wire,
|
||||
ConstantWireValue0,
|
||||
ConstantWireValue1,
|
||||
Bus
|
||||
)
|
||||
from ariths_gen.core.arithmetic_circuits import (
|
||||
ArithmeticCircuit,
|
||||
MultiplierCircuit
|
||||
GeneralCircuit
|
||||
)
|
||||
from ariths_gen.one_bit_circuits.one_bit_components import (
|
||||
HalfAdder,
|
||||
FullAdder,
|
||||
PGSumLogic,
|
||||
GreyCell,
|
||||
BlackCell
|
||||
)
|
||||
from ariths_gen.one_bit_circuits.logic_gates import (
|
||||
AndGate,
|
||||
NandGate,
|
||||
OrGate,
|
||||
NorGate,
|
||||
XorGate,
|
||||
XnorGate,
|
||||
NotGate
|
||||
)
|
||||
from ariths_gen.multi_bit_circuits.adders import (
|
||||
UnsignedCarryLookaheadAdder,
|
||||
UnsignedPGRippleCarryAdder,
|
||||
UnsignedRippleCarryAdder,
|
||||
SignedCarryLookaheadAdder,
|
||||
SignedPGRippleCarryAdder,
|
||||
SignedRippleCarryAdder
|
||||
XorGate
|
||||
)
|
||||
import math
|
||||
|
||||
|
||||
class UnsignedKnowlesAdder(ArithmeticCircuit):
|
||||
class UnsignedKnowlesAdder(GeneralCircuit):
|
||||
"""Class representing unsigned Knowles adder (using valency-2 logic gates).
|
||||
|
||||
The Knowles adder belongs to a type of tree (parallel-prefix) adders.
|
||||
@ -88,7 +69,7 @@ class UnsignedKnowlesAdder(ArithmeticCircuit):
|
||||
"""
|
||||
def __init__(self, a: Bus, b: Bus, prefix: str = "", name: str = "u_ka", config_choice: int = 1, **kwargs):
|
||||
self.N = max(a.N, b.N)
|
||||
super().__init__(a=a, b=b, prefix=prefix, name=name, out_N=self.N+1, **kwargs)
|
||||
super().__init__(inputs=[a, b], prefix=prefix, name=name, out_N=self.N+1, **kwargs)
|
||||
|
||||
# Bus sign extension in case buses have different lengths
|
||||
self.a.bus_extend(N=self.N, prefix=a.prefix)
|
||||
@ -143,7 +124,7 @@ class UnsignedKnowlesAdder(ArithmeticCircuit):
|
||||
self.generate_sig[i_wire+1].append(self.get_previous_component().get_generate_wire())
|
||||
|
||||
|
||||
class SignedKnowlesAdder(UnsignedKnowlesAdder, ArithmeticCircuit):
|
||||
class SignedKnowlesAdder(UnsignedKnowlesAdder, GeneralCircuit):
|
||||
"""Class representing signed Knowles adder (using valency-2 logic gates).
|
||||
|
||||
The Knowles adder belongs to a type of tree (parallel-prefix) adders.
|
||||
|
@ -1,41 +1,22 @@
|
||||
from ariths_gen.wire_components import (
|
||||
Wire,
|
||||
ConstantWireValue0,
|
||||
ConstantWireValue1,
|
||||
Bus
|
||||
)
|
||||
from ariths_gen.core.arithmetic_circuits import (
|
||||
ArithmeticCircuit,
|
||||
MultiplierCircuit
|
||||
GeneralCircuit
|
||||
)
|
||||
from ariths_gen.one_bit_circuits.one_bit_components import (
|
||||
HalfAdder,
|
||||
FullAdder,
|
||||
PGSumLogic,
|
||||
GreyCell,
|
||||
BlackCell
|
||||
)
|
||||
from ariths_gen.one_bit_circuits.logic_gates import (
|
||||
AndGate,
|
||||
NandGate,
|
||||
OrGate,
|
||||
NorGate,
|
||||
XorGate,
|
||||
XnorGate,
|
||||
NotGate
|
||||
)
|
||||
from ariths_gen.multi_bit_circuits.adders import (
|
||||
UnsignedCarryLookaheadAdder,
|
||||
UnsignedPGRippleCarryAdder,
|
||||
UnsignedRippleCarryAdder,
|
||||
SignedCarryLookaheadAdder,
|
||||
SignedPGRippleCarryAdder,
|
||||
SignedRippleCarryAdder
|
||||
XorGate
|
||||
)
|
||||
import math
|
||||
|
||||
|
||||
class UnsignedKoggeStoneAdder(ArithmeticCircuit):
|
||||
class UnsignedKoggeStoneAdder(GeneralCircuit):
|
||||
"""Class representing unsigned Kogge-Stone adder (using valency-2 logic gates).
|
||||
|
||||
The Kogge-Stone adder belongs to a type of tree (parallel-prefix) adders.
|
||||
@ -86,7 +67,7 @@ class UnsignedKoggeStoneAdder(ArithmeticCircuit):
|
||||
"""
|
||||
def __init__(self, a: Bus, b: Bus, prefix: str = "", name: str = "u_ksa", **kwargs):
|
||||
self.N = max(a.N, b.N)
|
||||
super().__init__(a=a, b=b, prefix=prefix, name=name, out_N=self.N+1, **kwargs)
|
||||
super().__init__(inputs=[a, b], prefix=prefix, name=name, out_N=self.N+1, **kwargs)
|
||||
|
||||
# Bus sign extension in case buses have different lengths
|
||||
self.a.bus_extend(N=self.N, prefix=a.prefix)
|
||||
@ -129,7 +110,7 @@ class UnsignedKoggeStoneAdder(ArithmeticCircuit):
|
||||
self.generate_sig[i_wire+1].append(self.get_previous_component().get_generate_wire())
|
||||
|
||||
|
||||
class SignedKoggeStoneAdder(UnsignedKoggeStoneAdder, ArithmeticCircuit):
|
||||
class SignedKoggeStoneAdder(UnsignedKoggeStoneAdder, GeneralCircuit):
|
||||
"""Class representing signed Kogge-Stone adder (using valency-2 logic gates).
|
||||
|
||||
The Kogge-Stone adder belongs to a type of tree (parallel-prefix) adders.
|
||||
|
@ -1,41 +1,22 @@
|
||||
from ariths_gen.wire_components import (
|
||||
Wire,
|
||||
ConstantWireValue0,
|
||||
ConstantWireValue1,
|
||||
Bus
|
||||
)
|
||||
from ariths_gen.core.arithmetic_circuits import (
|
||||
ArithmeticCircuit,
|
||||
MultiplierCircuit
|
||||
GeneralCircuit
|
||||
)
|
||||
from ariths_gen.one_bit_circuits.one_bit_components import (
|
||||
HalfAdder,
|
||||
FullAdder,
|
||||
PGSumLogic,
|
||||
GreyCell,
|
||||
BlackCell
|
||||
)
|
||||
from ariths_gen.one_bit_circuits.logic_gates import (
|
||||
AndGate,
|
||||
NandGate,
|
||||
OrGate,
|
||||
NorGate,
|
||||
XorGate,
|
||||
XnorGate,
|
||||
NotGate
|
||||
)
|
||||
from ariths_gen.multi_bit_circuits.adders import (
|
||||
UnsignedCarryLookaheadAdder,
|
||||
UnsignedPGRippleCarryAdder,
|
||||
UnsignedRippleCarryAdder,
|
||||
SignedCarryLookaheadAdder,
|
||||
SignedPGRippleCarryAdder,
|
||||
SignedRippleCarryAdder
|
||||
XorGate
|
||||
)
|
||||
import math
|
||||
|
||||
|
||||
class UnsignedLadnerFischerAdder(ArithmeticCircuit):
|
||||
class UnsignedLadnerFischerAdder(GeneralCircuit):
|
||||
"""Class representing unsigned Ladner-Fischer adder (using valency-2 logic gates).
|
||||
|
||||
The Ladner-Fischer adder belongs to a type of tree (parallel-prefix) adders.
|
||||
@ -88,7 +69,7 @@ class UnsignedLadnerFischerAdder(ArithmeticCircuit):
|
||||
"""
|
||||
def __init__(self, a: Bus, b: Bus, prefix: str = "", name: str = "u_lfa", config_choice: int = 1, **kwargs):
|
||||
self.N = max(a.N, b.N)
|
||||
super().__init__(a=a, b=b, prefix=prefix, name=name, out_N=self.N+1, **kwargs)
|
||||
super().__init__(inputs=[a, b], prefix=prefix, name=name, out_N=self.N+1, **kwargs)
|
||||
|
||||
# Bus sign extension in case buses have different lengths
|
||||
self.a.bus_extend(N=self.N, prefix=a.prefix)
|
||||
@ -188,7 +169,7 @@ class UnsignedLadnerFischerAdder(ArithmeticCircuit):
|
||||
self.generate_sig[i_wire+1].append(self.get_previous_component().get_generate_wire())
|
||||
|
||||
|
||||
class SignedLadnerFischerAdder(UnsignedLadnerFischerAdder, ArithmeticCircuit):
|
||||
class SignedLadnerFischerAdder(UnsignedLadnerFischerAdder, GeneralCircuit):
|
||||
"""Class representing signed Ladner-Fischer adder (using valency-2 logic gates).
|
||||
|
||||
The Ladner-Fischer adder belongs to a type of tree (parallel-prefix) adders.
|
||||
|
@ -1,30 +1,21 @@
|
||||
from ariths_gen.wire_components import (
|
||||
Wire,
|
||||
ConstantWireValue0,
|
||||
ConstantWireValue1,
|
||||
Bus
|
||||
)
|
||||
from ariths_gen.core.arithmetic_circuits import (
|
||||
ArithmeticCircuit,
|
||||
MultiplierCircuit
|
||||
GeneralCircuit
|
||||
)
|
||||
from ariths_gen.one_bit_circuits.one_bit_components import (
|
||||
HalfAdder,
|
||||
FullAdder,
|
||||
PGSumLogic
|
||||
)
|
||||
from ariths_gen.one_bit_circuits.logic_gates import (
|
||||
AndGate,
|
||||
NandGate,
|
||||
OrGate,
|
||||
NorGate,
|
||||
XorGate,
|
||||
XnorGate,
|
||||
NotGate
|
||||
XorGate
|
||||
)
|
||||
|
||||
|
||||
class UnsignedPGRippleCarryAdder(ArithmeticCircuit):
|
||||
class UnsignedPGRippleCarryAdder(GeneralCircuit):
|
||||
"""Class representing unsigned ripple carry adder with propagate/generate logic.
|
||||
|
||||
Unsigned ripple carry adder with PG logic represents slightly different rca implementation
|
||||
@ -69,7 +60,7 @@ class UnsignedPGRippleCarryAdder(ArithmeticCircuit):
|
||||
"""
|
||||
def __init__(self, a: Bus, b: Bus, prefix: str = "", name: str = "u_pg_rca", **kwargs):
|
||||
self.N = max(a.N, b.N)
|
||||
super().__init__(a=a, b=b, prefix=prefix, name=name, out_N=self.N+1, **kwargs)
|
||||
super().__init__(inputs=[a, b], prefix=prefix, name=name, out_N=self.N+1, **kwargs)
|
||||
|
||||
# Bus sign extension in case buses have different lengths
|
||||
self.a.bus_extend(N=self.N, prefix=a.prefix)
|
||||
@ -96,7 +87,7 @@ class UnsignedPGRippleCarryAdder(ArithmeticCircuit):
|
||||
self.out.connect(self.N, obj_or.out)
|
||||
|
||||
|
||||
class SignedPGRippleCarryAdder(UnsignedPGRippleCarryAdder, ArithmeticCircuit):
|
||||
class SignedPGRippleCarryAdder(UnsignedPGRippleCarryAdder, GeneralCircuit):
|
||||
"""Class representing signed ripple carry adder with propagate/generate logic.
|
||||
|
||||
Signed ripple carry adder with PG logic represents slightly different rca implementation
|
||||
|
@ -1,33 +1,23 @@
|
||||
from ariths_gen.wire_components import (
|
||||
Wire,
|
||||
ConstantWireValue0,
|
||||
ConstantWireValue1,
|
||||
Bus
|
||||
)
|
||||
from ariths_gen.core.arithmetic_circuits import (
|
||||
ArithmeticCircuit,
|
||||
MultiplierCircuit
|
||||
GeneralCircuit
|
||||
)
|
||||
from ariths_gen.one_bit_circuits.one_bit_components import (
|
||||
HalfAdder,
|
||||
FullAdder
|
||||
)
|
||||
from ariths_gen.one_bit_circuits.logic_gates import (
|
||||
AndGate,
|
||||
NandGate,
|
||||
OrGate,
|
||||
NorGate,
|
||||
XorGate,
|
||||
XnorGate,
|
||||
NotGate
|
||||
XorGate
|
||||
)
|
||||
|
||||
|
||||
class UnsignedRippleCarryAdder(ArithmeticCircuit):
|
||||
class UnsignedRippleCarryAdder(GeneralCircuit):
|
||||
"""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.
|
||||
N one bit adders, where the first is a half adder and rest are full adders.
|
||||
|
||||
Its downside is its long propagation delay the bigger the circuit is.
|
||||
|
||||
@ -53,7 +43,7 @@ class UnsignedRippleCarryAdder(ArithmeticCircuit):
|
||||
"""
|
||||
def __init__(self, a: Bus, b: Bus, prefix: str = "", name: str = "u_rca", **kwargs):
|
||||
self.N = max(a.N, b.N)
|
||||
super().__init__(a=a, b=b, prefix=prefix, name=name, out_N=self.N+1, **kwargs)
|
||||
super().__init__(inputs=[a, b], prefix=prefix, name=name, out_N=self.N+1, **kwargs)
|
||||
|
||||
# Bus sign extension in case buses have different lengths
|
||||
self.a.bus_extend(N=self.N, prefix=a.prefix)
|
||||
@ -74,11 +64,11 @@ class UnsignedRippleCarryAdder(ArithmeticCircuit):
|
||||
self.out.connect(self.N, obj_adder.get_carry_wire())
|
||||
|
||||
|
||||
class SignedRippleCarryAdder(UnsignedRippleCarryAdder, ArithmeticCircuit):
|
||||
class SignedRippleCarryAdder(UnsignedRippleCarryAdder, GeneralCircuit):
|
||||
"""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.
|
||||
N one bit adders, where the 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.
|
||||
|
@ -1,40 +1,21 @@
|
||||
from ariths_gen.wire_components import (
|
||||
Wire,
|
||||
ConstantWireValue0,
|
||||
ConstantWireValue1,
|
||||
Bus
|
||||
)
|
||||
from ariths_gen.core.arithmetic_circuits import (
|
||||
ArithmeticCircuit,
|
||||
MultiplierCircuit
|
||||
GeneralCircuit
|
||||
)
|
||||
from ariths_gen.one_bit_circuits.one_bit_components import (
|
||||
HalfAdder,
|
||||
FullAdder,
|
||||
PGSumLogic,
|
||||
GreyCell,
|
||||
BlackCell
|
||||
)
|
||||
from ariths_gen.one_bit_circuits.logic_gates import (
|
||||
AndGate,
|
||||
NandGate,
|
||||
OrGate,
|
||||
NorGate,
|
||||
XorGate,
|
||||
XnorGate,
|
||||
NotGate
|
||||
)
|
||||
from ariths_gen.multi_bit_circuits.adders import (
|
||||
UnsignedCarryLookaheadAdder,
|
||||
UnsignedPGRippleCarryAdder,
|
||||
UnsignedRippleCarryAdder,
|
||||
SignedCarryLookaheadAdder,
|
||||
SignedPGRippleCarryAdder,
|
||||
SignedRippleCarryAdder
|
||||
XorGate
|
||||
)
|
||||
|
||||
|
||||
class UnsignedSklanskyAdder(ArithmeticCircuit):
|
||||
class UnsignedSklanskyAdder(GeneralCircuit):
|
||||
"""Class representing unsigned Sklansky (or divide-and-conquer) adder (using valency-2 logic gates).
|
||||
|
||||
The Sklansky adder belongs to a type of tree (parallel-prefix) adders.
|
||||
@ -85,7 +66,7 @@ class UnsignedSklanskyAdder(ArithmeticCircuit):
|
||||
"""
|
||||
def __init__(self, a: Bus, b: Bus, prefix: str = "", name: str = "u_sa", **kwargs):
|
||||
self.N = max(a.N, b.N)
|
||||
super().__init__(a=a, b=b, prefix=prefix, name=name, out_N=self.N+1, **kwargs)
|
||||
super().__init__(inputs=[a, b], prefix=prefix, name=name, out_N=self.N+1, **kwargs)
|
||||
|
||||
# Bus sign extension in case buses have different lengths
|
||||
self.a.bus_extend(N=self.N, prefix=a.prefix)
|
||||
@ -132,7 +113,7 @@ class UnsignedSklanskyAdder(ArithmeticCircuit):
|
||||
prev_stage_int_value += 2**stage
|
||||
|
||||
|
||||
class SignedSklanskyAdder(UnsignedSklanskyAdder, ArithmeticCircuit):
|
||||
class SignedSklanskyAdder(UnsignedSklanskyAdder, GeneralCircuit):
|
||||
"""Class representing signed Sklansky (or divide-and-conquer) adder (using valency-2 logic gates).
|
||||
|
||||
The Sklansky adder belongs to a type of tree (parallel-prefix) adders.
|
||||
|
@ -0,0 +1 @@
|
||||
from .quad import QuAdder
|
179
ariths_gen/multi_bit_circuits/approximate_adders/quad.py
Normal file
179
ariths_gen/multi_bit_circuits/approximate_adders/quad.py
Normal file
@ -0,0 +1,179 @@
|
||||
"""
|
||||
Implementation of QuAdder
|
||||
|
||||
For more information, see:
|
||||
M. A. Hanif, R. Hafiz, O. Hasan and M. Shafique, "QuAd: Design and analysis of Quality-area optimal Low-Latency approximate Adders," 2017 54th ACM/EDAC/IEEE Design Automation Conference (DAC), Austin, TX, USA, 2017, pp. 1-6, doi: 10.1145/3061639.3062306.
|
||||
|
||||
"""
|
||||
|
||||
from ...wire_components import (
|
||||
ConstantWireValue0,
|
||||
Bus
|
||||
)
|
||||
from ariths_gen.core.arithmetic_circuits import (
|
||||
GeneralCircuit
|
||||
)
|
||||
|
||||
from ariths_gen.multi_bit_circuits.adders.ripple_carry_adder import (
|
||||
UnsignedRippleCarryAdder
|
||||
)
|
||||
import warnings
|
||||
|
||||
|
||||
class QuAdder(GeneralCircuit):
|
||||
"""
|
||||
Implementation of QuAd
|
||||
|
||||
https://ieeexplore.ieee.org/document/8060326
|
||||
|
||||
The implementation is inspired by Matlab code from the authors of the paper:
|
||||
```matlab
|
||||
temp_count=1;
|
||||
for iij=1:length(R_vect)
|
||||
fprintf(fileID,['wire [' num2str(R_vect(iij)+P_vect(iij)) ':0] temp' num2str(temp_count) ';\n']);
|
||||
temp_count=temp_count + 1;
|
||||
end
|
||||
|
||||
temp_count=1;
|
||||
for iiij=1:length(R_vect)
|
||||
if (sum(R_vect(1:iiij))+P_vect(1)-1) == (sum(R_vect(1:iiij))+P_vect(1)-R_vect(iiij)-P_vect(iiij))
|
||||
fprintf(fileID,['aassign temp' num2str(temp_count) '[' num2str(R_vect(iiij)+P_vect(iiij)) ':0] = in1[' num2str(sum(R_vect(1:iiij))+P_vect(1)-1) '] + in2[' num2str(sum(R_vect(1:iiij))+P_vect(1)-1) '];\n']);
|
||||
else
|
||||
disp(R_vect(1:iiij))
|
||||
fprintf(fileID,['bassign temp' num2str(temp_count) '[' num2str(R_vect(iiij)+P_vect(iiij)) ':0] = in1[' num2str(sum(R_vect(1:iiij))+P_vect(1)-1) ':' num2str(sum(R_vect(1:iiij))+P_vect(1)-R_vect(iiij)-P_vect(iiij)) '] + in2[' num2str(sum(R_vect(1:iiij))+P_vect(1)-1) ':' num2str(sum(R_vect(1:iiij))+P_vect(1)-R_vect(iiij)-P_vect(iiij)) '];\n']);
|
||||
end
|
||||
temp_count=temp_count+1;
|
||||
end
|
||||
|
||||
statement='};\n';
|
||||
temp_count=1;
|
||||
for iiij=1:length(R_vect)
|
||||
if iiij ~= length(R_vect)
|
||||
if (R_vect(iiij)==1)
|
||||
statement = [', temp' num2str(temp_count) '[' num2str(R_vect(iiij)+P_vect(iiij)-1) '] ' statement];
|
||||
else
|
||||
statement = [', temp' num2str(temp_count) '[' num2str(R_vect(iiij)+P_vect(iiij)-1) ':' num2str(P_vect(iiij)) '] ' statement];
|
||||
end
|
||||
else
|
||||
statement = ['assign res[' num2str(N) ':0] =' '{ temp' num2str(temp_count) '[' num2str(R_vect(iiij)+P_vect(iiij)) ':' num2str(P_vect(iiij)) '] ' statement];
|
||||
end
|
||||
temp_count=temp_count+1;
|
||||
end
|
||||
```
|
||||
|
||||
|
||||
"""
|
||||
|
||||
def log(self, *args):
|
||||
if self.use_log:
|
||||
print(*args)
|
||||
|
||||
def __init__(self, a, b, R, P, prefix, name="quad", adder_type=None, use_log=False, **kwargs):
|
||||
"""
|
||||
:param a: Bus first input
|
||||
:param b: Bus second input
|
||||
:param R: list of integers, defines the resultant bits of all the sub-adders (the first index specifies the resultant bits of sub-adder 1 and so on)
|
||||
:param P: list of integers, defines the prediction bits of all the sub-adders (again the first index specifies the prediction bits of sub-adder 1 and so on)
|
||||
"""
|
||||
|
||||
if not adder_type:
|
||||
adder_type = UnsignedRippleCarryAdder
|
||||
|
||||
# Assumptions checks
|
||||
assert len(R) == len(P), "R and P must have the same length"
|
||||
print([P[i] < P[i-1] + R[i-1] for i in range(1, len(P))])
|
||||
assert all([P[i] < P[i-1] + R[i-1] for i in range(1, len(P))]
|
||||
), "Pi must be lower than Pi-1 + Ri-1"
|
||||
assert sum(R) == a.N, "Sum of R must be equal to number of bits"
|
||||
|
||||
self.use_log = use_log
|
||||
|
||||
self.N = max(a.N, b.N)
|
||||
super().__init__(inputs=[a, b], prefix=prefix, name=name, out_N=self.N+1, **kwargs)
|
||||
|
||||
# Bus sign extension in case buses have different lengths
|
||||
self.a.bus_extend(N=self.N, prefix=a.prefix)
|
||||
self.b.bus_extend(N=self.N, prefix=b.prefix)
|
||||
|
||||
# warnings.warn("QuAdder is not tested yet")
|
||||
|
||||
# Connect all outputs to zero
|
||||
for i in range(self.N+1):
|
||||
self.out[i] = ConstantWireValue0()
|
||||
|
||||
# Declaration of temporary wires (just for debug purposes)
|
||||
temp_count = 0
|
||||
for iiij in range(0, len(R)):
|
||||
self.log('wire [' + str(R[iiij]+P[iiij]) +
|
||||
':0] temp' + str(temp_count) + ';')
|
||||
temp_count = temp_count + 1
|
||||
|
||||
def bus_subconnect(out_bus, in_bus, out_indexes, in_indexes):
|
||||
out_indexes = list(out_indexes)
|
||||
in_indexes = list(in_indexes)
|
||||
assert len(out_indexes) == len(in_indexes)
|
||||
|
||||
for i, j in zip(out_indexes, in_indexes):
|
||||
if j >= in_bus.N:
|
||||
out_bus[i] = ConstantWireValue0() # unsigned extension
|
||||
else:
|
||||
out_bus.connect(i, in_bus.get_wire(j)) # [i] = in_bus[j]
|
||||
|
||||
# Connection of adders
|
||||
temp_count = 0
|
||||
temp_bus = []
|
||||
for iiij in range(0, len(R)):
|
||||
# Former verilog output
|
||||
self.log("assign temp{}[{}:0] = in1[{}:{}] + in2[{}:{}];".format(
|
||||
temp_count,
|
||||
R[iiij]+P[iiij],
|
||||
sum(R[0:iiij + 1]) + P[0]-1,
|
||||
sum(R[0:iiij + 1]) + P[0]-R[iiij]-P[iiij],
|
||||
sum(R[0:iiij + 1]) + P[0]-1,
|
||||
sum(R[0:iiij + 1]) + P[0]-R[iiij]-P[iiij]
|
||||
))
|
||||
|
||||
a1 = Bus(f"{prefix}_temp_{temp_count}_a", R[iiij]+P[iiij])
|
||||
b1 = Bus(f"{prefix}_temp_{temp_count}_b", R[iiij]+P[iiij])
|
||||
|
||||
bus_subconnect(b1, self.b,
|
||||
range(R[iiij]+P[iiij]),
|
||||
range(sum(R[0:iiij + 1])+P[0]-R[iiij]-P[iiij], sum(R[0:iiij + 1])+P[0]))
|
||||
|
||||
bus_subconnect(a1, self.a,
|
||||
range(R[iiij]+P[iiij]),
|
||||
range(sum(R[0:iiij + 1])+P[0]-R[iiij]-P[iiij], sum(R[0:iiij + 1])+P[0]))
|
||||
|
||||
temp_bus.append(self.add_component(
|
||||
adder_type(a1, b1, prefix=f"{prefix}_add_{temp_count}")
|
||||
|
||||
))
|
||||
temp_count = temp_count+1
|
||||
|
||||
# Final connection
|
||||
temp_count = 0
|
||||
statement = "}"
|
||||
wire_id = 0
|
||||
for iiij in range(0, len(R)):
|
||||
if iiij != len(R) - 1:
|
||||
if R[iiij] == 1:
|
||||
statement = ', temp{}[{}]'.format(
|
||||
temp_count, R[iiij]+P[iiij] - 1) + statement
|
||||
else:
|
||||
statement = ', temp{}[{}:{}]'.format(
|
||||
temp_count, R[iiij]+P[iiij] - 1, P[iiij]) + statement
|
||||
|
||||
else:
|
||||
statement = 'assign res[' + str(self.N) + ':0] =' + '{ temp' + str(
|
||||
temp_count) + '[' + str(R[iiij]+P[iiij]) + ':' + str(P[iiij]) + '] ' + statement
|
||||
|
||||
self.log(statement)
|
||||
for i in range(P[iiij], R[iiij]+P[iiij]):
|
||||
self.log(temp_count, i, wire_id, temp_bus[temp_count].out[i])
|
||||
self.out[wire_id] = temp_bus[temp_count].out[i]
|
||||
wire_id += 1
|
||||
|
||||
temp_count = temp_count+1
|
||||
|
||||
# Last carry (MSB)
|
||||
self.out[wire_id] = temp_bus[temp_count - 1].out[R[iiij]+P[iiij]]
|
@ -13,3 +13,12 @@ from ariths_gen.multi_bit_circuits.approximate_multipliers.broken_carry_save_mul
|
||||
from ariths_gen.multi_bit_circuits.approximate_multipliers.truncated_carry_save_multiplier import (
|
||||
UnsignedTruncatedCarrySaveMultiplier
|
||||
)
|
||||
|
||||
from ariths_gen.multi_bit_circuits.approximate_multipliers.recursive_multiplier import (
|
||||
UnsignedAccurateTwoBitMultiplier,
|
||||
UnsignedApproximateTwoBitMultiplierM1,
|
||||
UnsignedApproximateTwoBitMultiplierM2,
|
||||
UnsignedApproximateTwoBitMultiplierM3,
|
||||
UnsignedApproximateTwoBitMultiplierM4,
|
||||
UnsignedRecursiveMultiplier
|
||||
)
|
||||
|
@ -1,11 +1,8 @@
|
||||
from ariths_gen.wire_components import (
|
||||
Wire,
|
||||
ConstantWireValue0,
|
||||
ConstantWireValue1,
|
||||
Bus
|
||||
)
|
||||
from ariths_gen.core.arithmetic_circuits import (
|
||||
ArithmeticCircuit,
|
||||
MultiplierCircuit
|
||||
)
|
||||
from ariths_gen.one_bit_circuits.one_bit_components import (
|
||||
@ -13,17 +10,7 @@ from ariths_gen.one_bit_circuits.one_bit_components import (
|
||||
FullAdder
|
||||
)
|
||||
from ariths_gen.one_bit_circuits.logic_gates import (
|
||||
AndGate,
|
||||
NandGate,
|
||||
OrGate,
|
||||
NorGate,
|
||||
XorGate,
|
||||
XnorGate,
|
||||
NotGate
|
||||
)
|
||||
from ariths_gen.multi_bit_circuits.multipliers import (
|
||||
UnsignedArrayMultiplier,
|
||||
SignedArrayMultiplier
|
||||
AndGate
|
||||
)
|
||||
|
||||
|
||||
@ -107,7 +94,7 @@ class UnsignedBrokenArrayMultiplier(MultiplierCircuit):
|
||||
# Vertical cut should be greater or equal to horizontal cut
|
||||
assert vertical_cut >= horizontal_cut
|
||||
|
||||
super().__init__(a=a, b=b, prefix=prefix, name=name, out_N=self.N*2, **kwargs)
|
||||
super().__init__(inputs=[a, b], prefix=prefix, name=name, out_N=self.N*2, **kwargs)
|
||||
|
||||
# Bus sign extension in case buses have different lengths
|
||||
self.a.bus_extend(N=self.N, prefix=a.prefix)
|
||||
|
@ -1,11 +1,8 @@
|
||||
from ariths_gen.wire_components import (
|
||||
Wire,
|
||||
ConstantWireValue0,
|
||||
ConstantWireValue1,
|
||||
Bus
|
||||
)
|
||||
from ariths_gen.core.arithmetic_circuits import (
|
||||
ArithmeticCircuit,
|
||||
MultiplierCircuit,
|
||||
)
|
||||
from ariths_gen.core.logic_gate_circuits import (
|
||||
@ -16,13 +13,7 @@ from ariths_gen.one_bit_circuits.one_bit_components import (
|
||||
FullAdder
|
||||
)
|
||||
from ariths_gen.one_bit_circuits.logic_gates import (
|
||||
AndGate,
|
||||
NandGate,
|
||||
OrGate,
|
||||
NorGate,
|
||||
XorGate,
|
||||
XnorGate,
|
||||
NotGate
|
||||
AndGate
|
||||
)
|
||||
from ariths_gen.multi_bit_circuits.adders import (
|
||||
UnsignedCarryLookaheadAdder
|
||||
@ -114,7 +105,7 @@ class UnsignedBrokenCarrySaveMultiplier(MultiplierCircuit):
|
||||
# Vertical cut should be greater or equal to horizontal cut
|
||||
assert vertical_cut >= horizontal_cut
|
||||
|
||||
super().__init__(a=a, b=b, prefix=prefix, name=name, out_N=self.N*2, **kwargs)
|
||||
super().__init__(inputs=[a, b], prefix=prefix, name=name, out_N=self.N*2, **kwargs)
|
||||
|
||||
# Bus sign extension in case buses have different lengths
|
||||
self.a.bus_extend(N=self.N, prefix=a.prefix)
|
||||
@ -213,7 +204,6 @@ class UnsignedBrokenCarrySaveMultiplier(MultiplierCircuit):
|
||||
adder_name = unsigned_adder_class_name(a=a, b=b).prefix + str(final_cpa_N)
|
||||
adder_a = Bus(prefix=f"a", wires_list=previous_sums)
|
||||
adder_b = Bus(prefix=f"b", wires_list=previous_carries)
|
||||
|
||||
final_adder = unsigned_adder_class_name(a=adder_a, b=adder_b, prefix=self.prefix, name=adder_name, inner_component=True)
|
||||
self.add_component(final_adder)
|
||||
[self.out.connect(o, final_adder.out.get_wire(o-max(self.N, self.vertical_cut)), inserted_wire_desired_index=o-max(self.N, self.vertical_cut)) for o in range(max(self.N, self.vertical_cut), self.out.N)]
|
||||
|
@ -0,0 +1,512 @@
|
||||
from ariths_gen.wire_components import (
|
||||
ConstantWireValue0,
|
||||
Bus
|
||||
)
|
||||
from ariths_gen.core.arithmetic_circuits import (
|
||||
MultiplierCircuit
|
||||
)
|
||||
from ariths_gen.one_bit_circuits.logic_gates import (
|
||||
AndGate,
|
||||
OrGate,
|
||||
XorGate,
|
||||
NotGate
|
||||
)
|
||||
from ariths_gen.multi_bit_circuits.adders import (
|
||||
UnsignedCarryLookaheadAdder
|
||||
)
|
||||
import math
|
||||
|
||||
|
||||
class UnsignedAccurateTwoBitMultiplier(MultiplierCircuit):
|
||||
"""Class representing unsigned two-bit accurate multiplier.
|
||||
|
||||
```
|
||||
A1B1 A1B0 A0B1 A0B0
|
||||
│ │ │ │ │ │ │ │
|
||||
┌▼─▼┐ ┌▼─▼┐ ┌▼─▼┐ ┌▼─▼┐
|
||||
│AND│ │AND│ │AND│ │AND│
|
||||
└─┬─┘ └─┬─┘ └─┬─┘ └─┬─┘
|
||||
│ └──┬──┐└┬─┐ │
|
||||
│ │ │ │ │ │
|
||||
│ │ │ │ │ │
|
||||
│ │ ┌▼─▼┐│ │
|
||||
│ │ │AND││ │
|
||||
│ │ └─┬─┘│ │
|
||||
└─────┐ ┌─┼───┘┌─┘ │
|
||||
│ │ └──┐ │ │
|
||||
┌◄─►┐ ┌▼─▼┐ ┌▼─▼┐ │
|
||||
│AND│ │XOR│ │XOR│ │
|
||||
└─┬─┘ └─┬─┘ └─┬─┘ │
|
||||
│ │ │ │
|
||||
▼ ▼ ▼ ▼
|
||||
O3 O2 O1 O0
|
||||
```
|
||||
|
||||
Description of the __init__ method.
|
||||
|
||||
Args:
|
||||
a (Bus): First input bus.
|
||||
b (Bus): Second input bus.
|
||||
prefix (str, optional): Prefix name of unsigned two-bit accurate multiplier. Defaults to "".
|
||||
name (str, optional): Name of unsigned two-bit accurate multiplier. Defaults to "u_2bit_accm".
|
||||
"""
|
||||
def __init__(self, a: Bus, b: Bus, prefix: str = "", name: str = "u_2bit_accm", **kwargs):
|
||||
self.N = max(a.N, b.N)
|
||||
assert self.N == 2
|
||||
super().__init__(inputs=[a, b], prefix=prefix, name=name, out_N=self.N*2, **kwargs)
|
||||
|
||||
# Bus sign extension in case buses have different lengths
|
||||
self.a.bus_extend(N=self.N, prefix=a.prefix)
|
||||
self.b.bus_extend(N=self.N, prefix=b.prefix)
|
||||
|
||||
and_obj1 = AndGate(self.a.get_wire(0), self.b.get_wire(0), prefix=self.prefix+"_and0")
|
||||
and_obj2 = AndGate(self.a.get_wire(0), self.b.get_wire(1), prefix=self.prefix+"_and1")
|
||||
and_obj3 = AndGate(self.a.get_wire(1), self.b.get_wire(0), prefix=self.prefix+"_and2")
|
||||
and_obj4 = AndGate(self.a.get_wire(1), self.b.get_wire(1), prefix=self.prefix+"_and3")
|
||||
|
||||
xor_obj1 = XorGate(and_obj2.out, and_obj3.out, prefix=self.prefix+"_xor0")
|
||||
and_obj5 = AndGate(and_obj2.out, and_obj3.out, prefix=self.prefix+"_and4")
|
||||
xor_obj2 = XorGate(and_obj5.out, and_obj4.out, prefix=self.prefix+"_xor1")
|
||||
and_obj6 = AndGate(and_obj5.out, and_obj4.out, prefix=self.prefix+"_and5")
|
||||
[self.add_component(obj) for obj in [and_obj1, and_obj2, and_obj3, and_obj4, xor_obj1, and_obj5, xor_obj2, and_obj6]]
|
||||
|
||||
self.out.connect(0, and_obj1.out)
|
||||
self.out.connect(1, xor_obj1.out)
|
||||
self.out.connect(2, xor_obj2.out)
|
||||
self.out.connect(3, and_obj6.out)
|
||||
|
||||
|
||||
class SignedAccurateTwoBitMultiplier(MultiplierCircuit):
|
||||
"""Class representing signed two-bit accurate multiplier.
|
||||
|
||||
Description of the __init__ method.
|
||||
|
||||
Args:
|
||||
a (Bus): First input bus.
|
||||
b (Bus): Second input bus.
|
||||
prefix (str, optional): Prefix name of signed two-bit accurate multiplier. Defaults to "".
|
||||
name (str, optional): Name of signed two-bit accurate multiplier. Defaults to "s_2bit_accm".
|
||||
"""
|
||||
def __init__(self, a: Bus, b: Bus, prefix: str = "", name: str = "s_2bit_accm", **kwargs):
|
||||
self.N = max(a.N, b.N)
|
||||
raise NotImplementedError("SignedAccurateTwoBitMultiplier is not implemented yet.")
|
||||
|
||||
|
||||
class UnsignedApproximateTwoBitMultiplierM1(MultiplierCircuit):
|
||||
"""Class representing unsigned two-bit approximate multiplier variant M1.
|
||||
|
||||
M1 ax variant defined here: https://ieeexplore.ieee.org/document/8727537
|
||||
|
||||
```
|
||||
A1B1 A1B0 A0B1 A0B0
|
||||
│ │ │ │ │ │ │ │
|
||||
┌▼─▼┐ ┌▼─▼┐ ┌▼─▼┐ ┌▼─▼┐
|
||||
│AND│ │AND│ │AND│ │AND│
|
||||
└─┬─┘ └─┬─┘ └─┬─┘ └─┬─┘
|
||||
│ │ └┐ │
|
||||
│ └─────┐ │ │
|
||||
└──────┐ ┌▼─▼┐ │
|
||||
│ │ OR│ │
|
||||
│ └─┬─┘ │
|
||||
│ │ │
|
||||
▼ ▼ ▼ ▼
|
||||
O3=0 O2 O1 O0
|
||||
```
|
||||
|
||||
Description of the __init__ method.
|
||||
|
||||
Args:
|
||||
a (Bus): First input bus.
|
||||
b (Bus): Second input bus.
|
||||
prefix (str, optional): Prefix name of unsigned two-bit approximate multiplier m1. Defaults to "".
|
||||
name (str, optional): Name of unsigned two-bit approximate multiplier m1. Defaults to "u_2bit_axm1".
|
||||
"""
|
||||
def __init__(self, a: Bus, b: Bus, prefix: str = "", name: str = "u_2bit_axm1", **kwargs):
|
||||
self.N = max(a.N, b.N)
|
||||
assert self.N == 2
|
||||
super().__init__(inputs=[a, b], prefix=prefix, name=name, out_N=self.N*2, **kwargs)
|
||||
|
||||
# Bus sign extension in case buses have different lengths
|
||||
self.a.bus_extend(N=self.N, prefix=a.prefix)
|
||||
self.b.bus_extend(N=self.N, prefix=b.prefix)
|
||||
|
||||
and_obj1 = AndGate(self.a.get_wire(0), self.b.get_wire(0), prefix=self.prefix+"_and0")
|
||||
and_obj2 = AndGate(self.a.get_wire(0), self.b.get_wire(1), prefix=self.prefix+"_and1")
|
||||
and_obj3 = AndGate(self.a.get_wire(1), self.b.get_wire(0), prefix=self.prefix+"_and2")
|
||||
and_obj4 = AndGate(self.a.get_wire(1), self.b.get_wire(1), prefix=self.prefix+"_and3")
|
||||
|
||||
or_obj1 = OrGate(and_obj2.out, and_obj3.out, prefix=self.prefix+"_or0")
|
||||
[self.add_component(obj) for obj in [and_obj1, and_obj2, and_obj3, and_obj4, or_obj1]]
|
||||
|
||||
self.out.connect(0, and_obj1.out)
|
||||
self.out.connect(1, or_obj1.out)
|
||||
self.out.connect(2, and_obj4.out)
|
||||
self.out.connect(3, ConstantWireValue0())
|
||||
|
||||
|
||||
class SignedApproximateTwoBitMultiplierM1(MultiplierCircuit):
|
||||
"""Class representing signed two-bit approximate multiplier variant M1.
|
||||
|
||||
Description of the __init__ method.
|
||||
|
||||
Args:
|
||||
a (Bus): First input bus.
|
||||
b (Bus): Second input bus.
|
||||
prefix (str, optional): Prefix name of signed two-bit approximate multiplier m1. Defaults to "".
|
||||
name (str, optional): Name of signed two-bit approximate multiplier m1. Defaults to "s_2bit_axm1".
|
||||
"""
|
||||
def __init__(self, a: Bus, b: Bus, prefix: str = "", name: str = "s_2bit_axm1", **kwargs):
|
||||
raise NotImplementedError("SignedApproximateTwoBitMultiplierM1 is not implemented yet.")
|
||||
|
||||
|
||||
class UnsignedApproximateTwoBitMultiplierM2(MultiplierCircuit):
|
||||
"""Class representing unsigned two-bit approximate multiplier variant M2.
|
||||
|
||||
M2 ax variant defined here: https://ieeexplore.ieee.org/document/8727537
|
||||
|
||||
```
|
||||
A1B1 A1B0 A0B1
|
||||
│ │ │ │ │ │
|
||||
┌▼─▼┐ ┌▼─▼┐ ┌▼─▼┐
|
||||
│AND│ │AND│ │AND│
|
||||
└─┬─┘ └─┬─┘ └─┬─┘
|
||||
│ │┌─────┴┐
|
||||
│ ┌┴┼────┐ │
|
||||
│ │ │ │ │
|
||||
│ ┌▼─▼┐ ┌▼─▼┐
|
||||
│ │XOR│ │AND│
|
||||
│ └─┬─┘ └─┬─┘
|
||||
└─────┐ ┌─────┼──────┤
|
||||
│ │ │ │
|
||||
┌▼─▼┐ │ │
|
||||
│XOR│ │ │
|
||||
└─┬─┘ │ │
|
||||
┌──────┼──────┼──────┤
|
||||
│ │ │ │
|
||||
▼ ▼ ▼ ▼
|
||||
O3 O2 O1 O0
|
||||
```
|
||||
|
||||
Description of the __init__ method.
|
||||
|
||||
Args:
|
||||
a (Bus): First input bus.
|
||||
b (Bus): Second input bus.
|
||||
prefix (str, optional): Prefix name of unsigned two-bit approximate multiplier m1. Defaults to "".
|
||||
name (str, optional): Name of unsigned two-bit approximate multiplier m1. Defaults to "u_2bit_axm1".
|
||||
"""
|
||||
def __init__(self, a: Bus, b: Bus, prefix: str = "", name: str = "u_2bit_axm1", **kwargs):
|
||||
self.N = max(a.N, b.N)
|
||||
assert self.N == 2
|
||||
super().__init__(inputs=[a, b], prefix=prefix, name=name, out_N=self.N*2, **kwargs)
|
||||
|
||||
# Bus sign extension in case buses have different lengths
|
||||
self.a.bus_extend(N=self.N, prefix=a.prefix)
|
||||
self.b.bus_extend(N=self.N, prefix=b.prefix)
|
||||
|
||||
and_obj1 = AndGate(self.a.get_wire(0), self.b.get_wire(1), prefix=self.prefix+"_and0")
|
||||
and_obj2 = AndGate(self.a.get_wire(1), self.b.get_wire(0), prefix=self.prefix+"_and1")
|
||||
and_obj3 = AndGate(self.a.get_wire(1), self.b.get_wire(1), prefix=self.prefix+"_and2")
|
||||
|
||||
and_obj4 = AndGate(and_obj1.out, and_obj2.out, prefix=self.prefix+"_and3")
|
||||
xor_obj1 = XorGate(and_obj1.out, and_obj2.out, prefix=self.prefix+"_xor0")
|
||||
|
||||
xor_obj2 = XorGate(and_obj4.out, and_obj3.out, prefix=self.prefix+"_xor1")
|
||||
[self.add_component(obj) for obj in [and_obj1, and_obj2, and_obj3, and_obj4, xor_obj1, xor_obj2]]
|
||||
|
||||
self.out.connect(0, and_obj4.out)
|
||||
self.out.connect(1, xor_obj1.out)
|
||||
self.out.connect(2, xor_obj2.out)
|
||||
self.out.connect(3, and_obj4.out)
|
||||
|
||||
|
||||
class SignedApproximateTwoBitMultiplierM2(MultiplierCircuit):
|
||||
"""Class representing signed two-bit approximate multiplier variant M2.
|
||||
|
||||
Description of the __init__ method.
|
||||
|
||||
Args:
|
||||
a (Bus): First input bus.
|
||||
b (Bus): Second input bus.
|
||||
prefix (str, optional): Prefix name of signed two-bit approximate multiplier m2. Defaults to "".
|
||||
name (str, optional): Name of signed two-bit approximate multiplier m2. Defaults to "s_2bit_axm2".
|
||||
"""
|
||||
def __init__(self, a: Bus, b: Bus, prefix: str = "", name: str = "s_2bit_axm2", **kwargs):
|
||||
raise NotImplementedError("SignedApproximateTwoBitMultiplierM2 is not implemented yet.")
|
||||
|
||||
|
||||
class UnsignedApproximateTwoBitMultiplierM3(MultiplierCircuit):
|
||||
"""Class representing unsigned two-bit approximate multiplier variant M3.
|
||||
|
||||
M3 ax variant defined here: https://ieeexplore.ieee.org/document/8727537
|
||||
|
||||
```
|
||||
A1B1 A1B0 A0B1 A0B0
|
||||
│ │ │ │ │ │ │ │
|
||||
┌▼─▼┐ ┌▼─▼┐ ┌▼─▼┐ ┌▼─▼┐
|
||||
│AND│ │AND│ │AND│ │AND│
|
||||
└─┬─┘ └─┬─┘ └─┬─┘ └─┬─┘
|
||||
│ │ └┐ │
|
||||
│ │ │ │
|
||||
│ └─────┐ │ │
|
||||
│ ┌▼─▼┐ │
|
||||
│ │ OR│ │
|
||||
│ └─┬─┘ │
|
||||
│┌─────┬──────┼──────┤
|
||||
││ │ │ │
|
||||
││ ┌─▼─┐ │ │
|
||||
││ │NOT│ │ │
|
||||
││ └─┬─┘ │ │
|
||||
┌┴┼────┐└┐ │ │
|
||||
│ │ │ │ │ │
|
||||
┌▼─▼┐ ┌▼─▼┐ │ │
|
||||
│AND│ │AND│ │ │
|
||||
└─┬─┘ └─┬─┘ │ │
|
||||
│ │ │ │
|
||||
▼ ▼ ▼ ▼
|
||||
O3 O2 O1 O0
|
||||
```
|
||||
|
||||
Description of the __init__ method.
|
||||
|
||||
Args:
|
||||
a (Bus): First input bus.
|
||||
b (Bus): Second input bus.
|
||||
prefix (str, optional): Prefix name of unsigned two-bit approximate multiplier m3. Defaults to "".
|
||||
name (str, optional): Name of unsigned two-bit approximate multiplier m3. Defaults to "u_2bit_axm3".
|
||||
"""
|
||||
def __init__(self, a: Bus, b: Bus, prefix: str = "", name: str = "u_2bit_axm3", **kwargs):
|
||||
self.N = max(a.N, b.N)
|
||||
assert self.N == 2
|
||||
super().__init__(inputs=[a, b], prefix=prefix, name=name, out_N=self.N*2, **kwargs)
|
||||
|
||||
# Bus sign extension in case buses have different lengths
|
||||
self.a.bus_extend(N=self.N, prefix=a.prefix)
|
||||
self.b.bus_extend(N=self.N, prefix=b.prefix)
|
||||
|
||||
and_obj1 = AndGate(self.a.get_wire(0), self.b.get_wire(0), prefix=self.prefix+"_and0")
|
||||
and_obj2 = AndGate(self.a.get_wire(0), self.b.get_wire(1), prefix=self.prefix+"_and1")
|
||||
and_obj3 = AndGate(self.a.get_wire(1), self.b.get_wire(0), prefix=self.prefix+"_and2")
|
||||
and_obj4 = AndGate(self.a.get_wire(1), self.b.get_wire(1), prefix=self.prefix+"_and3")
|
||||
|
||||
or_obj1 = OrGate(and_obj2.out, and_obj3.out, prefix=self.prefix+"_xor0")
|
||||
not_obj1 = NotGate(and_obj1.out, prefix=self.prefix+"_not0")
|
||||
|
||||
and_obj5 = AndGate(not_obj1.out, and_obj4.out, prefix=self.prefix+"_and4")
|
||||
and_obj6 = AndGate(and_obj1.out, and_obj4.out, prefix=self.prefix+"_and5")
|
||||
[self.add_component(obj) for obj in [and_obj1, and_obj2, and_obj3, and_obj4, or_obj1, not_obj1, and_obj5, and_obj6]]
|
||||
|
||||
self.out.connect(0, and_obj1.out)
|
||||
self.out.connect(1, or_obj1.out)
|
||||
self.out.connect(2, and_obj5.out)
|
||||
self.out.connect(3, and_obj6.out)
|
||||
|
||||
|
||||
class SignedApproximateTwoBitMultiplierM3(MultiplierCircuit):
|
||||
"""Class representing signed two-bit approximate multiplier variant M3.
|
||||
|
||||
Description of the __init__ method.
|
||||
|
||||
Args:
|
||||
a (Bus): First input bus.
|
||||
b (Bus): Second input bus.
|
||||
prefix (str, optional): Prefix name of signed two-bit approximate multiplier m3. Defaults to "".
|
||||
name (str, optional): Name of signed two-bit approximate multiplier m3. Defaults to "s_2bit_axm3".
|
||||
"""
|
||||
def __init__(self, a: Bus, b: Bus, prefix: str = "", name: str = "s_2bit_axm3", **kwargs):
|
||||
raise NotImplementedError("SignedApproximateTwoBitMultiplierM3 is not implemented yet.")
|
||||
|
||||
|
||||
class UnsignedApproximateTwoBitMultiplierM4(MultiplierCircuit):
|
||||
"""Class representing unsigned two-bit approximate multiplier variant M4.
|
||||
|
||||
M4 ax variant defined here: https://ieeexplore.ieee.org/document/8727537
|
||||
|
||||
```
|
||||
A1B1 A1B0 A0B1 A0B0
|
||||
│ │ │ │ │ │ │ │
|
||||
┌▼─▼┐ ┌▼─▼┐ ┌▼─▼┐ ┌▼─▼┐
|
||||
│AND│ │AND│ │AND│ │AND│
|
||||
└─┬─┘ └─┬─┘ └─┬─┘ └─┬─┘
|
||||
│ │ └┐ │
|
||||
│ └─────┐ │ │
|
||||
└──────┐ ┌▼─▼┐ │
|
||||
│ │XOR│ │
|
||||
│ └─┬─┘ │
|
||||
│ │ │
|
||||
▼ ▼ ▼ ▼
|
||||
O3=0 O2 O1 O0
|
||||
```
|
||||
|
||||
Description of the __init__ method.
|
||||
|
||||
Args:
|
||||
a (Bus): First input bus.
|
||||
b (Bus): Second input bus.
|
||||
prefix (str, optional): Prefix name of unsigned two-bit approximate multiplier m4. Defaults to "".
|
||||
name (str, optional): Name of unsigned two-bit approximate multiplier m4. Defaults to "u_2bit_axm4".
|
||||
"""
|
||||
def __init__(self, a: Bus, b: Bus, prefix: str = "", name: str = "u_2bit_axm4", **kwargs):
|
||||
self.N = max(a.N, b.N)
|
||||
assert self.N == 2
|
||||
super().__init__(inputs=[a, b], prefix=prefix, name=name, out_N=self.N*2, **kwargs)
|
||||
|
||||
# Bus sign extension in case buses have different lengths
|
||||
self.a.bus_extend(N=self.N, prefix=a.prefix)
|
||||
self.b.bus_extend(N=self.N, prefix=b.prefix)
|
||||
|
||||
and_obj1 = AndGate(self.a.get_wire(0), self.b.get_wire(0), prefix=self.prefix+"_and0")
|
||||
and_obj2 = AndGate(self.a.get_wire(0), self.b.get_wire(1), prefix=self.prefix+"_and1")
|
||||
and_obj3 = AndGate(self.a.get_wire(1), self.b.get_wire(0), prefix=self.prefix+"_and2")
|
||||
and_obj4 = AndGate(self.a.get_wire(1), self.b.get_wire(1), prefix=self.prefix+"_and3")
|
||||
|
||||
xor_obj1 = XorGate(and_obj2.out, and_obj3.out, prefix=self.prefix+"_xor0")
|
||||
[self.add_component(obj) for obj in [and_obj1, and_obj2, and_obj3, and_obj4, xor_obj1]]
|
||||
|
||||
self.out.connect(0, and_obj1.out)
|
||||
self.out.connect(1, xor_obj1.out)
|
||||
self.out.connect(2, and_obj4.out)
|
||||
self.out.connect(3, ConstantWireValue0())
|
||||
|
||||
|
||||
class SignedApproximateTwoBitMultiplierM4(MultiplierCircuit):
|
||||
"""Class representing signed two-bit approximate multiplier variant M4.
|
||||
|
||||
Description of the __init__ method.
|
||||
|
||||
Args:
|
||||
a (Bus): First input bus.
|
||||
b (Bus): Second input bus.
|
||||
prefix (str, optional): Prefix name of signed two-bit approximate multiplier m4. Defaults to "".
|
||||
name (str, optional): Name of signed two-bit approximate multiplier m4. Defaults to "s_2bit_axm4".
|
||||
"""
|
||||
def __init__(self, a: Bus, b: Bus, prefix: str = "", name: str = "s_2bit_axm4", **kwargs):
|
||||
raise NotImplementedError("SignedApproximateTwoBitMultiplierM4 is not implemented yet.")
|
||||
|
||||
|
||||
class UnsignedRecursiveMultiplier(MultiplierCircuit):
|
||||
"""Class representing unsigned recursive multiplier.
|
||||
|
||||
Input bit-vector length N can be any power of two greater than 1 (e.g. 2, 4, 8, ...).
|
||||
|
||||
The internal structure of the recursive multiplier is composed of subsequent two-bit submultipliers provided in the input `submultipliers` list.
|
||||
The `submultipliers` list should contain the classes of the two-bit submultipliers that will be used for instantiation. If None are provided, accurate two-bit submultipliers are assumed.
|
||||
|
||||
The number of submultipliers required is equal to (N/2)² for N > 2. For N = 2, only one two-bit submultiplier is required.
|
||||
|
||||
Description of the __init__ method.
|
||||
|
||||
Args:
|
||||
a (Bus): First input bus.
|
||||
b (Bus): Second input bus.
|
||||
prefix (str, optional): Prefix name of unsigned recursive multiplier. Defaults to "".
|
||||
name (str, optional): Name of unsigned recursive multiplier. Defaults to "u_rm".
|
||||
submultipliers (list, optional): List of composite two bit submultiplier classes for instantiation. If None are provided, accurate submultipliers are assumed. Defaults to None.
|
||||
Defaults (if None) to the required number of UnsignedAccurateTwoBitMultiplier instances.
|
||||
unsigned_adder_class_name (str, optional): Unsigned multi bit adder used to obtain final sums of products. Defaults to UnsignedCarryLookaheadAdder.
|
||||
"""
|
||||
def __init__(self, a: Bus, b: Bus, prefix: str = "", name: str = "u_rm", submultipliers: list = None, unsigned_adder_class_name: str = UnsignedCarryLookaheadAdder, **kwargs):
|
||||
self.N = max(a.N, b.N)
|
||||
assert self.N > 1 and self.N & (self.N-1) == 0 # assure that N is a power of two greater than 1 (So allowed N is 2, 4, 8, ..)
|
||||
super().__init__(inputs=[a, b], prefix=prefix, name=name, out_N=self.N*2, **kwargs)
|
||||
|
||||
# Bus sign extension in case buses have different lengths
|
||||
self.a.bus_extend(N=self.N, prefix=a.prefix)
|
||||
self.b.bus_extend(N=self.N, prefix=b.prefix)
|
||||
|
||||
if submultipliers is None: # By default, we assume composition from accurate two bit submultipliers
|
||||
if self.N == 2:
|
||||
submultipliers = [UnsignedAccurateTwoBitMultiplier]
|
||||
else:
|
||||
submultipliers = [UnsignedAccurateTwoBitMultiplier for _ in range((self.N//2)**2)]
|
||||
|
||||
assert (self.N > 2 and len(submultipliers) == (self.N//2)**2) or (self.N == 2 and len(submultipliers) == 1)
|
||||
|
||||
if self.N == 2: # Base case for just one two-bit multiplier
|
||||
# TODO add suffix in ariths_gen rework
|
||||
mult = submultipliers[0](Bus(prefix=self.prefix + "_a", wires_list=self.a.bus), Bus(prefix=self.prefix + "_b", wires_list=self.b.bus), prefix=self.prefix + "_" + str(self.get_instance_num(cls=submultipliers[0])), **kwargs)
|
||||
self.add_component(mult)
|
||||
self.out.connect_bus(mult.out)
|
||||
else:
|
||||
# Levels of construction of the recursive multiplier
|
||||
# recursive_levels = int(math.log2(self.N)-1) # Number of recursive levels based on the power ith power of 2 (e.g. for N=8, we have 2 recursive levels)
|
||||
block_level = 1
|
||||
partial_products = []
|
||||
|
||||
for m in range(len(submultipliers)): # Iterate over all 2-bit submultipliers (equals range(0, 4**recursive_levels))
|
||||
a_bus_offset = 0
|
||||
b_bus_offset = 0
|
||||
curr_level = block_level
|
||||
curr_id = m
|
||||
|
||||
# Determine the wires offsets from MSB (for input bus `a` and `b`) for the current 2-bit submultiplier
|
||||
# There is a pattern, for example for N=8, we have 16 two-bit submultipliers with offsets:
|
||||
# Mult ID: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
|
||||
# MSB a_offsets: 0 0 2 2 0 0 2 2 4 4 6 6 4 4 6 6
|
||||
# MSB b_offsets: 0 2 0 2 4 6 4 6 0 2 0 2 4 6 4 6
|
||||
while curr_level != 0:
|
||||
# A offset
|
||||
if curr_id // ((4**curr_level)//2) != 0:
|
||||
a_bus_offset += 2**curr_level
|
||||
|
||||
# B offset
|
||||
if (curr_id // ((4**curr_level)//4)) % 2 != 0:
|
||||
b_bus_offset += 2**curr_level
|
||||
|
||||
curr_level -= 1
|
||||
curr_id -= (4**curr_level)*((curr_id // (4**curr_level)))
|
||||
|
||||
# Create 2-bit submultiplier with the corresponding input bits
|
||||
# TODO add suffix in ariths_gen rework
|
||||
submult_a_bus = Bus(prefix=f"mult{m}_a", wires_list=self.a.bus[::-1][0+a_bus_offset:0+a_bus_offset+2][::-1], N=2)
|
||||
submult_b_bus = Bus(prefix=f"mult{m}_b", wires_list=self.b.bus[::-1][0+b_bus_offset:0+b_bus_offset+2][::-1], N=2)
|
||||
submult = submultipliers[m](submult_a_bus, submult_b_bus, prefix=self.prefix + "_" + str(self.get_instance_num(cls=submultipliers[m])))
|
||||
self.add_component(submult)
|
||||
|
||||
# Create wire vectors holding partial products for final summation
|
||||
pp = Bus(prefix=f"pp_{m}", N=self.out.N, wires_list=[ConstantWireValue0() for _ in range(self.out.N)])
|
||||
# [pp.connect_bus(submult.out, offset=(self.out.N-4)-(a_bus_offset+b_bus_offset))]
|
||||
[pp.connect((self.out.N-1)-(a_bus_offset+b_bus_offset)-i, submult.out[3-i], inserted_wire_desired_index=3-i) for i in range(4)]
|
||||
partial_products.append(pp)
|
||||
|
||||
# Distinction of levels of blocks to properly instantiate and connect 2-bit submultipliers
|
||||
# For example, for N=8, we have 4 two-bit submultipliers in the first level, but then we have 4 times the
|
||||
# same structure (4 two-bit mults) as the base component for the second level, similarly for N=16, but
|
||||
# with additional third layer (consisting of 16 two-bit submultipliers)
|
||||
if (m+1) % (4**block_level) == 0:
|
||||
block_level += 1 # Increase the block level
|
||||
|
||||
# Create tree of partial product adders
|
||||
while len(partial_products) != 1:
|
||||
N = len(partial_products)//2
|
||||
# Creation of composite unsigned multi bit adders from set of partial product vectors and addition of generated blocks outputs for next iteration
|
||||
for bus_index in range(0, N):
|
||||
# TODO arithsgen_rework: update check for bus declaration and assignment (if true do declare/assign again - here we would not create new bus, just assign the existing one); it should create cleaner outcode with unncecessary new bus declarations
|
||||
adder_name = unsigned_adder_class_name(a=a, b=b).prefix + str(self.get_instance_num(cls=unsigned_adder_class_name))
|
||||
# TODO rework the buses
|
||||
bus_a = Bus(prefix=f"{adder_name}_a", wires_list=partial_products[bus_index].bus, out_bus=True) if partial_products[bus_index].out_bus else partial_products[bus_index]
|
||||
bus_b = Bus(prefix=f"{adder_name}_b", wires_list=partial_products[bus_index+N].bus, out_bus=True) if partial_products[bus_index+N].out_bus else partial_products[bus_index+N]
|
||||
adder = unsigned_adder_class_name(a=bus_a, b=bus_b, prefix=self.prefix, name=adder_name, inner_component=True, **kwargs)
|
||||
self.add_component(adder)
|
||||
partial_products.append(adder.out)
|
||||
|
||||
# Update partial products list for next iteration until it contains only one output vector
|
||||
partial_products = partial_products[2*N:]
|
||||
# Connect the final output of the recursive multiplier
|
||||
self.out.connect_bus(partial_products[0])
|
||||
|
||||
|
||||
class SignedRecursiveMultiplier(MultiplierCircuit):
|
||||
"""Class representing signed recursive multiplier.
|
||||
|
||||
TODO
|
||||
Description of the __init__ method.
|
||||
|
||||
Args:
|
||||
a (Bus): First input bus.
|
||||
b (Bus): Second input bus.
|
||||
prefix (str, optional): Prefix name of signed recursive multiplier. Defaults to "".
|
||||
name (str, optional): Name of signed recursive multiplier. Defaults to "s_rm".
|
||||
"""
|
||||
def __init__(self, a: Bus, b: Bus, prefix: str = "", name: str = "s_rm", **kwargs):
|
||||
raise NotImplementedError("SignedRecursiveMultiplier is not implemented yet.")
|
@ -1,11 +1,8 @@
|
||||
from ariths_gen.wire_components import (
|
||||
Wire,
|
||||
ConstantWireValue0,
|
||||
ConstantWireValue1,
|
||||
Bus
|
||||
)
|
||||
from ariths_gen.core.arithmetic_circuits import (
|
||||
ArithmeticCircuit,
|
||||
MultiplierCircuit
|
||||
)
|
||||
from ariths_gen.one_bit_circuits.one_bit_components import (
|
||||
@ -13,13 +10,7 @@ from ariths_gen.one_bit_circuits.one_bit_components import (
|
||||
FullAdder
|
||||
)
|
||||
from ariths_gen.one_bit_circuits.logic_gates import (
|
||||
AndGate,
|
||||
NandGate,
|
||||
OrGate,
|
||||
NorGate,
|
||||
XorGate,
|
||||
XnorGate,
|
||||
NotGate
|
||||
AndGate
|
||||
)
|
||||
|
||||
|
||||
@ -87,7 +78,7 @@ class UnsignedTruncatedArrayMultiplier(MultiplierCircuit):
|
||||
# Cut level should be: 0 <= truncation_cut < N
|
||||
assert truncation_cut < self.N
|
||||
|
||||
super().__init__(a=a, b=b, prefix=prefix, name=name, out_N=self.N*2, **kwargs)
|
||||
super().__init__(inputs=[a, b], prefix=prefix, name=name, out_N=self.N*2, **kwargs)
|
||||
|
||||
# Bus sign extension in case buses have different lengths
|
||||
self.a.bus_extend(N=self.N, prefix=a.prefix)
|
||||
|
@ -1,11 +1,8 @@
|
||||
from ariths_gen.wire_components import (
|
||||
Wire,
|
||||
ConstantWireValue0,
|
||||
ConstantWireValue1,
|
||||
Bus
|
||||
)
|
||||
from ariths_gen.core.arithmetic_circuits import (
|
||||
ArithmeticCircuit,
|
||||
MultiplierCircuit
|
||||
)
|
||||
from ariths_gen.one_bit_circuits.one_bit_components import (
|
||||
@ -14,12 +11,6 @@ from ariths_gen.one_bit_circuits.one_bit_components import (
|
||||
)
|
||||
from ariths_gen.one_bit_circuits.logic_gates import (
|
||||
AndGate,
|
||||
NandGate,
|
||||
OrGate,
|
||||
NorGate,
|
||||
XorGate,
|
||||
XnorGate,
|
||||
NotGate
|
||||
)
|
||||
from ariths_gen.multi_bit_circuits.adders import (
|
||||
UnsignedCarryLookaheadAdder
|
||||
@ -101,7 +92,7 @@ class UnsignedTruncatedCarrySaveMultiplier(MultiplierCircuit):
|
||||
# Cut level should be: 0 <= truncation_cut < N
|
||||
assert truncation_cut < self.N
|
||||
|
||||
super().__init__(a=a, b=b, prefix=prefix, name=name, out_N=self.N*2, **kwargs)
|
||||
super().__init__(inputs=[a, b], prefix=prefix, name=name, out_N=self.N*2, **kwargs)
|
||||
|
||||
# Bus sign extension in case buses have different lengths
|
||||
self.a.bus_extend(N=self.N, prefix=a.prefix)
|
||||
|
@ -1,31 +1,20 @@
|
||||
from ariths_gen.wire_components import (
|
||||
Wire,
|
||||
ConstantWireValue0,
|
||||
ConstantWireValue1,
|
||||
Bus
|
||||
)
|
||||
from ariths_gen.core.arithmetic_circuits import (
|
||||
ArithmeticCircuit,
|
||||
MultiplierCircuit
|
||||
GeneralCircuit
|
||||
)
|
||||
from ariths_gen.one_bit_circuits.one_bit_components import (
|
||||
HalfAdder,
|
||||
FullAdder,
|
||||
TwoOneMultiplexer,
|
||||
FullSubtractor
|
||||
)
|
||||
from ariths_gen.one_bit_circuits.logic_gates import (
|
||||
AndGate,
|
||||
NandGate,
|
||||
OrGate,
|
||||
NorGate,
|
||||
XorGate,
|
||||
XnorGate,
|
||||
NotGate
|
||||
)
|
||||
|
||||
|
||||
class ArrayDivider(ArithmeticCircuit):
|
||||
class ArrayDivider(GeneralCircuit):
|
||||
"""Class representing array divider.
|
||||
|
||||
Array divider performs division between two N bit numbers and stores their
|
||||
@ -96,7 +85,7 @@ class ArrayDivider(ArithmeticCircuit):
|
||||
"""
|
||||
def __init__(self, a: Bus, b: Bus, prefix: str = "", name: str = "arrdiv", **kwargs):
|
||||
self.N = max(a.N, b.N)
|
||||
super().__init__(a=a, b=b, prefix=prefix, name=name, out_N=self.N, **kwargs)
|
||||
super().__init__(inputs=[a, b], prefix=prefix, name=name, out_N=self.N, **kwargs)
|
||||
|
||||
# Bus sign extension in case buses have different lengths
|
||||
self.a.bus_extend(N=self.N, prefix=a.prefix)
|
||||
|
@ -1,11 +1,9 @@
|
||||
from ariths_gen.wire_components import (
|
||||
Wire,
|
||||
ConstantWireValue0,
|
||||
ConstantWireValue1,
|
||||
Bus
|
||||
)
|
||||
from ariths_gen.core.arithmetic_circuits import (
|
||||
ArithmeticCircuit,
|
||||
MultiplierCircuit
|
||||
)
|
||||
from ariths_gen.one_bit_circuits.one_bit_components import (
|
||||
@ -15,11 +13,8 @@ from ariths_gen.one_bit_circuits.one_bit_components import (
|
||||
from ariths_gen.one_bit_circuits.logic_gates import (
|
||||
AndGate,
|
||||
NandGate,
|
||||
OrGate,
|
||||
NorGate,
|
||||
XorGate,
|
||||
XnorGate,
|
||||
NotGate
|
||||
XorGate
|
||||
)
|
||||
|
||||
|
||||
@ -83,7 +78,7 @@ class UnsignedArrayMultiplier(MultiplierCircuit):
|
||||
"""
|
||||
def __init__(self, a: Bus, b: Bus, prefix: str = "", name: str = "u_arrmul", **kwargs):
|
||||
self.N = max(a.N, b.N)
|
||||
super().__init__(a=a, b=b, prefix=prefix, name=name, out_N=self.N*2, **kwargs)
|
||||
super().__init__(inputs=[a, b], prefix=prefix, name=name, out_N=self.N*2, **kwargs)
|
||||
|
||||
# Bus sign extension in case buses have different lengths
|
||||
self.a.bus_extend(N=self.N, prefix=a.prefix)
|
||||
@ -190,7 +185,7 @@ class SignedArrayMultiplier(MultiplierCircuit):
|
||||
"""
|
||||
def __init__(self, a: Bus, b: Bus, prefix: str = "", name: str = "s_arrmul", **kwargs):
|
||||
self.N = max(a.N, b.N)
|
||||
super().__init__(a=a, b=b, prefix=prefix, name=name, out_N=self.N*2, signed=True, **kwargs)
|
||||
super().__init__(inputs=[a, b], prefix=prefix, name=name, out_N=self.N*2, signed=True, **kwargs)
|
||||
|
||||
# Bus sign extension in case buses have different lengths
|
||||
self.a.bus_extend(N=self.N, prefix=a.prefix)
|
||||
|
@ -1,11 +1,9 @@
|
||||
from ariths_gen.wire_components import (
|
||||
Wire,
|
||||
ConstantWireValue0,
|
||||
ConstantWireValue1,
|
||||
Bus
|
||||
)
|
||||
from ariths_gen.core.arithmetic_circuits import (
|
||||
ArithmeticCircuit,
|
||||
MultiplierCircuit
|
||||
)
|
||||
from ariths_gen.one_bit_circuits.one_bit_components import (
|
||||
@ -15,11 +13,7 @@ from ariths_gen.one_bit_circuits.one_bit_components import (
|
||||
from ariths_gen.one_bit_circuits.logic_gates import (
|
||||
AndGate,
|
||||
NandGate,
|
||||
OrGate,
|
||||
NorGate,
|
||||
XorGate,
|
||||
XnorGate,
|
||||
NotGate
|
||||
NorGate
|
||||
)
|
||||
from ariths_gen.multi_bit_circuits.adders import (
|
||||
UnsignedCarryLookaheadAdder
|
||||
@ -96,7 +90,7 @@ class UnsignedCarrySaveMultiplier(MultiplierCircuit):
|
||||
"""
|
||||
def __init__(self, a: Bus, b: Bus, prefix: str = "", name: str = "u_csamul", unsigned_adder_class_name: str = UnsignedCarryLookaheadAdder, **kwargs):
|
||||
self.N = max(a.N, b.N)
|
||||
super().__init__(a=a, b=b, prefix=prefix, name=name, out_N=self.N*2, **kwargs)
|
||||
super().__init__(inputs=[a, b], prefix=prefix, name=name, out_N=self.N*2, **kwargs)
|
||||
|
||||
# Bus sign extension in case buses have different lengths
|
||||
self.a.bus_extend(N=self.N, prefix=a.prefix)
|
||||
@ -219,7 +213,7 @@ class SignedCarrySaveMultiplier(MultiplierCircuit):
|
||||
"""
|
||||
def __init__(self, a: Bus, b: Bus, prefix: str = "", name: str = "s_csamul", unsigned_adder_class_name: str = UnsignedCarryLookaheadAdder, **kwargs):
|
||||
self.N = max(a.N, b.N)
|
||||
super().__init__(a=a, b=b, prefix=prefix, name=name, out_N=self.N*2, signed=True, **kwargs)
|
||||
super().__init__(inputs=[a, b], prefix=prefix, name=name, out_N=self.N*2, signed=True, **kwargs)
|
||||
|
||||
# Bus sign extension in case buses have different lengths
|
||||
self.a.bus_extend(N=self.N, prefix=a.prefix)
|
||||
|
@ -1,12 +1,12 @@
|
||||
from ariths_gen.multi_bit_circuits.adders.carry_lookahead_adder import UnsignedCarryLookaheadAdder
|
||||
from ariths_gen.multi_bit_circuits.adders.carry_lookahead_adder import (
|
||||
UnsignedCarryLookaheadAdder
|
||||
)
|
||||
from ariths_gen.wire_components import (
|
||||
Wire,
|
||||
ConstantWireValue0,
|
||||
ConstantWireValue1,
|
||||
Bus
|
||||
)
|
||||
from ariths_gen.core.arithmetic_circuits import (
|
||||
ArithmeticCircuit,
|
||||
MultiplierCircuit
|
||||
)
|
||||
from ariths_gen.one_bit_circuits.one_bit_components import (
|
||||
@ -14,13 +14,7 @@ from ariths_gen.one_bit_circuits.one_bit_components import (
|
||||
FullAdder
|
||||
)
|
||||
from ariths_gen.one_bit_circuits.logic_gates import (
|
||||
AndGate,
|
||||
NandGate,
|
||||
OrGate,
|
||||
NorGate,
|
||||
XorGate,
|
||||
XnorGate,
|
||||
NotGate
|
||||
XorGate
|
||||
)
|
||||
|
||||
|
||||
@ -50,7 +44,7 @@ class UnsignedDaddaMultiplier(MultiplierCircuit):
|
||||
"""
|
||||
def __init__(self, a: Bus, b: Bus, prefix: str = "", name: str = "u_dadda_cla", unsigned_adder_class_name: str = UnsignedCarryLookaheadAdder, **kwargs):
|
||||
self.N = max(a.N, b.N)
|
||||
super().__init__(a=a, b=b, prefix=prefix, name=name, out_N=self.N*2, **kwargs)
|
||||
super().__init__(inputs=[a, b], prefix=prefix, name=name, out_N=self.N*2, **kwargs)
|
||||
|
||||
# Bus sign extension in case buses have different lengths
|
||||
self.a.bus_extend(N=self.N, prefix=a.prefix)
|
||||
@ -154,7 +148,7 @@ class SignedDaddaMultiplier(MultiplierCircuit):
|
||||
"""
|
||||
def __init__(self, a: Bus, b: Bus, prefix: str = "", name: str = "s_dadda_cla", unsigned_adder_class_name: str = UnsignedCarryLookaheadAdder, **kwargs):
|
||||
self.N = max(a.N, b.N)
|
||||
super().__init__(a=a, b=b, prefix=prefix, name=name, out_N=self.N*2, signed=True, **kwargs)
|
||||
super().__init__(inputs=[a, b], prefix=prefix, name=name, out_N=self.N*2, signed=True, **kwargs)
|
||||
|
||||
# Bus sign extension in case buses have different lengths
|
||||
self.a.bus_extend(N=self.N, prefix=a.prefix)
|
||||
|
@ -1,12 +1,12 @@
|
||||
from ariths_gen.multi_bit_circuits.adders.carry_lookahead_adder import UnsignedCarryLookaheadAdder
|
||||
from ariths_gen.multi_bit_circuits.adders.carry_lookahead_adder import (
|
||||
UnsignedCarryLookaheadAdder
|
||||
)
|
||||
from ariths_gen.wire_components import (
|
||||
Wire,
|
||||
ConstantWireValue0,
|
||||
ConstantWireValue1,
|
||||
Bus
|
||||
)
|
||||
from ariths_gen.core.arithmetic_circuits import (
|
||||
ArithmeticCircuit,
|
||||
MultiplierCircuit
|
||||
)
|
||||
from ariths_gen.one_bit_circuits.one_bit_components import (
|
||||
@ -14,13 +14,7 @@ from ariths_gen.one_bit_circuits.one_bit_components import (
|
||||
FullAdder
|
||||
)
|
||||
from ariths_gen.one_bit_circuits.logic_gates import (
|
||||
AndGate,
|
||||
NandGate,
|
||||
OrGate,
|
||||
NorGate,
|
||||
XorGate,
|
||||
XnorGate,
|
||||
NotGate
|
||||
XorGate
|
||||
)
|
||||
from ariths_gen.multi_bit_circuits.adders import (
|
||||
CarrySaveAdderComponent,
|
||||
@ -86,7 +80,7 @@ class UnsignedWallaceMultiplier(MultiplierCircuit):
|
||||
"""
|
||||
def __init__(self, a: Bus, b: Bus, prefix: str = "", name: str = "u_wallace_cla", use_csa: bool = True, unsigned_adder_class_name: str = UnsignedCarryLookaheadAdder, **kwargs):
|
||||
self.N = max(a.N, b.N)
|
||||
super().__init__(a=a, b=b, prefix=prefix, name=name, out_N=self.N*2, **kwargs)
|
||||
super().__init__(inputs=[a, b], prefix=prefix, name=name, out_N=self.N*2, **kwargs)
|
||||
|
||||
# Bus sign extension in case buses have different lengths
|
||||
self.a.bus_extend(N=self.N, prefix=a.prefix)
|
||||
@ -249,7 +243,7 @@ class SignedWallaceMultiplier(MultiplierCircuit):
|
||||
"""
|
||||
def __init__(self, a: Bus, b: Bus, prefix: str = "", name: str = "s_wallace_cla", use_csa: bool = True, unsigned_adder_class_name: str = UnsignedCarryLookaheadAdder, **kwargs):
|
||||
self.N = max(a.N, b.N)
|
||||
super().__init__(a=a, b=b, prefix=prefix, name=name, out_N=self.N*2, signed=True, **kwargs)
|
||||
super().__init__(inputs=[a, b], prefix=prefix, name=name, out_N=self.N*2, signed=True, **kwargs)
|
||||
|
||||
# Bus sign extension in case buses have different lengths
|
||||
self.a.bus_extend(N=self.N, prefix=a.prefix)
|
||||
|
21
ariths_gen/multi_bit_circuits/others/__init__.py
Normal file
21
ariths_gen/multi_bit_circuits/others/__init__.py
Normal file
@ -0,0 +1,21 @@
|
||||
|
||||
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,
|
||||
UnsignedCompareLTE,
|
||||
UnsignedCompareGT,
|
||||
UnsignedCompareGTE
|
||||
)
|
||||
|
||||
from ariths_gen.multi_bit_circuits.others.popcount_compare import (
|
||||
PopCountCompare
|
||||
)
|
61
ariths_gen/multi_bit_circuits/others/bit_reduce.py
Normal file
61
ariths_gen/multi_bit_circuits/others/bit_reduce.py
Normal file
@ -0,0 +1,61 @@
|
||||
from ariths_gen.wire_components import (
|
||||
Bus
|
||||
)
|
||||
from ariths_gen.core.arithmetic_circuits import (
|
||||
GeneralCircuit
|
||||
)
|
||||
from ariths_gen.one_bit_circuits.logic_gates import (
|
||||
AndGate,
|
||||
OrGate
|
||||
)
|
||||
from ariths_gen.core.logic_gate_circuits import (
|
||||
TwoInputLogicGate
|
||||
)
|
||||
|
||||
|
||||
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
|
||||
super().__init__(name=name, prefix=prefix, inputs=[a], out_N=1, **kwargs)
|
||||
|
||||
# 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 = branch + "A")
|
||||
c = create_tree(c_in, depth= depth + 1, branch = 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 = "andreduce", **kwargs):
|
||||
super().__init__(a=a, gate=AndGate, prefix=prefix, name=name, **kwargs)
|
155
ariths_gen/multi_bit_circuits/others/compare.py
Normal file
155
ariths_gen/multi_bit_circuits/others/compare.py
Normal file
@ -0,0 +1,155 @@
|
||||
from ariths_gen.wire_components import (
|
||||
ConstantWireValue0,
|
||||
ConstantWireValue1,
|
||||
Bus
|
||||
)
|
||||
from ariths_gen.core.arithmetic_circuits import (
|
||||
GeneralCircuit
|
||||
)
|
||||
from ariths_gen.one_bit_circuits.logic_gates import (
|
||||
AndGate,
|
||||
XnorGate,
|
||||
NotGate
|
||||
)
|
||||
from ariths_gen.multi_bit_circuits.others import (
|
||||
OrReduce
|
||||
)
|
||||
|
||||
|
||||
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.N = max(a.N, b.N)
|
||||
super().__init__(name=name, prefix=prefix, inputs=[a, b], out_N=1)
|
||||
|
||||
# create wires
|
||||
psum = ConstantWireValue1()
|
||||
|
||||
res = Bus(N = self.N, prefix=self.prefix + "res")
|
||||
|
||||
for i in reversed(range(self.N)):
|
||||
iA = self.a[i] if i < self.a.N else ConstantWireValue0()
|
||||
iB = self.b[i] if i < self.b.N else ConstantWireValue0()
|
||||
|
||||
i1 = self.add_component(NotGate(iA, f"{self.prefix}_i1_{i}")).out
|
||||
i2 = iB
|
||||
|
||||
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(iA, iB, f"{self.prefix}_pi_{i}", parent_component=self)).out
|
||||
psum = self.add_component(AndGate(pi, psum, f"{self.prefix}_psum_{i}")).out
|
||||
|
||||
|
||||
red = self.add_component(OrReduce(res, prefix=f"{self.prefix}_orred", inner_component=True))
|
||||
self.out.connect_bus(red.out)
|
||||
|
||||
|
||||
class UnsignedCompareLTE(GeneralCircuit):
|
||||
"""Class representing unsigned compare
|
||||
|
||||
Returns true if a <= b
|
||||
"""
|
||||
|
||||
def __init__(self, a: Bus, b: Bus, prefix : str = "", name : str = "cmp_lte", **kwargs):
|
||||
self.N = max(a.N, b.N)
|
||||
super().__init__(name=name, prefix=prefix, inputs=[a, b], out_N=1)
|
||||
|
||||
# create wires
|
||||
psum = ConstantWireValue1()
|
||||
|
||||
res = Bus(N = self.N + 1, prefix=self.prefix + "res")
|
||||
|
||||
for i in reversed(range(self.N)):
|
||||
iA = self.a[i] if i < self.a.N else ConstantWireValue0()
|
||||
iB = self.b[i] if i < self.b.N else ConstantWireValue0()
|
||||
|
||||
i1 = self.add_component(NotGate(iA, f"{self.prefix}_i1_{i}")).out
|
||||
i2 = iB
|
||||
|
||||
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(iA, iB, f"{self.prefix}_pi_{i}", parent_component=self)).out
|
||||
psum = self.add_component(AndGate(pi, psum, f"{self.prefix}_psum_{i}")).out
|
||||
|
||||
res[self.N] = psum # or all equal (xor)
|
||||
|
||||
red = self.add_component(OrReduce(res, prefix=f"{self.prefix}_orred", inner_component=True))
|
||||
|
||||
self.out.connect_bus(red.out)
|
||||
|
||||
|
||||
|
||||
class UnsignedCompareGT(GeneralCircuit):
|
||||
"""Class representing unsigned compare
|
||||
|
||||
|
||||
Returns true if a < b
|
||||
"""
|
||||
|
||||
def __init__(self, a: Bus, b: Bus, prefix : str = "", name : str = "cmp_gt", **kwargs):
|
||||
self.N = max(a.N, b.N)
|
||||
super().__init__(name=name, prefix=prefix, inputs=[a, b], out_N=1)
|
||||
|
||||
# create wires
|
||||
psum = ConstantWireValue1()
|
||||
|
||||
res = Bus(N = self.N, prefix=self.prefix + "res")
|
||||
|
||||
for i in reversed(range(self.N)):
|
||||
iA = self.a[i] if i < self.a.N else ConstantWireValue0()
|
||||
iB = self.b[i] if i < self.b.N else ConstantWireValue0()
|
||||
|
||||
i1 = iA
|
||||
i2 = self.add_component(NotGate(iB, f"{self.prefix}_i2_{i}")).out
|
||||
|
||||
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(iA, iB, f"{self.prefix}_pi_{i}", parent_component=self)).out
|
||||
psum = self.add_component(AndGate(pi, psum, f"{self.prefix}_psum_{i}")).out
|
||||
|
||||
|
||||
red = self.add_component(OrReduce(res, prefix=f"{self.prefix}_orred", inner_component=True))
|
||||
self.out.connect_bus(red.out)
|
||||
|
||||
|
||||
class UnsignedCompareGTE(GeneralCircuit):
|
||||
"""Class representing unsigned compare
|
||||
|
||||
Returns true if a <= b
|
||||
"""
|
||||
|
||||
def __init__(self, a: Bus, b: Bus, prefix : str = "", name : str = "cmp_gte", **kwargs):
|
||||
self.N = max(a.N, b.N)
|
||||
super().__init__(name=name, prefix=prefix, inputs=[a, b], out_N=1)
|
||||
|
||||
# create wires
|
||||
psum = ConstantWireValue1()
|
||||
|
||||
res = Bus(N = self.N + 1, prefix=self.prefix + "res")
|
||||
|
||||
for i in reversed(range(self.N)):
|
||||
iA = self.a[i] if i < self.a.N else ConstantWireValue0()
|
||||
iB = self.b[i] if i < self.b.N else ConstantWireValue0()
|
||||
|
||||
i1 = iA
|
||||
i2 = self.add_component(NotGate(iB, f"{self.prefix}_i1_{i}", parent_component=self)).out
|
||||
|
||||
and1 = self.add_component(AndGate(i1, i2, f"{self.prefix}_and1_{i}", parent_component=self)).out
|
||||
res[i] = self.add_component(AndGate(and1, psum, f"{self.prefix}_and2_{i}", parent_component=self)).out
|
||||
|
||||
pi = self.add_component(XnorGate(iA, iB, f"{self.prefix}_pi_{i}", parent_component=self)).out
|
||||
psum = self.add_component(AndGate(pi, psum, f"{self.prefix}_psum_{i}", parent_component=self)).out
|
||||
|
||||
res[self.N] = psum # or all equal (xor)
|
||||
|
||||
red = self.add_component(OrReduce(res, prefix=f"{self.prefix}_orred", inner_component=True, parent_component=self))
|
||||
|
||||
self.out.connect_bus(red.out)
|
59
ariths_gen/multi_bit_circuits/others/popcount.py
Normal file
59
ariths_gen/multi_bit_circuits/others/popcount.py
Normal file
@ -0,0 +1,59 @@
|
||||
from ariths_gen.wire_components import (
|
||||
Bus
|
||||
)
|
||||
from ariths_gen.core.arithmetic_circuits import (
|
||||
GeneralCircuit,
|
||||
GeneralCircuit
|
||||
)
|
||||
from ariths_gen.multi_bit_circuits.adders import (
|
||||
UnsignedRippleCarryAdder
|
||||
)
|
||||
from typing import Optional
|
||||
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 : Optional[GeneralCircuit] = None, prefix : str = "", name : str = "popcnt", **kwargs):
|
||||
self.N = a.N
|
||||
outc = ceil(log2(self.N + 1))
|
||||
#print("outc", outc)
|
||||
super().__init__(name=name, prefix=prefix, inputs=[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{branch}_{depth}A")
|
||||
c_in = Bus(N=a.N - half, prefix=f"c_inn{branch}_{depth}B")
|
||||
#print(a, half, a.N)
|
||||
for i, j in enumerate(range(half)):
|
||||
b_in.connect(i, a.get_wire(j))
|
||||
for i, j in enumerate(range(half, a.N)):
|
||||
c_in.connect(i, a.get_wire(j))
|
||||
|
||||
b = create_tree(b_in, depth=depth + 1, branch = branch + "A")
|
||||
c = create_tree(c_in, depth= depth + 1, branch = branch + "B")
|
||||
d = self.adder(a=b, b=c,
|
||||
prefix = f"{self.prefix}_add{branch}_{depth}", inner_component=True,
|
||||
parent_component=self)
|
||||
self.add_component(d)
|
||||
return d.out
|
||||
|
||||
sumbus = create_tree(self.a,0, "X")
|
||||
#print(sumbus)
|
||||
self.out.connect_bus(sumbus)
|
45
ariths_gen/multi_bit_circuits/others/popcount_compare.py
Normal file
45
ariths_gen/multi_bit_circuits/others/popcount_compare.py
Normal file
@ -0,0 +1,45 @@
|
||||
from ariths_gen.wire_components import (
|
||||
Bus
|
||||
)
|
||||
from ariths_gen.core.arithmetic_circuits import (
|
||||
GeneralCircuit
|
||||
)
|
||||
|
||||
from ariths_gen.multi_bit_circuits.others import (
|
||||
UnsignedPopCount,
|
||||
UnsignedCompareGTE
|
||||
)
|
||||
|
||||
|
||||
class PopCountCompare(GeneralCircuit):
|
||||
"""Class representing a circiut
|
||||
if number of ones in a is larger or equal than number of ones in b
|
||||
|
||||
│ │ │ │ │ │ │ │ │
|
||||
▼ ▼ ▼ ▼ ▼ ▼ ▼ ▼ ▼
|
||||
┌───────┐ ┌─────────┐
|
||||
│ popcnt│ │ popcnt │
|
||||
└──┬────┘ └─────┬───┘
|
||||
│ │
|
||||
└───────┐ ┌──────┘
|
||||
┌──▼───▼──┐
|
||||
│ <= │
|
||||
└────┬────┘
|
||||
│
|
||||
▼
|
||||
"""
|
||||
|
||||
def __init__(self, a: Bus, b: Bus, prefix : str = "", name : str = "popcnt_cmp", **kwargs):
|
||||
super().__init__(name=name, prefix=prefix, inputs=[a, b], out_N=1, **kwargs)
|
||||
|
||||
p1 = self.add_component(UnsignedPopCount(a=Bus(wires_list=self.a.bus, prefix=f"{prefix}_popcount1_a"),
|
||||
prefix=f"{prefix}_popcount1",
|
||||
inner_component=True)).out
|
||||
p2 = self.add_component(UnsignedPopCount(a=Bus(wires_list=self.b.bus, prefix=f"{prefix}_popcount2_a"),
|
||||
prefix=f"{prefix}_popcount2",
|
||||
inner_component=True)).out
|
||||
#N = max(p1.N, p2.N)
|
||||
#p1.bus_extend(N)
|
||||
#p2.bus_extend(N)
|
||||
red = self.add_component(UnsignedCompareGTE(p1, p2, prefix=f"{prefix}_cmp", inner_component = True))
|
||||
self.out.connect_bus(red.out)
|
8
ariths_gen/multi_bit_circuits/subtractors/__init__.py
Normal file
8
ariths_gen/multi_bit_circuits/subtractors/__init__.py
Normal file
@ -0,0 +1,8 @@
|
||||
from ariths_gen.multi_bit_circuits.subtractors.ripple_carry_subtractor import (
|
||||
UnsignedRippleCarrySubtractor, SignedRippleCarrySubtractor
|
||||
)
|
||||
|
||||
|
||||
from ariths_gen.multi_bit_circuits.subtractors.ripple_borrow_subtractor import (
|
||||
UnsignedRippleBorrowSubtractor, SignedRippleBorrowSubtractor
|
||||
)
|
@ -0,0 +1,101 @@
|
||||
from ariths_gen.wire_components import (
|
||||
Bus
|
||||
)
|
||||
from ariths_gen.core.arithmetic_circuits import (
|
||||
GeneralCircuit
|
||||
)
|
||||
from ariths_gen.one_bit_circuits.one_bit_components import (
|
||||
HalfSubtractor,
|
||||
FullSubtractor
|
||||
)
|
||||
from ariths_gen.one_bit_circuits.logic_gates import (
|
||||
XorGate
|
||||
)
|
||||
|
||||
|
||||
class UnsignedRippleBorrowSubtractor(GeneralCircuit):
|
||||
"""Class representing unsigned ripple borrow subtractor.
|
||||
|
||||
Unsigned ripple borrow subtractor represents N-bit unsigned subtractor which is composed of
|
||||
N one bit subtractors, where the first is a half subtractor and rest are full subtractor.
|
||||
```
|
||||
B3 A3 B2 A2 B1 A1 B0 A0
|
||||
│ │ │ │ │ │ │ │
|
||||
┌─▼──▼─┐ ┌─▼──▼─┐ ┌─▼──▼─┐ ┌─▼──▼─┐
|
||||
│ │ B3│ │ B2│ │ B1│ │
|
||||
┌──┤ FS │◄──┤ FS │◄──┤ FS │◄──┤ HS │
|
||||
│ │ │ │ │ │ │ │ │
|
||||
│ └──────┘ └──────┘ └──────┘ └──────┘
|
||||
▼ ▼ ▼ ▼ ▼
|
||||
Bout D3 D2 D1 D0
|
||||
```
|
||||
|
||||
Description of the __init__ method.
|
||||
|
||||
Args:
|
||||
a (Bus): First input bus.
|
||||
b (Bus): Second input bus.
|
||||
prefix (str, optional): Prefix name of unsigned rbs. Defaults to "".
|
||||
name (str, optional): Name of unsigned rbs. Defaults to "u_rbs".
|
||||
"""
|
||||
def __init__(self, a: Bus, b: Bus, prefix: str = "", name: str = "u_rbs", **kwargs):
|
||||
self.N = max(a.N, b.N)
|
||||
super().__init__(inputs=[a, b], prefix=prefix, name=name, out_N=self.N + 1, signed_out=True, **kwargs)
|
||||
|
||||
# Bus sign extension in case buses have different lengths
|
||||
self.a.bus_extend(N=self.N, prefix=a.prefix)
|
||||
self.b.bus_extend(N=self.N, prefix=b.prefix)
|
||||
|
||||
# Gradual addition of 1-bit subtractor components
|
||||
for input_index in range(self.N):
|
||||
# First adder is a half subtractor
|
||||
if input_index == 0:
|
||||
obj_subtractor = HalfSubtractor(self.a.get_wire(input_index), self.b.get_wire(input_index), prefix=self.prefix+"_hs")
|
||||
# Rest adders are full subtractor
|
||||
else:
|
||||
obj_subtractor = FullSubtractor(self.a.get_wire(input_index), self.b.get_wire(input_index), obj_subtractor.get_borrow_wire(), prefix=self.prefix+"_fs"+str(input_index))
|
||||
|
||||
self.add_component(obj_subtractor)
|
||||
self.out.connect(input_index, obj_subtractor.get_difference_wire())
|
||||
if input_index == (self.N-1):
|
||||
self.out.connect(self.N, obj_subtractor.get_borrow_wire())
|
||||
|
||||
|
||||
class SignedRippleBorrowSubtractor(UnsignedRippleBorrowSubtractor, GeneralCircuit):
|
||||
"""Class representing signed ripple borrow subtractor.
|
||||
|
||||
Signed ripple borrow subtractor represents N-bit signed subtractor which is composed of
|
||||
N one bit subtractor, where the first is a half subtractor and rest are full subtractor.
|
||||
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 │ B4│ │ B3│ │ B2│ │ B1│ │
|
||||
│Extend│◄──┤ FS │◄──┤ FS │◄──┤ FS │◄──┤ HS │
|
||||
│ │ │ │ │ │ │ │ │ │
|
||||
└──┬───┘ └──┬───┘ └──┬───┘ └──┬───┘ └──┬───┘
|
||||
▼ ▼ ▼ ▼ ▼
|
||||
Bout D3 D2 D1 D0
|
||||
```
|
||||
|
||||
Description of the __init__ method.
|
||||
|
||||
Args:
|
||||
a (Bus): First input bus.
|
||||
b (Bus): Second input bus.
|
||||
prefix (str, optional): Prefix name of signed rbs. Defaults to "".
|
||||
name (str, optional): Name of signed rbs. Defaults to "s_rbs".
|
||||
"""
|
||||
def __init__(self, a: Bus, b: Bus, prefix: str = "", name: str = "s_rbs", **kwargs):
|
||||
super().__init__(a=a, b=b, prefix=prefix, name=name, signed=True, **kwargs)
|
||||
|
||||
# Additional XOR gates to ensure correct sign extension in case of sign addition
|
||||
sign_xor_1 = XorGate(self.get_previous_component(1).a, self.get_previous_component(1).b, prefix=self.prefix+"_xor"+str(self.get_instance_num(cls=XorGate)), parent_component=self)
|
||||
self.add_component(sign_xor_1)
|
||||
sign_xor_2 = XorGate(sign_xor_1.out, self.get_previous_component(2).get_borrow_wire(), prefix=self.prefix+"_xor"+str(self.get_instance_num(cls=XorGate)), parent_component=self)
|
||||
self.add_component(sign_xor_2)
|
||||
self.out.connect(self.N, sign_xor_2.out)
|
@ -0,0 +1,108 @@
|
||||
from ariths_gen.wire_components import (
|
||||
Bus
|
||||
)
|
||||
from ariths_gen.core.arithmetic_circuits import (
|
||||
GeneralCircuit
|
||||
)
|
||||
from ariths_gen.one_bit_circuits.one_bit_components import (
|
||||
HalfAdder,
|
||||
FullAdder
|
||||
)
|
||||
from ariths_gen.one_bit_circuits.logic_gates import (
|
||||
XorGate, NotGate
|
||||
)
|
||||
from ariths_gen.wire_components.wires import ConstantWireValue1
|
||||
|
||||
|
||||
class UnsignedRippleCarrySubtractor(GeneralCircuit):
|
||||
"""Class representing unsigned ripple carry subtractor.
|
||||
|
||||
Unsigned ripple carry subtractor represents N-bit unsigned subtractor which is composed of
|
||||
N one bit fulladders. The first FA has carry-in set to one and the B inputs of all FAs are inverted.
|
||||
```
|
||||
── ── ── ──
|
||||
B3 A3 B2 A2 B1 A1 B0 A0
|
||||
│ │ │ │ │ │ │ │
|
||||
┌─▼──▼─┐ ┌─▼──▼─┐ ┌─▼──▼─┐ ┌─▼──▼─┐
|
||||
│ │ C3│ │ C2│ │ C1│ │ 1
|
||||
┌──┤ FA │◄──┤ FA │◄──┤ FA │◄──┤ FA │◄──
|
||||
│ │ │ │ │ │ │ │ │
|
||||
│ └──────┘ └──────┘ └──────┘ └──────┘
|
||||
▼ ▼ ▼ ▼ ▼
|
||||
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 rcs. Defaults to "".
|
||||
name (str, optional): Name of unsigned rcs. Defaults to "u_rcs".
|
||||
"""
|
||||
def __init__(self, a: Bus, b: Bus, prefix: str = "", name: str = "u_rcs", **kwargs):
|
||||
self.N = max(a.N, b.N)
|
||||
super().__init__(inputs=[a, b], prefix=prefix, name=name, out_N=self.N + 1, signed_out=True, **kwargs)
|
||||
|
||||
# Bus sign extension in case buses have different lengths
|
||||
self.a.bus_extend(N=self.N, prefix=a.prefix)
|
||||
self.b.bus_extend(N=self.N, prefix=b.prefix)
|
||||
|
||||
# Gradual addition of 1-bit adder components
|
||||
for input_index in range(self.N):
|
||||
b_not = self.add_component(NotGate(self.b.get_wire(input_index), prefix=self.prefix+"_not"+str(input_index)))
|
||||
# First adder is a half adder
|
||||
if input_index == 0:
|
||||
obj_adder = FullAdder(self.a.get_wire(input_index), b_not.out, ConstantWireValue1(), prefix=self.prefix+"_ha")
|
||||
# Rest adders are full adders
|
||||
else:
|
||||
obj_adder = FullAdder(self.a.get_wire(input_index), b_not.out, obj_adder.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())
|
||||
|
||||
# invert the last carry wire
|
||||
self.add_component(NotGate(obj_adder.get_carry_wire(), prefix=self.prefix+"_not_c"+str(self.N-1)))
|
||||
self.out.connect(self.N, self.get_previous_component().out)
|
||||
|
||||
|
||||
class SignedRippleCarrySubtractor(UnsignedRippleCarrySubtractor, GeneralCircuit):
|
||||
"""Class representing signed ripple carry subtractor.
|
||||
|
||||
Signed ripple carry subtractor represents N-bit signed subtractor which is composed of
|
||||
N one bit fulladders. The first FA has carry-in set to one and the B inputs of all FAs are inverted.
|
||||
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│ │ 1
|
||||
│Extend│◄──┤ FA │◄──┤ FA │◄──┤ FA │◄──┤ FA │◄──
|
||||
│ │ │ │ │ │ │ │ │ │
|
||||
└──┬───┘ └──┬───┘ └──┬───┘ └──┬───┘ └──┬───┘
|
||||
▼ ▼ ▼ ▼ ▼
|
||||
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 rcs. Defaults to "".
|
||||
name (str, optional): Name of signed rcs. Defaults to "s_rcs".
|
||||
"""
|
||||
def __init__(self, a: Bus, b: Bus, prefix: str = "", name: str = "s_rcs", **kwargs):
|
||||
self.N = max(a.N, b.N)
|
||||
super().__init__(a=a, b=b, prefix=prefix, name=name, signed=True, **kwargs)
|
||||
|
||||
# Additional XOR gates to ensure correct sign extension in case of sign addition
|
||||
sign_xor_1 = XorGate(self.get_previous_component(2).a, self.get_previous_component(2).b, prefix=self.prefix+"_xor"+str(self.get_instance_num(cls=XorGate)), parent_component=self)
|
||||
self.add_component(sign_xor_1)
|
||||
sign_xor_2 = XorGate(sign_xor_1.out, self.get_previous_component(3).get_carry_wire(), prefix=self.prefix+"_xor"+str(self.get_instance_num(cls=XorGate)), parent_component=self)
|
||||
self.add_component(sign_xor_2)
|
||||
self.out.connect(self.N, sign_xor_2.out)
|
@ -92,7 +92,8 @@ class NandGate(TwoInputInvertedLogicGate):
|
||||
# If constant input is present, logic gate is not generated and corresponding
|
||||
# input value is propagated to the output to connect to other components
|
||||
if a.is_const() and a.value == 1:
|
||||
output = NotGate(a=b, prefix=prefix, outid=outid, parent_component=parent_component)
|
||||
assert self.parent_component, "Parent component for gate {self} is not defined"
|
||||
output = NotGate(a=b, prefix=prefix + "_not", outid=outid, parent_component=parent_component)
|
||||
self.parent_component.add_component(output) if parent_component is not None else None
|
||||
self.out = output.out
|
||||
self.disable_generation = True
|
||||
@ -100,7 +101,8 @@ class NandGate(TwoInputInvertedLogicGate):
|
||||
self.out = ConstantWireValue1()
|
||||
self.disable_generation = True
|
||||
elif b.is_const() and b.value == 1:
|
||||
output = NotGate(a=a, prefix=prefix, outid=outid, parent_component=parent_component)
|
||||
assert self.parent_component, "Parent component for gate {self} is not defined"
|
||||
output = NotGate(a=a, prefix=prefix + "_not", outid=outid, parent_component=parent_component)
|
||||
self.parent_component.add_component(output) if parent_component is not None else None
|
||||
self.out = output.out
|
||||
self.disable_generation = True
|
||||
@ -217,7 +219,8 @@ class NorGate(TwoInputInvertedLogicGate):
|
||||
self.out = ConstantWireValue0()
|
||||
self.disable_generation = True
|
||||
elif a.is_const() and a.value == 0:
|
||||
output = NotGate(a=b, prefix=prefix, outid=outid, parent_component=parent_component)
|
||||
assert self.parent_component, "Parent component for gate {self} is not defined"
|
||||
output = NotGate(a=b, prefix=prefix + "_not", outid=outid, parent_component=parent_component)
|
||||
self.parent_component.add_component(output) if parent_component is not None else None
|
||||
self.out = output.out
|
||||
self.disable_generation = True
|
||||
@ -225,7 +228,8 @@ class NorGate(TwoInputInvertedLogicGate):
|
||||
self.out = ConstantWireValue0()
|
||||
self.disable_generation = True
|
||||
elif b.is_const() and b.value == 0:
|
||||
output = NotGate(a=a, prefix=prefix, outid=outid, parent_component=parent_component)
|
||||
assert self.parent_component, "Parent component for gate {self} is not defined"
|
||||
output = NotGate(a=a, prefix=prefix + "_not", outid=outid, parent_component=parent_component)
|
||||
self.parent_component.add_component(output) if parent_component is not None else None
|
||||
self.out = output.out
|
||||
self.disable_generation = True
|
||||
@ -277,7 +281,8 @@ class XorGate(TwoInputLogicGate):
|
||||
# If constant input is present, logic gate is not generated and corresponding
|
||||
# input value is propagated to the output to connect to other components
|
||||
if a.is_const() and a.value == 1:
|
||||
output = NotGate(a=b, prefix=prefix, outid=outid, parent_component=parent_component)
|
||||
assert self.parent_component, "Parent component for gate {self} is not defined"
|
||||
output = NotGate(a=b, prefix=prefix + "_not", outid=outid, parent_component=parent_component)
|
||||
self.parent_component.add_component(output) if parent_component is not None else None
|
||||
self.out = output.out
|
||||
self.disable_generation = True
|
||||
@ -285,7 +290,8 @@ class XorGate(TwoInputLogicGate):
|
||||
self.out = b
|
||||
self.disable_generation = True
|
||||
elif b.is_const() and b.value == 1:
|
||||
output = NotGate(a=a, prefix=prefix, outid=outid, parent_component=parent_component)
|
||||
assert self.parent_component, "Parent component for gate {self} is not defined"
|
||||
output = NotGate(a=a, prefix=prefix + "_not", outid=outid, parent_component=parent_component)
|
||||
self.parent_component.add_component(output) if parent_component is not None else None
|
||||
self.out = output.out
|
||||
self.disable_generation = True
|
||||
@ -343,7 +349,8 @@ class XnorGate(TwoInputInvertedLogicGate):
|
||||
self.out = b
|
||||
self.disable_generation = True
|
||||
elif a.is_const() and a.value == 0:
|
||||
output = NotGate(a=b, prefix=prefix, outid=outid, parent_component=parent_component)
|
||||
assert self.parent_component, "Parent component for gate {self} is not defined"
|
||||
output = NotGate(a=b, prefix=prefix + "_not", outid=outid, parent_component=parent_component)
|
||||
self.parent_component.add_component(output) if parent_component is not None else None
|
||||
self.out = output.out
|
||||
self.disable_generation = True
|
||||
@ -351,7 +358,8 @@ class XnorGate(TwoInputInvertedLogicGate):
|
||||
self.out = a
|
||||
self.disable_generation = True
|
||||
elif b.is_const() and b.value == 0:
|
||||
output = NotGate(a=a, prefix=prefix, outid=outid, parent_component=parent_component)
|
||||
assert self.parent_component, "Parent component for gate {self} is not defined"
|
||||
output = NotGate(a=a, prefix=prefix + "_not", outid=outid, parent_component=parent_component)
|
||||
self.parent_component.add_component(output) if parent_component is not None else None
|
||||
self.out = output.out
|
||||
self.disable_generation = True
|
||||
|
@ -24,10 +24,11 @@ class BlackCell(FourInputOneBitCircuit):
|
||||
b (Wire, optional): Second input wire, represents propagate signal from the current stage. Defaults to Wire(name="p1").
|
||||
c (Wire, optional): Third input wire, represents generate signal from a preceding stage. Defaults to Wire(name="g0").
|
||||
d (Wire, optional): Fourth input wire, represents propagate signal from a preceding stage. Defaults to Wire(name="p0").
|
||||
prefix (str, optional): Prefix name of grey cell. Defaults to "gc".
|
||||
prefix (str, optional): Prefix name of grey cell. Defaults to "".
|
||||
name (str, optional): Name of grey cell. Defaults to "gc".
|
||||
"""
|
||||
def __init__(self, a: Wire = Wire(name="g1"), b: Wire = Wire(name="p1"), c: Wire = Wire(name="g0"), d: Wire = Wire(name="p0"), prefix: str = "bc"):
|
||||
super().__init__(a, b, c, d, prefix)
|
||||
def __init__(self, a: Wire = Wire(name="g1"), b: Wire = Wire(name="p1"), c: Wire = Wire(name="g0"), d: Wire = Wire(name="p0"), prefix: str = "", name: str = "bc"):
|
||||
super().__init__(a, b, c, d, prefix=prefix, name=name)
|
||||
# 2 wires for component's bus output (generate, propagate)
|
||||
self.out = Bus(self.prefix+"_out", 2)
|
||||
|
||||
|
@ -21,12 +21,13 @@ class FullAdder(ThreeInputOneBitCircuit):
|
||||
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".
|
||||
prefix (str, optional): Prefix name of full adder. Defaults to "".
|
||||
name (str, optional): Name of full adder. Defaults to "fa".
|
||||
"""
|
||||
use_verilog_instance = False
|
||||
|
||||
def __init__(self, a: Wire = Wire(name="a"), b: Wire = Wire(name="b"), c: Wire = Wire(name="cin"), prefix: str = "fa"):
|
||||
super().__init__(a, b, c, prefix)
|
||||
def __init__(self, a: Wire = Wire(name="a"), b: Wire = Wire(name="b"), c: Wire = Wire(name="cin"), prefix: str = "", name: str = "fa"):
|
||||
super().__init__(a, b, c, prefix=prefix, name=name)
|
||||
# 2 wires for component's bus output (sum, cout)
|
||||
self.out = Bus(self.prefix+"_out", 2)
|
||||
|
||||
@ -76,13 +77,12 @@ class FullAdder(ThreeInputOneBitCircuit):
|
||||
return " " + self.use_verilog_instance.format(
|
||||
**{
|
||||
"unit": self.prefix,
|
||||
"wirea": self.a.prefix,
|
||||
"wireb": self.b.prefix,
|
||||
"wirec": self.c.prefix,
|
||||
"wirea": f"1'b{self.a.value}" if self.a.is_const() else self.a.name,
|
||||
"wireb": f"1'b{self.b.value}" if self.b.is_const() else self.b.name,
|
||||
"wirec": f"1'b{self.c.value}" if self.c.is_const() else self.c.name,
|
||||
"wireys": self.get_sum_wire().prefix,
|
||||
"wireyc": self.get_carry_wire().prefix,
|
||||
}
|
||||
) + ";\n"
|
||||
}) + ";\n"
|
||||
|
||||
def get_self_init_v_hier(self):
|
||||
""" support of custom PDK """
|
||||
@ -93,7 +93,8 @@ class FullAdder(ThreeInputOneBitCircuit):
|
||||
for o in self.out.bus:
|
||||
unique_out_wires.append(o.name+"_outid"+str(self.out.bus.index(o))) if o.is_const() or o.name in [self.a.name, self.b.name] else unique_out_wires.append(o.name)
|
||||
|
||||
return " " + self.use_verilog_instance.format(**{
|
||||
return " " + self.use_verilog_instance.format(
|
||||
**{
|
||||
"unit": self.prefix,
|
||||
"wirea": self.a.name,
|
||||
"wireb": self.b.name,
|
||||
@ -102,6 +103,15 @@ class FullAdder(ThreeInputOneBitCircuit):
|
||||
"wireyc": unique_out_wires[1],
|
||||
}) + ";\n"
|
||||
|
||||
def get_circuit_v(self):
|
||||
""" support of custom PDK """
|
||||
if not self.use_verilog_instance:
|
||||
return super().get_circuit_v()
|
||||
|
||||
return f"{self.get_prototype_v_hier()}" + \
|
||||
f"{self.get_self_init_v_hier()}" + \
|
||||
f"endmodule"
|
||||
|
||||
|
||||
class FullAdderP(FullAdder, ThreeInputOneBitCircuit):
|
||||
"""Class representing three input one bit full adder with additional output wire for P signal.
|
||||
@ -120,10 +130,11 @@ class FullAdderP(FullAdder, ThreeInputOneBitCircuit):
|
||||
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 with p logic. Defaults to "fa_p".
|
||||
prefix (str, optional): Prefix name of full adder with p logic. Defaults to "".
|
||||
name (str, optional): Name of full adder with p logic. Defaults to "fa_p".
|
||||
"""
|
||||
def __init__(self, a: Wire = Wire(name="a"), b: Wire = Wire(name="b"), c: Wire = Wire(name="cin"), prefix: str = "fa_p"):
|
||||
super().__init__(a, b, c, prefix)
|
||||
def __init__(self, a: Wire = Wire(name="a"), b: Wire = Wire(name="b"), c: Wire = Wire(name="cin"), prefix: str = "", name: str = "fa_p"):
|
||||
super().__init__(a, b, c, prefix=prefix, name=name)
|
||||
# 3 wires for component's bus output (sum, cout, propagate)
|
||||
self.out.bus_extend(3)
|
||||
self.out.connect(2, self.get_previous_component(5).out)
|
||||
@ -155,10 +166,11 @@ class FullAdderPG(FullAdder, ThreeInputOneBitCircuit):
|
||||
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 with pg logic. Defaults to "fa_pg".
|
||||
prefix (str, optional): Prefix name of full adder with pg logic. Defaults to "".
|
||||
name (str, optional): Name of full adder with pg logic. Defaults to "fa_pg".
|
||||
"""
|
||||
def __init__(self, a: Wire = Wire(name="a"), b: Wire = Wire(name="b"), c: Wire = Wire(name="cin"), prefix: str = "fa_pg"):
|
||||
super().__init__(a, b, c, prefix)
|
||||
def __init__(self, a: Wire = Wire(name="a"), b: Wire = Wire(name="b"), c: Wire = Wire(name="cin"), prefix: str = "", name: str = "fa_pg"):
|
||||
super().__init__(a, b, c, prefix=prefix, name=name)
|
||||
# 4 wires for component's bus output (sum, cout, propagate, generate)
|
||||
self.out.bus_extend(4)
|
||||
self.out.connect(2, self.get_previous_component(5).out)
|
||||
@ -198,10 +210,11 @@ class PGSumLogic(ThreeInputOneBitCircuit):
|
||||
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 pg sum logic. Defaults to "pg_sum".
|
||||
prefix (str, optional): Prefix name of pg sum logic. Defaults to "".
|
||||
name (str, optional): Name of pg sum logic. Defaults to "pg_sum".
|
||||
"""
|
||||
def __init__(self, a: Wire = Wire(name="a"), b: Wire = Wire(name="b"), c: Wire = Wire(name="cin"), prefix: str = "pg_sum"):
|
||||
super().__init__(a, b, c, prefix)
|
||||
def __init__(self, a: Wire = Wire(name="a"), b: Wire = Wire(name="b"), c: Wire = Wire(name="cin"), prefix: str = "", name: str = "pg_sum"):
|
||||
super().__init__(a, b, c, prefix=prefix, name=name)
|
||||
# 3 wires for component's bus output (sum, propagate, generate)
|
||||
self.out = Bus(self.prefix+"_out", 3)
|
||||
|
||||
@ -262,10 +275,13 @@ class TwoOneMultiplexer(ThreeInputOneBitCircuit):
|
||||
a (Wire, optional): First input wire. Defaults to Wire(name="d0").
|
||||
b (Wire, optional): Second input wire. Defaults to Wire(name="d1").
|
||||
c (Wire, optional): Select signal. Defaults to Wire(name="sel").
|
||||
prefix (str, optional): Prefix name of two to one multiplexer. Defaults to "mux2to1".
|
||||
prefix (str, optional): Prefix name of two to one multiplexer. Defaults to "".
|
||||
name (str, optional): Name of two to one multiplexer. Defaults to "mux2to1".
|
||||
"""
|
||||
def __init__(self, a: Wire = Wire(name="d0"), b: Wire = Wire(name="d1"), c: Wire = Wire(name="sel"), prefix: str = "mux2to1"):
|
||||
super().__init__(a, b, c, prefix)
|
||||
use_verilog_instance = False
|
||||
|
||||
def __init__(self, a: Wire = Wire(name="d0"), b: Wire = Wire(name="d1"), c: Wire = Wire(name="sel"), prefix: str = "", name: str = "mux2to1"):
|
||||
super().__init__(a, b, c, prefix=prefix, name=name)
|
||||
# Represents select signal (self.c naming for proper unified generation)
|
||||
self.c = c
|
||||
# 1 wire for component's output bus
|
||||
@ -295,6 +311,53 @@ class TwoOneMultiplexer(ThreeInputOneBitCircuit):
|
||||
"""
|
||||
return self.out.get_wire(0)
|
||||
|
||||
def get_init_v_flat(self):
|
||||
""" support of custom PDK """
|
||||
if not self.use_verilog_instance:
|
||||
return super().get_init_v_flat()
|
||||
|
||||
if self.out[0].is_const():
|
||||
return ""
|
||||
else:
|
||||
# TODO - replace by one verilog_instance_format!
|
||||
neg_out_w_name = f"neg_{self.out.get_wire(0).name}"
|
||||
return f" wire {neg_out_w_name};\n " + self.use_verilog_instance.format(
|
||||
**{
|
||||
"unit": self.prefix,
|
||||
"wirea": self.a.get_wire_value_v_hier(), # former version: f"1'b{self.a.value}" if self.a.is_const() else self.a.name,
|
||||
"wireb": self.b.get_wire_value_v_hier(), #f"1'b{self.b.value}" if self.b.is_const() else self.b.name,
|
||||
"wires": self.c.get_wire_value_v_hier(), #f"1'b{self.c.value}" if self.c.is_const() else self.c.name,
|
||||
"wirey": neg_out_w_name,
|
||||
}) + ";\n" + f" assign {self.out.get_wire(0).name} = ~{neg_out_w_name};\n"
|
||||
|
||||
def get_self_init_v_hier(self):
|
||||
""" support of custom PDK """
|
||||
if not self.use_verilog_instance:
|
||||
return super().get_self_init_v_hier()
|
||||
|
||||
unique_out_wires = []
|
||||
for o in self.out.bus:
|
||||
unique_out_wires.append(o.name+"_outid"+str(self.out.bus.index(o))) if o.is_const() or o.name in [self.a.name, self.b.name] else unique_out_wires.append(o.name)
|
||||
|
||||
neg_out_w_name = f"neg_{unique_out_wires[0]}"
|
||||
return f" wire {neg_out_w_name};\n " + self.use_verilog_instance.format(
|
||||
**{
|
||||
"unit": self.prefix,
|
||||
"wirea": self.a.name,
|
||||
"wireb": self.b.name,
|
||||
"wires": self.c.name,
|
||||
"wirey": neg_out_w_name
|
||||
}) + ";\n" + f" assign {unique_out_wires[0]} = ~{neg_out_w_name};\n"
|
||||
|
||||
def get_circuit_v(self):
|
||||
""" support of custom PDK """
|
||||
if not self.use_verilog_instance:
|
||||
return super().get_circuit_v()
|
||||
|
||||
return f"{self.get_prototype_v_hier()}" + \
|
||||
f"{self.get_self_init_v_hier()}" + \
|
||||
f"endmodule"
|
||||
|
||||
|
||||
class FullSubtractor(ThreeInputOneBitCircuit):
|
||||
"""Class representing three input one bit full subtractor.
|
||||
@ -313,10 +376,11 @@ class FullSubtractor(ThreeInputOneBitCircuit):
|
||||
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): Input borrow wire. Defaults to Wire(name="bin").
|
||||
prefix (str, optional): Prefix name of full subtractor. Defaults to "fs".
|
||||
prefix (str, optional): Prefix name of full subtractor. Defaults to "".
|
||||
name (str, optional): Name of full subtractor. Defaults to "fs".
|
||||
"""
|
||||
def __init__(self, a: Wire = Wire(name="a"), b: Wire = Wire(name="b"), c: Wire = Wire(name="bin"), prefix: str = "fs"):
|
||||
super().__init__(a, b, c, prefix)
|
||||
def __init__(self, a: Wire = Wire(name="a"), b: Wire = Wire(name="b"), c: Wire = Wire(name="bin"), prefix: str = "", name: str = "fs"):
|
||||
super().__init__(a, b, c, prefix=prefix, name=name)
|
||||
# 2 wires for component's bus output (difference, bout)
|
||||
self.out = Bus(self.prefix+"_out", 2)
|
||||
|
||||
@ -380,10 +444,11 @@ class GreyCell(ThreeInputOneBitCircuit):
|
||||
a (Wire, optional): First input wire, represents generate signal from the current stage. Defaults to Wire(name="g1").
|
||||
b (Wire, optional): Second input wire, represents propagate signal from the current stage. Defaults to Wire(name="p1").
|
||||
c (Wire, optional): Third input wire, represents generate signal from a preceding stage. Defaults to Wire(name="g0").
|
||||
prefix (str, optional): Prefix name of grey cell. Defaults to "gc".
|
||||
prefix (str, optional): Prefix name of grey cell. Defaults to "".
|
||||
name (str, optional): Name of grey cell. Defaults to "gc".
|
||||
"""
|
||||
def __init__(self, a: Wire = Wire(name="g1"), b: Wire = Wire(name="p1"), c: Wire = Wire(name="g0"), prefix: str = "gc"):
|
||||
super().__init__(a, b, c, prefix)
|
||||
def __init__(self, a: Wire = Wire(name="g1"), b: Wire = Wire(name="p1"), c: Wire = Wire(name="g0"), prefix: str = "", name: str = "gc"):
|
||||
super().__init__(a, b, c, prefix=prefix, name=name)
|
||||
# 1 wire for component's bus output (output generate)
|
||||
self.out = Bus(self.prefix+"_out", 1)
|
||||
|
||||
|
@ -19,12 +19,13 @@ class HalfAdder(TwoInputOneBitCircuit):
|
||||
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 half adder. Defaults to "ha".
|
||||
prefix (str, optional): Prefix name of half adder. Defaults to "".
|
||||
name (str, optional): Name of half adder. Defaults to "ha".
|
||||
"""
|
||||
use_verilog_instance = False
|
||||
|
||||
def __init__(self, a: Wire = Wire(name="a"), b: Wire = Wire(name="b"), prefix: str = "ha"):
|
||||
super().__init__(a, b, prefix)
|
||||
def __init__(self, a: Wire = Wire(name="a"), b: Wire = Wire(name="b"), prefix: str = "", name: str = "ha"):
|
||||
super().__init__(a, b, prefix=prefix, name=name)
|
||||
# 2 wires for component's bus output (sum, cout)
|
||||
self.out = Bus(self.prefix+"_out", 2)
|
||||
|
||||
@ -64,12 +65,11 @@ class HalfAdder(TwoInputOneBitCircuit):
|
||||
return " " + self.use_verilog_instance.format(
|
||||
**{
|
||||
"unit": self.prefix,
|
||||
"wirea": self.a.prefix,
|
||||
"wireb": self.b.prefix,
|
||||
"wirea": f"1'b{self.a.value}" if self.a.is_const() else self.a.name,
|
||||
"wireb": f"1'b{self.b.value}" if self.b.is_const() else self.b.name,
|
||||
"wireys": self.get_sum_wire().prefix,
|
||||
"wireyc": self.get_carry_wire().prefix,
|
||||
}
|
||||
) + ";\n"
|
||||
}) + ";\n"
|
||||
|
||||
def get_self_init_v_hier(self):
|
||||
""" support of custom PDK """
|
||||
@ -80,7 +80,8 @@ class HalfAdder(TwoInputOneBitCircuit):
|
||||
for o in self.out.bus:
|
||||
unique_out_wires.append(o.name+"_outid"+str(self.out.bus.index(o))) if o.is_const() or o.name in [self.a.name, self.b.name] else unique_out_wires.append(o.name)
|
||||
|
||||
return " " + self.use_verilog_instance.format(**{
|
||||
return " " + self.use_verilog_instance.format(
|
||||
**{
|
||||
"unit": self.prefix,
|
||||
"wirea": self.a.name,
|
||||
"wireb": self.b.name,
|
||||
@ -88,6 +89,15 @@ class HalfAdder(TwoInputOneBitCircuit):
|
||||
"wireyc": unique_out_wires[1],
|
||||
}) + ";\n"
|
||||
|
||||
def get_circuit_v(self):
|
||||
""" support of custom PDK """
|
||||
if not self.use_verilog_instance:
|
||||
return super().get_circuit_v()
|
||||
|
||||
return f"{self.get_prototype_v_hier()}" + \
|
||||
f"{self.get_self_init_v_hier()}" + \
|
||||
f"endmodule"
|
||||
|
||||
|
||||
class PGLogicBlock(TwoInputOneBitCircuit):
|
||||
"""Class representing two input one bit propagate/generate logic block.
|
||||
@ -105,10 +115,11 @@ class PGLogicBlock(TwoInputOneBitCircuit):
|
||||
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 pg logic block. Defaults to "pg_logic".
|
||||
prefix (str, optional): Prefix name of pg logic block. Defaults to "".
|
||||
name (str, optional): Name of pg logic block. Defaults to "pg_logic".
|
||||
"""
|
||||
def __init__(self, a: Wire = Wire(name="a"), b: Wire = Wire(name="b"), prefix: str = "pg_logic"):
|
||||
super().__init__(a, b, prefix)
|
||||
def __init__(self, a: Wire = Wire(name="a"), b: Wire = Wire(name="b"), prefix: str = "", name: str = "pg_logic"):
|
||||
super().__init__(a, b, prefix=prefix, name=name)
|
||||
# 3 wires for component's bus output (propagate, generate, sum)
|
||||
self.out = Bus(self.prefix+"_out", 3)
|
||||
|
||||
@ -165,10 +176,11 @@ class HalfSubtractor(TwoInputOneBitCircuit):
|
||||
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 half subtractor adder. Defaults to "hs".
|
||||
prefix (str, optional): Prefix name of half subtractor adder. Defaults to "".
|
||||
name (str, optional): Name of half subtractor adder. Defaults to "hs".
|
||||
"""
|
||||
def __init__(self, a: Wire = Wire(name="a"), b: Wire = Wire(name="b"), prefix: str = "hs"):
|
||||
super().__init__(a, b, prefix)
|
||||
def __init__(self, a: Wire = Wire(name="a"), b: Wire = Wire(name="b"), prefix: str = "", name: str = "hs"):
|
||||
super().__init__(a, b, prefix=prefix, name=name)
|
||||
# 2 wires for component's bus output (difference, bout)
|
||||
self.out = Bus(self.prefix+"_out", 2)
|
||||
|
||||
|
@ -18,3 +18,4 @@ from .one_bit_circuits import (
|
||||
def set_pdk45_library():
|
||||
one_bit_components.FullAdder.use_verilog_instance = "FAX1 {unit} (.A({wirea}), .B({wireb}), .C({wirec}), .YS({wireys}), .YC({wireyc}))"
|
||||
one_bit_components.HalfAdder.use_verilog_instance = "HAX1 {unit} (.A({wirea}), .B({wireb}), .YS({wireys}), .YC({wireyc}))"
|
||||
one_bit_components.TwoOneMultiplexer.use_verilog_instance = "MUX2X1 {unit} (.A({wirea}), .B({wireb}), .S({wires}), .Y({wirey}))"
|
||||
|
@ -18,7 +18,7 @@ class Bus():
|
||||
if wires_list is None:
|
||||
self.prefix = prefix
|
||||
# Adding wires into current bus's wires list (wire names are concatenated from bus prefix and their index position inside the bus in square brackets)
|
||||
self.bus = [Wire(name=prefix+f"[{i}]", prefix=prefix, index=i, parent_bus=self) for i in range(N)]
|
||||
self.bus = [Wire(name=prefix+f"[{i}]" if N != 1 else prefix, prefix=prefix, index=i, parent_bus=self) for i in range(N)]
|
||||
self.N = N
|
||||
else:
|
||||
self.prefix = prefix
|
||||
@ -75,11 +75,9 @@ class Bus():
|
||||
Returns:
|
||||
Wire: Returning wire from the bus.
|
||||
"""
|
||||
assert wire_index < self.N, f"Wire index {wire_index} is out of bounds of the bus {self.prefix} with size {self.N}"
|
||||
return self.bus[wire_index]
|
||||
|
||||
def __getitem__(self, i):
|
||||
return self.bus[i]
|
||||
|
||||
def __getitem__(self, i):
|
||||
return self.get_wire(i)
|
||||
|
||||
@ -138,7 +136,7 @@ class Bus():
|
||||
mapped_positions = [(w_id, self.bus[w_id]) for w_id in range(self.N)]
|
||||
return "".join([f" {self.prefix} = 0\n"] + [f" {self.prefix} = ({self.prefix}) | {w[1].return_wire_value_python_flat(offset=w[0])}" for w in mapped_positions])
|
||||
|
||||
def return_bus_wires_sign_extend_python_flat(self):
|
||||
def return_bus_wires_sign_extend_python_flat(self, retype: bool = False):
|
||||
"""Sign extends the bus's corresponding Python variable (object) to ensure proper flat Python code variable signedness.
|
||||
|
||||
Returns:
|
||||
@ -146,7 +144,19 @@ class Bus():
|
||||
"""
|
||||
if self.signed is True:
|
||||
last_bus_wire = self.bus[-1]
|
||||
return "".join([f" {self.prefix} = ({self.prefix}) | {last_bus_wire.return_wire_value_python_flat(offset=i)}" for i in range(len(self.bus), 64)])
|
||||
|
||||
assert self.N < 64, "Sign extension is not supported for bus with more than 64 bits"
|
||||
if retype:
|
||||
rewrite = f"""
|
||||
if hasattr({self.prefix}, 'astype'):
|
||||
{self.prefix} = {self.prefix}.astype("int64")
|
||||
else:
|
||||
from ctypes import c_int64
|
||||
{self.prefix} = c_int64({self.prefix}).value\n"""
|
||||
else:
|
||||
rewrite = ""
|
||||
|
||||
return "".join([f" {self.prefix} = ({self.prefix}) | {last_bus_wire.return_wire_value_python_flat(offset=i)}" for i in range(len(self.bus), 64)]) + rewrite
|
||||
else:
|
||||
return ""
|
||||
|
||||
@ -181,7 +191,7 @@ class Bus():
|
||||
"""
|
||||
# Ensures correct binding between the bus wire index and the wire itself
|
||||
# It is used for the case when multiple of the same wire (e.g. `ContantWireValue0()`) are present in the bus (its id would otherwise be incorrect when using `self.bus.index(_)`)
|
||||
mapped_positions = [(w_id, self.bus[w_id]) for w_id in range(self.N)]
|
||||
mapped_positions = [(w_id, w) for w_id, w in enumerate(self.bus) if ((w.parent_bus is None) or (w.parent_bus is not None and w.prefix != self.prefix) or (w.is_const()))]
|
||||
return "".join([f" {self.prefix} |= {w[1].return_wire_value_c_hier(offset=w[0])}" for w in mapped_positions])
|
||||
|
||||
def return_bus_wires_sign_extend_c_flat(self):
|
||||
@ -228,7 +238,7 @@ class Bus():
|
||||
"""
|
||||
# Ensures correct binding between the bus wire index and the wire itself
|
||||
# It is used for the case when multiple of the same wire (e.g. `ContantWireValue0()`) are present in the bus (its id would otherwise be incorrect when using `self.bus.index(_)`)
|
||||
mapped_positions = [(w_id, self.bus[w_id]) for w_id in range(self.N)]
|
||||
mapped_positions = [(w_id, w) for w_id, w in enumerate(self.bus) if ((w.parent_bus is None) or (w.parent_bus is not None and w.prefix != self.prefix) or (w.is_const()))]
|
||||
return "".join([f" assign {self.prefix}[{w[0]}] = {w[1].return_wire_value_v_hier()}" for w in mapped_positions])
|
||||
|
||||
def get_unique_assign_out_wires_v(self, circuit_block: object):
|
||||
@ -244,12 +254,17 @@ 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])
|
||||
|
||||
""" BLIF CODE GENERATION """
|
||||
def get_wire_declaration_blif(self, array: bool = True):
|
||||
"""Declare each wire from the bus independently in Blif code representation.
|
||||
def get_wire_declaration_v(self):
|
||||
"""Declare the wire in Verilog code representation.
|
||||
|
||||
Args:
|
||||
array (bool, optional): Specifies whether to declare wires from bus by their offset e.g. out[0] or by their wire name e.g. out_0. Defaults to True.
|
||||
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):
|
||||
"""Declare each wire from the bus independently in Blif code representation.
|
||||
|
||||
Returns:
|
||||
str: Blif code for declaration of individual bus wires.
|
||||
@ -257,6 +272,7 @@ class Bus():
|
||||
# Ensures correct binding between the bus wire index and the wire itself
|
||||
# It is used for the case when multiple of the same wire (e.g. `ContantWireValue0()`) are present in the bus (its id would otherwise be incorrect when using `self.bus.index(_)`)
|
||||
mapped_positions = [(w_id, self.bus[w_id]) for w_id in range(self.N)]
|
||||
array = True if self.N > 1 else False
|
||||
return "".join([f" {w[1].get_declaration_blif(prefix=self.prefix, offset=w[0], array=array)}" for w in mapped_positions])
|
||||
|
||||
def get_wire_assign_blif(self, output: bool = False):
|
||||
@ -271,7 +287,10 @@ class Bus():
|
||||
# Ensures correct binding between the bus wire index and the wire itself
|
||||
# It is used for the case when multiple of the same wire (e.g. `ContantWireValue0()`) are present in the bus (its id would otherwise be incorrect when using `self.bus.index(_)`)
|
||||
mapped_positions = [(w_id, self.bus[w_id]) for w_id in range(self.N)]
|
||||
return "".join([w[1].get_assign_blif(prefix=self.prefix+f"[{w[0]}]", output=output) for w in mapped_positions])
|
||||
if self.N > 1:
|
||||
return "".join([w[1].get_assign_blif(prefix=self.prefix+f"[{w[0]}]", output=output) for w in mapped_positions])
|
||||
else:
|
||||
return "".join([w[1].get_assign_blif(prefix=self.prefix, output=output) for w in mapped_positions])
|
||||
|
||||
def get_unique_assign_out_wires_blif(self, function_block_out_bus: object):
|
||||
"""Assigns unique output wires to their respective outputs of subcomponent's function block modul in hierarchical Blif subcomponent's invocation.
|
||||
|
@ -1,3 +1,5 @@
|
||||
import re
|
||||
|
||||
class Wire():
|
||||
"""Class representing basic wire used to interconnect components.
|
||||
|
||||
@ -17,6 +19,9 @@ class Wire():
|
||||
self.prefix = name if prefix == "" else prefix
|
||||
self.parent_bus = parent_bus
|
||||
|
||||
def __str__(self):
|
||||
return f"wire{self.name}{self.value}{self.index}"
|
||||
|
||||
@staticmethod
|
||||
def is_const():
|
||||
"""Information whether wire carries constant value.
|
||||
@ -71,8 +76,14 @@ class Wire():
|
||||
return f"({self.c_const})"
|
||||
# If wire is part of an input bus (where wire names are concatenated from bus prefix and their index position inside the bus in square brackets)
|
||||
# then the wire value is obtained from bitwise shifting the required wire from the parent bus ('parent_bus.prefix' is the same value as 'self.prefix')
|
||||
elif self.is_buswire():
|
||||
elif self.is_buswire() and self.name == f"{self.prefix}[{self.index}]":
|
||||
return f"(({self.prefix} >> {self.index}) & 0x01)"
|
||||
elif self.is_buswire():
|
||||
g = re.match(r"(.*)\[(\d+)\]", self.name)
|
||||
if g:
|
||||
return f"(({g.group(1)} >> {g.group(2)}) & 0x01)"
|
||||
else:
|
||||
return f"(({self.name} >> 0) & 0x01)"
|
||||
else:
|
||||
return f"(({self.name} >> 0) & 0x01)"
|
||||
|
||||
@ -186,7 +197,7 @@ class Wire():
|
||||
|
||||
""" BLIF CODE GENERATION """
|
||||
def get_declaration_blif(self, prefix: str = "", offset: int = 0, array: bool = False):
|
||||
"""Wire declaration in Blif code.
|
||||
"""Declaration of wire which is part of a bus in Blif code.
|
||||
|
||||
Declares basic wire name if wire is not part of a bus
|
||||
or declares wire by an offset of its position within the bus.
|
||||
@ -202,8 +213,18 @@ class Wire():
|
||||
if array is True:
|
||||
return f"{prefix}[{offset}]"
|
||||
else:
|
||||
return f"{self.name}"
|
||||
return f"{prefix}"
|
||||
|
||||
def get_wire_declaration_blif(self):
|
||||
"""Declaration of a single wire in Blif code.
|
||||
|
||||
Used for declaration of modul inputs.
|
||||
|
||||
Returns:
|
||||
str: Blif code for declaration of a wire.
|
||||
"""
|
||||
return f"{self.prefix} "
|
||||
|
||||
def get_assign_blif(self, prefix: str, output: bool = False):
|
||||
"""Assignment of wire value to another desired wire in Blif code.
|
||||
|
||||
@ -242,10 +263,8 @@ class Wire():
|
||||
"""
|
||||
if self.is_const():
|
||||
return self.blif_const
|
||||
elif self.parent_bus is not None and self.parent_bus.N > 1:
|
||||
return self.name
|
||||
else:
|
||||
return self.prefix
|
||||
return self.name
|
||||
|
||||
def __str__(self):
|
||||
if self.is_const():
|
||||
@ -255,6 +274,11 @@ class Wire():
|
||||
else:
|
||||
return f"<w={self.name}>"
|
||||
|
||||
""" define read-only parameter N"""
|
||||
@property
|
||||
def N(self):
|
||||
return 1
|
||||
|
||||
|
||||
# Wires with constant values #
|
||||
class ConstantWireValue0(Wire):
|
||||
|
@ -1,9 +1,8 @@
|
||||
|
||||
from ariths_gen.core.arithmetic_circuits.arithmetic_circuit import ArithmeticCircuit
|
||||
from ariths_gen.core.arithmetic_circuits import GeneralCircuit
|
||||
from ariths_gen.wire_components import Bus, Wire
|
||||
from ariths_gen.multi_bit_circuits.adders import UnsignedRippleCarryAdder
|
||||
from ariths_gen.multi_bit_circuits.multipliers import UnsignedArrayMultiplier, UnsignedDaddaMultiplier
|
||||
from ariths_gen.multi_bit_circuits.multipliers import UnsignedArrayMultiplier
|
||||
import os
|
||||
|
||||
|
||||
@ -13,8 +12,8 @@ class MAC(GeneralCircuit):
|
||||
assert a.N == b.N
|
||||
assert r.N == 2 * a.N
|
||||
|
||||
self.mul = self.add_component(UnsignedArrayMultiplier(a=a, b=b, prefix=self.prefix, name=f"u_arrmul{a.N}", inner_component=True))
|
||||
self.add = self.add_component(UnsignedRippleCarryAdder(a=r, b=self.mul.out, prefix=self.prefix, name=f"u_rca{r.N}", inner_component=True))
|
||||
self.mul = self.add_component(UnsignedArrayMultiplier(a=Bus(wires_list=a.bus, prefix=a.prefix), b=Bus(wires_list=b.bus, prefix=b.prefix), prefix=self.prefix, name=f"u_arrmul{a.N}", inner_component=True))
|
||||
self.add = self.add_component(UnsignedRippleCarryAdder(a=Bus(wires_list=r.bus, prefix=r.prefix), b=self.mul.out, prefix=self.prefix, name=f"u_rca{r.N}", inner_component=True))
|
||||
self.out.connect_bus(connecting_bus=self.add.out)
|
||||
|
||||
|
||||
|
79
generate_quad_lib.py
Normal file
79
generate_quad_lib.py
Normal file
@ -0,0 +1,79 @@
|
||||
"""
|
||||
This script generate the library of all possible QuAdders with N bits.
|
||||
Note that the adders are not Pareto-optimal.
|
||||
"""
|
||||
|
||||
from ariths_gen.core.arithmetic_circuits.arithmetic_circuit import ArithmeticCircuit
|
||||
from ariths_gen.core.arithmetic_circuits import GeneralCircuit
|
||||
from ariths_gen.wire_components import Bus, Wire
|
||||
from ariths_gen.multi_bit_circuits.adders import UnsignedRippleCarryAdder
|
||||
from ariths_gen.multi_bit_circuits.approximate_adders import QuAdder
|
||||
from ariths_gen.multi_bit_circuits.multipliers import UnsignedArrayMultiplier, UnsignedDaddaMultiplier
|
||||
import os, sys
|
||||
|
||||
import numpy as np
|
||||
import itertools
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
N = 8
|
||||
directory = f"lib_quad/lib_quad{N}"
|
||||
os.makedirs(directory, exist_ok=True)
|
||||
|
||||
# generate the C code
|
||||
cfile = open(f"{directory}/lib_quad_{N}.c", "w")
|
||||
hfile = open(f"{directory}/lib_quad_{N}.h", "w")
|
||||
hfile.write("#include <stdint.h>\n")
|
||||
|
||||
data = {}
|
||||
|
||||
# verilog code is zipped
|
||||
import zipfile
|
||||
vfile = zipfile.ZipFile(file=f"{directory}/lib_quad_{N}.zip", mode="w", compression=zipfile.ZIP_DEFLATED)
|
||||
cnt = 0
|
||||
|
||||
# up to 3 stages
|
||||
for n in [1, 2, 3]:
|
||||
Rall = list(itertools.product(range(1, N + 1), repeat=n))
|
||||
for R in Rall:
|
||||
# skip invalid R
|
||||
if sum(R) != N:
|
||||
continue
|
||||
|
||||
for P in itertools.product(range(0, N + 1), repeat=n):
|
||||
# test the condition from the paper
|
||||
if not all([P[i] < P[i-1] + R[i-1] for i in range(1, len(P))]):
|
||||
continue
|
||||
print(cnt, R, P) # print the current configuration
|
||||
|
||||
prefix = f"quad_{N}"
|
||||
name = "r_{}_p_{}".format("_".join([str(r) for r in R]), "_".join([str(p) for p in P]))
|
||||
|
||||
try:
|
||||
c = QuAdder(Bus("a", N), Bus("b", N), R = R, P=P, name=name, prefix=prefix, use_log=False)
|
||||
c.get_c_code_flat(file_object=cfile)
|
||||
vf = vfile.open(f"{prefix}_{name}.v", "w")
|
||||
# convert byte file vf to text file
|
||||
import io
|
||||
vt = io.TextIOWrapper(vf, encoding="utf-8")
|
||||
c.get_v_code_flat(file_object=vt)
|
||||
vt.close()
|
||||
|
||||
cfile.write("\n\n")
|
||||
hfile.write(f"uint64_t {prefix}_{name}(uint64_t a, uint64_t b);")
|
||||
|
||||
data[f"{name}_{prefix}"] = {
|
||||
"bw": N,
|
||||
"cfun": f"{prefix}_{name}",
|
||||
"verilog": f"{prefix}_{name}.v",
|
||||
"verilog_entity": f"{prefix}_{name}",
|
||||
"quad_r" : R,
|
||||
"quad_p" : P,
|
||||
}
|
||||
cnt += 1
|
||||
except IOError as e:
|
||||
print(R, P, e)
|
||||
|
||||
# store the metadata
|
||||
import json, gzip
|
||||
json.dump(data, gzip.open(f"{directory}/lib_quad_{N}.json.gz", "wt"), indent=4)
|
@ -54,6 +54,13 @@ from ariths_gen.multi_bit_circuits.adders import (
|
||||
SignedCarryIncrementAdder
|
||||
)
|
||||
|
||||
from ariths_gen.multi_bit_circuits.subtractors import (
|
||||
UnsignedRippleCarrySubtractor,
|
||||
SignedRippleCarrySubtractor,
|
||||
UnsignedRippleBorrowSubtractor,
|
||||
SignedRippleBorrowSubtractor
|
||||
)
|
||||
|
||||
from ariths_gen.multi_bit_circuits.multipliers import (
|
||||
UnsignedDaddaMultiplier,
|
||||
UnsignedArrayMultiplier,
|
||||
@ -65,6 +72,11 @@ from ariths_gen.multi_bit_circuits.multipliers import (
|
||||
SignedCarrySaveMultiplier
|
||||
)
|
||||
|
||||
from ariths_gen.multi_bit_circuits.approximate_multipliers import (
|
||||
UnsignedRecursiveMultiplier,
|
||||
UnsignedAccurateTwoBitMultiplier
|
||||
)
|
||||
|
||||
from ariths_gen.multi_bit_circuits.dividers import (
|
||||
ArrayDivider
|
||||
)
|
||||
@ -217,6 +229,23 @@ if __name__ == "__main__":
|
||||
circuit = SignedLadnerFischerAdder(a, b, name=name, config_choice=1)
|
||||
export_circuit(circuit, name)
|
||||
|
||||
""" SUBTRACTORS """
|
||||
name = f"u_rcs{N}"
|
||||
circuit = UnsignedRippleCarrySubtractor(a, b, name=name)
|
||||
export_circuit(circuit, name)
|
||||
|
||||
name = f"s_rcs{N}"
|
||||
circuit = SignedRippleCarrySubtractor(a, b, name=name)
|
||||
export_circuit(circuit, name)
|
||||
|
||||
name = f"u_rbs{N}"
|
||||
circuit = UnsignedRippleBorrowSubtractor(a, b, name=name)
|
||||
export_circuit(circuit, name)
|
||||
|
||||
name = f"s_rbs{N}"
|
||||
circuit = SignedRippleBorrowSubtractor(a, b, name=name)
|
||||
export_circuit(circuit, name)
|
||||
|
||||
""" MULTIPLIERS """
|
||||
# Arrmul
|
||||
name = f"u_arrmul{N}"
|
||||
@ -226,6 +255,11 @@ if __name__ == "__main__":
|
||||
name = f"s_arrmul{N}"
|
||||
circuit = SignedArrayMultiplier(a, b, name=name)
|
||||
export_circuit(circuit, name)
|
||||
|
||||
# Accurate recursive multiplier
|
||||
name = f"u_recmul{N}"
|
||||
circuit = UnsignedRecursiveMultiplier(a, b, name=name)
|
||||
export_circuit(circuit, name)
|
||||
|
||||
# Csamul (Braun multiplier) – the ppa adders are also configurable as above if desirable
|
||||
name = f"u_csamul_cla{N}"
|
||||
|
@ -1,6 +1,6 @@
|
||||
[metadata]
|
||||
name = ariths_gen
|
||||
version = 0.0.1
|
||||
version = 0.0.2
|
||||
description = "arithmetic circuits generator"
|
||||
|
||||
|
||||
|
18
tests/subtractor_signed.c
Normal file
18
tests/subtractor_signed.c
Normal file
@ -0,0 +1,18 @@
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
|
||||
int64_t CNAME(int64_t a, int64_t b);
|
||||
|
||||
|
||||
int main() {
|
||||
int result = 0;
|
||||
for (int i = -128; i < 128; i++){
|
||||
for (int j = -128; j < 128; j++){
|
||||
result = i - j;
|
||||
|
||||
assert(result == (int)CNAME(i,j));
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
18
tests/subtractor_unsigned.c
Normal file
18
tests/subtractor_unsigned.c
Normal file
@ -0,0 +1,18 @@
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
|
||||
uint64_t CNAME(uint64_t a, uint64_t b);
|
||||
|
||||
|
||||
int main() {
|
||||
int result = 0;
|
||||
for (int i = 0; i < 256; i++){
|
||||
for (int j = 0; j < 256; j++){
|
||||
result = i - j;
|
||||
|
||||
assert(result == (int)CNAME(i,j));
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
@ -22,7 +22,7 @@ module add_signed_tb;
|
||||
|
||||
#period;
|
||||
|
||||
//$assert(b == 0);c
|
||||
//$assert(b == 0);
|
||||
if ( k + j != o) begin
|
||||
$display("Invalid output: %d + %d = %d", a, b, o);
|
||||
end
|
||||
|
33
tests/tb_subtractor_signed.v
Normal file
33
tests/tb_subtractor_signed.v
Normal file
@ -0,0 +1,33 @@
|
||||
|
||||
`timescale 1 ns/10 ps // time-unit = 1 ns, precision = 10 ps
|
||||
|
||||
module sub_signed_tb;
|
||||
reg signed [7:0] a;
|
||||
reg signed [7:0] b;
|
||||
wire signed [8:0] o;
|
||||
|
||||
integer k, j;
|
||||
localparam period = 20;
|
||||
|
||||
`dut dut(a, b, o); //.input_a(a), .input_b(b), .cgp_circuit_out(o));
|
||||
|
||||
always
|
||||
begin
|
||||
|
||||
for(k = -127; k < 128; k = k+1) begin
|
||||
for(j = -127; j < 128; j = j+1) begin
|
||||
|
||||
assign a = k;
|
||||
assign b = j;
|
||||
|
||||
#period;
|
||||
|
||||
//$assert(b == 0);
|
||||
if ( k - j != o) begin
|
||||
$display("Invalid output: %d - %d = %d", a, b, o);
|
||||
end
|
||||
end;
|
||||
end;
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
33
tests/tb_subtractor_unsigned.v
Normal file
33
tests/tb_subtractor_unsigned.v
Normal file
@ -0,0 +1,33 @@
|
||||
|
||||
`timescale 1 ns/10 ps // time-unit = 1 ns, precision = 10 ps
|
||||
|
||||
module sub_unsigned_tb;
|
||||
reg [7:0] a;
|
||||
reg [7:0] b;
|
||||
wire signed [8:0] o; // output must be signed for a subtractor
|
||||
|
||||
integer k, j;
|
||||
localparam period = 20;
|
||||
|
||||
`dut dut(a, b, o); //.input_a(a), .input_b(b), .cgp_circuit_out(o));
|
||||
|
||||
always
|
||||
begin
|
||||
|
||||
for(k = 0; k < 256; k = k+1) begin
|
||||
for(j = 0; j < 256; j = j+1) begin
|
||||
|
||||
assign a = k;
|
||||
assign b = j;
|
||||
|
||||
#period;
|
||||
|
||||
//$assert(b == 0);
|
||||
if ( k - j != o) begin
|
||||
$display("Invalid output: %d - %d = %d", a, b, o);
|
||||
end
|
||||
end;
|
||||
end;
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
@ -1,3 +1,11 @@
|
||||
import os
|
||||
import sys
|
||||
# Add the parent directory to the system path
|
||||
DIR_PATH = os.path.dirname(os.path.abspath(__file__))
|
||||
sys.path.insert(0, os.path.join(DIR_PATH, '..'))
|
||||
import numpy as np
|
||||
import math
|
||||
|
||||
from ariths_gen.wire_components import (
|
||||
Wire,
|
||||
ConstantWireValue0,
|
||||
@ -36,6 +44,11 @@ from ariths_gen.multi_bit_circuits.adders import (
|
||||
SignedCarryIncrementAdder
|
||||
)
|
||||
|
||||
from ariths_gen.multi_bit_circuits.subtractors import (
|
||||
UnsignedRippleCarrySubtractor, SignedRippleCarrySubtractor,
|
||||
UnsignedRippleBorrowSubtractor, SignedRippleBorrowSubtractor
|
||||
)
|
||||
|
||||
from ariths_gen.multi_bit_circuits.multipliers import (
|
||||
UnsignedDaddaMultiplier,
|
||||
UnsignedArrayMultiplier,
|
||||
@ -51,7 +64,9 @@ from ariths_gen.multi_bit_circuits.approximate_multipliers import (
|
||||
UnsignedTruncatedArrayMultiplier,
|
||||
UnsignedTruncatedCarrySaveMultiplier,
|
||||
UnsignedBrokenArrayMultiplier,
|
||||
UnsignedBrokenCarrySaveMultiplier
|
||||
UnsignedBrokenCarrySaveMultiplier,
|
||||
UnsignedRecursiveMultiplier,
|
||||
UnsignedAccurateTwoBitMultiplier
|
||||
)
|
||||
|
||||
from ariths_gen.one_bit_circuits.logic_gates import (
|
||||
@ -63,8 +78,6 @@ from ariths_gen.one_bit_circuits.logic_gates import (
|
||||
XnorGate,
|
||||
NotGate
|
||||
)
|
||||
import numpy as np
|
||||
import math
|
||||
|
||||
|
||||
def test_unsigned_approxmul(values=False):
|
||||
@ -120,6 +133,19 @@ def test_unsigned_mul():
|
||||
assert mul(0, 0) == 0
|
||||
r = mul(av, bv)
|
||||
np.testing.assert_array_equal(expected, r)
|
||||
|
||||
# Accurate variant of recursive multiplier
|
||||
for c in [UnsignedRecursiveMultiplier]:
|
||||
N_rec = 8
|
||||
a_rec = Bus(N=N_rec, prefix="a")
|
||||
b_rec = Bus(N=N_rec, prefix="b")
|
||||
av_rec = np.arange(2**N_rec)
|
||||
bv_rec = av_rec.reshape(-1, 1)
|
||||
expected_rec = av_rec * bv_rec
|
||||
mul = c(a_rec, b_rec, submultipliers=[UnsignedAccurateTwoBitMultiplier for _ in range((N_rec//2)**2)])
|
||||
assert mul(0, 0) == 0
|
||||
r = mul(av_rec, bv_rec)
|
||||
np.testing.assert_array_equal(expected_rec, r)
|
||||
|
||||
# Configurable PPA
|
||||
for c in [UnsignedDaddaMultiplier, UnsignedCarrySaveMultiplier, UnsignedWallaceMultiplier]:
|
||||
@ -291,6 +317,37 @@ def test_signed_add():
|
||||
np.testing.assert_array_equal(expected, r)
|
||||
|
||||
|
||||
|
||||
def test_unsigned_sub():
|
||||
""" Test unsigned subtractor """
|
||||
N = 9
|
||||
a = Bus(N=N, prefix="a")
|
||||
b = Bus(N=N, prefix="b")
|
||||
av = np.arange(2**N)
|
||||
bv = av.reshape(-1, 1)
|
||||
expected = av - bv
|
||||
|
||||
for c in [UnsignedRippleCarrySubtractor, UnsignedRippleBorrowSubtractor]:
|
||||
sub = c(a, b)
|
||||
r = sub(av, bv)
|
||||
np.testing.assert_array_equal(expected, r)
|
||||
|
||||
|
||||
def test_signed_sub():
|
||||
""" Test signed subtractor """
|
||||
N = 9
|
||||
a = Bus(N=N, prefix="a")
|
||||
b = Bus(N=N, prefix="b")
|
||||
av = np.arange(-(2**(N-1)), 2**(N-1))
|
||||
bv = av.reshape(-1, 1)
|
||||
expected = av - bv
|
||||
|
||||
# Non configurable multi-bit adders
|
||||
for c in [SignedRippleCarrySubtractor, SignedRippleBorrowSubtractor]:
|
||||
sub = c(a, b)
|
||||
r = sub(av, bv)
|
||||
np.testing.assert_array_equal(expected, r)
|
||||
|
||||
def test_mac():
|
||||
class MAC(GeneralCircuit):
|
||||
def __init__(self, a: Bus, b: Bus, r: Bus, prefix: str = "", name: str = "mac", **kwargs):
|
||||
@ -298,8 +355,8 @@ def test_mac():
|
||||
assert a.N == b.N
|
||||
assert r.N == 2 * a.N
|
||||
|
||||
self.mul = self.add_component(UnsignedArrayMultiplier(a=a, b=b, prefix=self.prefix, name=f"u_arrmul{a.N}", inner_component=True))
|
||||
self.add = self.add_component(UnsignedRippleCarryAdder(a=r, b=self.mul.out, prefix=self.prefix, name=f"u_rca{r.N}", inner_component=True))
|
||||
self.mul = self.add_component(UnsignedArrayMultiplier(a=Bus(wires_list=a.bus, prefix=a.prefix), b=Bus(wires_list=b.bus, prefix=b.prefix), prefix=self.prefix, name=f"u_arrmul{a.N}", inner_component=True))
|
||||
self.add = self.add_component(UnsignedRippleCarryAdder(a=Bus(wires_list=r.bus, prefix=r.prefix), b=self.mul.out, prefix=self.prefix, name=f"u_rca{r.N}", inner_component=True))
|
||||
self.out.connect_bus(connecting_bus=self.add.out)
|
||||
|
||||
# usage
|
||||
@ -316,7 +373,7 @@ def test_mac():
|
||||
|
||||
def test_direct():
|
||||
class err_circuit(GeneralCircuit):
|
||||
def __init__(self, prefix: str = "", name: str = "adder", inner_component: bool = True, a: Bus = Bus(), b: Bus = Bus()):
|
||||
def __init__(self, a: Bus = Bus(), b: Bus = Bus(), prefix: str = "", name: str = "adder", inner_component: bool = False):
|
||||
super().__init__(prefix=prefix, name=name, out_N=(a.N + 1), inner_component=inner_component, inputs=[a, b])
|
||||
self.N = 1
|
||||
self.prefix = prefix
|
||||
@ -341,3 +398,54 @@ def test_direct():
|
||||
expected = np.array([[0, 3, 0, 3], [2, 3, 2, 3], [0, 3, 0, 3], [2, 3, 2, 3]])
|
||||
np.testing.assert_equal(r, expected)
|
||||
print(r)
|
||||
|
||||
|
||||
def test_wire_as_bus():
|
||||
""" accept a wire as a bus """
|
||||
class test_circuit(GeneralCircuit):
|
||||
def __init__(self, a: Wire, b: Wire, c: Bus, prefix="test_circuit", **kwargs):
|
||||
super().__init__(prefix=prefix, name="test_circuit", inputs=[a, b, c], out_N=1, **kwargs)
|
||||
g = self.add_component(AndGate(a, b, prefix="g2"))
|
||||
g2 = self.add_component(AndGate(g.out, c[0], prefix="g3"))
|
||||
g3 = self.add_component(AndGate(g2.out, c[1], prefix="g4"))
|
||||
self.out[0] = g3.out
|
||||
|
||||
circ = test_circuit(Wire("a"), Wire("b"), Bus("c", 2), "c1")
|
||||
r = circ(np.array([0, 1]),
|
||||
np.array([0, 1]).reshape(-1, 1),
|
||||
np.arange(4).reshape(-1, 1, 1))
|
||||
assert r.sum() == 1
|
||||
assert r[-1, -1, -1] == 1
|
||||
|
||||
def test_unique():
|
||||
from ariths_gen.wire_components import Wire, Bus
|
||||
import pytest
|
||||
import sys
|
||||
from ariths_gen.one_bit_circuits.logic_gates import AndGate
|
||||
from ariths_gen.one_bit_circuits.one_bit_components import TwoOneMultiplexer
|
||||
from ariths_gen.core.arithmetic_circuits import GeneralCircuit
|
||||
|
||||
class test_circuit(GeneralCircuit):
|
||||
def __init__(self, a: Bus, prefix="test_circuit", **kwargs):
|
||||
super().__init__(prefix=prefix, name="test_circuit", inputs=[a], out_N=1, **kwargs)
|
||||
g = self.add_component(AndGate(a[0], a[1], prefix="g2"))
|
||||
g2 = self.add_component(AndGate(g.out, a[2], prefix="g2"))
|
||||
g3 = self.add_component(AndGate(g2.out, g.out, prefix="g2"))
|
||||
self.out[0] = g3.out
|
||||
|
||||
with pytest.raises(AssertionError):
|
||||
circ = test_circuit(Bus("a", 3), "c1")
|
||||
circ.get_v_code_flat(file_object=sys.stdout)
|
||||
|
||||
if __name__ == "__main__":
|
||||
test_unsigned_approxmul()
|
||||
test_unsigned_mul()
|
||||
test_signed_mul()
|
||||
test_unsigned_add()
|
||||
test_signed_add()
|
||||
test_unsigned_sub()
|
||||
test_signed_sub()
|
||||
test_mac()
|
||||
test_direct()
|
||||
test_wire_as_bus()
|
||||
print("Python tests were successful!")
|
||||
|
31
tests/test_ax.py
Normal file
31
tests/test_ax.py
Normal file
@ -0,0 +1,31 @@
|
||||
"""
|
||||
Testing the QuAdder
|
||||
"""
|
||||
import os
|
||||
import sys
|
||||
# Add the parent directory to the system path
|
||||
DIR_PATH = os.path.dirname(os.path.abspath(__file__))
|
||||
sys.path.insert(0, os.path.join(DIR_PATH, '..'))
|
||||
import numpy as np
|
||||
import itertools
|
||||
from ariths_gen.wire_components import Bus
|
||||
from ariths_gen.multi_bit_circuits.approximate_adders import QuAdder
|
||||
|
||||
|
||||
def test_quadder():
|
||||
c = QuAdder(Bus("a", 8), Bus("b", 8), R=[4, 2, 2], P=[0, 2, 2], prefix="quad")
|
||||
c.get_v_code_hier(file_object=sys.stdout)
|
||||
|
||||
x = np.arange(0, 256).reshape(-1, 1)
|
||||
y = np.arange(0, 256).reshape(1, -1)
|
||||
|
||||
r = c(x, y)
|
||||
r2 = x + y
|
||||
|
||||
assert np.abs(r - r2).max() == 64
|
||||
np.testing.assert_equal(np.abs(r - r2).mean(), 7.5)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
test_quadder()
|
||||
print("Quadder Python tests were successful!")
|
@ -1,3 +1,13 @@
|
||||
import os
|
||||
import sys
|
||||
# Add the parent directory to the system path
|
||||
DIR_PATH = os.path.dirname(os.path.abspath(__file__))
|
||||
sys.path.insert(0, os.path.join(DIR_PATH, '..'))
|
||||
|
||||
import numpy as np
|
||||
import math
|
||||
from io import StringIO
|
||||
|
||||
from ariths_gen.wire_components import (
|
||||
Wire,
|
||||
ConstantWireValue0,
|
||||
@ -36,6 +46,13 @@ from ariths_gen.multi_bit_circuits.adders import (
|
||||
SignedCarryIncrementAdder
|
||||
)
|
||||
|
||||
from ariths_gen.multi_bit_circuits.subtractors import (
|
||||
UnsignedRippleBorrowSubtractor,
|
||||
UnsignedRippleCarrySubtractor,
|
||||
SignedRippleBorrowSubtractor,
|
||||
SignedRippleCarrySubtractor
|
||||
)
|
||||
|
||||
from ariths_gen.multi_bit_circuits.multipliers import (
|
||||
UnsignedDaddaMultiplier,
|
||||
UnsignedArrayMultiplier,
|
||||
@ -57,10 +74,6 @@ from ariths_gen.multi_bit_circuits.approximate_multipliers import (
|
||||
|
||||
from ariths_gen.core.cgp_circuit import UnsignedCGPCircuit, SignedCGPCircuit
|
||||
|
||||
import numpy as np
|
||||
import math
|
||||
from io import StringIO
|
||||
|
||||
|
||||
def test_cgp_unsigned_add():
|
||||
""" Test unsigned adders """
|
||||
@ -199,7 +212,62 @@ def test_cgp_signed_add():
|
||||
np.testing.assert_array_equal(expected, r)
|
||||
|
||||
|
||||
def test_unsigned_mul():
|
||||
def test_cgp_unsigned_sub():
|
||||
""" Test unsigned subtractors """
|
||||
N = 7
|
||||
a = Bus(N=N, prefix="a")
|
||||
b = Bus(N=N, prefix="b")
|
||||
av = np.arange(2**N)
|
||||
bv = av.reshape(-1, 1)
|
||||
expected = av - bv
|
||||
|
||||
#for c in [UnsignedRippleBorrowSubtractor, UnsignedRippleCarrySubtractor]:
|
||||
for c in [UnsignedRippleBorrowSubtractor]:
|
||||
sub = c(a, b)
|
||||
code = StringIO()
|
||||
sub.get_cgp_code_flat(code)
|
||||
cgp_code = code.getvalue()
|
||||
|
||||
sub2 = UnsignedCGPCircuit(cgp_code, [N, N], signed_out=True)
|
||||
o = StringIO()
|
||||
sub2.get_v_code_flat(o)
|
||||
print(o.getvalue())
|
||||
|
||||
r = sub2(av, bv)
|
||||
assert sub(0, 0) == 0
|
||||
assert sub2(0, 0) == 0
|
||||
np.testing.assert_array_equal(expected, r)
|
||||
|
||||
|
||||
def test_cgp_signed_sub():
|
||||
""" Test signed subtractors """
|
||||
N = 7
|
||||
a = Bus(N=N, prefix="a")
|
||||
b = Bus(N=N, prefix="b")
|
||||
av = np.arange(-(2**(N-1)), 2**(N-1))
|
||||
bv = av.reshape(-1, 1)
|
||||
expected = av - bv
|
||||
|
||||
for c in [SignedRippleBorrowSubtractor, SignedRippleCarrySubtractor]:
|
||||
sub = c(a, b)
|
||||
r = sub(av, bv)
|
||||
code = StringIO()
|
||||
sub.get_cgp_code_flat(code)
|
||||
cgp_code = code.getvalue()
|
||||
print(cgp_code)
|
||||
|
||||
sub2 = SignedCGPCircuit(cgp_code, [N, N])
|
||||
o = StringIO()
|
||||
sub2.get_v_code_flat(o)
|
||||
print(o.getvalue())
|
||||
|
||||
r = sub2(av, bv)
|
||||
assert sub(0, 0) == 0
|
||||
assert sub2(0, 0) == 0
|
||||
np.testing.assert_array_equal(expected, r)
|
||||
|
||||
|
||||
def test_cgp_unsigned_mul():
|
||||
""" Test unsigned multipliers """
|
||||
N = 7
|
||||
a = Bus(N=N, prefix="a")
|
||||
@ -311,7 +379,7 @@ def test_unsigned_mul():
|
||||
np.testing.assert_array_equal(expected, r)
|
||||
|
||||
|
||||
def test_signed_mul():
|
||||
def test_cgp_signed_mul():
|
||||
""" Test signed multipliers """
|
||||
N = 7
|
||||
a = Bus(N=N, prefix="a")
|
||||
@ -429,3 +497,14 @@ def test_cgp_variant1():
|
||||
|
||||
c = UnsignedCGPCircuit(cgp, [8, 8], name="cgp_circuit")
|
||||
assert c(0, 0) == 8 # TypeError: 'int' object is not subscriptable
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
test_cgp_unsigned_add()
|
||||
test_cgp_signed_add()
|
||||
test_cgp_unsigned_sub()
|
||||
test_cgp_signed_sub()
|
||||
test_cgp_unsigned_mul()
|
||||
test_cgp_signed_mul()
|
||||
test_cgp_variant1()
|
||||
print("CGP Python tests were successful!")
|
||||
|
@ -53,6 +53,14 @@ test_circuit "adder_unsigned" "u_ka8"
|
||||
test_circuit "adder_unsigned" "u_lfa8"
|
||||
|
||||
|
||||
test_circuit "subtractor_signed" "s_rcs8"
|
||||
test_circuit "subtractor_signed" "s_rbs8"
|
||||
|
||||
|
||||
test_circuit "subtractor_unsigned" "u_rcs8"
|
||||
test_circuit "subtractor_unsigned" "u_rbs8"
|
||||
|
||||
|
||||
test_circuit "multiplier_signed" "s_arrmul8"
|
||||
test_circuit "multiplier_signed" "s_csamul_cla8"
|
||||
test_circuit "multiplier_signed" "s_csamul_rca8"
|
||||
@ -109,6 +117,7 @@ test_circuit "multiplier_signed" "s_dadda_lfa8"
|
||||
|
||||
|
||||
test_circuit "multiplier_unsigned" "u_arrmul8"
|
||||
test_circuit "multiplier_unsigned" "u_recmul8"
|
||||
test_circuit "multiplier_unsigned" "u_csamul_cla8"
|
||||
test_circuit "multiplier_unsigned" "u_csamul_rca8"
|
||||
test_circuit "multiplier_unsigned" "u_csamul_pg_rca8"
|
||||
@ -253,6 +262,7 @@ fi
|
||||
# exporting u_ka8
|
||||
# exporting u_lfa8
|
||||
# exporting u_arrmul8
|
||||
# exporting u_recmul8
|
||||
# exporting u_csamul_cla8"
|
||||
# exporting u_csamul_rca8"
|
||||
# exporting u_csamul_pg_rca8"
|
||||
|
@ -57,6 +57,10 @@ test_circuit "adder_unsigned" "u_ka8"
|
||||
test_circuit "adder_unsigned" "u_lfa8"
|
||||
|
||||
|
||||
test_circuit "subtractor_signed" "s_rcs8"
|
||||
test_circuit "subtractor_signed" "s_rbs8"
|
||||
|
||||
|
||||
test_circuit "multiplier_signed" "s_arrmul8"
|
||||
test_circuit "multiplier_signed" "s_csamul_cla8"
|
||||
test_circuit "multiplier_signed" "s_csamul_rca8"
|
||||
@ -113,6 +117,7 @@ test_circuit "multiplier_signed" "s_dadda_lfa8"
|
||||
|
||||
|
||||
test_circuit "multiplier_unsigned" "u_arrmul8"
|
||||
test_circuit "multiplier_unsigned" "u_recmul8"
|
||||
test_circuit "multiplier_unsigned" "u_csamul_cla8"
|
||||
test_circuit "multiplier_unsigned" "u_csamul_rca8"
|
||||
test_circuit "multiplier_unsigned" "u_csamul_pg_rca8"
|
||||
|
@ -66,6 +66,14 @@ test_circuit "adder_unsigned" "u_ka8"
|
||||
test_circuit "adder_unsigned" "u_lfa8"
|
||||
|
||||
|
||||
test_circuit "subtractor_signed" "s_rcs8"
|
||||
test_circuit "subtractor_signed" "s_rbs8"
|
||||
|
||||
|
||||
test_circuit "subtractor_unsigned" "u_rcs8"
|
||||
test_circuit "subtractor_unsigned" "u_rbs8"
|
||||
|
||||
|
||||
test_circuit "multiplier_signed" "s_arrmul8"
|
||||
test_circuit "multiplier_signed" "s_csamul_cla8"
|
||||
test_circuit "multiplier_signed" "s_csamul_rca8"
|
||||
@ -122,6 +130,7 @@ test_circuit "multiplier_signed" "s_dadda_lfa8"
|
||||
|
||||
|
||||
test_circuit "multiplier_unsigned" "u_arrmul8"
|
||||
test_circuit "multiplier_unsigned" "u_recmul8"
|
||||
test_circuit "multiplier_unsigned" "u_csamul_cla8"
|
||||
test_circuit "multiplier_unsigned" "u_csamul_rca8"
|
||||
test_circuit "multiplier_unsigned" "u_csamul_pg_rca8"
|
||||
|
379
tests/test_compare.py
Normal file
379
tests/test_compare.py
Normal file
@ -0,0 +1,379 @@
|
||||
import os
|
||||
import sys
|
||||
# Add the parent directory to the system path
|
||||
DIR_PATH = os.path.dirname(os.path.abspath(__file__))
|
||||
sys.path.insert(0, os.path.join(DIR_PATH, '..'))
|
||||
|
||||
import numpy as np
|
||||
import math
|
||||
from io import StringIO
|
||||
|
||||
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, UnsignedCompareLTE, UnsignedCompareGT, UnsignedCompareGTE
|
||||
)
|
||||
|
||||
from ariths_gen.core.cgp_circuit import UnsignedCGPCircuit, SignedCGPCircuit
|
||||
|
||||
|
||||
import numpy as np
|
||||
import math
|
||||
from io import StringIO
|
||||
|
||||
|
||||
def test_compare_lt_same():
|
||||
""" 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)
|
||||
cmp.get_v_code_hier(open("tmp.verilog", "w"))
|
||||
|
||||
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)
|
||||
|
||||
|
||||
|
||||
|
||||
def test_compare_lt_small():
|
||||
""" Test unsigned comparator """
|
||||
N = 8
|
||||
|
||||
a = Bus(N=N, prefix="a")
|
||||
b = Bus(N=N//2, prefix="b")
|
||||
av = np.arange(2**N).reshape(1, -1)
|
||||
bv = np.arange(2**(N //2)).reshape(-1, 1)
|
||||
|
||||
|
||||
cmp = UnsignedCompareLT(a=a, b=b)
|
||||
#o = StringIO()
|
||||
cmp.get_python_code_flat(open("tmp.py", "w"))
|
||||
cmp.get_c_code_flat(open("tmp.c", "w"))
|
||||
cmp.get_cgp_code_flat(open("tmp.cgp", "w"))
|
||||
#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)
|
||||
|
||||
|
||||
|
||||
|
||||
def test_compare_lte_same():
|
||||
""" 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 = UnsignedCompareLTE(a=a, b=b)
|
||||
cmp.get_v_code_hier(open("tmp.verilog", "w"))
|
||||
|
||||
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)
|
||||
|
||||
|
||||
|
||||
|
||||
def test_compare_lte_small():
|
||||
""" Test unsigned comparator """
|
||||
N = 8
|
||||
|
||||
a = Bus(N=N, prefix="a")
|
||||
b = Bus(N=N//2, prefix="b")
|
||||
av = np.arange(2**N).reshape(1, -1)
|
||||
bv = np.arange(2**(N //2)).reshape(-1, 1)
|
||||
|
||||
|
||||
cmp = UnsignedCompareLTE(a=a, b=b)
|
||||
#o = StringIO()
|
||||
cmp.get_python_code_flat(open("tmp.py", "w"))
|
||||
cmp.get_c_code_flat(open("tmp.c", "w"))
|
||||
cmp.get_cgp_code_flat(open("tmp.cgp", "w"))
|
||||
#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)
|
||||
|
||||
|
||||
def test_compare_lte_small2():
|
||||
""" Test unsigned comparator """
|
||||
N = 8
|
||||
|
||||
a = Bus(N=N//2, prefix="a")
|
||||
b = Bus(N=N, prefix="b")
|
||||
av = np.arange(2**(N // 2)).reshape(1, -1)
|
||||
bv = np.arange(2**(N)).reshape(-1, 1)
|
||||
|
||||
|
||||
cmp = UnsignedCompareLTE(a=a, b=b)
|
||||
#o = StringIO()
|
||||
cmp.get_python_code_flat(open("tmp.py", "w"))
|
||||
cmp.get_c_code_flat(open("tmp.c", "w"))
|
||||
cmp.get_cgp_code_flat(open("tmp.cgp", "w"))
|
||||
#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)
|
||||
|
||||
|
||||
def test_compare_gt_same():
|
||||
""" 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 = UnsignedCompareGT(a=a, b=b)
|
||||
cmp.get_v_code_hier(open("tmp.verilog", "w"))
|
||||
|
||||
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)
|
||||
|
||||
|
||||
|
||||
|
||||
def test_compare_gt_small():
|
||||
""" Test unsigned comparator """
|
||||
N = 8
|
||||
|
||||
a = Bus(N=N, prefix="a")
|
||||
b = Bus(N=N//2, prefix="b")
|
||||
av = np.arange(2**N).reshape(1, -1)
|
||||
bv = np.arange(2**(N //2)).reshape(-1, 1)
|
||||
|
||||
|
||||
cmp = UnsignedCompareGT(a=a, b=b)
|
||||
#o = StringIO()
|
||||
cmp.get_python_code_flat(open("tmp.py", "w"))
|
||||
cmp.get_c_code_flat(open("tmp.c", "w"))
|
||||
cmp.get_cgp_code_flat(open("tmp.cgp", "w"))
|
||||
#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)
|
||||
|
||||
|
||||
|
||||
def test_compare_gte_same():
|
||||
""" 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 = UnsignedCompareGTE(a=a, b=b)
|
||||
cmp.get_v_code_hier(open("tmp.verilog", "w"))
|
||||
|
||||
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)
|
||||
|
||||
|
||||
|
||||
|
||||
def test_compare_gte_small():
|
||||
""" Test unsigned comparator """
|
||||
N = 8
|
||||
|
||||
a = Bus(N=N, prefix="a")
|
||||
b = Bus(N=N//2, prefix="b")
|
||||
av = np.arange(2**N).reshape(1, -1)
|
||||
bv = np.arange(2**(N //2)).reshape(-1, 1)
|
||||
|
||||
|
||||
cmp = UnsignedCompareGTE(a=a, b=b)
|
||||
#o = StringIO()
|
||||
cmp.get_python_code_flat(open("tmp.py", "w"))
|
||||
cmp.get_c_code_flat(open("tmp.c", "w"))
|
||||
cmp.get_cgp_code_flat(open("tmp.cgp", "w"))
|
||||
#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)
|
||||
|
||||
|
||||
|
||||
def test_compare_gte_cgp_same():
|
||||
""" 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 = UnsignedCompareGTE(a=a, b=b)
|
||||
o = StringIO()
|
||||
cmp.get_v_code_hier(open("tmp.verilog", "w"))
|
||||
cmp.get_cgp_code_flat(o)
|
||||
|
||||
cgp = UnsignedCGPCircuit(o.getvalue(), [N, N])
|
||||
|
||||
v = cgp(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)
|
||||
|
||||
|
||||
|
||||
|
||||
def test_compare_gte_cgp_small():
|
||||
""" Test unsigned comparator """
|
||||
N = 8
|
||||
|
||||
a = Bus(N=N, prefix="a")
|
||||
b = Bus(N=N//2, prefix="b")
|
||||
av = np.arange(2**N).reshape(1, -1)
|
||||
bv = np.arange(2**(N //2)).reshape(-1, 1)
|
||||
|
||||
|
||||
cmp = UnsignedCompareGTE(a=a, b=b)
|
||||
o = StringIO()
|
||||
cmp.get_v_code_flat(open("tmp.verilog", "w"))
|
||||
cmp.get_cgp_code_flat(o)
|
||||
cmp.get_cgp_code_flat(open("tmp.cgp", "w"))
|
||||
|
||||
cgp = UnsignedCGPCircuit(o.getvalue(), [N, N // 2])
|
||||
|
||||
v = cgp(av, bv)
|
||||
#cmp.get_v_code_hier(open("tmp.verilog", "w"))
|
||||
#print(o.getvalue())
|
||||
|
||||
# av = 0
|
||||
# bv = 5
|
||||
|
||||
|
||||
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)
|
97
tests/test_popcnt.py
Normal file
97
tests/test_popcnt.py
Normal file
@ -0,0 +1,97 @@
|
||||
import os
|
||||
import sys
|
||||
# Add the parent directory to the system path
|
||||
DIR_PATH = os.path.dirname(os.path.abspath(__file__))
|
||||
sys.path.insert(0, os.path.join(DIR_PATH, '..'))
|
||||
|
||||
from io import StringIO
|
||||
from ariths_gen.core.cgp_circuit import UnsignedCGPCircuit
|
||||
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
|
||||
|
||||
|
||||
def test_popcount(verbose: bool = False):
|
||||
""" 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())
|
||||
|
||||
if verbose:
|
||||
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)
|
||||
|
||||
if verbose:
|
||||
print("r = ", r)
|
||||
|
||||
expected = np.sum(r, axis=1)
|
||||
|
||||
if verbose:
|
||||
print("expected = ", expected)
|
||||
|
||||
np.testing.assert_array_equal(popcnt(av), expected)
|
||||
|
||||
def test_popcount_cgp(verbose: bool = False):
|
||||
""" 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_cgp_code_flat(o)
|
||||
cgp = UnsignedCGPCircuit(o.getvalue(), [N])
|
||||
v = cgp(av)
|
||||
|
||||
if verbose:
|
||||
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)
|
||||
|
||||
if verbose:
|
||||
print("r = ", r)
|
||||
|
||||
expected = np.sum(r, axis=1)
|
||||
|
||||
if verbose:
|
||||
print("expected = ", expected)
|
||||
|
||||
np.testing.assert_array_equal(v, expected)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
test_popcount()
|
||||
test_popcount_cgp()
|
||||
print("Python popcount tests were successful!")
|
203
tests/test_popcount_compare.py
Normal file
203
tests/test_popcount_compare.py
Normal file
@ -0,0 +1,203 @@
|
||||
import os
|
||||
import sys
|
||||
# Add the parent directory to the system path
|
||||
DIR_PATH = os.path.dirname(os.path.abspath(__file__))
|
||||
sys.path.insert(0, os.path.join(DIR_PATH, '..'))
|
||||
|
||||
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 (
|
||||
PopCountCompare
|
||||
)
|
||||
|
||||
from ariths_gen.core.cgp_circuit import UnsignedCGPCircuit, SignedCGPCircuit
|
||||
|
||||
|
||||
import numpy as np
|
||||
import math
|
||||
from io import StringIO
|
||||
|
||||
from itertools import product
|
||||
|
||||
def test_popcountcompare_same(verbose: bool = False):
|
||||
N = 10
|
||||
|
||||
a = Bus(N=N, prefix="a")
|
||||
b = Bus(N=N, prefix="b")
|
||||
|
||||
test_cases = list(product(list(range(2**N)), repeat=2))
|
||||
all = np.array(test_cases)
|
||||
|
||||
if verbose:
|
||||
print(all)
|
||||
|
||||
av = all[:, 0]
|
||||
bv = all[:, 1]
|
||||
def popcnt(x):
|
||||
mask = x & (2**np.arange(N)).reshape(-1, 1)
|
||||
return np.sum(mask > 0, axis=0)
|
||||
cnta = (popcnt(av))
|
||||
cntb = (popcnt(bv))
|
||||
|
||||
if verbose:
|
||||
print(cnta)
|
||||
print(cntb)
|
||||
|
||||
cmp = PopCountCompare(a=a, b=b)
|
||||
cmp.get_v_code_hier(open("tmp.verilog", "w"))
|
||||
|
||||
v = cmp(av, bv)
|
||||
|
||||
if verbose:
|
||||
print("ret = ", v)
|
||||
|
||||
expected = np.array(cnta >= cntb).astype(int)
|
||||
|
||||
if verbose:
|
||||
print("expected = ", expected)
|
||||
|
||||
#expected = np.sum(r, axis=1)
|
||||
np.testing.assert_array_equal(v, expected)
|
||||
|
||||
|
||||
def test_popcountcompare_small(verbose: bool = False):
|
||||
N = 10
|
||||
|
||||
a = Bus(N=N, prefix="a")
|
||||
b = Bus(N=N // 2, prefix="b")
|
||||
|
||||
test_cases = list(product(range(2**N), range(2**(N//2))))
|
||||
all = np.array(test_cases)
|
||||
|
||||
if verbose:
|
||||
print(all)
|
||||
|
||||
av = all[:, 0]
|
||||
bv = all[:, 1]
|
||||
def popcnt(x):
|
||||
mask = x & (2**np.arange(N)).reshape(-1, 1)
|
||||
return np.sum(mask > 0, axis=0)
|
||||
cnta = (popcnt(av))
|
||||
cntb = (popcnt(bv))
|
||||
|
||||
if verbose:
|
||||
print(cnta)
|
||||
print(cntb)
|
||||
|
||||
cmp = PopCountCompare(a=a, b=b)
|
||||
cmp.get_v_code_hier(open("tmp.verilog", "w"))
|
||||
|
||||
v = cmp(av, bv)
|
||||
|
||||
if verbose:
|
||||
print("ret = ", v)
|
||||
|
||||
expected = np.array(cnta >= cntb).astype(int)
|
||||
|
||||
if verbose:
|
||||
print("expected = ", expected)
|
||||
|
||||
#expected = np.sum(r, axis=1)
|
||||
np.testing.assert_array_equal(v, expected)
|
||||
|
||||
|
||||
def test_popcountcompare_small2(verbose: bool = False):
|
||||
N = 10
|
||||
|
||||
a = Bus(N=N // 2, prefix="a")
|
||||
b = Bus(N=N, prefix="b")
|
||||
|
||||
test_cases = list(product( range(2**(N//2)), range(2**N)))
|
||||
all = np.array(test_cases)
|
||||
|
||||
if verbose:
|
||||
print(all)
|
||||
|
||||
av = all[:, 0]
|
||||
bv = all[:, 1]
|
||||
def popcnt(x):
|
||||
mask = x & (2**np.arange(N)).reshape(-1, 1)
|
||||
return np.sum(mask > 0, axis=0)
|
||||
cnta = (popcnt(av))
|
||||
cntb = (popcnt(bv))
|
||||
|
||||
if verbose:
|
||||
print(cnta)
|
||||
print(cntb)
|
||||
|
||||
cmp = PopCountCompare(a=a, b=b)
|
||||
cmp.get_v_code_hier(open("tmp.verilog", "w"))
|
||||
|
||||
v = cmp(av, bv)
|
||||
|
||||
if verbose:
|
||||
print("ret = ", v)
|
||||
|
||||
expected = np.array(cnta >= cntb).astype(int)
|
||||
|
||||
if verbose:
|
||||
print("expected = ", expected)
|
||||
|
||||
#expected = np.sum(r, axis=1)
|
||||
np.testing.assert_array_equal(v, expected)
|
||||
|
||||
|
||||
def test_popcountcompare_small2_cgp(verbose: bool = False):
|
||||
N = 10
|
||||
|
||||
a = Bus(N=N // 2, prefix="a")
|
||||
b = Bus(N=N, prefix="b")
|
||||
|
||||
test_cases = list(product( range(2**(N//2)), range(2**N)))
|
||||
all = np.array(test_cases)
|
||||
|
||||
if verbose:
|
||||
print(all)
|
||||
|
||||
av = all[:, 0]
|
||||
bv = all[:, 1]
|
||||
def popcnt(x):
|
||||
mask = x & (2**np.arange(N)).reshape(-1, 1)
|
||||
return np.sum(mask > 0, axis=0)
|
||||
cnta = (popcnt(av))
|
||||
cntb = (popcnt(bv))
|
||||
|
||||
if verbose:
|
||||
print(cnta)
|
||||
print(cntb)
|
||||
|
||||
cmp = PopCountCompare(a=a, b=b)
|
||||
cmp.get_v_code_hier(open("tmp.verilog", "w"))
|
||||
cmp.get_cgp_code_flat(open("tmp.cgp", "w"))
|
||||
|
||||
o = StringIO()
|
||||
cmp.get_cgp_code_flat(o)
|
||||
cgp = UnsignedCGPCircuit(o.getvalue(), [N//2, N])
|
||||
|
||||
v = cgp(av, bv)
|
||||
|
||||
if verbose:
|
||||
print("ret = ", v)
|
||||
|
||||
expected = np.array(cnta >= cntb).astype(int)
|
||||
|
||||
if verbose:
|
||||
print("expected = ", expected)
|
||||
|
||||
#expected = np.sum(r, axis=1)
|
||||
np.testing.assert_array_equal(v, expected)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
test_popcountcompare_same()
|
||||
test_popcountcompare_small()
|
||||
test_popcountcompare_small2()
|
||||
test_popcountcompare_small2_cgp()
|
||||
print("Python popcount_compare tests were successful!")
|
102
tests/test_reduce.py
Normal file
102
tests/test_reduce.py
Normal file
@ -0,0 +1,102 @@
|
||||
import os
|
||||
import sys
|
||||
# Add the parent directory to the system path
|
||||
DIR_PATH = os.path.dirname(os.path.abspath(__file__))
|
||||
sys.path.insert(0, os.path.join(DIR_PATH, '..'))
|
||||
|
||||
import numpy as np
|
||||
import math
|
||||
from io import StringIO
|
||||
|
||||
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
|
||||
)
|
||||
|
||||
|
||||
def test_orreduce(verbose: bool = False):
|
||||
""" 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)
|
||||
|
||||
if verbose:
|
||||
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)
|
||||
|
||||
if verbose:
|
||||
print("r = ", r)
|
||||
|
||||
expected = np.bitwise_or.reduce(r, axis=1)
|
||||
|
||||
if verbose:
|
||||
print("expected = ", expected)
|
||||
|
||||
np.testing.assert_array_equal(reduce(av), expected)
|
||||
|
||||
|
||||
def test_andreduce(verbose: bool = False):
|
||||
""" 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)
|
||||
|
||||
if verbose:
|
||||
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)
|
||||
|
||||
if verbose:
|
||||
print("r = ", r)
|
||||
|
||||
expected = np.bitwise_and.reduce(r, axis=1)
|
||||
|
||||
if verbose:
|
||||
print("expected = ", expected)
|
||||
|
||||
np.testing.assert_array_equal(reduce(av), expected)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
test_andreduce()
|
||||
test_orreduce()
|
||||
print("Python reduce tests were successful!")
|
Loading…
x
Reference in New Issue
Block a user