mirror of
https://github.com/ehw-fit/ariths-gen.git
synced 2025-04-08 00:02:12 +01:00
commit
d445f9e3c7
6
.github/workflows/generate.yml
vendored
6
.github/workflows/generate.yml
vendored
@ -28,10 +28,12 @@ jobs:
|
||||
# You can test your matrix by printing the current Python version
|
||||
- name: Display Python version
|
||||
run: python -c "import sys; print(sys.version)"
|
||||
- name: Run generating
|
||||
- name: Run generating adds and mults
|
||||
run: python generate_test.py
|
||||
- name: Run generating
|
||||
- name: Run generating mac
|
||||
run: python generate_mac.py
|
||||
- name: Run generating axmults
|
||||
run: python generate_axmuls.py
|
||||
- name: Upload results
|
||||
uses: actions/upload-artifact@v1.0.0
|
||||
with:
|
||||
|
119
README.md
119
README.md
@ -1,34 +1,126 @@
|
||||
# ArithsGen – tool for arithmetic circuits generation
|
||||
### FIT BUT bachelor's degree project
|
||||
[](https://www.python.org/)
|
||||
[](https://ehw-fit.github.io/ariths-gen)
|
||||
|
||||
|
||||
## Description
|
||||
ArithsGen presents an open source tool that enables generation of various arithmetic circuits along with the possibility to export them to various representations which all serve their specific purpose. C language for easy simulation, Verilog for logic synthesis, BLIF for formal verification possibilities and CGP to enable further global optimization.
|
||||
|
||||
In contrast to standard HDL languages Python supports
|
||||
* Multiple output formats (BLIF, Verilog, C, Integer netlis)
|
||||
* Advanced langugage construction (better configuration, inheritance, etc.)
|
||||
* Support of various PDKs (for using library cells as half-adders and full-adders)
|
||||
|
||||
## Prebuild circuits
|
||||
To enable the fast work with the circuits, we published pre-build arithmetic circuits in various formats in [generated_circuits](generated_circuits) folder and as a [Release](https://github.com/ehw-fit/ariths-gen/releases).
|
||||
|
||||
### Usage
|
||||
```bash
|
||||
python3 ariths_gen.py
|
||||
python3 generate_test.py
|
||||
cd test_circuits
|
||||
ls
|
||||
```
|
||||
|
||||
### Example of generation
|
||||
#Example generation of Verilog representation of 8-bit unsigned dadda multiplier that uses cla to provide the final product
|
||||
a = Bus(N=8, prefix="a_bus")
|
||||
b = Bus(N=8, prefix="b_bus")
|
||||
```py
|
||||
#Example generation of Verilog representation of 8-bit unsigned dadda multiplier that uses cla to provide the final product
|
||||
a = Bus(N=8, prefix="a_bus")
|
||||
b = Bus(N=8, prefix="b_bus")
|
||||
|
||||
u_dadda = UnsignedDaddaMultiplier(a=a, b=b, prefix="h_u_dadda_cla8", unsigned_adder_class_name=UnsignedCarryLookaheadAdder)
|
||||
u_dadda.get_v_code_hier(open("h_u_dadda_cla8.v", "w"))
|
||||
u_dadda = UnsignedDaddaMultiplier(a=a, b=b, prefix="h_u_dadda_cla8", unsigned_adder_class_name=UnsignedCarryLookaheadAdder)
|
||||
u_dadda.get_v_code_hier(open("h_u_dadda_cla8.v", "w"))
|
||||
```
|
||||
|
||||
### Simple arithmetic circuits
|
||||
See [Ripple Carry Adder](ariths_gen/multi_bit_circuits/adders/ripple_carry_adder.py) file for a basic example.
|
||||
|
||||
### Complex circuits
|
||||
It is possible to combine some basic circuit to generate more complex circuits (such as MAC). The design can be parametrised (i.e., you can pass `UnsignedArraymultiplier` as an input parameter).
|
||||
|
||||
```py
|
||||
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
|
||||
import os
|
||||
|
||||
class MAC(GeneralCircuit):
|
||||
def __init__(self, a: Bus, b: Bus, r: Bus, prefix: str = "", name: str = "mac", **kwargs):
|
||||
super().__init__(prefix=prefix, name=name, out_N=2*a.N+1, inputs=[a, b, r], **kwargs)
|
||||
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.out.connect_bus(connecting_bus=self.add.out)
|
||||
|
||||
# usage
|
||||
if __name__ == "__main__":
|
||||
os.makedirs("test_circuits/mac", exist_ok=True)
|
||||
mymac = MAC(Bus("a", 8), Bus("b", 8), Bus("acc", 16))
|
||||
mymac.get_v_code_hier(open("test_circuits/mac/mac_hier.v", "w"))
|
||||
mymac.get_c_code_hier(open("test_circuits/mac/mac_hier.c", "w"))
|
||||
mymac.get_c_code_flat(open("test_circuits/mac/mac_flat.c", "w"))
|
||||
```
|
||||
|
||||
## Documentation
|
||||
Code documentation is provided using **pdoc** documentation generator tool. Source: https://pdoc3.github.io/pdoc/.
|
||||
The automatically generated documentation is available at
|
||||
https://ehw-fit.github.io/ariths-gen/ .
|
||||
|
||||
### Installation
|
||||
```bash
|
||||
pip3 install pdoc3
|
||||
|
||||
## Supporting various PDK kits
|
||||
When one uses a specific process design kit (PDK), it is not effective to implement half- and full-adders using two-inputs logic gates. These circuits are directly implemented as CMOS modules and are more effective than heuristic optimization by synthesis tool. If you want to use for example FreePDK45 library, you can call a following function before verilog code generating.
|
||||
|
||||
```py
|
||||
from ariths_gen import set_pdk45_library
|
||||
set_pdk45_library()
|
||||
```
|
||||
|
||||
### Usage
|
||||
You can add a support of arbitrary PDK (see an [example](ariths_gen/pdk.py) ).
|
||||
|
||||
|
||||
## Approximate circuits
|
||||
Besides the accurate arithmetic circuits you can generate some approximate circuits. Currently we support _Broken Array Multiplier_ and _Truncated Multiplier_. For more details please follow files in folder [approximate_multipliers](ariths_gen/multi_bit_circuits/approximate_multipliers/). You can simply run
|
||||
```bash
|
||||
pdoc --html ariths_gen
|
||||
python3 generate_axmuls.py
|
||||
```
|
||||
to get the approximate circuits.
|
||||
|
||||
The module supports also evaluation of the proposed circuits. You can call the instation as a function (even with numpy-array input) to obtain the results for one multiplication
|
||||
|
||||
```py
|
||||
from ariths_gen.wire_components.buses import Bus
|
||||
from ariths_gen.multi_bit_circuits.approximate_multipliers import UnsignedBrokenArrayMultiplier
|
||||
a = Bus(N=8, prefix="a_bus")
|
||||
b = Bus(N=8, prefix="b_bus")
|
||||
|
||||
# Create BAM
|
||||
bam = UnsignedBrokenArrayMultiplier(a, b, horizontal_cut=4, vertical_cut=4)
|
||||
|
||||
print("43 * 84 = ", bam(43, 84), " expected: ", 43 * 84)
|
||||
# 43 * 84 = 3440 expected: 3612
|
||||
```
|
||||
even for all possible combinations
|
||||
|
||||
```py
|
||||
# Evaluate all using b'casting
|
||||
import numpy as np
|
||||
import matplotlib.pyplot as plt
|
||||
va = np.arange(256).reshape(1, -1)
|
||||
vb = va.reshape(-1, 1)
|
||||
r = bam(va, vb)
|
||||
|
||||
cax = plt.imshow(np.abs(r - (va * vb)))
|
||||
plt.colorbar(cax)
|
||||
plt.title("Absolute difference")
|
||||
plt.xlabel("a")
|
||||
plt.ylabel("b")
|
||||
|
||||
print("Mean average error", np.abs(r - (va * vb)).mean())
|
||||
# Mean average error 956.25
|
||||
```
|
||||

|
||||
|
||||
## Formal verification
|
||||
The `yosys_equiv_check.sh` script enables to formally check the equivalence of generated Verilog and BLIF representations of the same circuit.
|
||||
@ -51,7 +143,6 @@ For more detailed description of script's usage, use help.
|
||||
|
||||
## CGP testing
|
||||
The `chr2c.py` script converts the input CGP chromosome generated by ArithsGen to the corresponding C code and prints it to standard output.
|
||||
It is a modified version of a script originally authored by Vojtech Mrazek.
|
||||
|
||||
### Usage
|
||||
```bash
|
||||
|
@ -11,5 +11,8 @@ from .one_bit_circuits import (
|
||||
from .multi_bit_circuits import (
|
||||
adders,
|
||||
multipliers,
|
||||
approximate_multipliers,
|
||||
dividers
|
||||
)
|
||||
|
||||
from .pdk import *
|
@ -127,16 +127,20 @@ class GeneralCircuit():
|
||||
return multi_bit_comps
|
||||
|
||||
@staticmethod
|
||||
def get_unique_types(components: list):
|
||||
def get_unique_types(components: list, multi_bit: bool = False):
|
||||
"""Retrieves just the unique representatives of class types present inside the provided components list.
|
||||
|
||||
Args:
|
||||
components (list): List of components to be filtered.
|
||||
multi_bit (bool): Specifies whether the provided components list is composed of multi bit type circuits. Defaults to False.
|
||||
|
||||
Returns:
|
||||
list: List of unique composite class types.
|
||||
"""
|
||||
return list({type(c): c for c in components}.values())
|
||||
if multi_bit is True:
|
||||
return list({(type(c), c.N): c for c in components}.values())
|
||||
else:
|
||||
return list({type(c): c for c in components}.values())
|
||||
|
||||
def get_component_types(self):
|
||||
"""Retrieves a list of all the unique types of subcomponents composing the circuit.
|
||||
@ -150,7 +154,7 @@ class GeneralCircuit():
|
||||
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())
|
||||
components=self.get_multi_bit_components(), multi_bit=True)
|
||||
|
||||
all_components = gate_comps + one_bit_comps + multi_bit_comps
|
||||
return all_components
|
||||
@ -407,7 +411,7 @@ 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.prefix.replace(circuit_prefix+"_", "")
|
||||
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"
|
||||
|
||||
@ -559,13 +563,9 @@ 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.prefix.replace(circuit_prefix+"_", "")
|
||||
|
||||
# Obtain proper circuit name with its bit width
|
||||
circuit_prefix = self.__class__(
|
||||
a=Bus("a"), b=Bus("b")).prefix + str(self.N)
|
||||
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_prefix)
|
||||
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"
|
||||
|
||||
@ -671,7 +671,7 @@ 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.prefix.replace(circuit_prefix+"_", "")
|
||||
circuit_type = self.__class__(a=Bus("a"), b=Bus("b")).prefix + str(self.N)
|
||||
return f"{self.a.get_wire_assign_blif(output=True)}" + \
|
||||
f"{self.b.get_wire_assign_blif(output=True)}" + \
|
||||
f".subckt {circuit_type}" + \
|
||||
|
@ -29,22 +29,43 @@ class MultiplierCircuit(ArithmeticCircuit):
|
||||
that are later used for generation into various representations.
|
||||
"""
|
||||
|
||||
def __init__(self, a, b, prefix, name, out_N, **kwargs):
|
||||
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)
|
||||
|
||||
# Array multipliers
|
||||
def get_previous_partial_product(self, a_index: int, b_index: int):
|
||||
def get_previous_partial_product(self, a_index: int, b_index: int, mult_type=""):
|
||||
"""Used in array multipliers to get previous row's component output wires for further connection to another component's input.
|
||||
|
||||
Args:
|
||||
a_index (int): First input wire index.
|
||||
b_index (int): Second input wire index.
|
||||
mult_type (string, optional): Specifies what type of multiplier circuit has called this method. It is used for proper retrieval of index into the components list to allow appropriate interconnection of the multiplier circuit's inner subcomponents. It expects "" for ordinary multipliers, `bam` or `tm` for specific approximate multipliers. Defaults to "".
|
||||
|
||||
Returns:
|
||||
Wire: Previous row's component wire of corresponding pp.
|
||||
"""
|
||||
# To get the index of previous row's connecting adder and its generated pp
|
||||
index = ((b_index-2) * (self.N*2)) + ((self.N-1)+2*(a_index+2))
|
||||
if mult_type == "bam":
|
||||
#TODO alter to be more compact
|
||||
ids_sum = 0
|
||||
for row in range(self.horizontal_cut + self.ommited_rows, b_index):
|
||||
first_row_elem_id = self.vertical_cut-row if self.vertical_cut-row > 0 else 0
|
||||
# First pp row composed just from gates
|
||||
if row == self.horizontal_cut + self.ommited_rows:
|
||||
# Minus one because the first component has index 0 instead of 1
|
||||
ids_sum += sum([1 for gate_pos in range(first_row_elem_id, self.N)])-1
|
||||
elif row == b_index-1:
|
||||
ids_sum += sum([2 for gate_adder_pos in range(first_row_elem_id, self.N) if gate_adder_pos <= a_index+1])
|
||||
else:
|
||||
ids_sum += sum([2 for gate_adder_pos in range(first_row_elem_id, self.N)])
|
||||
# Index calculation should be redone, but it works even this way
|
||||
index = ids_sum+2 if a_index == self.N-1 else ids_sum
|
||||
elif mult_type == "tm":
|
||||
index = ((b_index-self.truncation_cut-2) * ((self.N-self.truncation_cut)*2)) + ((self.N-self.truncation_cut-1)+2*(a_index-self.truncation_cut+2))
|
||||
else:
|
||||
index = ((b_index-2) * ((self.N)*2)) + ((self.N-1)+2*(a_index+2))
|
||||
|
||||
|
||||
|
||||
# Get carry wire as input for the last adder in current row
|
||||
if a_index == self.N-1:
|
||||
|
@ -67,7 +67,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__(a=a, b=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)
|
||||
|
@ -65,7 +65,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)
|
||||
|
||||
|
||||
# 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)
|
||||
|
@ -0,0 +1,9 @@
|
||||
from ariths_gen.multi_bit_circuits.approximate_multipliers.truncated_multiplier import (
|
||||
UnsignedTruncatedMultiplier,
|
||||
SignedTruncatedMultiplier
|
||||
)
|
||||
|
||||
from ariths_gen.multi_bit_circuits.approximate_multipliers.broken_array_multiplier import (
|
||||
UnsignedBrokenArrayMultiplier,
|
||||
SignedBrokenArrayMultiplier
|
||||
)
|
@ -0,0 +1,321 @@
|
||||
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 (
|
||||
HalfAdder,
|
||||
FullAdder,
|
||||
FullAdderPG
|
||||
)
|
||||
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
|
||||
)
|
||||
|
||||
|
||||
class UnsignedBrokenArrayMultiplier(MultiplierCircuit):
|
||||
"""Class representing unsigned broken array multiplier.
|
||||
|
||||
It represents an approximative version of unsigned array multiplier with simpler structure.
|
||||
It is created by modifying an ordinary N-bit unsigned array multiplier by omitting partial product
|
||||
stage cells by the specified horizontal and vertical cut levels.
|
||||
|
||||
The design promises better area and power parameters in exchange for the loss of computation precision.
|
||||
The BAM design allows to save more partial product stage adders than truncated multiplier.
|
||||
```
|
||||
VERTICAL CUT=4
|
||||
|
||||
│
|
||||
A3B0 A2B0 A1B0 A0B0
|
||||
│ ┌───┐ ┌───┐ ┌───┐ ┌───┐
|
||||
│AND│ │AND│ │AND│ │AND│
|
||||
│ └───┘ └───┘ └───┘ └───┘
|
||||
|
||||
│
|
||||
A3B1 A2B1 A1B1 A0B1
|
||||
│ ┌───┐ ┌───┐ ┌───┐ ┌───┐
|
||||
│AND│ │AND│ │AND│ │AND│
|
||||
│ └───┘ └───┘ └───┘ └───┘
|
||||
|
||||
│ ┌────┐ ┌────┐ ┌────┐ ┌────┐
|
||||
│ │ │ │ │ │ │ │
|
||||
│ │ HA │ │ FA │ │ FA │ │ HA │
|
||||
│ │ │ │ │ │ │ │
|
||||
│ └────┘ └────┘ └────┘ └────┘
|
||||
|
||||
─ ─ ─ ─ ─ ─ ─ ─ ─ ┼ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ HORIZONTAL CUT=2
|
||||
A3B2 A2B2 A1B2 A0B2
|
||||
┌▼─▼┐ │ ┌───┐ ┌───┐ ┌───┐
|
||||
│AND│ │AND│ │AND│ │AND│
|
||||
└┬──┘ │ └───┘ └───┘ └───┘
|
||||
│
|
||||
│ │ ┌────┐ ┌────┐ ┌────┐
|
||||
│ │ │ │ │ │ │
|
||||
│ │ │ FA │ │ FA │ │ HA │
|
||||
│ │ │ │ │ │ │
|
||||
│ │ └────┘ └────┘ └────┘
|
||||
A3B3 │ A2B3 A1B3 A0B3
|
||||
┌▼─▼┐ │ ┌▼─▼┐ │ ┌───┐ ┌───┐
|
||||
│AND│ │ │AND│ │AND│ │AND│
|
||||
└┬──┘ │ └┬──┘ │ └───┘ └───┘
|
||||
│ │ │
|
||||
┌▼───┐ ┌▼──▼┐ │ ┌────┐ ┌────┐
|
||||
│ │ │ │ │ │ │ │
|
||||
┌──────┤ HA │◄────┤ HA │ │ │ FA │ │ HA │
|
||||
│ │ │ │ │ │ │ │ │
|
||||
│ └──┬─┘ └──┬─┘ │ └────┘ └────┘
|
||||
│ │ │
|
||||
│ │ │ │
|
||||
▼ ▼ ▼ ▼ ▼ ▼ ▼ ▼
|
||||
P7 P6 P5 │ P4=0 P3=0 P2=0 P1=0 P0=0
|
||||
```
|
||||
Description of the __init__ method.
|
||||
|
||||
Args:
|
||||
a (Bus): First input bus.
|
||||
b (Bus): Second input bus.
|
||||
horizontal_cut (int, optional): Specifies horizontal cut used in broken array multiplier circuit creation. Defaults to 0.
|
||||
vertical_cut (int, optional): Specifies vertical cut used in broken array multiplier circuit creation. Defaults to 0.
|
||||
prefix (str, optional): Prefix name of unsigned broken array multiplier. Defaults to "".
|
||||
name (str, optional): Name of unsigned broken array multiplier. Defaults to "u_bam".
|
||||
"""
|
||||
def __init__(self, a: Bus, b: Bus, horizontal_cut: int = 0, vertical_cut: int = 0, prefix: str = "", name: str = "u_bam", **kwargs):
|
||||
# NOTE: If horizontal/vertical cut is specified as 0 the final circuit is a simple array multiplier
|
||||
self.horizontal_cut = horizontal_cut
|
||||
self.vertical_cut = vertical_cut
|
||||
|
||||
self.N = max(a.N, b.N)
|
||||
# Horizontal cut level should be: 0 <= horizontal_cut < N
|
||||
# Vertical cut level should be: horizontal_cut <= vertical_cut < 2*N
|
||||
assert horizontal_cut < self.N
|
||||
assert vertical_cut < 2*self.N
|
||||
|
||||
# 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)
|
||||
|
||||
# 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)
|
||||
|
||||
self.ommited_rows = 0
|
||||
# Gradual generation of partial products
|
||||
for b_multiplier_index in range(self.horizontal_cut, self.N):
|
||||
# Number of elements that should be ommited in the current level based on vertical cut
|
||||
pp_row_elems_to_skip = self.vertical_cut - b_multiplier_index if self.vertical_cut - b_multiplier_index > 0 else 0
|
||||
# Number of pp pairs present in the current row
|
||||
pp_row_elems = self.N-pp_row_elems_to_skip if self.N-pp_row_elems_to_skip > 0 else 0
|
||||
self.ommited_rows += 1 if pp_row_elems == 0 else 0
|
||||
|
||||
for a_multiplicand_index in range((self.N-pp_row_elems), self.N):
|
||||
# AND gates generation for calculation of partial products
|
||||
obj_and = AndGate(self.a.get_wire(a_multiplicand_index), self.b.get_wire(b_multiplier_index), prefix=self.prefix+"_and"+str(a_multiplicand_index)+"_"+str(b_multiplier_index))
|
||||
self.add_component(obj_and)
|
||||
|
||||
if b_multiplier_index != self.horizontal_cut + self.ommited_rows:
|
||||
if b_multiplier_index == self.horizontal_cut + self.ommited_rows + 1:
|
||||
previous_product = self.components[a_multiplicand_index + b_multiplier_index - self.vertical_cut].out
|
||||
else:
|
||||
previous_product = self.get_previous_partial_product(a_index=a_multiplicand_index, b_index=b_multiplier_index, mult_type="bam")
|
||||
|
||||
# HA generation for first 1-bit adder in each row starting from the second one
|
||||
if a_multiplicand_index == 0 or self.vertical_cut-b_multiplier_index == a_multiplicand_index:
|
||||
obj_adder = HalfAdder(self.get_previous_component().out, previous_product, prefix=self.prefix+"_ha"+str(a_multiplicand_index)+"_"+str(b_multiplier_index))
|
||||
self.add_component(obj_adder)
|
||||
# Product generation
|
||||
self.out.connect(b_multiplier_index, obj_adder.get_sum_wire())
|
||||
|
||||
# HA generation, last 1-bit adder in second row
|
||||
elif a_multiplicand_index == self.N-1 and b_multiplier_index == self.horizontal_cut+self.ommited_rows+1:
|
||||
obj_adder = HalfAdder(self.get_previous_component().out, self.get_previous_component(number=2).get_carry_wire(), prefix=self.prefix+"_ha"+str(a_multiplicand_index)+"_"+str(b_multiplier_index))
|
||||
self.add_component(obj_adder)
|
||||
|
||||
# FA generation
|
||||
else:
|
||||
obj_adder = FullAdder(self.get_previous_component().out, previous_product, self.get_previous_component(number=2).get_carry_wire(), prefix=self.prefix+"_fa"+str(a_multiplicand_index)+"_"+str(b_multiplier_index))
|
||||
self.add_component(obj_adder)
|
||||
|
||||
# PRODUCT GENERATION
|
||||
if (a_multiplicand_index == 0 and b_multiplier_index == self.horizontal_cut) or (self.horizontal_cut + self.ommited_rows == self.N-1):
|
||||
self.out.connect(a_multiplicand_index + b_multiplier_index, obj_and.out)
|
||||
|
||||
# 1 bit multiplier case
|
||||
if a_multiplicand_index == self.N-1 and b_multiplier_index == self.N-1:
|
||||
self.out.connect(a_multiplicand_index+b_multiplier_index+1, ConstantWireValue0())
|
||||
|
||||
elif b_multiplier_index == self.N-1 and self.horizontal_cut != self.N-1:
|
||||
self.out.connect(b_multiplier_index + a_multiplicand_index, obj_adder.get_sum_wire())
|
||||
|
||||
if a_multiplicand_index == self.N-1:
|
||||
self.out.connect(self.out.N-1, obj_adder.get_carry_wire())
|
||||
|
||||
# Connecting the output bits generated from ommited cells to ground
|
||||
if self.vertical_cut == 2*self.N-1:
|
||||
[self.out.connect(out_id, ConstantWireValue0()) for out_id in range(self.out.N)]
|
||||
else:
|
||||
for grounded_out_index in range(0, max(self.horizontal_cut, self.vertical_cut)):
|
||||
self.out.connect(grounded_out_index, ConstantWireValue0())
|
||||
|
||||
class SignedBrokenArrayMultiplier(MultiplierCircuit):
|
||||
"""Class representing signed broken array multiplier.
|
||||
|
||||
It represents an approximative version of signed array multiplier with simpler structure.
|
||||
It is created by modifying an ordinary N-bit unsigned array multiplier by omitting partial product
|
||||
stage cells by the specified horizontal and vertical cut levels.
|
||||
|
||||
The design promises better area and power parameters in exchange for the loss of computation precision.
|
||||
The BAM design allows to save more partial product stage adders than truncated multiplier.
|
||||
TODO
|
||||
```
|
||||
A3B0 A2B0 A1B0 A0B0
|
||||
│ │ │ │ │ │ │ │
|
||||
┌▼─▼─┐ ┌▼─▼┐ ┌▼─▼┐ ┌▼─▼┐
|
||||
│NAND│ │AND│ │AND│ │AND│
|
||||
└┬───┘ └┬──┘ └┬──┘ └─┬─┘
|
||||
A3B1 │ A2B1 │ A1B1 │ A0B1 │
|
||||
┌▼─▼─┐ │ ┌▼─▼┐ │ ┌▼─▼┐ │ ┌▼─▼┐ │
|
||||
│NAND│ │ │AND│ │ │AND│ │ │AND│ │
|
||||
1 └┬───┘ │ └┬──┘ │ └┬──┘ │ └┬──┘ │
|
||||
│ │ │ │ │ │ │ │ │
|
||||
┌▼──▼┐ ┌▼──▼┐ ┌▼──▼┐ ┌▼──▼┐ │
|
||||
│ │ │ │ │ │ │ │ │
|
||||
┌───────┤ FA │◄──┤ FA │◄──┤ FA │◄──┤ HA │ │
|
||||
│ │ │ │ │ │ │ │ │ │
|
||||
│ └┬───┘ └┬───┘ └┬───┘ └─┬──┘ │
|
||||
│ A3B2 │ A2B2 │ A1B2 │ A0B2 │ │
|
||||
│ ┌▼─▼─┐ │ ┌▼─▼┐ │ ┌▼─▼┐ │ ┌▼─▼┐ │ │
|
||||
│ │NAND│ │ │AND│ │ │AND│ │ │AND│ │ │
|
||||
│ └┬───┘ │ └┬──┘ │ └┬──┘ │ └┬──┘ │ │
|
||||
│ │ │ │ │ │ │ │ │ │
|
||||
┌▼──▼┐ ┌▼──▼┐ ┌▼──▼┐ ┌▼──▼┐ │ │
|
||||
│ │ │ │ │ │ │ │ │ │
|
||||
┌───────┤ FA │◄──┤ FA │◄──┤ FA │◄──┤ HA │ │ │
|
||||
│ │ │ │ │ │ │ │ │ │ │
|
||||
│ └┬───┘ └┬───┘ └┬───┘ └─┬──┘ │ │
|
||||
│ A3B3 │ A2B3 │ A1B3 │ A0B3 │ │ │
|
||||
│ ┌▼─▼┐ │ ┌▼─▼─┐ │ ┌▼─▼─┐ │ ┌▼─▼─┐ │ │ │
|
||||
│ │AND│ │ │NAND│ │ │NAND│ │ │NAND│ │ │ │
|
||||
1 │ └┬──┘ │ └┬───┘ │ └┬───┘ │ └┬───┘ │ │ │
|
||||
│ │ │ │ │ │ │ │ │ │ │ │
|
||||
┌─▼──┐ ┌▼──▼┐ ┌▼──▼┐ ┌▼──▼┐ ┌▼──▼┐ │ │ │
|
||||
│ │ │ │ │ │ │ │ │ │ │ │ │
|
||||
│XOR │◄──┤ FA │◄──┤ FA │◄──┤ FA │◄──┤ HA │ │ │ │
|
||||
│ │ │ │ │ │ │ │ │ │ │ │ │
|
||||
└─┬──┘ └─┬──┘ └─┬──┘ └─┬──┘ └─┬──┘ │ │ │
|
||||
│ │ │ │ │ │ │ │
|
||||
▼ ▼ ▼ ▼ ▼ ▼ ▼ ▼
|
||||
P7 P6 P5 P4 P3 P2 P1 P0
|
||||
```
|
||||
|
||||
Description of the __init__ method.
|
||||
|
||||
Args:
|
||||
a (Bus): First input bus.
|
||||
b (Bus): Second input bus.
|
||||
horizontal_cut (int, optional): Specifies horizontal cut used in signed broken array multiplier circuit creation. Defaults to 0.
|
||||
vertical_cut (int, optional): Specifies vertical cut used in signed broken array multiplier circuit creation. Defaults to 0.
|
||||
prefix (str, optional): Prefix name of signed broken array multiplier. Defaults to "".
|
||||
name (str, optional): Name of signed broken array multiplier. Defaults to "s_bam".
|
||||
"""
|
||||
def __init__(self, a: Bus, b: Bus, horizontal_cut: int = 0, vertical_cut: int = 0, prefix: str = "", name: str = "s_bam", **kwargs):
|
||||
# NOTE: If horizontal/vertical cut is specified as 0 the final circuit is a simple array multiplier
|
||||
self.horizontal_cut = horizontal_cut
|
||||
self.vertical_cut = vertical_cut
|
||||
|
||||
self.N = max(a.N, b.N)
|
||||
# Horizontal cut level should be: 0 <= horizontal_cut < N
|
||||
# Vertical cut level should be: horizontal_cut <= vertical_cut < 2*N
|
||||
assert horizontal_cut < self.N
|
||||
assert vertical_cut < 2*self.N
|
||||
|
||||
# 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, signed=True, **kwargs)
|
||||
self.c_data_type = "int64_t"
|
||||
|
||||
# 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)
|
||||
|
||||
self.ommited_rows = 0
|
||||
# Gradual generation of partial products
|
||||
for b_multiplier_index in range(self.horizontal_cut, self.N):
|
||||
# Number of elements that should be ommited in the current level based on vertical cut
|
||||
pp_row_elems_to_skip = self.vertical_cut - b_multiplier_index if self.vertical_cut - b_multiplier_index > 0 else 0
|
||||
# Number of pp pairs present in the current row
|
||||
pp_row_elems = self.N-pp_row_elems_to_skip if self.N-pp_row_elems_to_skip > 0 else 0
|
||||
self.ommited_rows += 1 if pp_row_elems == 0 else 0
|
||||
|
||||
for a_multiplicand_index in range(self.N-pp_row_elems, self.N):
|
||||
# AND and NAND gates generation for calculation of partial products and sign extension
|
||||
if (b_multiplier_index == self.N-1 and a_multiplicand_index != self.N-1) or (b_multiplier_index != self.N-1 and a_multiplicand_index == self.N-1):
|
||||
obj_nand = NandGate(self.a.get_wire(a_multiplicand_index), self.b.get_wire(b_multiplier_index), prefix=self.prefix+"_nand"+str(a_multiplicand_index)+"_"+str(b_multiplier_index), parent_component=self)
|
||||
self.add_component(obj_nand)
|
||||
else:
|
||||
obj_and = AndGate(self.a.get_wire(a_multiplicand_index), self.b.get_wire(b_multiplier_index), prefix=self.prefix+"_and"+str(a_multiplicand_index)+"_"+str(b_multiplier_index), parent_component=self)
|
||||
self.add_component(obj_and)
|
||||
|
||||
if b_multiplier_index != self.horizontal_cut + self.ommited_rows:
|
||||
if b_multiplier_index == self.horizontal_cut + self.ommited_rows + 1:
|
||||
previous_product = self.components[a_multiplicand_index + b_multiplier_index - self.vertical_cut].out
|
||||
else:
|
||||
previous_product = self.get_previous_partial_product(a_index=a_multiplicand_index, b_index=b_multiplier_index, mult_type="bam")
|
||||
|
||||
# HA generation for first 1-bit adder in each row starting from the second one
|
||||
if a_multiplicand_index == 0 or self.vertical_cut-b_multiplier_index == a_multiplicand_index:
|
||||
obj_adder = HalfAdder(self.get_previous_component().out, previous_product, prefix=self.prefix+"_ha"+str(a_multiplicand_index)+"_"+str(b_multiplier_index))
|
||||
self.add_component(obj_adder)
|
||||
# Product generation
|
||||
self.out.connect(b_multiplier_index, obj_adder.get_sum_wire())
|
||||
|
||||
# FA generation
|
||||
else:
|
||||
# Constant wire with value 1 used at the last FA in second row (as one of its inputs) for signed multiplication (based on Baugh Wooley algorithm)
|
||||
if a_multiplicand_index == self.N-1 and b_multiplier_index == self.horizontal_cut+self.ommited_rows+1:
|
||||
previous_product = ConstantWireValue1()
|
||||
|
||||
obj_adder = FullAdder(self.get_previous_component().out, previous_product, self.get_previous_component(number=2).get_carry_wire(), prefix=self.prefix+"_fa"+str(a_multiplicand_index)+"_"+str(b_multiplier_index))
|
||||
self.add_component(obj_adder)
|
||||
|
||||
# PRODUCT GENERATION
|
||||
if (a_multiplicand_index == 0 and b_multiplier_index == self.horizontal_cut) or (self.horizontal_cut + self.ommited_rows == self.N-1):
|
||||
self.out.connect(a_multiplicand_index + b_multiplier_index, obj_and.out)
|
||||
# 1 bit multiplier case
|
||||
if a_multiplicand_index == self.N-1 and b_multiplier_index == self.N-1:
|
||||
obj_nor = NorGate(ConstantWireValue1(), self.get_previous_component().out, prefix=self.prefix+"_nor_zero_extend", parent_component=self)
|
||||
self.add_component(obj_nor)
|
||||
|
||||
self.out.connect(a_multiplicand_index+1, obj_nor.out)
|
||||
|
||||
elif b_multiplier_index == self.N-1 and self.horizontal_cut != self.N-1:
|
||||
self.out.connect(b_multiplier_index + a_multiplicand_index, obj_adder.get_sum_wire())
|
||||
|
||||
if a_multiplicand_index == self.N-1:
|
||||
obj_xor = XorGate(self.get_previous_component().get_carry_wire(), ConstantWireValue1(), prefix=self.prefix+"_xor"+str(a_multiplicand_index+1)+"_"+str(b_multiplier_index), parent_component=self)
|
||||
self.add_component(obj_xor)
|
||||
|
||||
self.out.connect(self.out.N-1, obj_xor.out)
|
||||
|
||||
# Connecting the output bits generated from ommited cells to ground
|
||||
if self.vertical_cut == 2*self.N-1:
|
||||
[self.out.connect(out_id, ConstantWireValue0()) for out_id in range(self.out.N)]
|
||||
else:
|
||||
for grounded_out_index in range(0, max(self.horizontal_cut, self.vertical_cut)):
|
||||
self.out.connect(grounded_out_index, ConstantWireValue0())
|
@ -0,0 +1,270 @@
|
||||
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 (
|
||||
HalfAdder,
|
||||
FullAdder,
|
||||
FullAdderPG
|
||||
)
|
||||
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
|
||||
)
|
||||
|
||||
class UnsignedTruncatedMultiplier(MultiplierCircuit):
|
||||
"""Class representing unsigned truncated multiplier.
|
||||
|
||||
It represents an approximative version of unsigned array multiplier with simpler structure.
|
||||
It is created by modifying an ordinary N-bit unsigned array multiplier by ignoring
|
||||
(truncating) some of the partial products.
|
||||
|
||||
The design promises better area and power parameters in exchange for the loss of computation precision.
|
||||
```
|
||||
CUT=2
|
||||
A3B0 A2B0 │ A1B0 A0B0
|
||||
┌───┐ ┌───┐ ┌───┐ ┌───┐
|
||||
│AND│ │AND│ │ │AND│ │AND│
|
||||
└───┘ └───┘ └───┘ └───┘
|
||||
┌ ─ ─ ─ ┘
|
||||
A3B1 A2B1 A1B1 A0B1
|
||||
┌───┐ ┌───┐ │ ┌───┐ ┌───┐
|
||||
│AND│ │AND│ │AND│ │AND│
|
||||
└───┘ └───┘ │ └───┘ └───┘
|
||||
┌────┐ ┌────┐ ┌────┐ ┌────┐
|
||||
│ │ │ │ │ │ │ │ │
|
||||
│ HA │ │ FA │ │ FA │ │ HA │
|
||||
│ │ │ │ │ │ │ │ │
|
||||
└────┘ └────┘ └────┘ └────┘
|
||||
─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─┬ ─ ─ ─ ─ ┴─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ CUT=2
|
||||
A3B2 A2B2 A1B2 A0B2
|
||||
┌▼─▼┐ ┌▼─▼┐ │ ┌───┐ ┌───┐
|
||||
│AND│ │AND│ │AND│ │AND│
|
||||
└┬──┘ └┬──┘ │ └───┘ └───┘
|
||||
│ │ ┌────┐ ┌────┐
|
||||
│ │ │ │ │ │ │
|
||||
│ ┌ ─ ┼─ ─ ─ ┘ │ FA │ │ HA │
|
||||
│ │ │ │ │ │
|
||||
│ │ │ └────┘ └────┘
|
||||
A3B3 │ A2B3 │ A1B3 A0B3
|
||||
┌◄─►┐ │ ┌◄─►┐ │ │ ┌───┐ ┌───┐
|
||||
│AND│ │ │AND│ │ │AND│ │AND│
|
||||
└┬──┘ │ └┬──┘ │ │ └───┘ └───┘
|
||||
┌───▼┐ ┌▼──▼┐ ┌┼───┐ ┌────┐
|
||||
│ │ │ │ │ ││ │ │ │
|
||||
┌──────┤ HA │◄────┤ HA │ ││FA │ │ HA │
|
||||
│ │ │ │ │ │ ││ │ │ │
|
||||
│ └──┬─┘ └──┬─┘ └┼───┘ └────┘
|
||||
│ │ │ │ │
|
||||
▼ ▼ ▼ ▼ ▼ ▼ ▼ ▼
|
||||
P7 P6 P5 │ P4 P3=0 P2=0 P1=0 P0=0
|
||||
```
|
||||
Description of the __init__ method.
|
||||
|
||||
Args:
|
||||
a (Bus): First input bus.
|
||||
b (Bus): Second input bus.
|
||||
truncation_cut (int, optional): Specifies truncation cut level used in the truncated multiplier circuit creation. Note: If equal to 0, the final circuit behaves as an ordinary array multiplier. Defaults to 0.
|
||||
prefix (str, optional): Prefix name of unsigned truncated multiplier. Defaults to "".
|
||||
name (str, optional): Name of unsigned truncated multiplier. Defaults to "u_tm".
|
||||
"""
|
||||
def __init__(self, a: Bus, b: Bus, truncation_cut: int = 0, prefix: str = "", name: str = "u_tm", **kwargs):
|
||||
# NOTE: If truncation_cut is specified as 0 the final circuit is a simple array multiplier
|
||||
self.truncation_cut = truncation_cut
|
||||
|
||||
self.N = max(a.N, b.N)
|
||||
# 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)
|
||||
|
||||
# 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 generation of partial products
|
||||
for b_multiplier_index in range(self.truncation_cut, self.N):
|
||||
for a_multiplicand_index in range(self.truncation_cut, self.N):
|
||||
# AND gates generation for calculation of partial products
|
||||
obj_and = AndGate(self.a.get_wire(a_multiplicand_index), self.b.get_wire(b_multiplier_index), prefix=self.prefix+"_and"+str(a_multiplicand_index)+"_"+str(b_multiplier_index))
|
||||
self.add_component(obj_and)
|
||||
|
||||
if b_multiplier_index != self.truncation_cut:
|
||||
previous_product = self.components[a_multiplicand_index + b_multiplier_index - 2*self.truncation_cut].out if b_multiplier_index == self.truncation_cut + 1 else self.get_previous_partial_product(a_index=a_multiplicand_index, b_index=b_multiplier_index, mult_type="tm")
|
||||
|
||||
# HA generation for first 1-bit adder in each row starting from the second one
|
||||
if a_multiplicand_index == self.truncation_cut:
|
||||
obj_adder = HalfAdder(self.get_previous_component().out, previous_product, prefix=self.prefix+"_ha"+str(a_multiplicand_index)+"_"+str(b_multiplier_index))
|
||||
self.add_component(obj_adder)
|
||||
# Product generation
|
||||
self.out.connect(b_multiplier_index + self.truncation_cut, obj_adder.get_sum_wire())
|
||||
|
||||
# HA generation, last 1-bit adder in second row
|
||||
elif a_multiplicand_index == self.N-1 and b_multiplier_index == self.truncation_cut+1:
|
||||
obj_adder = HalfAdder(self.get_previous_component().out, self.get_previous_component(number=2).get_carry_wire(), prefix=self.prefix+"_ha"+str(a_multiplicand_index)+"_"+str(b_multiplier_index))
|
||||
self.add_component(obj_adder)
|
||||
|
||||
# FA generation
|
||||
else:
|
||||
obj_adder = FullAdder(self.get_previous_component().out, previous_product, self.get_previous_component(number=2).get_carry_wire(), prefix=self.prefix+"_fa"+str(a_multiplicand_index)+"_"+str(b_multiplier_index))
|
||||
self.add_component(obj_adder)
|
||||
|
||||
# PRODUCT GENERATION
|
||||
if (a_multiplicand_index == self.truncation_cut and b_multiplier_index == self.truncation_cut) or (self.truncation_cut == self.N-1):
|
||||
self.out.connect(a_multiplicand_index + b_multiplier_index, obj_and.out)
|
||||
|
||||
# 1 bit multiplier case
|
||||
if a_multiplicand_index == self.N-1 and b_multiplier_index == self.N-1:
|
||||
self.out.connect(a_multiplicand_index+b_multiplier_index+1, ConstantWireValue0())
|
||||
|
||||
elif b_multiplier_index == self.N-1 and self.truncation_cut != self.N-1:
|
||||
self.out.connect(b_multiplier_index + a_multiplicand_index, obj_adder.get_sum_wire())
|
||||
|
||||
if a_multiplicand_index == self.N-1:
|
||||
self.out.connect(self.out.N-1, obj_adder.get_carry_wire())
|
||||
|
||||
# Connecting the output bits generated from ommited cells to ground
|
||||
for grounded_out_index in range(0, self.truncation_cut*2):
|
||||
self.out.connect(grounded_out_index, ConstantWireValue0())
|
||||
|
||||
class SignedTruncatedMultiplier(MultiplierCircuit):
|
||||
"""Class representing signed truncated multiplier.
|
||||
|
||||
It represents an approximative version of signed array multiplier with simpler structure.
|
||||
It is created by modifying an ordinary N-bit signed array multiplier by ignoring
|
||||
(truncating) some of the partial products.
|
||||
|
||||
The design promises better area and power parameters in exchange for the loss of computation precision.
|
||||
|
||||
```TODO
|
||||
A3B0 A2B0 A1B0 A0B0
|
||||
│ │ │ │ │ │ │ │
|
||||
┌▼─▼─┐ ┌▼─▼┐ ┌▼─▼┐ ┌▼─▼┐
|
||||
│NAND│ │AND│ │AND│ │AND│
|
||||
└┬───┘ └┬──┘ └┬──┘ └─┬─┘
|
||||
A3B1 │ A2B1 │ A1B1 │ A0B1 │
|
||||
┌▼─▼─┐ │ ┌▼─▼┐ │ ┌▼─▼┐ │ ┌▼─▼┐ │
|
||||
│NAND│ │ │AND│ │ │AND│ │ │AND│ │
|
||||
1 └┬───┘ │ └┬──┘ │ └┬──┘ │ └┬──┘ │
|
||||
│ │ │ │ │ │ │ │ │
|
||||
┌▼──▼┐ ┌▼──▼┐ ┌▼──▼┐ ┌▼──▼┐ │
|
||||
│ │ │ │ │ │ │ │ │
|
||||
┌───────┤ FA │◄──┤ FA │◄──┤ FA │◄──┤ HA │ │
|
||||
│ │ │ │ │ │ │ │ │ │
|
||||
│ └┬───┘ └┬───┘ └┬───┘ └─┬──┘ │
|
||||
│ A3B2 │ A2B2 │ A1B2 │ A0B2 │ │
|
||||
│ ┌▼─▼─┐ │ ┌▼─▼┐ │ ┌▼─▼┐ │ ┌▼─▼┐ │ │
|
||||
│ │NAND│ │ │AND│ │ │AND│ │ │AND│ │ │
|
||||
│ └┬───┘ │ └┬──┘ │ └┬──┘ │ └┬──┘ │ │
|
||||
│ │ │ │ │ │ │ │ │ │
|
||||
┌▼──▼┐ ┌▼──▼┐ ┌▼──▼┐ ┌▼──▼┐ │ │
|
||||
│ │ │ │ │ │ │ │ │ │
|
||||
┌───────┤ FA │◄──┤ FA │◄──┤ FA │◄──┤ HA │ │ │
|
||||
│ │ │ │ │ │ │ │ │ │ │
|
||||
│ └┬───┘ └┬───┘ └┬───┘ └─┬──┘ │ │
|
||||
│ A3B3 │ A2B3 │ A1B3 │ A0B3 │ │ │
|
||||
│ ┌▼─▼┐ │ ┌▼─▼─┐ │ ┌▼─▼─┐ │ ┌▼─▼─┐ │ │ │
|
||||
│ │AND│ │ │NAND│ │ │NAND│ │ │NAND│ │ │ │
|
||||
1 │ └┬──┘ │ └┬───┘ │ └┬───┘ │ └┬───┘ │ │ │
|
||||
│ │ │ │ │ │ │ │ │ │ │ │
|
||||
┌─▼──┐ ┌▼──▼┐ ┌▼──▼┐ ┌▼──▼┐ ┌▼──▼┐ │ │ │
|
||||
│ │ │ │ │ │ │ │ │ │ │ │ │
|
||||
│XOR │◄──┤ FA │◄──┤ FA │◄──┤ FA │◄──┤ HA │ │ │ │
|
||||
│ │ │ │ │ │ │ │ │ │ │ │ │
|
||||
└─┬──┘ └─┬──┘ └─┬──┘ └─┬──┘ └─┬──┘ │ │ │
|
||||
│ │ │ │ │ │ │ │
|
||||
▼ ▼ ▼ ▼ ▼ ▼ ▼ ▼
|
||||
P7 P6 P5 P4 P3 P2 P1 P0
|
||||
```
|
||||
|
||||
Description of the __init__ method.
|
||||
|
||||
Args:
|
||||
a (Bus): First input bus.
|
||||
b (Bus): Second input bus.
|
||||
truncation_cut (int, optional): Specifies truncation cut level used in the truncated multiplier circuit creation. Note: If equal to 0, the final circuit behaves as an ordinary array multiplier. Defaults to 0.
|
||||
prefix (str, optional): Prefix name of signed truncated multiplier. Defaults to "".
|
||||
name (str, optional): Name of signed truncated multiplier. Defaults to "s_tm".
|
||||
"""
|
||||
def __init__(self, a: Bus, b: Bus, truncation_cut: int = 0, prefix: str = "", name: str = "s_tm", **kwargs):
|
||||
# NOTE: If truncation_cut is specified as 0 the final circuit is a simple array multiplier
|
||||
self.truncation_cut = truncation_cut
|
||||
|
||||
self.N = max(a.N, b.N)
|
||||
# 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, signed=True, **kwargs)
|
||||
self.c_data_type = "int64_t"
|
||||
|
||||
# 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 generation of partial products
|
||||
for b_multiplier_index in range(self.truncation_cut, self.N):
|
||||
for a_multiplicand_index in range(self.truncation_cut, self.N):
|
||||
# AND and NAND gates generation for calculation of partial products and sign extension
|
||||
if (b_multiplier_index == self.N-1 and a_multiplicand_index != self.N-1) or (b_multiplier_index != self.N-1 and a_multiplicand_index == self.N-1):
|
||||
obj_nand = NandGate(self.a.get_wire(a_multiplicand_index), self.b.get_wire(b_multiplier_index), prefix=self.prefix+"_nand"+str(a_multiplicand_index)+"_"+str(b_multiplier_index), parent_component=self)
|
||||
self.add_component(obj_nand)
|
||||
else:
|
||||
obj_and = AndGate(self.a.get_wire(a_multiplicand_index), self.b.get_wire(b_multiplier_index), prefix=self.prefix+"_and"+str(a_multiplicand_index)+"_"+str(b_multiplier_index), parent_component=self)
|
||||
self.add_component(obj_and)
|
||||
|
||||
if b_multiplier_index != self.truncation_cut:
|
||||
previous_product = self.components[a_multiplicand_index + b_multiplier_index - 2*self.truncation_cut].out if b_multiplier_index == self.truncation_cut + 1 else self.get_previous_partial_product(a_index=a_multiplicand_index, b_index=b_multiplier_index, mult_type="tm")
|
||||
# HA generation for first 1-bit adder in each row starting from the second one
|
||||
if a_multiplicand_index == self.truncation_cut:
|
||||
obj_adder = HalfAdder(self.get_previous_component().out, previous_product, prefix=self.prefix+"_ha"+str(a_multiplicand_index)+"_"+str(b_multiplier_index))
|
||||
self.add_component(obj_adder)
|
||||
# Product generation
|
||||
self.out.connect(b_multiplier_index + self.truncation_cut, obj_adder.get_sum_wire())
|
||||
|
||||
# FA generation
|
||||
else:
|
||||
# Constant wire with value 1 used at the last FA in second row (as one of its inputs) for signed multiplication (based on Baugh Wooley algorithm)
|
||||
if a_multiplicand_index == self.N-1 and b_multiplier_index == self.truncation_cut+1:
|
||||
previous_product = ConstantWireValue1()
|
||||
|
||||
obj_adder = FullAdder(self.get_previous_component().out, previous_product, self.get_previous_component(number=2).get_carry_wire(), prefix=self.prefix+"_fa"+str(a_multiplicand_index)+"_"+str(b_multiplier_index))
|
||||
self.add_component(obj_adder)
|
||||
|
||||
# PRODUCT GENERATION
|
||||
if (a_multiplicand_index == self.truncation_cut and b_multiplier_index == self.truncation_cut) or (self.truncation_cut == self.N-1):
|
||||
self.out.connect(a_multiplicand_index + b_multiplier_index, obj_and.out)
|
||||
|
||||
# 1 bit multiplier case
|
||||
if a_multiplicand_index == self.N-1 and b_multiplier_index == self.N-1:
|
||||
obj_nor = NorGate(ConstantWireValue1(), self.get_previous_component().out, prefix=self.prefix+"_nor_zero_extend", parent_component=self)
|
||||
self.add_component(obj_nor)
|
||||
|
||||
self.out.connect(a_multiplicand_index+1, obj_nor.out)
|
||||
|
||||
elif b_multiplier_index == self.N-1 and self.truncation_cut != self.N-1:
|
||||
self.out.connect(b_multiplier_index + a_multiplicand_index, obj_adder.get_sum_wire())
|
||||
|
||||
if a_multiplicand_index == self.N-1:
|
||||
obj_xor = XorGate(self.get_previous_component().get_carry_wire(), ConstantWireValue1(), prefix=self.prefix+"_xor"+str(a_multiplicand_index+1)+"_"+str(b_multiplier_index), parent_component=self)
|
||||
self.add_component(obj_xor)
|
||||
|
||||
self.out.connect(self.out.N-1, obj_xor.out)
|
||||
|
||||
# Connecting the output bits generated from ommited cells to ground
|
||||
for grounded_out_index in range(0, self.truncation_cut*2):
|
||||
self.out.connect(grounded_out_index, ConstantWireValue0())
|
@ -193,7 +193,7 @@ class SignedArrayMultiplier(MultiplierCircuit):
|
||||
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)
|
||||
self.c_data_type = "int64_t"
|
||||
|
||||
|
||||
# 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)
|
||||
|
@ -23,6 +23,8 @@ class FullAdder(ThreeInputOneBitCircuit):
|
||||
c (Wire, optional): Carry input wire. Defaults to Wire(name="cin").
|
||||
prefix (str, optional): Prefix 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)
|
||||
# 2 wires for component's bus output (sum, cout)
|
||||
@ -50,6 +52,40 @@ class FullAdder(ThreeInputOneBitCircuit):
|
||||
|
||||
self.out.connect(1, obj_or.out)
|
||||
|
||||
def get_init_v_flat(self):
|
||||
""" support of custom PDK """
|
||||
if not self.use_verilog_instance:
|
||||
return super().get_init_v_flat()
|
||||
|
||||
return " " + self.use_verilog_instance.format(
|
||||
**{
|
||||
"unit": self.prefix,
|
||||
"wirea": self.a.prefix,
|
||||
"wireb": self.b.prefix,
|
||||
"wirec": self.c.prefix,
|
||||
"wireys": self.get_sum_wire().prefix,
|
||||
"wireyc": self.get_carry_wire().prefix,
|
||||
}
|
||||
) + ";\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)
|
||||
|
||||
return " " + self.use_verilog_instance.format(**{
|
||||
"unit": self.prefix,
|
||||
"wirea": self.a.name,
|
||||
"wireb": self.b.name,
|
||||
"wirec": self.c.name,
|
||||
"wireys": unique_out_wires[0],
|
||||
"wireyc": unique_out_wires[1],
|
||||
}) + ";\n"
|
||||
|
||||
class FullAdderPG(ThreeInputOneBitCircuit):
|
||||
"""Class representing modified three input one bit full adder with propagate/generate logic.
|
||||
|
@ -21,6 +21,8 @@ class HalfAdder(TwoInputOneBitCircuit):
|
||||
b (Wire, optional): Second input wire. Defaults to Wire(name="b").
|
||||
prefix (str, optional): Prefix 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)
|
||||
# 2 wires for component's bus output (sum, cout)
|
||||
@ -38,6 +40,38 @@ class HalfAdder(TwoInputOneBitCircuit):
|
||||
self.add_component(obj_and)
|
||||
self.out.connect(1, obj_and.out)
|
||||
|
||||
def get_init_v_flat(self):
|
||||
""" support of custom PDK """
|
||||
if not self.use_verilog_instance:
|
||||
return super().get_init_v_flat()
|
||||
|
||||
return " " + self.use_verilog_instance.format(
|
||||
**{
|
||||
"unit": self.prefix,
|
||||
"wirea": self.a.prefix,
|
||||
"wireb": self.b.prefix,
|
||||
"wireys": self.get_sum_wire().prefix,
|
||||
"wireyc": self.get_carry_wire().prefix,
|
||||
}
|
||||
) + ";\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)
|
||||
|
||||
return " " + self.use_verilog_instance.format(**{
|
||||
"unit": self.prefix,
|
||||
"wirea": self.a.name,
|
||||
"wireb": self.b.name,
|
||||
"wireys": unique_out_wires[0],
|
||||
"wireyc": unique_out_wires[1],
|
||||
}) + ";\n"
|
||||
|
||||
class PGLogicBlock(TwoInputOneBitCircuit):
|
||||
"""Class representing two input one bit propagate/generate logic block.
|
||||
|
18
ariths_gen/pdk.py
Normal file
18
ariths_gen/pdk.py
Normal file
@ -0,0 +1,18 @@
|
||||
"""
|
||||
Support of custom PDK
|
||||
|
||||
This file defines functions for generating of full and half adders
|
||||
directly on the level of CMOS modules.
|
||||
|
||||
You may add your own modules as in example in set_pdk45_library()
|
||||
|
||||
Please call this function before calling get_verilog_code_XXX()
|
||||
"""
|
||||
from .one_bit_circuits import (
|
||||
one_bit_components
|
||||
)
|
||||
|
||||
|
||||
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}))"
|
@ -9,7 +9,7 @@ class Bus():
|
||||
Args:
|
||||
prefix (str, optional): Prefix name of the bus. Defaults to "bus".
|
||||
N (int, optional): Number of wires in the bus. Defaults to 1.
|
||||
wires_list (list, optional): List of Wire objects used to clone one bus to another. Defaults to 0.
|
||||
wires_list (list, optional): List of Wire objects used to clone one bus to another. Defaults to None.
|
||||
out_bus (bool, optional): Specifies whether this Bus is an output bus of some previous component. Defaults to False.
|
||||
signed (bool, optional): Specifies whether this Bus should consider signed numbers or not (used for C code generation). Defaults to False.
|
||||
"""
|
||||
@ -48,18 +48,23 @@ class Bus():
|
||||
"""
|
||||
return self.out_bus
|
||||
|
||||
def bus_extend(self, N: int, prefix: str = "bus"):
|
||||
def bus_extend(self, N: int, prefix: str = "bus", last_wire_extend: bool = True):
|
||||
"""Provides bus extension to contain more wires.
|
||||
|
||||
Args:
|
||||
N (int): Number of wires in the bus. Defaults to 1.
|
||||
prefix (str, optional): Prefix name of the bus. Defaults to "bus".
|
||||
last_wire_extend (bool, optional): Specifies whether the last wire of the bus should be extended (connected) to all the extending wires. Defaults to True.
|
||||
"""
|
||||
# Checks if any extension is neccesarry and if so, proceeds to wire extend the bus
|
||||
if self.N < N:
|
||||
# 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(self.N, N)]
|
||||
if last_wire_extend is True:
|
||||
for w_index in range(self.N, N):
|
||||
self.connect(bus_wire_index=w_index, inner_component_out_wire=self.get_wire(self.N - 1))
|
||||
self.N = N
|
||||
|
||||
|
||||
def get_wire(self, wire_index: int = 0):
|
||||
"""Retrieves a wire from the bus by a given index.
|
||||
|
60
generate_axmuls.py
Normal file
60
generate_axmuls.py
Normal file
@ -0,0 +1,60 @@
|
||||
|
||||
from ariths_gen.wire_components import (
|
||||
Wire,
|
||||
ConstantWireValue0,
|
||||
ConstantWireValue1,
|
||||
Bus
|
||||
)
|
||||
|
||||
from ariths_gen.multi_bit_circuits.approximate_multipliers import (
|
||||
UnsignedTruncatedMultiplier,
|
||||
UnsignedBrokenArrayMultiplier
|
||||
)
|
||||
|
||||
from ariths_gen.pdk import *
|
||||
import os
|
||||
from itertools import product
|
||||
|
||||
def open_file_with_folder(filename, mode):
|
||||
d = os.path.dirname(filename)
|
||||
if d:
|
||||
os.makedirs(d, exist_ok = True)
|
||||
return open(filename, mode)
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Optional use HA and FA technology from pdk45 library
|
||||
#set_pdk45_library()
|
||||
|
||||
# 8-bit unsigned BAMs
|
||||
root_path = "test_circuits/ax"
|
||||
|
||||
i = 0
|
||||
for h in range(0, 8):
|
||||
# v <= (8-1) + (8-2)
|
||||
for v in range(h, 13):
|
||||
i += 1
|
||||
N=8
|
||||
a = Bus(prefix="a", N=N)
|
||||
b = Bus(prefix="b", N=N)
|
||||
|
||||
u_bam = UnsignedBrokenArrayMultiplier(a, b, name=f"f_u_bam{N}_h{h}_v{v}", horizontal_cut=h, vertical_cut=v)
|
||||
u_bam.get_c_code_flat(open_file_with_folder(os.path.join(root_path, f"BAM/C/flat/f_u_bam{N}_h{h}_v{v}.c"), "w"))
|
||||
u_bam.get_v_code_flat(open_file_with_folder(os.path.join(root_path, f"BAM/Verilog/flat/f_u_bam{N}_h{h}_v{v}.v"), "w"))
|
||||
|
||||
u_bam = UnsignedBrokenArrayMultiplier(a, b, name=f"h_u_bam{N}_h{h}_v{v}", horizontal_cut=h, vertical_cut=v)
|
||||
u_bam.get_c_code_hier(open_file_with_folder(os.path.join(root_path, f"BAM/C/hier/h_u_bam{N}_h{h}_v{v}.c"), "w"))
|
||||
u_bam.get_v_code_hier(open_file_with_folder(os.path.join(root_path, f"BAM/Verilog/hier/h_u_bam{N}_h{h}_v{v}.v"), "w"))
|
||||
|
||||
# 8-bit unsigned TMs
|
||||
for i in range(0, 8):
|
||||
N=8
|
||||
a = Bus(prefix="a", N=N)
|
||||
b = Bus(prefix="b", N=N)
|
||||
|
||||
u_tm = UnsignedTruncatedMultiplier(a, b, name=f"f_u_tm{N}_k{i}", truncation_cut=i)
|
||||
u_tm.get_c_code_flat(open_file_with_folder(os.path.join(root_path, f"TM/C/flat/f_u_tm{N}_k{i}.c"), "w"))
|
||||
u_tm.get_v_code_flat(open_file_with_folder(os.path.join(root_path, f"TM/Verilog/flat/f_u_tm{N}_k{i}.v"), "w"))
|
||||
|
||||
u_tm = UnsignedTruncatedMultiplier(a, b, name=f"h_u_tm{N}_k{i}", truncation_cut=i)
|
||||
u_tm.get_c_code_hier(open_file_with_folder(os.path.join(root_path, f"TM/C/hier/h_u_tm{N}_k{i}.c"), "w"))
|
||||
u_tm.get_v_code_hier(open_file_with_folder(os.path.join(root_path, f"TM/Verilog/hier/h_u_tm{N}_k{i}.v"), "w"))
|
@ -17,8 +17,9 @@ class MAC(GeneralCircuit):
|
||||
self.out.connect_bus(connecting_bus=self.add.out)
|
||||
|
||||
# usage
|
||||
os.makedirs("test_circuits/mac", exist_ok=True)
|
||||
mymac = MAC(Bus("a", 8), Bus("b", 8), Bus("acc", 16))
|
||||
mymac.get_v_code_hier(open("test_circuits/mac/mac_hier.v", "w"))
|
||||
mymac.get_c_code_hier(open("test_circuits/mac/mac_hier.c", "w"))
|
||||
mymac.get_c_code_flat(open("test_circuits/mac/mac_flat.c", "w"))
|
||||
if __name__ == "__main__":
|
||||
os.makedirs("test_circuits/mac", exist_ok=True)
|
||||
mymac = MAC(Bus("a", 8), Bus("b", 8), Bus("acc", 16))
|
||||
mymac.get_v_code_hier(open("test_circuits/mac/mac_hier.v", "w"))
|
||||
mymac.get_c_code_hier(open("test_circuits/mac/mac_hier.c", "w"))
|
||||
mymac.get_c_code_flat(open("test_circuits/mac/mac_flat.c", "w"))
|
||||
|
@ -28,9 +28,54 @@ from ariths_gen.multi_bit_circuits.multipliers import (
|
||||
SignedWallaceMultiplier,
|
||||
)
|
||||
|
||||
|
||||
from ariths_gen.multi_bit_circuits.approximate_multipliers import (
|
||||
UnsignedTruncatedMultiplier,
|
||||
SignedTruncatedMultiplier,
|
||||
UnsignedBrokenArrayMultiplier,
|
||||
SignedBrokenArrayMultiplier
|
||||
)
|
||||
|
||||
import numpy as np
|
||||
|
||||
|
||||
def test_unsigned_approxmul(values = False):
|
||||
""" Test unsigned approximate multipliers """
|
||||
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 [UnsignedBrokenArrayMultiplier, UnsignedTruncatedMultiplier]:
|
||||
if c == UnsignedTruncatedMultiplier:
|
||||
mul = c(a=a, b=b, truncation_cut=2)
|
||||
elif c == UnsignedBrokenArrayMultiplier:
|
||||
mul = c(a=a, b=b, horizontal_cut=1, vertical_cut=2)
|
||||
r = mul(av, bv)
|
||||
|
||||
# WCE – worst case error; used for approximate multiplier error measurement
|
||||
WCE = np.amax(abs(np.subtract(r, expected)))
|
||||
|
||||
# WCRE – worst case relative error; used for approximate multiplier error measurement
|
||||
np.seterr(divide='ignore', invalid='ignore')
|
||||
WCRE = np.max(np.nan_to_num(abs(np.subtract(r, expected)) / expected))
|
||||
|
||||
if isinstance(mul, UnsignedTruncatedMultiplier):
|
||||
# WCE_TM(n,k) = (2^k - 1) * (2^(n+1) - 2^k - 1)
|
||||
expected_WCE = (2 ** mul.truncation_cut - 1) * (2 ** (mul.a.N+1) - 2 ** mul.truncation_cut - 1)
|
||||
elif isinstance(mul, UnsignedBrokenArrayMultiplier):
|
||||
# WCE_BAM(n,h,v) = (2^n - 1) * {SUM_i0_to_h-1}(2^i) + 2^h * {SUM_i0_to_v-h-1}(2^(v-h) - 2^i)
|
||||
sum_1 = sum([2**i for i in range(0, mul.horizontal_cut)])
|
||||
sum_2 = sum([2**(mul.vertical_cut-mul.horizontal_cut) - 2**i for i in range(0, mul.vertical_cut-mul.horizontal_cut)])
|
||||
expected_WCE = (2 ** mul.N - 1) * sum_1 + 2 ** mul.horizontal_cut * sum_2
|
||||
|
||||
# Test expected result
|
||||
assert expected_WCE == WCE
|
||||
if values is True:
|
||||
np.testing.assert_array_equal(expected, r)
|
||||
|
||||
|
||||
def test_unsigned_mul():
|
||||
""" Test unsigned multipliers """
|
||||
|
4
yosys_equiv_check.sh
Normal file → Executable file
4
yosys_equiv_check.sh
Normal file → Executable file
@ -83,6 +83,8 @@ else
|
||||
if [[ "$TOP_MODULE" != $(echo $BLIF_FILE | cut -c1-"$((${#BLIF_FILE}-5))") ]]; then
|
||||
echo "Input files have different names! Do they describe the same circuit design?"
|
||||
echo "For proper equivalence check, both designs should have the same name used for their filenames and also for their top module designs."
|
||||
echo $TOP_MODULE
|
||||
echo $(echo $BLIF_FILE | cut -c1-"$((${#BLIF_FILE}-5))")
|
||||
echo
|
||||
echo "Type -h | --help for more information."
|
||||
exit 2
|
||||
@ -136,4 +138,4 @@ else
|
||||
equiv_simple
|
||||
equiv_status -assert
|
||||
"
|
||||
fi
|
||||
fi
|
||||
|
Loading…
x
Reference in New Issue
Block a user