Added broken array multiplier and truncated multiplier implementations. Tried testing them, but seems buggy. Needs further work.

This commit is contained in:
Honza 2022-01-06 06:39:58 +01:00
parent f830029c54
commit 9aa0fb1858
6 changed files with 404 additions and 67 deletions

View File

@ -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:

View File

@ -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
)

View File

@ -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())

View File

@ -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())

View File

@ -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"))

View File

@ -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 """