mirror of
https://github.com/ehw-fit/ariths-gen.git
synced 2025-04-08 08:12:11 +01:00
Added broken array multiplier and truncated multiplier implementations. Tried testing them, but seems buggy. Needs further work.
This commit is contained in:
parent
f830029c54
commit
9aa0fb1858
@ -33,20 +33,33 @@ class MultiplierCircuit(ArithmeticCircuit):
|
||||
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, horizontal_break: int = 0, vertical_break: int=0):
|
||||
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.
|
||||
horizontal_break (int, optional): Specifies horizontal break used in truncated multiplier circuit creation. Defaults to 0.
|
||||
vertical_break (int, optional): Specifies vertical break used in truncated multiplier circuit creation. Defaults to 0.
|
||||
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-horizontal_break-2) * ((self.N-vertical_break)*2)) + ((self.N-vertical_break-1)+2*(a_index-vertical_break+2))
|
||||
if mult_type == "bam":
|
||||
ids_sum = 0
|
||||
for level in range(self.horizontal_cut, b_index):
|
||||
# First pp level composed just from gates
|
||||
if level == self.horizontal_cut:
|
||||
# Minus one because the first component has index 0 instead of 1
|
||||
ids_sum += sum([1 for gate_pos in range(self.vertical_cut-level, self.N)])-1
|
||||
else:
|
||||
ids_sum += sum([2 for gate_adder_pos in range(self.vertical_cut-level, self.N) if gate_adder_pos <= a_index+1])
|
||||
index = 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:
|
||||
|
@ -1,4 +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,295 @@
|
||||
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.
|
||||
TODO
|
||||
```
|
||||
A3B0 A2B0 A1B0 A0B0
|
||||
│ │ │ │ │ │ │ │
|
||||
┌▼─▼┐ ┌▼─▼┐ ┌▼─▼┐ ┌▼─▼┐
|
||||
│AND│ │AND│ │AND│ │AND│
|
||||
└┬──┘ └┬──┘ └┬──┘ └─┬─┘
|
||||
A3B1 │ A2B1 │ A1B1 │ A0B1 │
|
||||
┌▼─▼┐ │ ┌▼─▼┐ │ ┌▼─▼┐ │ ┌▼─▼┐ │
|
||||
│AND│ │ │AND│ │ │AND│ │ │AND│ │
|
||||
└┬──┘ │ └┬──┘ │ └┬──┘ │ └┬──┘ │
|
||||
│ │ │ │ │ │ │ │
|
||||
┌───▼┐ ┌▼──▼┐ ┌▼──▼┐ ┌▼──▼┐ │
|
||||
│ │ │ │ │ │ │ │ │
|
||||
┌───────┤ HA │◄──┤ FA │◄──┤ FA │◄──┤ HA │ │
|
||||
│ │ │ │ │ │ │ │ │ │
|
||||
│ └┬───┘ └┬───┘ └┬───┘ └─┬──┘ │
|
||||
│ A3B2 │ A2B2 │ A1B2 │ A0B2 │ │
|
||||
│ ┌▼─▼┐ │ ┌▼─▼┐ │ ┌▼─▼┐ │ ┌▼─▼┐ │ │
|
||||
│ │AND│ │ │AND│ │ │AND│ │ │AND│ │ │
|
||||
│ └┬──┘ │ └┬──┘ │ └┬──┘ │ └┬──┘ │ │
|
||||
│ │ │ │ │ │ │ │ │ │
|
||||
┌▼──▼┐ ┌▼──▼┐ ┌▼──▼┐ ┌▼──▼┐ │ │
|
||||
│ │ │ │ │ │ │ │ │ │
|
||||
┌───────┤ FA │◄──┤ FA │◄──┤ FA │◄──┤ HA │ │ │
|
||||
│ │ │ │ │ │ │ │ │ │ │
|
||||
│ └┬───┘ └┬───┘ └┬───┘ └─┬──┘ │ │
|
||||
│ A3B3 │ A2B3 │ A1B3 │ A0B3 │ │ │
|
||||
│ ┌▼─▼┐ │ ┌▼─▼┐ │ ┌▼─▼┐ │ ┌▼─▼┐ │ │ │
|
||||
│ │AND│ │ │AND│ │ │AND│ │ │AND│ │ │ │
|
||||
│ └┬──┘ │ └┬──┘ │ └┬──┘ │ └┬──┘ │ │ │
|
||||
│ │ │ │ │ │ │ │ │ │ │
|
||||
┌▼──▼┐ ┌▼──▼┐ ┌▼──▼┐ ┌▼──▼┐ │ │ │
|
||||
│ │ │ │ │ │ │ │ │ │ │
|
||||
┌──────┤ 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 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):
|
||||
# Vertical cut should be greater or equal to horizontal cut
|
||||
assert vertical_cut >= horizontal_cut
|
||||
|
||||
# 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)
|
||||
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.horizontal_cut, self.N):
|
||||
for a_multiplicand_index in range(self.N):
|
||||
# Skip generating the AND gates that should be ommited
|
||||
if a_multiplicand_index+b_multiplier_index < (self.vertical_cut):
|
||||
continue
|
||||
# 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:
|
||||
if b_multiplier_index == self.horizontal_cut + 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+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.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.horizontal_cut >= self.N or self.vertical_cut >= 2*self.N:
|
||||
[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):
|
||||
#TODO
|
||||
# NOTE: If horizontal/vertical break 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)
|
||||
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)
|
||||
|
||||
break_offsets = horizontal_cut + vertical_cut
|
||||
# Gradual generation of partial products
|
||||
for b_multiplier_index in range(self.horizontal_cut, self.N):
|
||||
for a_multiplicand_index in range(self.vertical_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.horizontal_cut and self.vertical_cut != self.N-1:
|
||||
if b_multiplier_index == self.horizontal_cut + 1:
|
||||
previous_product = self.components[a_multiplicand_index + b_multiplier_index - break_offsets].out
|
||||
else:
|
||||
previous_product = self.get_previous_partial_product(a_index=a_multiplicand_index, b_index=b_multiplier_index, horizontal_cut=horizontal_cut, vertical_cut=vertical_cut)
|
||||
|
||||
# HA generation for first 1-bit adder in each row starting from the second one
|
||||
if a_multiplicand_index == self.vertical_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, 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+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.vertical_cut and b_multiplier_index == self.horizontal_cut) or (self.horizontal_cut == self.N-1 or self.vertical_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.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.horizontal_cut >= self.N or self.vertical_cut >= self.N:
|
||||
[self.out.connect(out_id, ConstantWireValue0()) for out_id in range(self.out.N)]
|
||||
else:
|
||||
for grounded_out_index in range(0, break_offsets):
|
||||
self.out.connect(grounded_out_index, ConstantWireValue0())
|
@ -31,14 +31,13 @@ from ariths_gen.multi_bit_circuits.multipliers import(
|
||||
class UnsignedTruncatedMultiplier(MultiplierCircuit):
|
||||
"""Class representing unsigned truncated multiplier.
|
||||
|
||||
TODO
|
||||
Unsigned truncated multiplier represents N-bit multiplier composed of
|
||||
many AND gates and half/full adders to calculate partial products and
|
||||
gradually sum them.
|
||||
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.
|
||||
|
||||
Downside is its rather big area because it is composed of many logic gates.
|
||||
TODO
|
||||
```
|
||||
The design promises better area and power parameters in exchange for the loss of computation precision.
|
||||
|
||||
```TODO
|
||||
A3B0 A2B0 A1B0 A0B0
|
||||
│ │ │ │ │ │ │ │
|
||||
┌▼─▼┐ ┌▼─▼┐ ┌▼─▼┐ ┌▼─▼┐
|
||||
@ -84,15 +83,13 @@ class UnsignedTruncatedMultiplier(MultiplierCircuit):
|
||||
Args:
|
||||
a (Bus): First input bus.
|
||||
b (Bus): Second input bus.
|
||||
horizontal_break (int, optional): Specifies horizontal break used in truncated multiplier circuit creation. Defaults to 0.
|
||||
vertical_break (int, optional): Specifies vertical break used in truncated multiplier circuit creation. Defaults to 0.
|
||||
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, horizontal_break: int = 0, vertical_break: int = 0, prefix: str = "", name: str = "u_tm", **kwargs):
|
||||
# NOTE: If horizontal/vertical break is specified as 0 the final circuit is a simple array multiplier
|
||||
self.horizontal_break = horizontal_break
|
||||
self.vertical_break = vertical_break
|
||||
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)
|
||||
super().__init__(a=a, b=b, prefix=prefix, name=name, out_N=self.N*2, **kwargs)
|
||||
@ -101,29 +98,25 @@ class UnsignedTruncatedMultiplier(MultiplierCircuit):
|
||||
self.a.bus_extend(N=self.N, prefix=a.prefix)
|
||||
self.b.bus_extend(N=self.N, prefix=b.prefix)
|
||||
|
||||
break_offsets = horizontal_break + vertical_break
|
||||
# Gradual generation of partial products
|
||||
for b_multiplier_index in range(self.horizontal_break, self.N):
|
||||
for a_multiplicand_index in range(self.vertical_break, self.N):
|
||||
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.horizontal_break and self.vertical_break != self.N-1:
|
||||
if b_multiplier_index == self.horizontal_break + 1:
|
||||
previous_product = self.components[a_multiplicand_index + b_multiplier_index - break_offsets].out
|
||||
else:
|
||||
previous_product = self.get_previous_partial_product(a_index=a_multiplicand_index, b_index=b_multiplier_index, horizontal_break=horizontal_break, vertical_break=vertical_break)
|
||||
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.vertical_break:
|
||||
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.vertical_break, obj_adder.get_sum_wire())
|
||||
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.horizontal_break+1:
|
||||
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)
|
||||
|
||||
@ -132,37 +125,37 @@ class UnsignedTruncatedMultiplier(MultiplierCircuit):
|
||||
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.vertical_break and b_multiplier_index == self.horizontal_break) or (self.horizontal_break == self.N-1 or self.vertical_break == self.N-1):
|
||||
# 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.horizontal_break != self.N-1 and self.vertical_break != self.N-1:
|
||||
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
|
||||
if self.horizontal_break >= self.N or self.vertical_break >= self.N:
|
||||
if self.truncation_cut >= self.N:
|
||||
[self.out.connect(out_id, ConstantWireValue0()) for out_id in range(self.out.N)]
|
||||
else:
|
||||
for grounded_out_index in range(0, break_offsets):
|
||||
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.
|
||||
|
||||
TODO
|
||||
Signed array multiplier represents N-bit multiplier composed of
|
||||
many AND/NAND gates and half/full adders to calculate partial products and
|
||||
gradually sum them.
|
||||
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.
|
||||
|
||||
Downside is its rather big area because it is composed of many logic gates.
|
||||
TODO
|
||||
```
|
||||
The design promises better area and power parameters in exchange for the loss of computation precision.
|
||||
|
||||
```TODO
|
||||
A3B0 A2B0 A1B0 A0B0
|
||||
│ │ │ │ │ │ │ │
|
||||
┌▼─▼─┐ ┌▼─▼┐ ┌▼─▼┐ ┌▼─▼┐
|
||||
@ -208,15 +201,13 @@ class SignedTruncatedMultiplier(MultiplierCircuit):
|
||||
Args:
|
||||
a (Bus): First input bus.
|
||||
b (Bus): Second input bus.
|
||||
horizontal_break (int, optional): Specifies horizontal break used in signed truncated multiplier circuit creation. Defaults to 0.
|
||||
vertical_break (int, optional): Specifies vertical break used in signed truncated multiplier circuit creation. Defaults to 0.
|
||||
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, horizontal_break: int = 0, vertical_break: int = 0, prefix: str = "", name: str = "s_tm", **kwargs):
|
||||
# NOTE: If horizontal/vertical break is specified as 0 the final circuit is a simple array multiplier
|
||||
self.horizontal_break = horizontal_break
|
||||
self.vertical_break = vertical_break
|
||||
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)
|
||||
super().__init__(a=a, b=b, prefix=prefix, name=name, out_N=self.N*2, signed=True, **kwargs)
|
||||
@ -226,10 +217,9 @@ class SignedTruncatedMultiplier(MultiplierCircuit):
|
||||
self.a.bus_extend(N=self.N, prefix=a.prefix)
|
||||
self.b.bus_extend(N=self.N, prefix=b.prefix)
|
||||
|
||||
break_offsets = horizontal_break + vertical_break
|
||||
# Gradual generation of partial products
|
||||
for b_multiplier_index in range(self.horizontal_break, self.N):
|
||||
for a_multiplicand_index in range(self.vertical_break, self.N):
|
||||
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)
|
||||
@ -238,30 +228,26 @@ class SignedTruncatedMultiplier(MultiplierCircuit):
|
||||
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_break and self.vertical_break != self.N-1:
|
||||
if b_multiplier_index == self.horizontal_break + 1:
|
||||
previous_product = self.components[a_multiplicand_index + b_multiplier_index - break_offsets].out
|
||||
else:
|
||||
previous_product = self.get_previous_partial_product(a_index=a_multiplicand_index, b_index=b_multiplier_index, horizontal_break=horizontal_break, vertical_break=vertical_break)
|
||||
|
||||
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.vertical_break:
|
||||
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, obj_adder.get_sum_wire())
|
||||
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.horizontal_break+1:
|
||||
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.vertical_break and b_multiplier_index == self.horizontal_break) or (self.horizontal_break == self.N-1 or self.vertical_break == self.N-1):
|
||||
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
|
||||
@ -271,7 +257,7 @@ class SignedTruncatedMultiplier(MultiplierCircuit):
|
||||
|
||||
self.out.connect(a_multiplicand_index+1, obj_nor.out)
|
||||
|
||||
elif b_multiplier_index == self.N-1 and self.horizontal_break != self.N-1 and self.vertical_break != self.N-1:
|
||||
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:
|
||||
@ -281,8 +267,8 @@ class SignedTruncatedMultiplier(MultiplierCircuit):
|
||||
self.out.connect(self.out.N-1, obj_xor.out)
|
||||
|
||||
# Connecting the output bits generated from ommited cells to ground
|
||||
if self.horizontal_break >= self.N or self.vertical_break >= self.N:
|
||||
if self.truncation_cut >= self.N:
|
||||
[self.out.connect(out_id, ConstantWireValue0()) for out_id in range(self.out.N)]
|
||||
else:
|
||||
for grounded_out_index in range(0, break_offsets):
|
||||
for grounded_out_index in range(0, self.truncation_cut*2):
|
||||
self.out.connect(grounded_out_index, ConstantWireValue0())
|
||||
|
@ -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"))
|
||||
|
@ -31,6 +31,43 @@ from ariths_gen.multi_bit_circuits.multipliers import (
|
||||
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 = type(multiplier)(a=multiplier.a, b=multiplier.b, truncation_cut=multiplier.truncation_cut)
|
||||
elif c == UnsignedBrokenArrayMultiplier:
|
||||
mul = type(multiplier)(a=multiplier.a, b=multiplier.b, horizontal_cut=multiplier.horizontal_cut, vertical_cut=multiplier.vertical_cut)
|
||||
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(multiplier, UnsignedTruncatedMultiplier):
|
||||
# WCE_TM(n,k) = (2^k - 1) * (2^(n+1) - 2^k - 1)
|
||||
expected_WCE = (2 ** multiplier.truncation_cut - 1) * (2 ** (multiplier.a.N+1) - 2 ** multiplier.truncation_cut - 1)
|
||||
elif isinstance(multiplier, 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, multiplier.horizontal_cut)])
|
||||
sum_2 = sum([2**(multiplier.vertical_cut-multiplier.horizontal_cut) - 2**i for i in range(0, multiplier.vertical_cut-multiplier.horizontal_cut)])
|
||||
expected_WCE = (2 ** multiplier.N - 1) * sum_1 + 2 ** multiplier.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 """
|
||||
|
Loading…
x
Reference in New Issue
Block a user